[ImportVerilog] Add missing conversions, various fixes

Add missing conversions for cases where the Slang AST has expressions
operating directly on packed structs/arrays, but the Moore IR expects
a conversion to a simple bit vector type.

Also clean up a few things and remove an invalid `struct_inject`
conversion.
This commit is contained in:
Fabian Schuiki 2024-08-02 11:33:44 -07:00
parent 20cb546d18
commit 272af6c158
No known key found for this signature in database
GPG Key ID: C42F5825FC5275E6
4 changed files with 69 additions and 50 deletions

View File

@ -28,10 +28,21 @@ struct RvalueExprVisitor {
Value convertToSimpleBitVector(Value value) {
if (!value)
return {};
if (isa<moore::IntType>(value.getType()) ||
isa<moore::IntType>(
dyn_cast<moore::RefType>(value.getType()).getNestedType()))
if (isa<moore::IntType>(value.getType()))
return value;
// Some operations in Slang's AST, for example bitwise or `|`, don't cast
// packed struct/array operands to simple bit vectors but directly operate
// on the struct/array. Since the corresponding IR ops operate only on
// simple bit vectors, insert a conversion in this case.
if (auto packed = dyn_cast<moore::PackedType>(value.getType())) {
if (auto bits = packed.getBitSize()) {
auto sbvType =
moore::IntType::get(value.getContext(), *bits, packed.getDomain());
return builder.create<moore::ConversionOp>(loc, sbvType, value);
}
}
mlir::emitError(loc, "expression of type ")
<< value.getType() << " cannot be cast to a simple bit vector";
return {};
@ -55,16 +66,14 @@ struct RvalueExprVisitor {
Value visit(const slang::ast::LValueReferenceExpression &expr) {
assert(!context.lvalueStack.empty() && "parent assignments push lvalue");
auto lvalue = context.lvalueStack.back();
return builder.create<moore::ReadOp>(
loc, cast<moore::RefType>(lvalue.getType()).getNestedType(), lvalue);
return builder.create<moore::ReadOp>(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)) {
if (auto refType = dyn_cast<moore::RefType>(value.getType()))
value =
builder.create<moore::ReadOp>(loc, refType.getNestedType(), value);
if (isa<moore::RefType>(value.getType()))
value = builder.create<moore::ReadOp>(loc, value);
return value;
}
@ -92,19 +101,20 @@ struct RvalueExprVisitor {
auto type = context.convertType(*expr.type);
if (!type)
return {};
auto operand = context.convertRvalueExpression(expr.operand());
if (!operand)
return {};
return builder.create<moore::ConversionOp>(loc, type, operand);
return context.convertRvalueExpression(expr.operand(), type);
}
// Handle blocking and non-blocking assignments.
Value visit(const slang::ast::AssignmentExpression &expr) {
auto lhs = context.convertLvalueExpression(expr.left());
if (!lhs)
return {};
context.lvalueStack.push_back(lhs);
auto rhs = context.convertRvalueExpression(expr.right());
auto rhs = context.convertRvalueExpression(
expr.right(), cast<moore::RefType>(lhs.getType()).getNestedType());
context.lvalueStack.pop_back();
if (!lhs || !rhs)
if (!rhs)
return {};
if (expr.timingControl) {
@ -135,12 +145,7 @@ struct RvalueExprVisitor {
// 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::ReadOp>(
loc, cast<moore::RefType>(preValue.getType()).getNestedType(),
preValue);
auto preValue = builder.create<moore::ReadOp>(loc, arg);
auto one = builder.create<moore::ConstantOp>(
loc, cast<moore::IntType>(preValue.getType()), 1);
auto postValue =
@ -220,8 +225,10 @@ struct RvalueExprVisitor {
template <class ConcreteOp>
Value createBinary(Value lhs, Value rhs) {
lhs = convertToSimpleBitVector(lhs);
if (!lhs)
return {};
rhs = convertToSimpleBitVector(rhs);
if (!lhs || !rhs)
if (!rhs)
return {};
return builder.create<ConcreteOp>(loc, lhs, rhs);
}
@ -229,8 +236,10 @@ struct RvalueExprVisitor {
// Handle binary operators.
Value visit(const slang::ast::BinaryExpression &expr) {
auto lhs = context.convertRvalueExpression(expr.left());
if (!lhs)
return {};
auto rhs = context.convertRvalueExpression(expr.right());
if (!lhs || !rhs)
if (!rhs)
return {};
using slang::ast::BinaryOperator;
@ -904,9 +913,13 @@ struct LvalueExprVisitor {
};
} // namespace
Value Context::convertRvalueExpression(const slang::ast::Expression &expr) {
Value Context::convertRvalueExpression(const slang::ast::Expression &expr,
Type requiredType) {
auto loc = convertLocation(expr.sourceRange);
return expr.visit(RvalueExprVisitor(*this, loc));
auto value = expr.visit(RvalueExprVisitor(*this, loc));
if (value && requiredType && value.getType() != requiredType)
value = builder.create<moore::ConversionOp>(loc, requiredType, value);
return value;
}
Value Context::convertLvalueExpression(const slang::ast::Expression &expr) {

View File

@ -88,7 +88,8 @@ struct Context {
LogicalResult convertStatement(const slang::ast::Statement &stmt);
// Convert an expression AST node to MLIR ops.
Value convertRvalueExpression(const slang::ast::Expression &expr);
Value convertRvalueExpression(const slang::ast::Expression &expr,
Type requiredType = {});
Value convertLvalueExpression(const slang::ast::Expression &expr);
// Convert a slang timing control into an MLIR timing control.

View File

@ -364,7 +364,7 @@ struct ModuleVisitor : public BaseVisitor {
Value initial;
if (const auto *init = varNode.getInitializer()) {
initial = context.convertRvalueExpression(*init);
initial = context.convertRvalueExpression(*init, loweredType);
if (!initial)
return failure();
}
@ -384,7 +384,8 @@ struct ModuleVisitor : public BaseVisitor {
Value assignment;
if (netNode.getInitializer()) {
assignment = context.convertRvalueExpression(*netNode.getInitializer());
assignment = context.convertRvalueExpression(*netNode.getInitializer(),
loweredType);
if (!assignment)
return failure();
}
@ -413,21 +414,14 @@ struct ModuleVisitor : public BaseVisitor {
const auto &expr =
assignNode.getAssignment().as<slang::ast::AssignmentExpression>();
auto lhs = context.convertLvalueExpression(expr.left());
auto rhs = context.convertRvalueExpression(expr.right());
if (!lhs || !rhs)
if (!lhs)
return failure();
if (auto refOp = lhs.getDefiningOp<moore::StructExtractRefOp>()) {
auto input = refOp.getInput();
if (isa<moore::SVModuleOp>(input.getDefiningOp()->getParentOp())) {
builder.create<moore::StructInjectOp>(loc, input.getType(), input,
refOp.getFieldNameAttr(), rhs);
refOp->erase();
return success();
}
}
auto rhs = context.convertRvalueExpression(
expr.right(), cast<moore::RefType>(lhs.getType()).getNestedType());
if (!rhs)
return failure();
builder.create<moore::ContinuousAssignOp>(loc, lhs, rhs);
return success();

View File

@ -214,6 +214,21 @@ module Basic;
string s1;
assign s1 = "Hello World";
typedef struct packed { bit x; bit y; } MyStruct;
// CHECK: [[VAR_S2:%.+]] = moore.variable : <struct<{x: i1, y: i1}>>
MyStruct s2;
// CHECK: [[TMP1:%.+]] = moore.read [[VAR_S2]]
// CHECK: [[TMP2:%.+]] = moore.conversion [[TMP1]] : !moore.struct<{x: i1, y: i1}> -> !moore.i2
// CHECK: [[TMP3:%.+]] = moore.not [[TMP2]] : i2
// CHECK: [[TMP4:%.+]] = moore.conversion [[TMP3]] : !moore.i2 -> !moore.struct<{x: i1, y: i1}>
// CHECK: moore.assign [[VAR_S2]], [[TMP4]]
assign s2 = ~s2;
// CHECK: [[TMP1:%.+]] = moore.read [[VAR_S2]]
// CHECK: [[TMP2:%.+]] = moore.conversion [[TMP1]] : !moore.struct<{x: i1, y: i1}> -> !moore.i2
// CHECK: [[TMP3:%.+]] = moore.not [[TMP2]] : i2
// CHECK: [[TMP4:%.+]] = moore.conversion [[TMP3]] : !moore.i2 -> !moore.struct<{x: i1, y: i1}>
// CHECK: [[VAR_S3:%.+]] = moore.variable [[TMP4]] : <struct<{x: i1, y: i1}>>
MyStruct s3 = ~s2;
endmodule
// CHECK-LABEL: moore.module @Statements
@ -594,8 +609,7 @@ module Expressions;
c = -a;
// CHECK: [[TMP1:%.+]] = moore.read %v
// CHECK: [[TMP2:%.+]] = moore.conversion [[TMP1]] : !moore.array<2 x i4> -> !moore.i32
// CHECK: [[TMP3:%.+]] = moore.neg [[TMP2]] : i32
// CHECK: [[TMP4:%.+]] = moore.conversion [[TMP3]] : !moore.i32 -> !moore.i32
// CHECK: moore.neg [[TMP2]] : i32
c = -v;
// CHECK: [[TMP1:%.+]] = moore.read %a
// CHECK: moore.not [[TMP1]] : i32
@ -665,10 +679,9 @@ module Expressions;
// CHECK: moore.add [[TMP1]], [[TMP2]] : i32
c = a + b;
// CHECK: [[TMP1:%.+]] = moore.read %a
// CHECK: [[TMP2:%.+]] = moore.conversion [[TMP1]] : !moore.i32 -> !moore.i32
// CHECK: [[TMP3:%.+]] = moore.read %v
// CHECK: [[TMP4:%.+]] = moore.conversion [[TMP3]] : !moore.array<2 x i4> -> !moore.i32
// CHECK: moore.add [[TMP2]], [[TMP4]] : i32
// CHECK: [[TMP2:%.+]] = moore.read %v
// CHECK: [[TMP3:%.+]] = moore.conversion [[TMP2]] : !moore.array<2 x i4> -> !moore.i32
// CHECK: moore.add [[TMP1]], [[TMP3]] : i32
c = a + v;
// CHECK: [[TMP1:%.+]] = moore.read %a
// CHECK: [[TMP2:%.+]] = moore.read %b
@ -1080,11 +1093,9 @@ module Conversion;
// Sign conversion.
// CHECK: [[TMP1:%.+]] = moore.read %b
// CHECK: [[TMP2:%.+]] = moore.conversion [[TMP1]] : !moore.i32 -> !moore.i32
// CHECK: %d1 = moore.variable [[TMP2]]
// CHECK: [[TMP3:%.+]] = moore.read %b
// CHECK: [[TMP4:%.+]] = moore.conversion [[TMP3]] : !moore.i32 -> !moore.i32
// CHECK: %d2 = moore.variable [[TMP4]]
// CHECK: %d1 = moore.variable [[TMP1]]
// CHECK: [[TMP2:%.+]] = moore.read %b
// CHECK: %d2 = moore.variable [[TMP2]]
bit signed [31:0] d1 = signed'(b);
bit [31:0] d2 = unsigned'(b);