@@ -470,7 +470,8 @@ def StoreOp : CIR_Op<"store", [
470470//===----------------------------------------------------------------------===//
471471
472472def ReturnOp : CIR_Op<"return", [ParentOneOf<["FuncOp", "ScopeOp", "IfOp",
473- "DoWhileOp", "WhileOp", "ForOp"]>,
473+ "SwitchOp", "DoWhileOp","WhileOp",
474+ "ForOp", "CaseOp"]>,
474475 Terminator]> {
475476 let summary = "Return from function";
476477 let description = [{
@@ -609,8 +610,9 @@ def ConditionOp : CIR_Op<"condition", [
609610//===----------------------------------------------------------------------===//
610611
611612def YieldOp : CIR_Op<"yield", [ReturnLike, Terminator,
612- ParentOneOf<["IfOp", "ScopeOp", "WhileOp",
613- "ForOp", "DoWhileOp"]>]> {
613+ ParentOneOf<["IfOp", "ScopeOp", "SwitchOp",
614+ "WhileOp", "ForOp", "CaseOp",
615+ "DoWhileOp"]>]> {
614616 let summary = "Represents the default branching behaviour of a region";
615617 let description = [{
616618 The `cir.yield` operation terminates regions on different CIR operations,
@@ -753,6 +755,220 @@ def ScopeOp : CIR_Op<"scope", [
753755 ];
754756}
755757
758+ //===----------------------------------------------------------------------===//
759+ // SwitchOp
760+ //===----------------------------------------------------------------------===//
761+
762+ def CaseOpKind_DT : I32EnumAttrCase<"Default", 1, "default">;
763+ def CaseOpKind_EQ : I32EnumAttrCase<"Equal", 2, "equal">;
764+ def CaseOpKind_AO : I32EnumAttrCase<"Anyof", 3, "anyof">;
765+ def CaseOpKind_RG : I32EnumAttrCase<"Range", 4, "range">;
766+
767+ def CaseOpKind : I32EnumAttr<
768+ "CaseOpKind",
769+ "case kind",
770+ [CaseOpKind_DT, CaseOpKind_EQ, CaseOpKind_AO, CaseOpKind_RG]> {
771+ let cppNamespace = "::cir";
772+ }
773+
774+ def CaseOp : CIR_Op<"case", [
775+ DeclareOpInterfaceMethods<RegionBranchOpInterface>,
776+ RecursivelySpeculatable, AutomaticAllocationScope]> {
777+ let summary = "Case operation";
778+ let description = [{
779+ The `cir.case` operation represents a case within a C/C++ switch.
780+ The `cir.case` operation must be in a `cir.switch` operation directly
781+ or indirectly.
782+
783+ The `cir.case` have 4 kinds:
784+ - `equal, <constant>`: equality of the second case operand against the
785+ condition.
786+ - `anyof, [constant-list]`: equals to any of the values in a subsequent
787+ following list.
788+ - `range, [lower-bound, upper-bound]`: the condition is within the closed
789+ interval.
790+ - `default`: any other value.
791+
792+ Each case region must be explicitly terminated.
793+ }];
794+
795+ let arguments = (ins ArrayAttr:$value, CaseOpKind:$kind);
796+ let regions = (region AnyRegion:$caseRegion);
797+
798+ let assemblyFormat = "`(` $kind `,` $value `)` $caseRegion attr-dict";
799+
800+ let skipDefaultBuilders = 1;
801+ let builders = [
802+ OpBuilder<(ins "mlir::ArrayAttr":$value,
803+ "CaseOpKind":$kind,
804+ "mlir::OpBuilder::InsertPoint &":$insertPoint)>
805+ ];
806+ }
807+
808+ def SwitchOp : CIR_Op<"switch",
809+ [SameVariadicOperandSize,
810+ DeclareOpInterfaceMethods<RegionBranchOpInterface>,
811+ RecursivelySpeculatable, AutomaticAllocationScope, NoRegionArguments]> {
812+ let summary = "Switch operation";
813+ let description = [{
814+ The `cir.switch` operation represents C/C++ switch functionality for
815+ conditionally executing multiple regions of code. The operand to an switch
816+ is an integral condition value.
817+
818+ The set of `cir.case` operations and their enclosing `cir.switch`
819+ represent the semantics of a C/C++ switch statement. Users can use
820+ `collectCases(llvm::SmallVector<CaseOp> &cases)` to collect the `cir.case`
821+ operation in the `cir.switch` operation easily.
822+
823+ The `cir.case` operations don't have to be in the region of `cir.switch`
824+ directly. However, when all the `cir.case` operations live in the region
825+ of `cir.switch` directly and there are no other operations except the ending
826+ `cir.yield` operation in the region of `cir.switch` directly, we say the
827+ `cir.switch` operation is in a simple form. Users can use
828+ `bool isSimpleForm(llvm::SmallVector<CaseOp> &cases)` member function to
829+ detect if the `cir.switch` operation is in a simple form. The simple form
830+ makes it easier for analyses to handle the `cir.switch` operation
831+ and makes the boundary to give up clear.
832+
833+ To make the simple form as common as possible, CIR code generation attaches
834+ operations corresponding to the statements that lives between top level
835+ cases into the closest `cir.case` operation.
836+
837+ For example,
838+
839+ ```
840+ switch(int cond) {
841+ case 4:
842+ a++;
843+ b++;
844+ case 5:
845+ c++;
846+
847+ ...
848+ }
849+ ```
850+
851+ The statement `b++` is not a sub-statement of the case statement `case 4`.
852+ But to make the generated `cir.switch` a simple form, we will attach the
853+ statement `b++` into the closest `cir.case` operation. So that the generated
854+ code will be like:
855+
856+ ```
857+ cir.switch(int cond) {
858+ cir.case(equal, 4) {
859+ a++;
860+ b++;
861+ cir.yield
862+ }
863+ cir.case(equal, 5) {
864+ c++;
865+ cir.yield
866+ }
867+ ...
868+ }
869+ ```
870+
871+ For the same reason, we will hoist the case statement as the substatement
872+ of another case statement so that they will be in the same level. For
873+ example,
874+
875+ ```
876+ switch(int cond) {
877+ case 4:
878+ default;
879+ case 5:
880+ a++;
881+ ...
882+ }
883+ ```
884+
885+ will be generated as
886+
887+ ```
888+ cir.switch(int cond) {
889+ cir.case(equal, 4) {
890+ cir.yield
891+ }
892+ cir.case(default) {
893+ cir.yield
894+ }
895+ cir.case(equal, 5) {
896+ a++;
897+ cir.yield
898+ }
899+ ...
900+ }
901+ ```
902+
903+ The cir.switch is not be considered "simple" if any of the following is
904+ true:
905+ - There are case statements of the switch statement that are scope
906+ other than the top level compound statement scope. Note that a case
907+ statement itself doesn't form a scope.
908+ - The sub-statement of the switch statement is not a compound statement.
909+ - There is any code before the first case statement. For example,
910+
911+ ```
912+ switch(int cond) {
913+ l:
914+ b++;
915+
916+ case 4:
917+ a++;
918+ break;
919+
920+ case 5:
921+ goto l;
922+ ...
923+ }
924+ ```
925+
926+ the generated CIR for this non-simple switch would be:
927+
928+ ```
929+ cir.switch(int cond) {
930+ cir.label "l"
931+ b++;
932+ cir.case(4) {
933+ a++;
934+ cir.break
935+ }
936+ cir.case(5) {
937+ goto "l"
938+ }
939+ cir.yield
940+ }
941+ ```
942+ }];
943+
944+ let arguments = (ins CIR_IntType:$condition);
945+
946+ let regions = (region AnyRegion:$body);
947+
948+ let skipDefaultBuilders = 1;
949+ let builders = [
950+ OpBuilder<(ins "mlir::Value":$condition,
951+ "BuilderOpStateCallbackRef":$switchBuilder)>
952+ ];
953+
954+ let assemblyFormat = [{
955+ custom<SwitchOp>(
956+ $body, $condition, type($condition)
957+ )
958+ attr-dict
959+ }];
960+
961+ let extraClassDeclaration = [{
962+ // Collect cases in the switch.
963+ void collectCases(llvm::SmallVectorImpl<CaseOp> &cases);
964+
965+ // Check if the switch is in a simple form.
966+ // If yes, collect the cases to \param cases.
967+ // This is an expensive and need to be used with caution.
968+ bool isSimpleForm(llvm::SmallVectorImpl<CaseOp> &cases);
969+ }];
970+ }
971+
756972//===----------------------------------------------------------------------===//
757973// BrOp
758974//===----------------------------------------------------------------------===//
0 commit comments