[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 `:`
type($input) `,` type($lowBit) `->` type($result)
}];
}
}
def StructCreateOp : MooreOp<"struct_create", [SameOperandsAndResultType]> {
let summary = "Struct Create operation";
@ -817,11 +817,11 @@ def StructCreateOp : MooreOp<"struct_create", [SameOperandsAndResultType]> {
```
See IEEE 1800-2017 § 7.2. "Structures".
}];
let arguments = (ins UnpackedType:$structName);
let arguments = (ins UnpackedType:$input);
let results = (outs UnpackedType:$result);
let hasCustomAssemblyFormat = 1;
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".
}];
let arguments = (ins StrAttr:$memberName, UnpackedType:$structName);
let arguments = (ins StrAttr:$memberName, UnpackedType:$input);
let results = (outs UnpackedType:$result);
let assemblyFormat = [{
$structName `,` $memberName attr-dict `:`
type($structName) `->` type($result)
$input `,` $memberName attr-dict `:`
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

View File

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

View File

@ -280,7 +280,7 @@ def QueueType : MooreTypeDef<"Queue", [], "moore::UnpackedType"> {
class StructLikeType<
string name, list<Trait> traits = [], string baseCppClass = "::mlir::Type"
> : MooreTypeDef<name, traits, baseCppClass> {
let parameters = (ins ArrayRefParameter<"StructMember">:$members);
let parameters = (ins ArrayRefParameter<"StructLikeMember">:$members);
let assemblyFormat = [{
`<` 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

View File

@ -429,10 +429,20 @@ struct ExprVisitor {
}
Value visit(const slang::ast::MemberAccessExpression &expr) {
return builder.create<moore::StructExtractOp>(
loc, context.convertType(*expr.type),
builder.getStringAttr(expr.member.name),
context.convertExpression(expr.value()));
auto type = context.convertType(*expr.type);
auto valueType = expr.value().type;
auto 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.

View File

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

View File

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

View File

@ -122,7 +122,7 @@ module Statements;
automatic int a;
// CHECK moore.blocking_assign %i, %a : i32
i = a;
//===------------------------------------------------------------------===//
// Conditional statements
@ -321,6 +321,16 @@ module Expressions;
int a, b;
} c, d;
} 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
// 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: moore.blocking_assign %b, [[TMP2]] : i32
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
endmodule

View File

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

View File

@ -11,5 +11,9 @@ unrealized_conversion_cast to !moore.array<4 x 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}>
// -----
// 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 bitDynArrayType = OpenUnpackedArrayType::get(bitType);
auto s0 = StructType::get(&context, {StructMember{foo, bitType}});
auto s1 = StructType::get(
&context, {StructMember{foo, bitType}, StructMember{bar, bit8Type}});
auto s2 = StructType::get(
&context, {StructMember{foo, bitType}, StructMember{bar, logicType}});
auto s3 =
UnpackedStructType::get(&context, {StructMember{foo, bitType},
StructMember{bar, bitDynArrayType}});
auto s0 = StructType::get(&context, {StructLikeMember{foo, bitType}});
auto s1 = StructType::get(&context, {StructLikeMember{foo, bitType},
StructLikeMember{bar, bit8Type}});
auto s2 = StructType::get(&context, {StructLikeMember{foo, bitType},
StructLikeMember{bar, logicType}});
auto s3 = UnpackedStructType::get(
&context,
{StructLikeMember{foo, bitType}, StructLikeMember{bar, bitDynArrayType}});
// Value domain
ASSERT_EQ(s0.getDomain(), Domain::TwoValued);
@ -151,6 +151,27 @@ TEST(TypesTest, Structs) {
ASSERT_EQ(s1.getBitSize(), 9u);
ASSERT_EQ(s2.getBitSize(), 2u);
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