-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[mlir][emitc][cf] add 'cf.switch' support in CppEmitter #101478
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
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-mlir @llvm/pr-subscribers-mlir-emitc Author: Andrey Timonin (EtoAndruwa) ChangesAdded 'switch' operation from cf dialect to the CppEmitter. Full diff: https://github.com/llvm/llvm-project/pull/101478.diff 2 Files Affected:
diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
index 1dadb9dd691e7..a05cbccf1f429 100644
--- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp
+++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
@@ -774,6 +774,41 @@ static LogicalResult printOperation(CppEmitter &emitter,
return printBinaryOperation(emitter, operation, "||");
}
+static LogicalResult printOperation(CppEmitter &emitter,
+ cf::SwitchOp switchOp) {
+ raw_indented_ostream &os = emitter.ostream();
+ auto iteratorCaseValues = (*switchOp.getCaseValues()).begin();
+ auto iteratorCaseValuesEnd = (*switchOp.getCaseValues()).end();
+
+ os << "\nswitch(" << emitter.getOrCreateName(switchOp.getFlag()) << ") {";
+
+ for (const auto caseBlock : switchOp.getCaseDestinations()) {
+ if (iteratorCaseValues == iteratorCaseValuesEnd)
+ return switchOp.emitOpError("case's value is absent for case block");
+
+ os << "\ncase " << *(iteratorCaseValues++) << ": {\n";
+ os.indent() << "goto ";
+
+ if (!(emitter.hasBlockLabel(*caseBlock)))
+ return switchOp.emitOpError("unable to find label for case block");
+ os << emitter.getOrCreateName(*caseBlock) << ";\n";
+
+ os.unindent() << "}";
+ }
+
+ os << "\ndefault: {\n";
+ os.indent() << "goto ";
+
+ if (!(emitter.hasBlockLabel(*switchOp.getDefaultDestination())))
+ return switchOp.emitOpError("unable to find label for default block");
+ os << emitter.getOrCreateName(*switchOp.getDefaultDestination()) << ";\n";
+
+ os.unindent() << "}\n";
+ os << "}\n";
+
+ return success();
+}
+
static LogicalResult printOperation(CppEmitter &emitter, emitc::ForOp forOp) {
raw_indented_ostream &os = emitter.ostream();
@@ -998,7 +1033,7 @@ static LogicalResult printFunctionBody(CppEmitter &emitter,
// trailing semicolon is handled within the printOperation function.
bool trailingSemicolon =
!isa<cf::CondBranchOp, emitc::DeclareFuncOp, emitc::ForOp,
- emitc::IfOp, emitc::VerbatimOp>(op);
+ cf::SwitchOp, emitc::IfOp, emitc::VerbatimOp>(op);
if (failed(emitter.emitOperation(
op, /*trailingSemicolon=*/trailingSemicolon)))
@@ -1496,7 +1531,7 @@ LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) {
// Builtin ops.
.Case<ModuleOp>([&](auto op) { return printOperation(*this, op); })
// CF ops.
- .Case<cf::BranchOp, cf::CondBranchOp>(
+ .Case<cf::BranchOp, cf::CondBranchOp, cf::SwitchOp>(
[&](auto op) { return printOperation(*this, op); })
// EmitC ops.
.Case<emitc::AddOp, emitc::ApplyOp, emitc::AssignOp,
diff --git a/mlir/test/Target/Cpp/switch.mlir b/mlir/test/Target/Cpp/switch.mlir
new file mode 100644
index 0000000000000..68ba2c90dde6d
--- /dev/null
+++ b/mlir/test/Target/Cpp/switch.mlir
@@ -0,0 +1,37 @@
+// RUN: mlir-translate -mlir-to-cpp -declare-variables-at-top %s | FileCheck %s
+
+func.func @switch_func(%a: i32, %b: i32, %c: i32) -> () {
+ cf.switch %b : i32, [
+ default: ^bb1(%a : i32),
+ 42: ^bb1(%b : i32),
+ 43: ^bb2(%c : i32),
+ 44: ^bb3(%c : i32)
+ ]
+
+ ^bb1(%x1 : i32) :
+ %y1 = "emitc.add" (%x1, %x1) : (i32, i32) -> i32
+ return
+
+ ^bb2(%x2 : i32) :
+ %y2 = "emitc.sub" (%x2, %x2) : (i32, i32) -> i32
+ return
+
+ ^bb3(%x3 : i32) :
+ %y3 = "emitc.mul" (%x3, %x3) : (i32, i32) -> i32
+ return
+}
+// CHECK: void switch_func(int32_t [[V0:[^ ]*]], int32_t [[V1:[^ ]*]], int32_t [[V2:[^ ]*]]) {
+// CHECK: switch([[V1:[^ ]*]]) {
+// CHECK-NEXT: case 42: {
+// CHECK-NEXT: goto label2;
+// CHECK-NEXT: }
+// CHECK-NEXT: case 43: {
+// CHECK-NEXT: goto label3;
+// CHECK-NEXT: }
+// CHECK-NEXT: case 44: {
+// CHECK-NEXT: goto label4;
+// CHECK-NEXT: }
+// CHECK-NEXT: default: {
+// CHECK-NEXT: goto label2;
+// CHECK-NEXT: }
+// CHECK-NEXT: }
|
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.
Overall this looks good to me.
You should also add a case in this check, otherwise you get a trailing semicolon after the switch body (I think, I haven't run the code).
@@ -774,6 +774,41 @@ static LogicalResult printOperation(CppEmitter &emitter, | |||
return printBinaryOperation(emitter, operation, "||"); | |||
} | |||
|
|||
static LogicalResult printOperation(CppEmitter &emitter, |
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.
Move this function up, directly below the cf.cond_br
function.
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.
Fixed.
return switchOp.emitOpError("case's value is absent for case block"); | ||
|
||
os << "\ncase " << *(iteratorCaseValues++) << ": {\n"; | ||
os.indent() << "goto "; |
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.
You need to copy the operands into the variables for the successor block arguments like 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.
Thank you! That was a critical error. Fixed.
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 will fix the test right now too. Missed it.
emitc::IfOp, emitc::VerbatimOp>(op); | ||
cf::SwitchOp, emitc::IfOp, emitc::VerbatimOp>(op); |
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.
Order this by dialect name, please.
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.
Fixed.
ac9b5ca
to
34a1d6a
Compare
✅ With the latest revision this PR passed the C/C++ code formatter. |
34a1d6a
to
c4f21c2
Compare
c4f21c2
to
cbc69f9
Compare
I think the CI check for code formatting failed without any reason. I just checked code formatting locally. |
cbc69f9
to
b0dff6b
Compare
I'll wait on the opinion of @aniragil and/or @mgehre-amd before approving. On the one hand we've removed support for several non EmitC ops from the emitter in recent months by converting these to the Emitc dialect. On the other hand we don't have EmitC specific ops for branch like instructions the cf dialect could be lowered to yet. |
I agree with @simon-camp here, not sure if we want to add support for further non-EmitC ops to the emitter. I would therefore love to hear @mgehre-amd's and @aniragil's option. If we want to add this, please also also adjust https://github.com/llvm/llvm-project/blob/main/mlir/docs/Dialects/emitc.md accordingly as part of your PR. |
I share @simon-camp's and @marbre's concerns about extending the translator. We can implement this in either order:
WDYT? |
If an |
I don't think this that's going to work as is. You can't branch from a region to a basic block in a parent region (I think). Additionally The end goal is fine, I just can't estimate how much work this involves. |
But you're in the team that thinks that an |
We can add a switch op, but it shouldn't be a terminator in my opinion. So we would be able to lower |
Hello everyone! I am grateful for this productive discussion. It was great to hear of all of you. Should I close the PR and start the issue for |
Hmm, good points. So our options is either to (a) First replace translator support for (b) First support As @marbre mentioned, option (a) is better as it only involves "going forward" with emitc's design, tought it would take a bit more time. Also, option (b) risks changing the emitted code later when we move |
I'd add some background, since @EtoAndruwa works on my team. The only reason we went for In general, I'd prefer if
I wonder how the |
That's exactly how Do your |
Ah, no, we start from I just know that we'll need to patch emitter in the end of the day, so it should go upstream. |
Thanks for the context, this indeed clarifies things!
Agreed. If some emitc pass must take unstructured control flow into account it could either analyze the emitc control flow or be executed before cf-to-emitc.
We lower |
I'd separate between these issues:
|
This PR is continuation of the [previous one](#101478). As a result, the `emitc::SwitchOp` op was developed inspired by `scf::IndexSwitchOp`. Main points of PR: - Added the `emitc::SwitchOp` op to the EmitC dialect + CppEmitter - Corresponding tests were added - Conversion from the SCF dialect to the EmitC dialect for the op
Added 'switch' operation from cf dialect to the CppEmitter.