Skip to content

Commit

Permalink
[ImportVerilog] Add assign and pre/post increment/decrement expressio…
Browse files Browse the repository at this point in the history
…ns (#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>
  • Loading branch information
5 people committed Mar 22, 2024
1 parent 7a01c49 commit 8ec0523
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 4 deletions.
17 changes: 16 additions & 1 deletion include/circt/Dialect/Moore/MooreOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -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
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -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"> {
Expand Down
30 changes: 28 additions & 2 deletions lib/Conversion/ImportVerilog/Expressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -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 {};

Expand All @@ -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
Expand All @@ -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());
Expand Down Expand Up @@ -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");
Expand Down
6 changes: 6 additions & 0 deletions lib/Conversion/ImportVerilog/ImportVerilogInternals.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
90 changes: 89 additions & 1 deletion test/Conversion/ImportVerilog/basic.sv
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down

0 comments on commit 8ec0523

Please sign in to comment.