[ImportVerilog][Moore] Support union type (#7084)

* [ImportVerilog]Support union types.

* [ImportVerilog]Rename structMember with structLikeMember

* [ImportVerilog] check the type of new Expressions.

* [ImportVerilog]Add value check for new Expressions.

* clang-format modify recommand

* [importVerilog]Remove useless inlcludes

* [ImportVerilog]Add union size and domain

* [ImportVerilog]Give more specefic errors
This commit is contained in:
mingzheTerapines 2024-05-29 13:23:20 +08:00 committed by GitHub
parent 55610921c2
commit 00edb48ed5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 242 additions and 43 deletions

View File

@ -790,7 +790,7 @@ def ExtractOp : MooreOp<"extract"> {
$input `from` $lowBit attr-dict `:` $input `from` $lowBit attr-dict `:`
type($input) `,` type($lowBit) `->` type($result) type($input) `,` type($lowBit) `->` type($result)
}]; }];
} }
def StructCreateOp : MooreOp<"struct_create", [SameOperandsAndResultType]> { def StructCreateOp : MooreOp<"struct_create", [SameOperandsAndResultType]> {
let summary = "Struct Create operation"; let summary = "Struct Create operation";
@ -817,11 +817,11 @@ def StructCreateOp : MooreOp<"struct_create", [SameOperandsAndResultType]> {
``` ```
See IEEE 1800-2017 § 7.2. "Structures". See IEEE 1800-2017 § 7.2. "Structures".
}]; }];
let arguments = (ins UnpackedType:$structName); let arguments = (ins UnpackedType:$input);
let results = (outs UnpackedType:$result); let results = (outs UnpackedType:$result);
let hasCustomAssemblyFormat = 1; let hasCustomAssemblyFormat = 1;
let assemblyFormat = [{ let assemblyFormat = [{
$structName attr-dict `:` type($structName) $input attr-dict `:` type($input)
}]; }];
} }
@ -853,11 +853,12 @@ def StructExtractOp : MooreOp<"struct_extract"> {
``` ```
See IEEE 1800-2017 § 7.2.1 "Assigning to structures". See IEEE 1800-2017 § 7.2.1 "Assigning to structures".
}]; }];
let arguments = (ins StrAttr:$memberName, UnpackedType:$structName); let arguments = (ins StrAttr:$memberName, UnpackedType:$input);
let results = (outs UnpackedType:$result); let results = (outs UnpackedType:$result);
let assemblyFormat = [{ let assemblyFormat = [{
$structName `,` $memberName attr-dict `:` $input `,` $memberName attr-dict `:`
type($structName) `->` type($result) type($input) `->`
type($result)
}]; }];
} }
@ -894,4 +895,67 @@ def StructInjectOp : MooreOp<"struct_inject"> {
}]; }];
} }
def UnionCreateOp : MooreOp<"union_create"> {
let summary = "Union Create operation";
let description = [{
A union is a data type that represents a single piece
of storage that can be accessed using one of
the named member data types. Only one of the
data types in the union can be used at a time.
By default, a union is unpacked, meaning there
is no required representation for how members
of the union are stored. Dynamic types and chandle
types can only be used in tagged unions.
See IEEE 1800-2017 § 7.3 "Unions"
Example:
```
typedef union { int i; shortreal f; } num; // named union type
num n;
n.f = 0.0; // set n in floating point format
typedef struct {
bit isfloat;
union { int i; shortreal f; } n; // anonymous union type
} tagged_st; // named structure
```
See IEEE 1800-2017 § 7.3 "Unions"
}];
let arguments = (ins StrAttr:$unionName, UnpackedType:$input);
let results = (outs UnpackedType:$result);
let assemblyFormat = [{
$unionName `,` $input attr-dict `:`
type($input) `->` type($result)
}];
}
def UnionExtractOp : MooreOp<"union_extract"> {
let summary = "Union Extract operation";
let description = [{
With packed unions, writing one member and reading another is
independent of the byte ordering of the machine,
unlike an unpacked union of unpacked structures,
which are C-compatible and have members in ascending address order.
See IEEE 1800-2017 § 7.3.1 "Packed unions"
Example:
```
typedef union packed { // default unsigned
s_atmcell acell;
bit [423:0] bit_slice;
bit [52:0][7:0] byte_slice;
} u_atmcell;
u_atmcell u1;
byte b; bit [3:0] nib;
b = u1.bit_slice[415:408]; // same as b = u1.byte_slice[51];
nib = u1.bit_slice [423:420];
```
See IEEE 1800-2017 § 7.3.1 "Packed unions"
}];
let arguments = (ins StrAttr:$memberName, UnpackedType:$input);
let results = (outs UnpackedType:$result);
let assemblyFormat = [{
$input `,` $memberName attr-dict `:`
type($input) `->` type($result)
}];
}
#endif // CIRCT_DIALECT_MOORE_MOOREOPS #endif // CIRCT_DIALECT_MOORE_MOOREOPS

View File

@ -35,9 +35,11 @@ class QueueType;
class RealType; class RealType;
class StringType; class StringType;
class StructType; class StructType;
class UnionType;
class UnpackedType; class UnpackedType;
class UnpackedArrayType; class UnpackedArrayType;
class UnpackedStructType; class UnpackedStructType;
class UnpackedUnionType;
class VoidType; class VoidType;
/// The number of values each bit of a type can assume. /// The number of values each bit of a type can assume.
@ -81,7 +83,7 @@ public:
static bool classof(Type type) { static bool classof(Type type) {
return llvm::isa<PackedType, StringType, ChandleType, EventType, RealType, return llvm::isa<PackedType, StringType, ChandleType, EventType, RealType,
UnpackedArrayType, OpenUnpackedArrayType, AssocArrayType, UnpackedArrayType, OpenUnpackedArrayType, AssocArrayType,
QueueType, UnpackedStructType>(type); QueueType, UnpackedStructType, UnpackedUnionType>(type);
} }
/// Get the value domain of this type. /// Get the value domain of this type.
@ -130,8 +132,8 @@ protected:
class PackedType : public UnpackedType { class PackedType : public UnpackedType {
public: public:
static bool classof(Type type) { static bool classof(Type type) {
return llvm::isa<VoidType, IntType, ArrayType, OpenArrayType, StructType>( return llvm::isa<VoidType, IntType, ArrayType, OpenArrayType, StructType,
type); UnionType>(type);
} }
/// Get the value domain of this type. /// Get the value domain of this type.
@ -151,19 +153,19 @@ protected:
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
/// A member of a struct. /// A member of a struct.
struct StructMember { struct StructLikeMember {
/// The name of this member. /// The name of this member.
StringAttr name; StringAttr name;
/// The type of this member. /// The type of this member.
UnpackedType type; UnpackedType type;
bool operator==(const StructMember &other) const { bool operator==(const StructLikeMember &other) const {
return name == other.name && type == other.type; return name == other.name && type == other.type;
} }
}; };
// NOLINTNEXTLINE(readability-identifier-naming) // NOLINTNEXTLINE(readability-identifier-naming)
inline llvm::hash_code hash_value(const StructMember &x) { inline llvm::hash_code hash_value(const StructLikeMember &x) {
return llvm::hash_combine(x.name, x.type); return llvm::hash_combine(x.name, x.type);
} }

View File

@ -280,7 +280,7 @@ def QueueType : MooreTypeDef<"Queue", [], "moore::UnpackedType"> {
class StructLikeType< class StructLikeType<
string name, list<Trait> traits = [], string baseCppClass = "::mlir::Type" string name, list<Trait> traits = [], string baseCppClass = "::mlir::Type"
> : MooreTypeDef<name, traits, baseCppClass> { > : MooreTypeDef<name, traits, baseCppClass> {
let parameters = (ins ArrayRefParameter<"StructMember">:$members); let parameters = (ins ArrayRefParameter<"StructLikeMember">:$members);
let assemblyFormat = [{ let assemblyFormat = [{
`<` custom<Members>($members) `>` `<` custom<Members>($members) `>`
}]; }];
@ -305,6 +305,25 @@ def UnpackedStructType : StructLikeType<
}]; }];
} }
def UnionType : StructLikeType<"Union", [], "moore::PackedType"> {
let mnemonic = "union";
let summary = "a packed union type";
let description = [{
A packed union. All members are guaranteed to be packed as well.
}];
let genVerifyDecl = 1;
}
def UnpackedUnionType : StructLikeType<
"UnpackedUnion", [], "moore::UnpackedType"
> {
let mnemonic = "uunion";
let summary = "an unpacked union type";
let description = [{
An unpacked union.
}];
}
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// Constraints // Constraints

View File

@ -429,10 +429,20 @@ struct ExprVisitor {
} }
Value visit(const slang::ast::MemberAccessExpression &expr) { Value visit(const slang::ast::MemberAccessExpression &expr) {
return builder.create<moore::StructExtractOp>( auto type = context.convertType(*expr.type);
loc, context.convertType(*expr.type), auto valueType = expr.value().type;
builder.getStringAttr(expr.member.name), auto value = context.convertExpression(expr.value());
context.convertExpression(expr.value())); if (!type || !value)
return {};
if (valueType->isStruct()) {
return builder.create<moore::StructExtractOp>(
loc, type, builder.getStringAttr(expr.member.name), value);
}
if (valueType->isPackedUnion() || valueType->isUnpackedUnion()) {
return builder.create<moore::UnionExtractOp>(
loc, type, builder.getStringAttr(expr.member.name), value);
}
llvm_unreachable("unsupported symbol kind");
} }
// Handle set membership operator. // Handle set membership operator.

View File

@ -101,8 +101,9 @@ struct TypeVisitor {
} }
// Collect the members in a struct or union. // Collect the members in a struct or union.
LogicalResult collectMembers(const slang::ast::Scope &structType, LogicalResult
SmallVectorImpl<moore::StructMember> &members) { collectMembers(const slang::ast::Scope &structType,
SmallVectorImpl<moore::StructLikeMember> &members) {
for (auto &field : structType.membersOfType<slang::ast::FieldSymbol>()) { for (auto &field : structType.membersOfType<slang::ast::FieldSymbol>()) {
auto name = StringAttr::get(context.getContext(), field.name); auto name = StringAttr::get(context.getContext(), field.name);
auto innerType = context.convertType(*field.getDeclaredType()); auto innerType = context.convertType(*field.getDeclaredType());
@ -115,19 +116,33 @@ struct TypeVisitor {
// Handle packed and unpacked structs. // Handle packed and unpacked structs.
Type visit(const slang::ast::PackedStructType &type) { Type visit(const slang::ast::PackedStructType &type) {
SmallVector<moore::StructMember> members; SmallVector<moore::StructLikeMember> members;
if (failed(collectMembers(type, members))) if (failed(collectMembers(type, members)))
return {}; return {};
return moore::StructType::get(context.getContext(), members); return moore::StructType::get(context.getContext(), members);
} }
Type visit(const slang::ast::UnpackedStructType &type) { Type visit(const slang::ast::UnpackedStructType &type) {
SmallVector<moore::StructMember> members; SmallVector<moore::StructLikeMember> members;
if (failed(collectMembers(type, members))) if (failed(collectMembers(type, members)))
return {}; return {};
return moore::UnpackedStructType::get(context.getContext(), members); return moore::UnpackedStructType::get(context.getContext(), members);
} }
Type visit(const slang::ast::PackedUnionType &type) {
SmallVector<moore::StructLikeMember> members;
if (failed(collectMembers(type, members)))
return {};
return moore::UnionType::get(context.getContext(), members);
}
Type visit(const slang::ast::UnpackedUnionType &type) {
SmallVector<moore::StructLikeMember> members;
if (failed(collectMembers(type, members)))
return {};
return moore::UnpackedUnionType::get(context.getContext(), members);
}
/// Emit an error for all other types. /// Emit an error for all other types.
template <typename T> template <typename T>
Type visit(T &&node) { Type visit(T &&node) {

View File

@ -22,8 +22,9 @@ using mlir::AsmParser;
using mlir::AsmPrinter; using mlir::AsmPrinter;
static LogicalResult parseMembers(AsmParser &parser, static LogicalResult parseMembers(AsmParser &parser,
SmallVector<StructMember> &members); SmallVector<StructLikeMember> &members);
static void printMembers(AsmPrinter &printer, ArrayRef<StructMember> members); static void printMembers(AsmPrinter &printer,
ArrayRef<StructLikeMember> members);
static ParseResult parseMooreType(AsmParser &parser, Type &type); static ParseResult parseMooreType(AsmParser &parser, Type &type);
static void printMooreType(Type type, AsmPrinter &printer); static void printMooreType(Type type, AsmPrinter &printer);
@ -38,7 +39,7 @@ Domain UnpackedType::getDomain() const {
.Case<UnpackedArrayType, OpenUnpackedArrayType, AssocArrayType, .Case<UnpackedArrayType, OpenUnpackedArrayType, AssocArrayType,
QueueType>( QueueType>(
[&](auto type) { return type.getElementType().getDomain(); }) [&](auto type) { return type.getElementType().getDomain(); })
.Case<UnpackedStructType>([](auto type) { .Case<UnpackedStructType, UnpackedUnionType>([](auto type) {
for (const auto &member : type.getMembers()) for (const auto &member : type.getMembers())
if (member.type.getDomain() == Domain::FourValued) if (member.type.getDomain() == Domain::FourValued)
return Domain::FourValued; return Domain::FourValued;
@ -67,6 +68,17 @@ std::optional<unsigned> UnpackedType::getBitSize() const {
} }
return size; return size;
}) })
.Case<UnpackedUnionType>([](auto type) -> std::optional<unsigned> {
unsigned size = 0;
for (const auto &member : type.getMembers()) {
if (auto memberSize = member.type.getBitSize()) {
size = (*memberSize > size) ? *memberSize : size;
} else {
return std::nullopt;
}
}
return size;
})
.Default([](auto) { return std::nullopt; }); .Default([](auto) { return std::nullopt; });
} }
@ -91,7 +103,7 @@ Domain PackedType::getDomain() const {
.Case<IntType>([&](auto type) { return type.getDomain(); }) .Case<IntType>([&](auto type) { return type.getDomain(); })
.Case<ArrayType, OpenArrayType>( .Case<ArrayType, OpenArrayType>(
[&](auto type) { return type.getElementType().getDomain(); }) [&](auto type) { return type.getElementType().getDomain(); })
.Case<StructType>([](auto type) { .Case<StructType, UnionType>([](auto type) {
for (const auto &member : type.getMembers()) for (const auto &member : type.getMembers())
if (member.type.getDomain() == Domain::FourValued) if (member.type.getDomain() == Domain::FourValued)
return Domain::FourValued; return Domain::FourValued;
@ -119,7 +131,19 @@ std::optional<unsigned> PackedType::getBitSize() const {
} }
} }
return size; return size;
}); })
.Case<UnionType>([](auto type) -> std::optional<unsigned> {
unsigned size = 0;
for (const auto &member : type.getMembers()) {
if (auto memberSize = member.type.getBitSize()) {
size = (*memberSize > size) ? *memberSize : size;
} else {
return std::nullopt;
}
}
return size;
})
.Default([](auto) { return std::nullopt; });
} }
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
@ -128,7 +152,7 @@ std::optional<unsigned> PackedType::getBitSize() const {
/// Parse a list of struct members. /// Parse a list of struct members.
static LogicalResult parseMembers(AsmParser &parser, static LogicalResult parseMembers(AsmParser &parser,
SmallVector<StructMember> &members) { SmallVector<StructLikeMember> &members) {
return parser.parseCommaSeparatedList(AsmParser::Delimiter::Braces, [&]() { return parser.parseCommaSeparatedList(AsmParser::Delimiter::Braces, [&]() {
std::string name; std::string name;
UnpackedType type; UnpackedType type;
@ -142,10 +166,11 @@ static LogicalResult parseMembers(AsmParser &parser,
} }
/// Print a list of struct members. /// Print a list of struct members.
static void printMembers(AsmPrinter &printer, ArrayRef<StructMember> members) { static void printMembers(AsmPrinter &printer,
ArrayRef<StructLikeMember> members) {
printer << "{"; printer << "{";
llvm::interleaveComma(members, printer.getStream(), llvm::interleaveComma(members, printer.getStream(),
[&](const StructMember &member) { [&](const StructLikeMember &member) {
printer.printKeywordOrString(member.name); printer.printKeywordOrString(member.name);
printer << ": "; printer << ": ";
printer.printStrippedAttrOrType(member.type); printer.printStrippedAttrOrType(member.type);
@ -153,15 +178,26 @@ static void printMembers(AsmPrinter &printer, ArrayRef<StructMember> members) {
printer << "}"; printer << "}";
} }
LogicalResult StructType::verify(function_ref<InFlightDiagnostic()> emitError, static LogicalResult
ArrayRef<StructMember> members) { verifyAllMembersPacked(function_ref<InFlightDiagnostic()> emitError,
ArrayRef<StructLikeMember> members) {
if (!llvm::all_of(members, [](const auto &member) { if (!llvm::all_of(members, [](const auto &member) {
return llvm::isa<PackedType>(member.type); return llvm::isa<PackedType>(member.type);
})) }))
return emitError() << "StructType members must be packed types"; return emitError() << "StructType/UnionType members must be packed types";
return success(); return success();
} }
LogicalResult StructType::verify(function_ref<InFlightDiagnostic()> emitError,
ArrayRef<StructLikeMember> members) {
return verifyAllMembersPacked(emitError, members);
}
LogicalResult UnionType::verify(function_ref<InFlightDiagnostic()> emitError,
ArrayRef<StructLikeMember> members) {
return verifyAllMembersPacked(emitError, members);
}
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// Generated logic // Generated logic
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//

View File

@ -122,7 +122,7 @@ module Statements;
automatic int a; automatic int a;
// CHECK moore.blocking_assign %i, %a : i32 // CHECK moore.blocking_assign %i, %a : i32
i = a; i = a;
//===------------------------------------------------------------------===// //===------------------------------------------------------------------===//
// Conditional statements // Conditional statements
@ -321,6 +321,16 @@ module Expressions;
int a, b; int a, b;
} c, d; } c, d;
} struct1; } struct1;
// CHECK: %union0 = moore.variable : union<{a: i32, b: i32}>
union packed {
int a, b;
} union0;
// CHECK: %union1 = moore.variable : union<{c: union<{a: i32, b: i32}>, d: union<{a: i32, b: i32}>}>
union packed {
union packed {
int a, b;
} c, d;
} union1;
initial begin initial begin
// CHECK: moore.constant 0 : i32 // CHECK: moore.constant 0 : i32
@ -660,6 +670,24 @@ module Expressions;
// CHECK: [[TMP2:%.+]] = moore.struct_extract [[TMP1]], "b" : struct<{a: i32, b: i32}> -> i32 // CHECK: [[TMP2:%.+]] = moore.struct_extract [[TMP1]], "b" : struct<{a: i32, b: i32}> -> i32
// CHECK: moore.blocking_assign %b, [[TMP2]] : i32 // CHECK: moore.blocking_assign %b, [[TMP2]] : i32
b = struct1.d.b; b = struct1.d.b;
// CHECK: [[TMP:%.+]] = moore.union_extract %union0, "a" : union<{a: i32, b: i32}> -> i32
// CHECK: moore.blocking_assign [[TMP]], %a : i32
union0.a = a;
// CHECK: [[TMP:%.+]] = moore.union_extract %union0, "b" : union<{a: i32, b: i32}> -> i32
// CHECK: moore.blocking_assign %b, [[TMP]] : i32
b = union0.b;
// CHECK: [[TMP1:%.+]] = moore.union_extract %union1, "c" : union<{c: union<{a: i32, b: i32}>, d: union<{a: i32, b: i32}>}> -> union<{a: i32, b: i32}>
// CHECK: [[TMP2:%.+]] = moore.union_extract [[TMP1]], "a" : union<{a: i32, b: i32}> -> i32
// CHECK: moore.blocking_assign [[TMP2]], %a : i32
union1.c.a = a;
// CHECK: [[TMP1:%.+]] = moore.union_extract %union1, "d" : union<{c: union<{a: i32, b: i32}>, d: union<{a: i32, b: i32}>}> -> union<{a: i32, b: i32}>
// CHECK: [[TMP2:%.+]] = moore.union_extract [[TMP1]], "b" : union<{a: i32, b: i32}> -> i32
// CHECK: moore.blocking_assign %b, [[TMP2]] : i32
b = union1.d.b;
end end
endmodule endmodule

View File

@ -7,5 +7,5 @@
module Foo; module Foo;
// expected-error @below {{unsupported type}} // expected-error @below {{unsupported type}}
// expected-note @below {{}} // expected-note @below {{}}
union { bit a; logic b; } x; event notDone;
endmodule endmodule

View File

@ -11,5 +11,9 @@ unrealized_conversion_cast to !moore.array<4 x string>
unrealized_conversion_cast to !moore.open_array<string> unrealized_conversion_cast to !moore.open_array<string>
// ----- // -----
// expected-error @below {{StructType members must be packed types}} // expected-error @below {{StructType/UnionType members must be packed types}}
unrealized_conversion_cast to !moore.struct<{foo: string}> unrealized_conversion_cast to !moore.struct<{foo: string}>
// -----
// expected-error @below {{StructType/UnionType members must be packed types}}
unrealized_conversion_cast to !moore.union<{foo: string}>

View File

@ -131,14 +131,14 @@ TEST(TypesTest, Structs) {
auto bit8Type = IntType::getInt(&context, 8); auto bit8Type = IntType::getInt(&context, 8);
auto bitDynArrayType = OpenUnpackedArrayType::get(bitType); auto bitDynArrayType = OpenUnpackedArrayType::get(bitType);
auto s0 = StructType::get(&context, {StructMember{foo, bitType}}); auto s0 = StructType::get(&context, {StructLikeMember{foo, bitType}});
auto s1 = StructType::get( auto s1 = StructType::get(&context, {StructLikeMember{foo, bitType},
&context, {StructMember{foo, bitType}, StructMember{bar, bit8Type}}); StructLikeMember{bar, bit8Type}});
auto s2 = StructType::get( auto s2 = StructType::get(&context, {StructLikeMember{foo, bitType},
&context, {StructMember{foo, bitType}, StructMember{bar, logicType}}); StructLikeMember{bar, logicType}});
auto s3 = auto s3 = UnpackedStructType::get(
UnpackedStructType::get(&context, {StructMember{foo, bitType}, &context,
StructMember{bar, bitDynArrayType}}); {StructLikeMember{foo, bitType}, StructLikeMember{bar, bitDynArrayType}});
// Value domain // Value domain
ASSERT_EQ(s0.getDomain(), Domain::TwoValued); ASSERT_EQ(s0.getDomain(), Domain::TwoValued);
@ -151,6 +151,27 @@ TEST(TypesTest, Structs) {
ASSERT_EQ(s1.getBitSize(), 9u); ASSERT_EQ(s1.getBitSize(), 9u);
ASSERT_EQ(s2.getBitSize(), 2u); ASSERT_EQ(s2.getBitSize(), 2u);
ASSERT_EQ(s3.getBitSize(), std::nullopt); ASSERT_EQ(s3.getBitSize(), std::nullopt);
auto u0 = UnionType::get(&context, {StructLikeMember{foo, bitType}});
auto u1 = UnionType::get(&context, {StructLikeMember{foo, bitType},
StructLikeMember{bar, bit8Type}});
auto u2 = UnionType::get(&context, {StructLikeMember{foo, bitType},
StructLikeMember{bar, logicType}});
auto u3 = UnpackedUnionType::get(
&context,
{StructLikeMember{foo, bitType}, StructLikeMember{bar, bitDynArrayType}});
// Value domain
ASSERT_EQ(u0.getDomain(), Domain::TwoValued);
ASSERT_EQ(u1.getDomain(), Domain::TwoValued);
ASSERT_EQ(u2.getDomain(), Domain::FourValued);
ASSERT_EQ(u3.getDomain(), Domain::TwoValued);
// Bit size
ASSERT_EQ(u0.getBitSize(), 1u);
ASSERT_EQ(u1.getBitSize(), 8u);
ASSERT_EQ(u2.getBitSize(), 1u);
ASSERT_EQ(u3.getBitSize(), std::nullopt);
} }
} // namespace } // namespace