[clang] [CIR] Upstream initial support for switch statements (PR #137106)
Andy Kaylor via cfe-commits
cfe-commits at lists.llvm.org
Fri Apr 25 16:57:53 PDT 2025
================
@@ -753,6 +755,221 @@ def ScopeOp : CIR_Op<"scope", [
];
}
+//===----------------------------------------------------------------------===//
+// SwitchOp
+//===----------------------------------------------------------------------===//
+
+def CaseOpKind_DT : I32EnumAttrCase<"Default", 1, "default">;
+def CaseOpKind_EQ : I32EnumAttrCase<"Equal", 2, "equal">;
+def CaseOpKind_AO : I32EnumAttrCase<"Anyof", 3, "anyof">;
+def CaseOpKind_RG : I32EnumAttrCase<"Range", 4, "range">;
+
+def CaseOpKind : I32EnumAttr<
+ "CaseOpKind",
+ "case kind",
+ [CaseOpKind_DT, CaseOpKind_EQ, CaseOpKind_AO, CaseOpKind_RG]> {
+ let cppNamespace = "::cir";
+}
+
+def CaseOp : CIR_Op<"case", [
+ DeclareOpInterfaceMethods<RegionBranchOpInterface>,
+ RecursivelySpeculatable, AutomaticAllocationScope]> {
+ let summary = "Case operation";
+ let description = [{
+ The `cir.case` operation represents a case within a C/C++ switch.
+ The `cir.case` operation must be in a `cir.switch` operation directly
+ or indirectly.
+
+ The `cir.case` have 4 kinds:
+ - `equal, <constant>`: equality of the second case operand against the
+ condition.
+ - `anyof, [constant-list]`: equals to any of the values in a subsequent
+ following list.
+ - `range, [lower-bound, upper-bound]`: the condition is within the closed
+ interval.
+ - `default`: any other value.
+
+ Each case region must be explicitly terminated.
+ }];
+
+ let arguments = (ins ArrayAttr:$value, CaseOpKind:$kind);
+ let regions = (region AnyRegion:$caseRegion);
+
+ let assemblyFormat = "`(` $kind `,` $value `)` $caseRegion attr-dict";
+
+ let skipDefaultBuilders = 1;
+ let builders = [
+ OpBuilder<(ins "mlir::ArrayAttr":$value,
+ "CaseOpKind":$kind,
+ "mlir::OpBuilder::InsertPoint &":$insertPoint)>
+ ];
+}
+
+def SwitchOp : CIR_Op<"switch",
+ [SameVariadicOperandSize,
+ DeclareOpInterfaceMethods<RegionBranchOpInterface>,
+ RecursivelySpeculatable, AutomaticAllocationScope, NoRegionArguments]> {
+ let summary = "Switch operation";
+ let description = [{
+ The `cir.switch` operation represents C/C++ switch functionality for
+ conditionally executing multiple regions of code. The operand to an switch
+ is an integral condition value.
+
+ The set of `cir.case` operations and their enclosing `cir.switch`
+ represents the semantics of a C/C++ switch statement. Users can use
+ `collectCases(llvm::SmallVector<CaseOp> &cases)` to collect the `cir.case`
+ operation in the `cir.switch` operation easily.
+
+ The `cir.case` operations doesn't have to be in the region of `cir.switch`
+ directly. However, when all the `cir.case` operations lives in the region
+ of `cir.switch` directly and there is no other operations except the ending
+ `cir.yield` operation in the region of `cir.switch` directly, we call the
+ `cir.switch` operation is in a simple form. Users can use
+ `bool isSimpleForm(llvm::SmallVector<CaseOp> &cases)` member function to
+ detect if the `cir.switch` operation is in a simple form. The simple form
+ makes analysis easier to handle the `cir.switch` operation
+ and makes the boundary to give up pretty clear.
+
+ To make the simple form as common as possible, CIR code generation attaches
+ operations corresponding to the statements that lives between top level
+ cases into the closest `cir.case` operation.
+
+ For example,
+
+ ```
+ switch(int cond) {
+ case 4:
+ a++;
+
+ b++;
+ case 5;
+ c++;
+
+ ...
+ }
+ ```
+
+ The statement `b++` is not a sub-statement of the case statement `case 4`.
+ But to make the generated `cir.switch` a simple form, we will attach the
+ statement `b++` into the closest `cir.case` operation. So that the generated
+ code will be like:
+
+ ```
+ cir.switch(int cond) {
+ cir.case(equal, 4) {
+ a++;
+ b++;
+ cir.yield
+ }
+ cir.case(equal, 5) {
+ c++;
+ cir.yield
+ }
+ ...
+ }
+ ```
+
+ For the same reason, we will hoist the case statement as the substatement
+ of another case statement so that they will be in the same level. For
+ example,
+
+ ```
+ switch(int cond) {
+ case 4:
+ default;
+ case 5;
+ a++;
+ ...
+ }
+ ```
+
+ will be generated as
+
+ ```
+ cir.switch(int cond) {
+ cir.case(equal, 4) {
+ cir.yield
+ }
+ cir.case(default) {
+ cir.yield
+ }
+ cir.case(equal, 5) {
+ a++;
+ cir.yield
+ }
+ ...
+ }
+ ```
+
+ The cir.switch might not be considered "simple" if any of the following is
+ true:
+ - There are case statements of the switch statement lives in other scopes
+ other than the top level compound statement scope. Note that a case
+ statement itself doesn't form a scope.
+ - The sub-statement of the switch statement is not a compound statement.
+ - There are codes before the first case statement. For example,
+
+ ```
+ switch(int cond) {
+ l:
+ b++;
+
+ case 4:
+ a++;
+ break;
+
+ case 5:
+ goto l;
+ ...
+ }
+ ```
+
+ the generated CIR for this non-simple switch would be:
+
+ ```
+ cir.switch(int cond) {
+ cir.label "l"
+ b++;
+ cir.case(4) {
+ a++;
+ cir.break
+ }
+ cir.case(5) {
+ goto "l"
+ }
+ cir.yield
+ }
+ ```
+ }];
+
+ let arguments = (ins CIR_IntType:$condition);
+
+ let regions = (region AnyRegion:$body);
+
+ let skipDefaultBuilders = 1;
+ let builders = [
+ OpBuilder<(ins "mlir::Value":$condition,
+ "llvm::function_ref<void(mlir::OpBuilder &, mlir::Location, mlir::OperationState &)>":$switchBuilder)>
----------------
andykaylor wrote:
Can you reformat this to fit within 80 characters? Maybe by defining an alias similar to BuilderCallbackRef (as defined in CIRDialect.h)?
https://github.com/llvm/llvm-project/pull/137106
More information about the cfe-commits
mailing list