-
Notifications
You must be signed in to change notification settings - Fork 275
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
[Arc] Add basic support for assert operation. #6839
base: main
Are you sure you want to change the base?
Conversation
Aside from the conflicts, I will resolve them later: for the print message before the abort, I want to make something like |
You typically write them by hand. You could, however, run the
Did you check the location of the error and insert the
Can you give more detail about why |
There's a bit of an art to this. You're trying to balance:
Generally, your tests look good! Usually the only thing to avoid is exact copy-pasting of output. As Martin suggests, this can be a great starting point. Sometimes it is a useful exercise to intentionally write the test from scratch to make sure you aren't inadvertently relying on incorrect output. |
I think we need to implement like llvm |
@fabianschuiki, @maerhart ping 🔔 !! |
include/circt/Dialect/Arc/ArcOps.td
Outdated
@@ -880,4 +880,22 @@ def VectorizeReturnOp : ArcOp<"vectorize.return", [ | |||
let assemblyFormat = "operands attr-dict `:` qualified(type(operands))"; | |||
} | |||
|
|||
def AssertOp : ArcOp<"assert"> { | |||
let summary = "assert that a property holds."; |
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.
let summary = "assert that a property holds."; | |
let summary = "assert that a property holds"; |
include/circt/Dialect/Arc/ArcOps.td
Outdated
assert %proberty, %clock, "Some error message in case of failure!" | ||
``` | ||
}]; | ||
let arguments = (ins I1:$property, Optional<I1>:$clock, |
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 you add a paragrph in the description explaining why the clock operand is optional?
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.
verif.assert
doesn't have a clock operand. But I think I will need to give it a default value. verif.assert 0
, has no clock operand. what do you think?
// CHECK-NEXT: } | ||
%cond = hw.constant 0 : i1 | ||
%clock = hw.constant 1: i1 | ||
arc.assert %cond, %clock |
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 at this stage of the Arc lowering pipeline, the clock
operand should not be present anymore. Either it is folded into the cond
operand or it is gone because the assert is in a function that is called only at the posedge of this clock. So the lowering pass to LLVM would just error out. But the clock operand will be useful in the lowering from verif
and be dealt with in the passes between those two lowerings.
Also, shouldn't the clock operand be of seq.clock
type?
@fabianschuiki What do you think?
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 put a clock operand so we can make use of it for further steps, I am not sure why we need it in assert
which checks the condition.
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.
Yes, having the optional clock operand makes sense. But the clock operand just provides the raw value of the clock signal, but the assert is typically only checked at one or both of the clock edges (see ltl.clock
operation). This behavior needs to be lowered and will probably happen before the ArcToLLVM pass. During this process this edge-checking logic could be folded into the condition.
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.
the clock operand should not be present anymore. Either it is folded into the cond operand or it is gone because the assert is in a function that is called only at the posedge of this clock.
@maerhart, can you explain this a little further?
bbf58ea
to
a71c648
Compare
This operation looks a lot like the one proposed in #6982 |
std::string msgGlobalName = | ||
std::string("Arc_Assert_Op_Msg: ") + message.str(); |
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.
Maybe adding the source location to the message could be useful.
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.
@maerhart I can't think of a way to get the location of the here are two cases:
- If the message is provided directly to the operation (
arc.assert bool, clock, "some message"
), so the location of it is the location of the operation (akaarc.assert
), here we map the same message contents to the same global string(address) which is very useful. - If the message is provided as a variable (
arc.assert bool, clock, StrTypeOperand
), I need to find a way to get the location of the operand and then map all users to the same message, as the location will be different for different messages, and maybe they have the same contents, but in different locations.
Is there any way to convert Location
to StringRef
?
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.
@maerhart ping!
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.
Yes, I mean the location that you get from assertOp.getLoc()
. The Location
class has a print
function that you can use. Something like this:
std::string str;
llvm::raw_string_ostream(str);
loc.print(stream);
// use str here
Because the message is a string attribute, you can just concatenate them at compile-time and still merge globals referring to the same string.
This is not super important, so if you're having issues with it, we can also just leave it away.
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's great, but I tried it now it generates a location like this:
/Path/To/circt/test/Conversion/ArcToLLVM/lower-arc-to-llvm.mlir:276:3
I can use some regex
matching and the output will be lower-arc-to-llvm.mlir:276:3
, but I think it will differ from path to path, I tried but I couldn't match it so I ignored this part!
If you have further suggestions, I will address them.
@maerhart I don't know what's wrong with the checker! I run clang-format on my local repo and I don't get any errors!! |
In the github actions summary you can download a patch it produced. Here it is:
|
include/circt/Dialect/Arc/ArcOps.td
Outdated
If the property holds (= `true`) the operation has no effect. Otherwise, the | ||
execution aborts and provide an error message to the user. | ||
```mlir | ||
assert %proberty, %clock, "Some error message in case of failure!" |
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.
assert %proberty, %clock, "Some error message in case of failure!" | |
assert %property, %clock, "Some error message in case of failure!" |
@@ -624,7 +690,8 @@ void LowerArcToLLVMPass::runOnOperation() { | |||
StateReadOpLowering, | |||
StateWriteOpLowering, | |||
StorageGetOpLowering, | |||
ZeroCountOpLowering | |||
ZeroCountOpLowering, | |||
AssertOpLowering |
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.
Maintain alpha-sort?
```mlir | ||
assert %property | ||
assert %property, %clock | ||
assert %property, %clock, "Some error message in case of failure!" |
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 an assert have a message but no clock ("is that something we want to support")?
Consider differentiating these somehow, as-is this makes the parser do strange things if you omit the clock operand (arc.assert %in0, "Oops"
).
{ | ||
OpBuilder::InsertionGuard guard(rewriter); | ||
rewriter.setInsertionPointToStart(module.getBody()); | ||
putsFunc = lookupOrCreateFn( |
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.
This looks unsafe, and should go through rewriter?
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.
While looking into what to suggest, looks like there are various useful helpers for basically exactly this flow.
Additionally, cf.assert
has an LLVM lowering -- was wondering if makes sense to lower to/use cf.assert
since this seems rather similar?
Regardless, the lowering there uses the rewriter-aware and useful helpers for grabbing a print-like method as well as global strings:
It doesn't re-use the global string definition, FWIW, but anyway interesting related bits if not already on your radar.
And actually, looks like it ultimately just calls lookupOrCreateFn
(or wrappers) anyway: https://github.com/llvm/llvm-project/blob/cc54129b983799e1aaea77aa0ff3040dc30cbc8c/mlir/lib/Conversion/LLVMCommon/PrintCallHelper.cpp#L63 . Not sure how creating a global is safe?
Seems like could do what this is doing using rewriter (instead of the builder it creates)?
https://github.com/llvm/llvm-project/blob/cc54129b983799e1aaea77aa0ff3040dc30cbc8c/mlir/lib/Dialect/LLVMIR/IR/FunctionCallUtils.cpp#L51
(possible upstream patch, but if nothing else locally if looking to address that...)
assert %property, %clock, "Some error message in case of failure!" | ||
``` | ||
}]; | ||
let arguments = (ins I1:$property, Optional<ClockType>:$clock, |
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.
Just curious, should this be using ClockedOpInterface?
Also the clock isn't used in this PR, what role might if have / why not needed to be part of the lowering?
Type int8Type = rewriter.getI8Type(); | ||
|
||
// Generate new names for global strings. | ||
// Messgaes with the same contents, and the same locations |
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.
// Messgaes with the same contents, and the same locations | |
// Messages with the same contents, and the same locations |
// Messgaes with the same contents, and the same locations | ||
// are mapped to the same names | ||
std::string msgGlobalName = | ||
std::string("Arc_Assert_Op_Msg: ") + message.str(); |
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.
Especially with the colon and space it seems unlikely, but can we rely on that symbol not being defined?
Symbol name having the entire message seems like a lot? 🤔
We will implement this in two steps:
assert
operation in Arc by addingarc.assert
, and make sure it generates the correctLLVM
code.verif.assert
andsv.assert.concurrent
operations to Arc.