-
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] Add conditional operator. #6950
Conversation
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.
LGTM!
The lowering does not match the standard for 4-valued conditions with X or Z. Apparently it should then evaluate both the 2nd and 3rd operand, compare them for equivalence, etc. |
7bbb1bb
to
2bb3636
Compare
Good point! I have added some error tests for 4-valued conditions and look forward to your suggestions. |
2bb3636
to
d374251
Compare
Thanks! This only concerns literals now, right? What if the condition expression is not a literal but, e.g., some input port that is 4-valued and may contain 'x'? Can't we check the type of the expression for 4-valuedness instead of the expression itself, or would that be too restrictive for our use-cases? |
Thanks for your thoughtful consideration! Do you mean we need to check the runtime values? If so, I guess we can only analyze the type of expression at compile time rather than at runtime. Additionally, we cannot determine whether it is x or z based on the type of expression, e.g., x and z may be |
I think what @maerhart is getting at is the complicated handling that is necessary when the condition is X or Z ("11.4.11 Conditional operator" in IEEE 1800-2017). When the condition is of a two-valued type (derived from
So I think one thing you could do is explicitly check if the condition is 0 or 1, and otherwise print an error, raise an exception, or do something else. For example, pseudocode:
That would cause the simulation to fail properly if we encounter an unsupported condition in the mux, and we know where to start once we improve the lowering in the future. We'll probably want to have a dedicated op in the Moore dialect that performs the mux/conditional merging of the left and right expression. And we'll want to eventually implement all the rules in 11.4.11 and apply the correct one depending on the data type of what we're muxing. You can also kick the can down the road and introduce a new op that is similar to
That would allow you to have a later lowering pass that takes |
d374251
to
1b030b9
Compare
@fabianschuiki Thank you very much for your suggestion 😃 ! I chose the second method and defined two new operations for the conditional operator: |
If cond_predicate evaluates to an ambiguous value (x or z), then both the | ||
first expression and the second expression shall be evaluated, and compared | ||
for logical equivalence. If that comparison is true (1), the operator shall | ||
return either the first or second expression. Otherwise the operator returns | ||
a result based on the data types of the expressions. |
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.
Can I consider logical equivalence as <->
?
else | ||
builder.create<moore::YieldOp>(loc, convertToSimpleBitVector(trueValue)); | ||
|
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.
convertToSimpleBitVector()
may return {}
if the type of trueValue isn't SBVT(Like logic [0:5][2:0] p3
).
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.
Good point! I'll fix that.
else | ||
builder.create<moore::YieldOp>(loc, convertToSimpleBitVector(falseValue)); | ||
|
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.
As above.
If "moore.yield" has any operands, the operands must match the parent | ||
operation's results. |
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.
How do you ensure the types of operands of moore.yield
match the result types of moore.conditional
? Maybe you should add the related method to check this.
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.
Nice advice! However, Slang already ensures a match between result type and expressions type, so additional checks seem unnecessary.
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 you still need a check to make sure that the IR is not malformed. Most of the time the IR will come from import through Slang, but in many other cases the input IR will be handwritten in case of unit tests, or it will be generated from transformation passes. It is important that operations check that the IR is correct to catch bugs in those other cases as soon as possible.
The hw::OutputOp
, arc::OutputOp
, scf::YieldOp
, or func::ReturnOp
have to do a similar kind of check -- you might find some inspiration on how to do it there. I'm not 100% if the output/yield op verifies that it matches the parent condition/module/arc/func/if op, or if the parent op checks that it has the right terminator.
//===------------------------------------------------------------------===// | ||
// Conditional operator | ||
|
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.
nit: It would be better if you could add some non-SBVT tests.
1b030b9
to
ae9e495
Compare
auto trueValue = context.convertExpression(expr.left()); | ||
if (!trueValue) |
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.
In this case you should just return {}
, since convertExpression
returning a null value indicates that an error has happened that has already been reported in a diagnostic by the expression conversion code. Similar to how you further above check if (!cond) return {};
.
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.
If just return {}
, it will return an unexpected error that "error: 'moore.conditional' op expects a non-empty block". How can I avoid this error?
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.
That is very strange! This means that even though you returned {}
here, the Slang-to-MLIR conversion continued and at the end the MLIR verifier was called to make sure that the IR is correct. We might be missing additional checks for if (!<expr>) return {};
elsewhere in the converter.
In theory what should happen is: when you encounter an error and the convert*
functions start returning {}
, this abort and return with {}
should propagate all the way to the top, and in ImportVerilog, conversion should be aborted without the MLIR verifier running at all.
Do you have the input Verilog that causes this issue? I can try to help you find why this happens 😃
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.
Sure! Here is the input:
module top();
int a,b;
initial begin
a = b ? 'x : b;
end
endmodule
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.
Great catch! It looks like there was a very subtle bug in convertStatement
where a failure from convertExpression
would not be propagated properly. That was also the reason why we were able to check for multiple errors in errors.sv
in parallel, which was suspicious. (Import should have aborted after first error.)
I have pushed a fix straight to the main branch: dd68e3c
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.
Nice! Fixing this bug has been of great help to me. 🥰
auto falseValue = context.convertExpression(expr.right()); | ||
if (!falseValue) |
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.
Same as above: return {};
ae9e495
to
77f8f37
Compare
@fabianschuiki @hailongSun2000 I sincerely appreciate your suggestions! I have added verify to |
This looks very cool! Thanks for adding all the fixes and the new ops 😍. Can you add a test to Similarly, since you've added new error diagnostics to the dialect in |
77f8f37
to
065d35d
Compare
That's a good idea! I have added several tests for |
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, looks really nice! Thanks a lot for all your work 🥳
065d35d
to
f70c5b8
Compare
f70c5b8
to
1b4d59f
Compare
Handle conditional operator
?:
.Use
scf.if
to control the selection of expressions, and return the selected value throughscf.yield
.