mirror of https://github.com/llvm/circt.git
[ImportVerilog] Add assign and pre/post increment/decrement expressions (#6859)
Add support for pre and post increment and decrement expressions, like `x++` and `--x`, as well as assign expressions, like `a += 5`. Slang represents these assignments as `Assign(a, Add(LValueRef, 5))` in the AST. The `LValueRef` node contextually refers to the parent assignment's left-hand side. To deal with this, also add a corresponding lvalue stack to the conversion context. Assignments push and pop their lvalues onto and off of this stack. These expressions require a mechanism in the IR to express _when_ a variable is read. To capture this, add a new `moore.read_lvalue` op. It currently looks like an identity operation with a `MemRead` side effect. Further down the road, we may want to introduce a proper reference type for variables, ports, nets, and other things, and have `read_lvalue` and the various assigns operate on that type instead. This sets the foundation for that. Co-authored-by: Hailong Sun <hailong.sun@terapines.com> Co-authored-by: ShiZuoye <albertethon@163.com> Co-authored-by: hunterzju <hunter_ht@zju.edu.cn> Co-authored-by: Anqi Yu <anqi.yu@terapines.com>
This commit is contained in:
parent
7a01c493b6
commit
8ec05233d1
|
@ -144,13 +144,27 @@ def VariableOp : MooreOp<"variable", [
|
|||
See IEEE 1800-2017 § 6.8 "Variable declarations".
|
||||
}];
|
||||
let arguments = (ins StrAttr:$name, Optional<UnpackedType>:$initial);
|
||||
let results = (outs UnpackedType:$result);
|
||||
let results = (outs Res<UnpackedType, "", [MemAlloc]>:$result);
|
||||
let assemblyFormat = [{
|
||||
`` custom<ImplicitSSAName>($name) ($initial^)? attr-dict
|
||||
`:` type($result)
|
||||
}];
|
||||
}
|
||||
|
||||
def ReadLValueOp : MooreOp<"read_lvalue", [SameOperandsAndResultType]> {
|
||||
let summary = "Read the current value of a declaration";
|
||||
let description = [{
|
||||
Samples the current value of a declaration. This is a helper to capture the
|
||||
exact point at which declarations that can be targeted by assignments are
|
||||
read.
|
||||
}];
|
||||
let arguments = (ins Arg<UnpackedType, "", [MemRead]>:$input);
|
||||
let results = (outs UnpackedType:$result);
|
||||
let assemblyFormat = [{
|
||||
$input attr-dict `:` type($result)
|
||||
}];
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Assignments
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -183,6 +197,7 @@ def BlockingAssignOp : AssignOpBase<"blocking_assign"> {
|
|||
|
||||
See IEEE 1800-2017 § 10.4.1 "Blocking procedural assignments".
|
||||
}];
|
||||
let arguments = (ins Arg<AnyType, "", [MemWrite]>:$dst, AnyType:$src);
|
||||
}
|
||||
|
||||
def NonBlockingAssignOp : AssignOpBase<"nonblocking_assign"> {
|
||||
|
|
|
@ -53,6 +53,13 @@ struct ExprVisitor {
|
|||
return {};
|
||||
}
|
||||
|
||||
// Handle references to the left-hand side of a parent assignment.
|
||||
Value visit(const slang::ast::LValueReferenceExpression &expr) {
|
||||
assert(!context.lvalueStack.empty() && "parent assignments push lvalue");
|
||||
auto lvalue = context.lvalueStack.back();
|
||||
return builder.create<moore::ReadLValueOp>(loc, lvalue);
|
||||
}
|
||||
|
||||
// Handle named values, such as references to declared variables.
|
||||
Value visit(const slang::ast::NamedValueExpression &expr) {
|
||||
if (auto value = context.valueSymbols.lookup(&expr.symbol))
|
||||
|
@ -77,7 +84,9 @@ struct ExprVisitor {
|
|||
// Handle blocking and non-blocking assignments.
|
||||
Value visit(const slang::ast::AssignmentExpression &expr) {
|
||||
auto lhs = context.convertExpression(expr.left());
|
||||
context.lvalueStack.push_back(lhs);
|
||||
auto rhs = context.convertExpression(expr.right());
|
||||
context.lvalueStack.pop_back();
|
||||
if (!lhs || !rhs)
|
||||
return {};
|
||||
|
||||
|
@ -94,7 +103,7 @@ struct ExprVisitor {
|
|||
builder.create<moore::NonBlockingAssignOp>(loc, lhs, rhs);
|
||||
else
|
||||
builder.create<moore::BlockingAssignOp>(loc, lhs, rhs);
|
||||
return lhs;
|
||||
return rhs;
|
||||
}
|
||||
|
||||
// Helper function to convert an argument to a simple bit vector type, pass it
|
||||
|
@ -110,6 +119,20 @@ struct ExprVisitor {
|
|||
return result;
|
||||
}
|
||||
|
||||
// Helper function to create pre and post increments and decrements.
|
||||
Value createIncrement(Value arg, bool isInc, bool isPost) {
|
||||
auto preValue = convertToSimpleBitVector(arg);
|
||||
if (!preValue)
|
||||
return {};
|
||||
preValue = builder.create<moore::ReadLValueOp>(loc, preValue);
|
||||
auto one = builder.create<moore::ConstantOp>(loc, preValue.getType(), 1);
|
||||
auto postValue =
|
||||
isInc ? builder.create<moore::AddOp>(loc, preValue, one).getResult()
|
||||
: builder.create<moore::SubOp>(loc, preValue, one).getResult();
|
||||
builder.create<moore::BlockingAssignOp>(loc, arg, postValue);
|
||||
return isPost ? preValue : postValue;
|
||||
}
|
||||
|
||||
// Handle unary operators.
|
||||
Value visit(const slang::ast::UnaryExpression &expr) {
|
||||
auto arg = context.convertExpression(expr.operand());
|
||||
|
@ -155,10 +178,13 @@ struct ExprVisitor {
|
|||
return builder.create<moore::NotOp>(loc, arg);
|
||||
|
||||
case UnaryOperator::Preincrement:
|
||||
return createIncrement(arg, true, false);
|
||||
case UnaryOperator::Predecrement:
|
||||
return createIncrement(arg, false, false);
|
||||
case UnaryOperator::Postincrement:
|
||||
return createIncrement(arg, true, true);
|
||||
case UnaryOperator::Postdecrement:
|
||||
break;
|
||||
return createIncrement(arg, false, true);
|
||||
}
|
||||
|
||||
mlir::emitError(loc, "unsupported unary operator");
|
||||
|
|
|
@ -90,6 +90,12 @@ struct Context {
|
|||
llvm::ScopedHashTable<const slang::ast::ValueSymbol *, Value>;
|
||||
using ValueSymbolScope = ValueSymbols::ScopeTy;
|
||||
ValueSymbols valueSymbols;
|
||||
|
||||
/// A stack of assignment left-hand side values. Each assignment will push its
|
||||
/// lowered left-hand side onto this stack before lowering its right-hand
|
||||
/// side. This allows expressions to resolve the opaque
|
||||
/// `LValueReferenceExpression`s in the AST.
|
||||
SmallVector<Value> lvalueStack;
|
||||
};
|
||||
|
||||
} // namespace ImportVerilog
|
||||
|
|
|
@ -190,7 +190,7 @@ module Statements;
|
|||
x = y;
|
||||
|
||||
// CHECK: moore.blocking_assign %y, %z : !moore.bit
|
||||
// CHECK: moore.blocking_assign %x, %y : !moore.bit
|
||||
// CHECK: moore.blocking_assign %x, %z : !moore.bit
|
||||
x = (y = z);
|
||||
|
||||
// CHECK: moore.nonblocking_assign %x, %y : !moore.bit
|
||||
|
@ -262,6 +262,30 @@ module Expressions;
|
|||
// CHECK: [[TMP:%.+]] = moore.bool_cast %a : !moore.int -> !moore.bit
|
||||
// CHECK: moore.not [[TMP]] : !moore.bit
|
||||
x = !a;
|
||||
// CHECK: [[PRE:%.+]] = moore.read_lvalue %a : !moore.int
|
||||
// CHECK: [[TMP:%.+]] = moore.constant 1 : !moore.int
|
||||
// CHECK: [[POST:%.+]] = moore.add [[PRE]], [[TMP]] : !moore.int
|
||||
// CHECK: moore.blocking_assign %a, [[POST]]
|
||||
// CHECK: moore.blocking_assign %c, [[PRE]]
|
||||
c = a++;
|
||||
// CHECK: [[PRE:%.+]] = moore.read_lvalue %a : !moore.int
|
||||
// CHECK: [[TMP:%.+]] = moore.constant 1 : !moore.int
|
||||
// CHECK: [[POST:%.+]] = moore.sub [[PRE]], [[TMP]] : !moore.int
|
||||
// CHECK: moore.blocking_assign %a, [[POST]]
|
||||
// CHECK: moore.blocking_assign %c, [[PRE]]
|
||||
c = a--;
|
||||
// CHECK: [[PRE:%.+]] = moore.read_lvalue %a : !moore.int
|
||||
// CHECK: [[TMP:%.+]] = moore.constant 1 : !moore.int
|
||||
// CHECK: [[POST:%.+]] = moore.add [[PRE]], [[TMP]] : !moore.int
|
||||
// CHECK: moore.blocking_assign %a, [[POST]]
|
||||
// CHECK: moore.blocking_assign %c, [[POST]]
|
||||
c = ++a;
|
||||
// CHECK: [[PRE:%.+]] = moore.read_lvalue %a : !moore.int
|
||||
// CHECK: [[TMP:%.+]] = moore.constant 1 : !moore.int
|
||||
// CHECK: [[POST:%.+]] = moore.sub [[PRE]], [[TMP]] : !moore.int
|
||||
// CHECK: moore.blocking_assign %a, [[POST]]
|
||||
// CHECK: moore.blocking_assign %c, [[POST]]
|
||||
c = --a;
|
||||
|
||||
//===------------------------------------------------------------------===//
|
||||
// Binary operators
|
||||
|
@ -359,6 +383,70 @@ module Expressions;
|
|||
c = a >>> b;
|
||||
// CHECK: moore.shr %u, %b : !moore.int<unsigned>, !moore.int
|
||||
c = u >>> b;
|
||||
|
||||
//===------------------------------------------------------------------===//
|
||||
// Assign operators
|
||||
|
||||
// CHECK: [[TMP1:%.+]] = moore.read_lvalue %a
|
||||
// CHECK: [[TMP2:%.+]] = moore.add [[TMP1]], %b
|
||||
// CHECK: moore.blocking_assign %a, [[TMP2]]
|
||||
a += b;
|
||||
// CHECK: [[TMP1:%.+]] = moore.read_lvalue %a
|
||||
// CHECK: [[TMP2:%.+]] = moore.sub [[TMP1]], %b
|
||||
// CHECK: moore.blocking_assign %a, [[TMP2]]
|
||||
a -= b;
|
||||
// CHECK: [[TMP1:%.+]] = moore.read_lvalue %a
|
||||
// CHECK: [[TMP2:%.+]] = moore.mul [[TMP1]], %b
|
||||
// CHECK: moore.blocking_assign %a, [[TMP2]]
|
||||
a *= b;
|
||||
// CHECK: [[TMP1:%.+]] = moore.read_lvalue %f
|
||||
// CHECK: [[TMP2:%.+]] = moore.div [[TMP1]], %d
|
||||
// CHECK: moore.blocking_assign %f, [[TMP2]]
|
||||
f /= d;
|
||||
// CHECK: [[TMP1:%.+]] = moore.read_lvalue %f
|
||||
// CHECK: [[TMP2:%.+]] = moore.mod [[TMP1]], %d
|
||||
// CHECK: moore.blocking_assign %f, [[TMP2]]
|
||||
f %= d;
|
||||
// CHECK: [[TMP1:%.+]] = moore.read_lvalue %a
|
||||
// CHECK: [[TMP2:%.+]] = moore.and [[TMP1]], %b
|
||||
// CHECK: moore.blocking_assign %a, [[TMP2]]
|
||||
a &= b;
|
||||
// CHECK: [[TMP1:%.+]] = moore.read_lvalue %a
|
||||
// CHECK: [[TMP2:%.+]] = moore.or [[TMP1]], %b
|
||||
// CHECK: moore.blocking_assign %a, [[TMP2]]
|
||||
a |= b;
|
||||
// CHECK: [[TMP1:%.+]] = moore.read_lvalue %a
|
||||
// CHECK: [[TMP2:%.+]] = moore.xor [[TMP1]], %b
|
||||
// CHECK: moore.blocking_assign %a, [[TMP2]]
|
||||
a ^= b;
|
||||
// CHECK: [[TMP1:%.+]] = moore.read_lvalue %a
|
||||
// CHECK: [[TMP2:%.+]] = moore.shl [[TMP1]], %b
|
||||
// CHECK: moore.blocking_assign %a, [[TMP2]]
|
||||
a <<= b;
|
||||
// CHECK: [[TMP1:%.+]] = moore.read_lvalue %a
|
||||
// CHECK: [[TMP2:%.+]] = moore.shl [[TMP1]], %b
|
||||
// CHECK: moore.blocking_assign %a, [[TMP2]]
|
||||
a <<<= b;
|
||||
// CHECK: [[TMP1:%.+]] = moore.read_lvalue %a
|
||||
// CHECK: [[TMP2:%.+]] = moore.shr [[TMP1]], %b
|
||||
// CHECK: moore.blocking_assign %a, [[TMP2]]
|
||||
a >>= b;
|
||||
// CHECK: [[TMP1:%.+]] = moore.read_lvalue %a
|
||||
// CHECK: [[TMP2:%.+]] = moore.ashr [[TMP1]], %b
|
||||
// CHECK: moore.blocking_assign %a, [[TMP2]]
|
||||
a >>>= b;
|
||||
|
||||
// CHECK: [[A_ADD:%.+]] = moore.read_lvalue %a
|
||||
// CHECK: [[A_MUL:%.+]] = moore.read_lvalue %a
|
||||
// CHECK: [[A_DEC:%.+]] = moore.read_lvalue %a
|
||||
// CHECK: [[TMP1:%.+]] = moore.constant 1
|
||||
// CHECK: [[TMP2:%.+]] = moore.sub [[A_DEC]], [[TMP1]]
|
||||
// CHECK: moore.blocking_assign %a, [[TMP2]]
|
||||
// CHECK: [[TMP1:%.+]] = moore.mul [[A_MUL]], [[A_DEC]]
|
||||
// CHECK: moore.blocking_assign %a, [[TMP1]]
|
||||
// CHECK: [[TMP2:%.+]] = moore.add [[A_ADD]], [[TMP1]]
|
||||
// CHECK: moore.blocking_assign %a, [[TMP2]]
|
||||
a += (a *= a--);
|
||||
end
|
||||
endmodule
|
||||
|
||||
|
|
Loading…
Reference in New Issue