-
Notifications
You must be signed in to change notification settings - Fork 467
[OM] Add a folder for IntegerBinaryArithmeticOp and use it in Evaluator, extend StringConcat folder #10269
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
[OM] Add a folder for IntegerBinaryArithmeticOp and use it in Evaluator, extend StringConcat folder #10269
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -491,28 +491,15 @@ circt::om::Evaluator::evaluateIntegerBinaryArithmetic( | |
| assert(lhs && rhs && | ||
| "expected om::IntegerAttr for IntegerBinaryArithmeticOp operands"); | ||
|
|
||
| // Extend values if necessary to match bitwidth. Most interesting arithmetic | ||
| // on APSInt asserts that both operands are the same bitwidth, but the | ||
| // IntegerAttrs we are working with may have used the smallest necessary | ||
| // bitwidth to represent the number they hold, and won't necessarily match. | ||
| APSInt lhsVal = lhs.getValue().getAPSInt(); | ||
| APSInt rhsVal = rhs.getValue().getAPSInt(); | ||
| if (lhsVal.getBitWidth() > rhsVal.getBitWidth()) | ||
| rhsVal = rhsVal.extend(lhsVal.getBitWidth()); | ||
| else if (rhsVal.getBitWidth() > lhsVal.getBitWidth()) | ||
| lhsVal = lhsVal.extend(rhsVal.getBitWidth()); | ||
|
|
||
| // Perform arbitrary precision signed integer binary arithmetic. | ||
| FailureOr<APSInt> result = op.evaluateIntegerOperation(lhsVal, rhsVal); | ||
|
|
||
| if (failed(result)) | ||
| std::array<Attribute, 2> operandAttrs = {lhs, rhs}; | ||
| SmallVector<mlir::OpFoldResult, 1> results; | ||
| om::IntegerAttr resultAttr; | ||
| auto foldResult = op->fold(operandAttrs, results); | ||
| if (failed(foldResult) || results.size() != 1 || | ||
| !(resultAttr = llvm::dyn_cast_or_null<om::IntegerAttr>( | ||
| results[0].dyn_cast<Attribute>()))) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are these checks necessary/reachable given the constraints of the fold function?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the current implementation of folders that implement IntegerBinaryArithmetic, yes it's certainly not reachable. But I don't think this check is unnecessary since folder could return a value (even when all operands are attributes), and without this check we would get assertion failure or nullptr deference (e.g. string_concat didn't return an attribute when it had single operand without this PR). This part https://github.com/llvm/circt/pull/10265/changes#r3105827722 is more generic version of this with a slightly better error message. |
||
| return op->emitError("failed to evaluate integer operation"); | ||
|
|
||
| // Package the result as a new om::IntegerAttr. | ||
| MLIRContext *ctx = op->getContext(); | ||
| auto resultAttr = | ||
| om::IntegerAttr::get(ctx, mlir::IntegerAttr::get(ctx, result.value())); | ||
|
|
||
| // Finalize the op result value. | ||
| auto *handleValue = cast<evaluator::AttributeValue>(handle.value().get()); | ||
| auto resultStatus = handleValue->setAttr(resultAttr); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,7 +23,7 @@ func.func @ObjectsMustDCE() { | |
| return | ||
| } | ||
|
|
||
| om.class @StringConcatCanonicalization(%str1: !om.string, %str2: !om.string) -> (out1: !om.string, out2: !om.string, out3: !om.string, out4: !om.string, out5: !om.string, out6: !om.string, out7: !om.string) { | ||
| om.class @StringConcatCanonicalization(%str1: !om.string, %str2: !om.string) -> (out1: !om.string, out2: !om.string, out3: !om.string, out4: !om.string, out5: !om.string, out6: !om.string, out7: !om.string, out8: !om.string) { | ||
|
uenoku marked this conversation as resolved.
Outdated
|
||
| %s1 = om.constant "Hello" : !om.string | ||
| %s2 = om.constant "World" : !om.string | ||
| %s3 = om.constant "!" : !om.string | ||
|
|
@@ -43,6 +43,9 @@ om.class @StringConcatCanonicalization(%str1: !om.string, %str2: !om.string) -> | |
| // Single operand replaced with operand | ||
| %2 = om.string.concat %s1 : !om.string | ||
|
|
||
| // Single constant operand folds to the attribute. | ||
| %singleConst = om.string.concat %s3 : !om.string | ||
|
|
||
| // Empty concat | ||
| %3 = om.string.concat %empty, %empty : !om.string | ||
|
|
||
|
|
@@ -57,8 +60,40 @@ om.class @StringConcatCanonicalization(%str1: !om.string, %str2: !om.string) -> | |
| %nested = om.string.concat %str1, %str2 : !om.string | ||
| %concat1 = om.string.concat %nested, %s3 : !om.string | ||
|
|
||
| // CHECK: om.class.fields [[HELLOWORLD]], [[HELLO]], [[HELLO]], [[EMPTY]], [[HELLOWORLD]], [[CONCAT1]], [[NESTED]] | ||
| om.class.fields %0, %1, %2, %3, %5, %concat1, %nested : !om.string, !om.string, !om.string, !om.string, !om.string, !om.string, !om.string | ||
| // CHECK: om.class.fields [[HELLOWORLD]], [[HELLO]], [[HELLO]], [[CONST]], [[EMPTY]], [[HELLOWORLD]], [[CONCAT1]], [[NESTED]] | ||
| om.class.fields %0, %1, %2, %singleConst, %3, %5, %concat1, %nested : !om.string, !om.string, !om.string, !om.string, !om.string, !om.string, !om.string, !om.string | ||
| } | ||
|
|
||
| // CHECK-LABEL: @IntegerBinaryArithmeticFold | ||
| om.class @IntegerBinaryArithmeticFold(%x: !om.integer) -> (out1: !om.integer, out2: !om.integer, | ||
| out3: !om.integer, out4: !om.integer, | ||
| out5: !om.integer, out6: !om.integer) { | ||
| %i3 = om.constant #om.integer<3 : si4> : !om.integer | ||
| %i4 = om.constant #om.integer<4 : si4> : !om.integer | ||
| %i2 = om.constant #om.integer<2 : si4> : !om.integer | ||
| %neg1 = om.constant #om.integer<-1 : si4> : !om.integer | ||
| %i1 = om.constant #om.integer<1 : si4> : !om.integer | ||
| %wide = om.constant #om.integer<7 : si6> : !om.integer | ||
|
|
||
| // CHECK-DAG: [[ADD:%.+]] = om.constant #om.integer<7 : si4> : !om.integer | ||
| // CHECK-DAG: [[MUL:%.+]] = om.constant #om.integer<-4 : si4> : !om.integer | ||
| // CHECK-DAG: [[SHR:%.+]] = om.constant #om.integer<1 : si4> : !om.integer | ||
| // CHECK-DAG: [[SHL:%.+]] = om.constant #om.integer<-2 : si4> : !om.integer | ||
| // CHECK-DAG: [[WIDEADD:%.+]] = om.constant #om.integer<9 : si6> : !om.integer | ||
| // CHECK: [[DYN:%.+]] = om.integer.add %x, %{{.+}} : !om.integer | ||
| %0 = om.integer.add %i3, %i4 : !om.integer | ||
| %1 = om.integer.mul %i3, %i4 : !om.integer | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This folding to -4 is right in twos-complement, but may produce unexpected behavior if we're not careful as I think we view OM integers as arbitrary precision. I think the answer to this is just that this is how this works. |
||
| %2 = om.integer.shr %i4, %i2 : !om.integer | ||
| %3 = om.integer.shl %neg1, %i1 : !om.integer | ||
|
|
||
| // Mixed bit widths should still fold after extending operands. | ||
| %4 = om.integer.add %i2, %wide : !om.integer | ||
|
|
||
| // Non-constant operands should remain. | ||
| %5 = om.integer.add %x, %i1 : !om.integer | ||
|
|
||
| // CHECK: om.class.fields [[ADD]], [[MUL]], [[SHR]], [[SHL]], [[WIDEADD]], [[DYN]] | ||
| om.class.fields %0, %1, %2, %3, %4, %5 : !om.integer, !om.integer, !om.integer, !om.integer, !om.integer, !om.integer | ||
| } | ||
|
|
||
| // CHECK-LABEL: @PropEqFold | ||
|
|
||
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 forget how this works. If the folder doesn't apply, then that returns
{}. That won't trigger the failure here, right?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 folder doesn't apply, it returns LogicalResult::failure so it returns an error below (
resultswill be empty)