[ImportVerilog] Add rvalue assignment pattern support (#7428)

Add support for assignment patterns like `'{42, 9001}` in rvalue
position. These are roughly SystemVerilog's equivalent of
`struct_create` and `array_create`. This commit also adds an
`array_create` op to support assignment patterns for arrays.
This commit is contained in:
Fabian Schuiki 2024-08-04 11:01:00 -07:00 committed by GitHub
parent 17e85f15c7
commit e95e92dd19
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 186 additions and 0 deletions

View File

@ -1037,6 +1037,20 @@ def DynExtractRefOp : MooreOp<"dyn_extract_ref", [Pure]> {
}];
}
//===----------------------------------------------------------------------===//
// Array Manipulation
//===----------------------------------------------------------------------===//
def ArrayCreateOp : MooreOp<"array_create", [Pure, SameTypeOperands]> {
let summary = "Create an array value from individual elements";
let arguments = (ins Variadic<UnpackedType>:$elements);
let results = (outs AnyStaticArrayType:$result);
let assemblyFormat = [{
$elements attr-dict `:` type($elements) `->` type($result)
}];
let hasVerifier = 1;
}
//===----------------------------------------------------------------------===//
// Struct Manipulation
//===----------------------------------------------------------------------===//

View File

@ -406,6 +406,12 @@ def BitType : MooreType<CPred<[{
}];
}
/// A packed or unpacked array type with a fixed size.
def AnyStaticArrayType : MooreType<
Or<[ArrayType.predicate, UnpackedArrayType.predicate]>,
"packed or unpacked static array type",
"moore::UnpackedType">;
/// A packed or unpacked struct type.
def AnyStructType : MooreType<
Or<[StructType.predicate, UnpackedStructType.predicate]>,

View File

@ -727,6 +727,70 @@ struct RvalueExprVisitor {
return builder.create<moore::StringConstantOp>(loc, type, expr.getValue());
}
/// Handle assignment patterns.
Value visitAssignmentPattern(
const slang::ast::AssignmentPatternExpressionBase &expr,
unsigned replCount = 1) {
auto type = context.convertType(*expr.type);
// Convert the individual elements first.
auto elementCount = expr.elements().size();
SmallVector<Value> elements;
elements.reserve(replCount * elementCount);
for (auto elementExpr : expr.elements()) {
auto value = context.convertRvalueExpression(*elementExpr);
if (!value)
return {};
elements.push_back(value);
}
for (unsigned replIdx = 1; replIdx < replCount; ++replIdx)
for (unsigned elementIdx = 0; elementIdx < elementCount; ++elementIdx)
elements.push_back(elements[elementIdx]);
// Handle packed structs.
if (auto structType = dyn_cast<moore::StructType>(type)) {
assert(structType.getMembers().size() == elements.size());
return builder.create<moore::StructCreateOp>(loc, structType, elements);
}
// Handle unpacked structs.
if (auto structType = dyn_cast<moore::UnpackedStructType>(type)) {
assert(structType.getMembers().size() == elements.size());
return builder.create<moore::StructCreateOp>(loc, structType, elements);
}
// Handle packed arrays.
if (auto arrayType = dyn_cast<moore::ArrayType>(type)) {
assert(arrayType.getSize() == elements.size());
return builder.create<moore::ArrayCreateOp>(loc, arrayType, elements);
}
// Handle unpacked arrays.
if (auto arrayType = dyn_cast<moore::UnpackedArrayType>(type)) {
assert(arrayType.getSize() == elements.size());
return builder.create<moore::ArrayCreateOp>(loc, arrayType, elements);
}
mlir::emitError(loc) << "unsupported assignment pattern with type " << type;
return {};
}
Value visit(const slang::ast::SimpleAssignmentPatternExpression &expr) {
return visitAssignmentPattern(expr);
}
Value visit(const slang::ast::StructuredAssignmentPatternExpression &expr) {
return visitAssignmentPattern(expr);
}
Value visit(const slang::ast::ReplicatedAssignmentPatternExpression &expr) {
slang::ast::EvalContext evalContext(context.compilation,
slang::ast::EvalFlags::CacheResults);
auto count = expr.count().eval(evalContext).integer().as<unsigned>();
assert(count && "Slang guarantees constant non-zero replication count");
return visitAssignmentPattern(expr, *count);
}
/// Emit an error for all other expressions.
template <typename T>
Value visit(T &&node) {

View File

@ -512,6 +512,39 @@ LogicalResult ConcatRefOp::inferReturnTypes(
return success();
}
//===----------------------------------------------------------------------===//
// ArrayCreateOp
//===----------------------------------------------------------------------===//
static std::pair<unsigned, UnpackedType> getArrayElements(Type type) {
if (auto arrayType = dyn_cast<ArrayType>(type))
return {arrayType.getSize(), arrayType.getElementType()};
if (auto arrayType = dyn_cast<UnpackedArrayType>(type))
return {arrayType.getSize(), arrayType.getElementType()};
assert(0 && "expected ArrayType or UnpackedArrayType");
return {};
}
LogicalResult ArrayCreateOp::verify() {
auto [size, elementType] = getArrayElements(getType());
// Check that the number of operands matches the array size.
if (getElements().size() != size)
return emitOpError() << "has " << getElements().size()
<< " operands, but result type requires " << size;
// Check that the operand types match the array element type. We only need to
// check one of the operands, since the `SameTypeOperands` trait ensures all
// operands have the same type.
if (size > 0) {
auto value = getElements()[0];
if (value.getType() != elementType)
return emitOpError() << "operands have type " << value.getType()
<< ", but array requires " << elementType;
}
return success();
}
//===----------------------------------------------------------------------===//
// StructCreateOp
//===----------------------------------------------------------------------===//

View File

@ -493,6 +493,10 @@ module Expressions;
struct packed {
int a, b;
} struct0;
// CHECK: %ustruct0 = moore.variable : <ustruct<{a: i32, b: i32}>>
struct {
int a, b;
} ustruct0;
// CHECK: %struct1 = moore.variable : <struct<{c: struct<{a: i32, b: i32}>, d: struct<{a: i32, b: i32}>}>>
struct packed {
struct packed {
@ -512,6 +516,10 @@ module Expressions;
// CHECK: %r1 = moore.variable : <real>
// CHECK: %r2 = moore.variable : <real>
real r1,r2;
// CHECK: %arrayInt = moore.variable : <array<2 x i32>>
bit [1:0][31:0] arrayInt;
// CHECK: %uarrayInt = moore.variable : <uarray<2 x i32>>
bit [31:0] uarrayInt [2];
initial begin
// CHECK: moore.constant 0 : i32
@ -1058,6 +1066,64 @@ module Expressions;
// CHECK: [[TMP2:%.+]] = moore.struct_extract [[TMP1]], "b" : struct<{a: i32, b: i32}> -> i32
// CHECK: moore.blocking_assign %b, [[TMP2]]
b = struct0.b;
//===------------------------------------------------------------------===//
// Assignment Patterns
// CHECK: [[TMP0:%.+]] = moore.constant 17
// CHECK: [[TMP1:%.+]] = moore.constant 17
// CHECK: moore.struct_create [[TMP0]], [[TMP1]] : !moore.i32, !moore.i32 -> struct<{a: i32, b: i32}>
struct0 = '{default: 17};
// CHECK: [[TMP0:%.+]] = moore.constant 1337
// CHECK: [[TMP1:%.+]] = moore.constant 1337
// CHECK: moore.struct_create [[TMP0]], [[TMP1]] : !moore.i32, !moore.i32 -> struct<{a: i32, b: i32}>
struct0 = '{int: 1337};
// CHECK: [[TMP0:%.+]] = moore.constant 420
// CHECK: moore.struct_create [[TMP0]], [[TMP0]] : !moore.i32, !moore.i32 -> struct<{a: i32, b: i32}>
struct0 = '{2{420}};
// CHECK: [[TMP0:%.+]] = moore.constant 42
// CHECK: [[TMP1:%.+]] = moore.constant 9001
// CHECK: moore.struct_create [[TMP0]], [[TMP1]] : !moore.i32, !moore.i32 -> struct<{a: i32, b: i32}>
struct0 = '{a: 42, b: 9001};
// CHECK: [[TMP0:%.+]] = moore.constant 43
// CHECK: [[TMP1:%.+]] = moore.constant 9002
// CHECK: moore.struct_create [[TMP0]], [[TMP1]] : !moore.i32, !moore.i32 -> struct<{a: i32, b: i32}>
struct0 = '{43, 9002};
// CHECK: [[TMP0:%.+]] = moore.constant 44
// CHECK: [[TMP1:%.+]] = moore.constant 9003
// CHECK: moore.struct_create [[TMP0]], [[TMP1]] : !moore.i32, !moore.i32 -> ustruct<{a: i32, b: i32}>
ustruct0 = '{44, 9003};
// CHECK: [[TMP0:%.+]] = moore.constant 1
// CHECK: [[TMP1:%.+]] = moore.constant 2
// CHECK: [[TMP2:%.+]] = moore.struct_create [[TMP0]], [[TMP1]] : !moore.i32, !moore.i32 -> struct<{a: i32, b: i32}>
// CHECK: [[TMP0:%.+]] = moore.constant 3
// CHECK: [[TMP1:%.+]] = moore.constant 4
// CHECK: [[TMP3:%.+]] = moore.struct_create [[TMP0]], [[TMP1]] : !moore.i32, !moore.i32 -> struct<{a: i32, b: i32}>
// CHECK: moore.struct_create [[TMP2]], [[TMP3]] : !moore.struct<{a: i32, b: i32}>, !moore.struct<{a: i32, b: i32}> -> struct<{c: struct<{a: i32, b: i32}>, d: struct<{a: i32, b: i32}>}>
struct1 = '{c: '{a: 1, b: 2}, d: '{a: 3, b: 4}};
// CHECK: [[TMP0:%.+]] = moore.constant 42
// CHECK: [[TMP1:%.+]] = moore.constant 9001
// CHECK: moore.array_create [[TMP0]], [[TMP1]] : !moore.i32, !moore.i32 -> array<2 x i32>
arrayInt = '{42, 9001};
// CHECK: [[TMP0:%.+]] = moore.constant 43
// CHECK: [[TMP1:%.+]] = moore.constant 9002
// CHECK: moore.array_create [[TMP0]], [[TMP1]] : !moore.i32, !moore.i32 -> uarray<2 x i32>
uarrayInt = '{43, 9002};
// CHECK: [[TMP0:%.+]] = moore.constant 1
// CHECK: [[TMP1:%.+]] = moore.constant 2
// CHECK: [[TMP2:%.+]] = moore.constant 3
// CHECK: [[TMP3:%.+]] = moore.array_create [[TMP0]], [[TMP1]], [[TMP2]], [[TMP0]], [[TMP1]], [[TMP2]] : !moore.i4, !moore.i4, !moore.i4, !moore.i4, !moore.i4, !moore.i4 -> uarray<6 x i4>
// CHECK: moore.array_create [[TMP3]], [[TMP3]], [[TMP3]] : !moore.uarray<6 x i4>, !moore.uarray<6 x i4>, !moore.uarray<6 x i4> -> uarray<3 x uarray<6 x i4>>
arr = '{3{'{2{4'd1, 4'd2, 4'd3}}}};
//===------------------------------------------------------------------===//
// Builtin Functions

View File

@ -275,6 +275,9 @@ moore.module @Expressions(
moore.yield %b : i32
}
// CHECK: moore.array_create [[A]], [[B]] : !moore.i32, !moore.i32 -> array<2 x i32>
moore.array_create %a, %b : !moore.i32, !moore.i32 -> array<2 x i32>
// CHECK: moore.struct_create [[A]], [[B]] : !moore.i32, !moore.i32 -> struct<{a: i32, b: i32}>
moore.struct_create %a, %b : !moore.i32, !moore.i32 -> struct<{a: i32, b: i32}>
// CHECK: moore.struct_extract [[STRUCT1]], "a" : struct<{a: i32, b: i32}> -> i32