-
Notifications
You must be signed in to change notification settings - Fork 347
[FIRRTL] Const handling in cast ops #5151
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
Conversation
4941449
to
54c0555
Compare
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.
Generally LGTM, added some comments/questions.
Does const work w/as{UInt,SInt,Clock,...} (just curious: how)? At a glance don't see it mentioned in other PR's, but they do feel like "cast" operations.
Will look at other PR's to put this in context, thanks I think that helps a lot.
if (*inTypeBits == *resTypeBits) | ||
if (*inTypeBits == *resTypeBits) { | ||
// non-'const' cannot be casted to 'const' | ||
if (containsConst(getType()) && !isConst(getOperand().getType())) |
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 rule here is that bitcasts where the destination type has const anywhere /must/ originate from a fully-const input (even if they're the same type), is that intended/right? Is there a way to go from a bundle<x: const.uint<1>>
to bundle<x: const.sint<1>>
(or const.uint<1>
to const.sint<1>
for that matter?).
Checking if the bitcast portions are const-compatible sounds like a pain so not really suggesting that, but consider adding a test for the behavior or otherwise make that clear.
As an aside, the difference between a "const" type and a type that has const isn't always clear (which seems context-dependent), if there is phrasing that helps here that'd be great, especially for user-facing diagnostics.
An aggregate with all elements "const" being different than a const aggregate feels a little strange (as does const.bundle<x : const.uint<1>>
) but I know that whole deal went through iterations and not sure how that could best be changed so there are canonical representations (e.g., outer const implies inner const).
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'm not sure how important this is (might be but might very much not be), doesn't seem like we use bitcast a lot?
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 idea is mainly to disallow non-const to const as a basic sanity-check without doing the full const-compatible check, since a const-cast would be more appropriate.
But on the other hand, bitcasts seem to be purely internal as opposed to an actual firrtl op and aren't introduced until the lower-types pass, which is after where the drop-const pass will be, so maybe I don't need to do anything for this op?
.Default(false); | ||
} | ||
|
||
LogicalResult ConstCastOp::verify() { |
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 adding the verifier!
lib/Dialect/FIRRTL/FIRRTLTypes.cpp
Outdated
auto destElement = destElements[i]; | ||
auto srcElement = srcElements[i]; | ||
if (destElement.name != srcElement.name || | ||
destElement.isFlip != srcElement.isFlip || |
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.
Not a big deal but was curious: apparently can cast const.bundle<a: T, const b: U>
to const.bundle<const a: T, b: U>
which seems harmless since AFAIK the two are basically equivalent anyway (?).
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.
It's weird but I consider it to be ok.
084edfe
to
029a445
Compare
bool srcIsConst = srcType.isConst() || srcOuterTypeIsConst; | ||
|
||
// Cannot cast non-'const' src to 'const' dest | ||
if (destType.isConst() && !srcIsConst) | ||
return false; |
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 move all the ground type handling down to the bottom like you did in the areTypesEquivalent
PR.
I am trying to think of some weird cases where flip matters, I think I have one but it might not even matter. I guess a big question I have is, when are we going to end up using this operation? Is this for early lowering of connects to strict connects? Here is the rough example: firrtl.module(in %in : !firrtl.bundle<a flip: uint<1>>, out %out : !firrtl.bundle<a flip: const.uint<1>>) {
// Option 0: don't use strict connect:
firrtl.connect %out, %in : !firrtl.bundle<a flip: const.uint<1>>, !firrtl.bundle<a flip: uint<1>>
// Option 1: const-cast has the same "flow" as its input, meaning that it creates an alias to the input value.
// This allows us to drive directly to the result of the cast:
%0 = firrtl.constCast %out : (!firrtl.bundle<a flip: const.uint<1>>) -> !firrtl.bundle<a flip: uint<1>>
firrtl.strictconnect %0, %in : !firrtl.bundle<a flip: uint<1>>
// Option 2: const-cast can add "const" to anything with a flip:
%0 = firrtl.constCast %in : (!firrtl.bundle<a flip: uint<1>>) -> !firrtl.bundle<a flip: const.uint<1>>
firrtl.connect %out, %0 : !firrtl.bundle<a flip: const.uint<1>>
} edit: it appears to be important over here: https://github.com/llvm/circt/pull/5153/files#diff-c8689caabdf068960b794c6990c63337929f28b354cf839c2a43abb2973c23a6R126-R130 So it seems like we might fail to const-cast the two, and then hit the assert below? |
: PredOpTrait<"operand constness must match", | ||
CPred<"isConst($" # a # ".getType()) == isConst($" # b # ".getType())">>; | ||
|
||
def UninferredResetCastOp : FIRRTLOp<"resetCast", [HasCustomSSAName, Pure, SameGroundTypeOperandConstness<"input", "result">]> { |
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.
We do try to keep the tablegen files to 80 (I think) columns.
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.
Updated
@@ -938,6 +943,17 @@ def UninferredWidthCastOp : FIRRTLOp<"widthCast", [HasCustomSSAName, Pure]> { | |||
"$input attr-dict `:` functional-type($input, $result)"; | |||
} | |||
|
|||
def ConstCastOp : FIRRTLOp<"constCast", [HasCustomSSAName, Pure]> { | |||
let description = [{ | |||
Cast from a 'const' to a non-'const' type. |
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 like to add an example just demonstrating the op syntax. It makes the website documentation a little bit more 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.
Example added.
/// Returns true if the type is or contains a 'const' type. | ||
bool containsConst(Type type); | ||
|
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.
It seems like it should go in FIRRTLTypes.h
or FIRRTLUtils.h
.
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.
Yeah, not sure why I put it here in the first place. Moved along with isConst
to FIRRTLTypes.h
. Also updated the documentation to better reflect the spec.
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.
A couple nits
I think Relatedly, I'm thinking I should enforce that only passive types are const-castable to keep the logic simpler. Based on the |
029a445
to
236befe
Compare
Updated to require passive types for the cast op. |
Sure, the more powerful the cast op is, the less connect operations we will need, its just a trade-off. Since we are breaking down connects eventually anyways, you might as well only allow ground types to be const-cast :P. But it looks like enforcing a passive operand will make the current code work with no changes. |
lib/Dialect/FIRRTL/FIRRTLTypes.cpp
Outdated
return llvm::all_of_zip(destElements, srcElements, | ||
[&](const BundleType::BundleElement &destElement, | ||
const BundleType::BundleElement &srcElement) { | ||
return destElement.name == srcElement.name && | ||
destElement.isFlip == srcElement.isFlip && | ||
areTypesConstCastable(destElement.type, | ||
srcElement.type, | ||
srcIsConst); | ||
}); |
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.
Should this check !destElement.isFlip && !srcElement.isFlip
, i.e., that the bundle is passive?
If this is only called by the ConstCastOp
verifier (I believe the plan is that it is not), then this will have already been checked.
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 added a passive check above so every element doesn't need to check flip.
This commit adds a ConstCastOp for casting away constness from types. This op and const types will be dropped early in the firrtl pipeline in a followup commit. Verification has been added to other casting ops to ensure that type constness is taken into account.
…peOperandConstness
313216c
to
3e54136
Compare
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 thanks!
lib/Dialect/FIRRTL/FIRRTLTypes.cpp
Outdated
auto destElements = destBundleType.getElements(); | ||
auto srcElements = srcBundleType.getElements(); | ||
size_t numDestElements = destElements.size(); | ||
if (numDestElements != srcElements.size()) | ||
return false; | ||
|
||
return llvm::all_of_zip(destElements, srcElements, | ||
[&](const BundleType::BundleElement &destElement, | ||
const BundleType::BundleElement &srcElement) { | ||
return destElement.name == srcElement.name && | ||
areTypesConstCastable(destElement.type, | ||
srcElement.type, | ||
srcIsConst); | ||
DFF1 }); |
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.
Might try inlining the locals or const auto &
for arguments and see if the auto-formatter picks a different layout. I really don't love when it indents the body of the lambda so far. (If you like it like this its totally fine).
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.
Seems slightly better with const auto &
This commit adds a ConstCastOp for casting away constness from types. This op and const types will be dropped early in the firrtl pipeline in a followup commit.
Verification has been added to other casting ops to ensure that type constness is taken into account.