-
Notifications
You must be signed in to change notification settings - Fork 298
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ImportVerilog] Support member-access expression #7039
Conversation
Support member-access expression. Add container multiSymbolValue for multi-symbols pointing one value. Signed-off-by: mingzheTerapines <mingzhe.zhang@terapines.com>
nit: Maybe you should add some error tests. |
Separate two containers and their annotations.
I think more tests could be added around Union / Struct(packed / unpacked)/ Enum types which would probably need member-access feature. If it is unsupported currently, adding the test into error tests might be better. |
use auto instead of const slang::ast::Expression * declare concatName with expr.member.name
Simplfy string allocation.
The signing of unpacked structures is not allowed.- IEEE Standard
The signing of unpacked structures is not allowed. -IEEE standards |
Add packed unsigned struct occasion for testing.
|
Support Union Type Modify uniont tyep to event type as error type example.
Add error example for unpacked union.
@cepheus69 @hailongSun2000 I also supported union type, I was hoping you can review again and give some advice. |
Dear @fabianschuiki , I added
Because I can not change
to
because scopedhashtable 's key can not be set as a llvm::smallvector. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think Moore is missing struct supports (and arrays) so probably we need to add StructFieldAccess
op or something.
std::map<llvm::SmallVector<const slang::ast::ValueSymbol *>, Value>;
I think we continue using the original data structure. Member-access expression doesn't create a new value symbol so I think we should just track the root expression.
while (true) { | ||
std::string structName((*exprIt).getSymbolReference()->name); | ||
concatName = structName + "." + concatName; | ||
symbolVec.push_back(static_cast<const slang::ast::ValueSymbol *>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we could create SturctFieldAccessOp
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your reply, I will consider.
Thanks for working on this @mingzheTerapines! I agree with @uenoku: it would be good to define a struct field access op that can represent accesses into struct fields. Something like In one of your examples from the LLVM Discourse, you had the following SystemVerilog: always_comb begin
typedef struct packed signed {int a, b;} int64;
int64 ii;
int64 bb;
ii.b = x;
bb.b = x;
end After ImportVerilog I'd expect there to be only 2 variables, moore.procedure always_comb {
%ii = moore.variable : !moore.packed<struct<{a: i32, b: i32}>>
%bb = moore.variable : !moore.packed<struct<{a: i32, b: i32}>>
%0 = moore.struct_field %ii, "b" : !moore.packed<struct<{a: i32, b: i32}>> -> !moore.i32
moore.blocking_assign %0, %x : !moore.i32
%1 = moore.struct_field %bb, "b" : !moore.packed<struct<{a: i32, b: i32}>> -> !moore.i32
moore.blocking_assign %1, %x : !moore.i32
} The MooreToCore pass would then probably resolve these assignments to moore.procedure always_comb {
// moore.variable becomes:
%ii0 = moore.struct_create {a: %0, b: %0} : !moore.packed<struct<{a: i32, b: i32}>>
// moore.struct_field and blocking_assign become:
%ii1 = moore.struct_inject %ii0, "b", %x : !moore.packed<struct<{a: i32, b: i32}>>
} If you'd like some inspiration for struct ops, you may want to look at the HW dialect which defines a bunch of ops to create structs, access fields, etc. |
@fabianschuiki Sorry, I don't understand. Did you mean I need to construct three ops: moore.struct_field, moore.struct_create and moore.struct_inject, and imporve MooreToCore pass to resolove moore.struct_field op to moore.struct_create and moore.struct_inject this two ops? |
@mingzheTerapines I think what @fabianschuiki means is defining |
Define a struct field access op that can represent accesses into struct fields.
Seems this may not tell core(HW) this expression miss the meaning if this expression is blocking. But HW dialect do not have this kind of concern. Shall I use moore.struct_inject only one expression or I use moore.struct_blocking_inject and moore_nonblocking_inject two ops? |
I would keep the blocking/non-blocking aspect out of the new op, and just have a Passes like Mem2Reg can then use this new op to convert assignments to individual struct fields into assignments of the entire struct. For example, the IR might initially look like this: moore.procedure always_comb {
// struct packed { ... } y;
%y = moore.variable : !moore.packed<struct<{a: i32, b: i32}>>
// y.a = x;
%0 = moore.struct_field %y, "a" : !moore.packed<struct<{a: i32, b: i32}>> -> !moore.i32
moore.blocking_assign %0, %x : !moore.i32
} The Mem2Reg pass, or MooreToCore, or a dedicated assignment coarsening pass, can then convert the assignment to struct field moore.procedure always_comb {
%y = moore.variable : !moore.packed<struct<{a: i32, b: i32}>>
%0 = moore.struct_inject %y, "a", %x, !moore.packed<struct<{a: i32, b: i32}>>
moore.blocking_assign %y, %0 : !moore.packed<struct<{a: i32, b: i32}>>
} This turns the subfield assignment moore.procedure always_comb {
%c0_i32 = moore.constant 0 : !moore.i32
%y0 = moore.struct_create {a: %c0_i32, b: %c0_i32} : !moore.packed<struct<{a: i32, b: i32}>>
%y1 = moore.struct_inject %y0, "a", %x, !moore.packed<struct<{a: i32, b: i32}>>
} I'm pretty sure that Mem2Reg can resolve these struct field assignments directly: there is a By defining a |
Thanks @fabianschuiki , I already directly rewrite code to
in this PR.
in another PR. |
Add struct inject and extract op. Remove union support.
Hmmm, I think in
|
@fabianschuiki I am concerning this kind of one-step transforming becoming two-step transforming will cost more space and timing while compiling, also may mismatch some sentences. But I am not sure. If you are sure this method is proper, I will think about it. =) |
@fabianschuiki Mem2reg is another pass, maybe I will submit the rest in another PR. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool this looks really nice! Thanks for working on this, and for also testing multiple nested struct accesses 🥳.
I wouldn't worry too much about performance at this stage (beyond obvious scalability issues and performance issues in the code itself). It's much more important to have a correct pipeline to go from Verilog to the core dialects, and to make the pipeline consist of orthogonal, independent passes that slowly lower the Verilog input.
Generally, you want the import conversion (ImportVerilog in this case) to mainly map from an external representation such as the Slang AST to MLIR ops, without doing any significant rewriting, lowering, or other processing. And you want the export conversion (MooreToCore in this case) to mainly translate from ops in the Moore dialect to the core dialect, without doing any significant rewriting, lowering, or other processing. All the actual lowering work you want to have in separate, isolated passes that are scheduled in between ImportVerilog and MooreToCore. This could include Mem2Reg, variable removal, pattern-matching always blocks and converting them to registers, etc. This makes it a lot easier to develop and verify the individual passes.
There was a great talk about this at EuroLLVM 2023: https://youtu.be/hIt6J1_E21c
Support member-access expression.
Add container multiSymbolValue for multi-symbols pointing one value.