[Mlir-commits] [mlir] 97f0ab7 - [mlir][emitc] Add 'emitc.switch' op to the dialect (#102331)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Fri Aug 16 09:25:09 PDT 2024


Author: Andrey Timonin
Date: 2024-08-16T18:25:06+02:00
New Revision: 97f0ab71c002e8a14142be3a117b3091ab75c552

URL: https://github.com/llvm/llvm-project/commit/97f0ab71c002e8a14142be3a117b3091ab75c552
DIFF: https://github.com/llvm/llvm-project/commit/97f0ab71c002e8a14142be3a117b3091ab75c552.diff

LOG: [mlir][emitc] Add 'emitc.switch' op to the dialect (#102331)

This PR is continuation of the [previous
one](https://github.com/llvm/llvm-project/pull/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: 
    mlir/test/Conversion/SCFToEmitC/switch.mlir
    mlir/test/Target/Cpp/switch.mlir

Modified: 
    mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
    mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
    mlir/lib/Dialect/EmitC/IR/EmitC.cpp
    mlir/lib/Target/Cpp/TranslateToCpp.cpp
    mlir/test/Dialect/EmitC/invalid_ops.mlir
    mlir/test/Dialect/EmitC/ops.mlir
    mlir/test/Target/Cpp/invalid.mlir

Removed: 
    


################################################################################
diff  --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index 16fa58ce47013d..40903b4e288ca6 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -1188,7 +1188,7 @@ def EmitC_AssignOp : EmitC_Op<"assign", []> {
 }
 
 def EmitC_YieldOp : EmitC_Op<"yield",
-      [Pure, Terminator, ParentOneOf<["ExpressionOp", "IfOp", "ForOp"]>]> {
+      [Pure, Terminator, ParentOneOf<["ExpressionOp", "IfOp", "ForOp", "SwitchOp"]>]> {
   let summary = "Block termination operation";
   let description = [{
     The `emitc.yield` terminates its parent EmitC op's region, optionally yielding
@@ -1302,5 +1302,87 @@ def EmitC_SubscriptOp : EmitC_Op<"subscript", []> {
   let assemblyFormat = "$value `[` $indices `]` attr-dict `:` functional-type(operands, results)";
 }
 
+def EmitC_SwitchOp : EmitC_Op<"switch", [RecursiveMemoryEffects,
+    SingleBlockImplicitTerminator<"emitc::YieldOp">,
+    DeclareOpInterfaceMethods<RegionBranchOpInterface,
+                              ["getRegionInvocationBounds",
+                               "getEntrySuccessorRegions"]>]> {
+  let summary = "Switch operation";
+  let description = [{
+    The `emitc.switch` is a control-flow operation that branches to one of
+    the given regions based on the values of the argument and the cases.
+    The operand to a switch operation is a opaque, integral or pointer
+    wide types.
+
+    The operation always has a "default" region and any number of case regions
+    denoted by integer constants. Control-flow transfers to the case region
+    whose constant value equals the value of the argument. If the argument does
+    not equal any of the case values, control-flow transfer to the "default"
+    region.
+
+    The operation does not return any value. Moreover, case regions must be
+    explicitly terminated using the `emitc.yield` operation. Default region is
+    yielded implicitly.
+
+    Example:
+
+    ```mlir
+    // Example:
+    emitc.switch %0 : i32
+    case 2 {
+      %1 = emitc.call_opaque "func_b" () : () -> i32
+      emitc.yield
+    }
+    case 5 {
+      %2 = emitc.call_opaque "func_a" () : () -> i32
+      emitc.yield
+    }
+    default {
+      %3 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+      emitc.call_opaque "func2" (%3) : (f32) -> ()
+    }
+    ```
+    ```c++
+    // Code emitted for the operations above.
+    switch (v1) {
+    case 2: {
+      int32_t v2 = func_b();
+      break;
+    }
+    case 5: {
+      int32_t v3 = func_a();
+      break;
+    }
+    default: {
+      float v4 = 4.200000000e+01f;
+      func2(v4);
+      break;
+    }
+    ```
+  }];
+
+  let arguments = (ins IntegerIndexOrOpaqueType:$arg, DenseI64ArrayAttr:$cases);
+  let results = (outs);
+  let regions = (region SizedRegion<1>:$defaultRegion,
+                        VariadicRegion<SizedRegion<1>>:$caseRegions);
+
+  let assemblyFormat = [{
+    $arg `:` type($arg) attr-dict custom<SwitchCases>($cases, $caseRegions) `\n`
+    `` `default` $defaultRegion
+  }];
+
+  let extraClassDeclaration = [{
+    /// Get the number of cases.
+    unsigned getNumCases();
+
+    /// Get the default region body.
+    Block &getDefaultBlock();
+
+    /// Get the body of a case region.
+    Block &getCaseBlock(unsigned idx);
+  }];
+
+  let hasVerifier = 1;
+}
 
 #endif // MLIR_DIALECT_EMITC_IR_EMITC

diff  --git a/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp b/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
index 0a892422252554..ede811c6e1bb1e 100644
--- a/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
+++ b/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
@@ -94,6 +94,19 @@ static void lowerYield(SmallVector<Value> &resultVariables,
   rewriter.eraseOp(yield);
 }
 
+// Lower the contents of an scf::if/scf::index_switch regions to an
+// emitc::if/emitc::switch region. The contents of the lowering region is
+// moved into the respective lowered region, but the scf::yield is replaced not
+// only with an emitc::yield, but also with a sequence of emitc::assign ops that
+// set the yielded values into the result variables.
+static void lowerRegion(SmallVector<Value> &resultVariables,
+                        PatternRewriter &rewriter, Region &region,
+                        Region &loweredRegion) {
+  rewriter.inlineRegionBefore(region, loweredRegion, loweredRegion.end());
+  Operation *terminator = loweredRegion.back().getTerminator();
+  lowerYield(resultVariables, rewriter, cast<scf::YieldOp>(terminator));
+}
+
 LogicalResult ForLowering::matchAndRewrite(ForOp forOp,
                                            PatternRewriter &rewriter) const {
   Location loc = forOp.getLoc();
@@ -145,18 +158,6 @@ LogicalResult IfLowering::matchAndRewrite(IfOp ifOp,
   SmallVector<Value> resultVariables =
       createVariablesForResults(ifOp, rewriter);
 
-  // Utility function to lower the contents of an scf::if region to an emitc::if
-  // region. The contents of the scf::if regions is moved into the respective
-  // emitc::if regions, but the scf::yield is replaced not only with an
-  // emitc::yield, but also with a sequence of emitc::assign ops that set the
-  // yielded values into the result variables.
-  auto lowerRegion = [&resultVariables, &rewriter](Region &region,
-                                                   Region &loweredRegion) {
-    rewriter.inlineRegionBefore(region, loweredRegion, loweredRegion.end());
-    Operation *terminator = loweredRegion.back().getTerminator();
-    lowerYield(resultVariables, rewriter, cast<scf::YieldOp>(terminator));
-  };
-
   Region &thenRegion = ifOp.getThenRegion();
   Region &elseRegion = ifOp.getElseRegion();
 
@@ -166,20 +167,59 @@ LogicalResult IfLowering::matchAndRewrite(IfOp ifOp,
       rewriter.create<emitc::IfOp>(loc, ifOp.getCondition(), false, false);
 
   Region &loweredThenRegion = loweredIf.getThenRegion();
-  lowerRegion(thenRegion, loweredThenRegion);
+  lowerRegion(resultVariables, rewriter, thenRegion, loweredThenRegion);
 
   if (hasElseBlock) {
     Region &loweredElseRegion = loweredIf.getElseRegion();
-    lowerRegion(elseRegion, loweredElseRegion);
+    lowerRegion(resultVariables, rewriter, elseRegion, loweredElseRegion);
   }
 
   rewriter.replaceOp(ifOp, resultVariables);
   return success();
 }
 
+// Lower scf::index_switch to emitc::switch, implementing result values as
+// emitc::variable's updated within the case and default regions.
+struct IndexSwitchOpLowering : public OpRewritePattern<IndexSwitchOp> {
+  using OpRewritePattern<IndexSwitchOp>::OpRewritePattern;
+
+  LogicalResult matchAndRewrite(IndexSwitchOp indexSwitchOp,
+                                PatternRewriter &rewriter) const override;
+};
+
+LogicalResult
+IndexSwitchOpLowering::matchAndRewrite(IndexSwitchOp indexSwitchOp,
+                                       PatternRewriter &rewriter) const {
+  Location loc = indexSwitchOp.getLoc();
+
+  // Create an emitc::variable op for each result. These variables will be
+  // assigned to by emitc::assign ops within the case and default regions.
+  SmallVector<Value> resultVariables =
+      createVariablesForResults(indexSwitchOp, rewriter);
+
+  auto loweredSwitch = rewriter.create<emitc::SwitchOp>(
+      loc, indexSwitchOp.getArg(), indexSwitchOp.getCases(),
+      indexSwitchOp.getNumCases());
+
+  // Lowering all case regions.
+  for (auto pair : llvm::zip(indexSwitchOp.getCaseRegions(),
+                             loweredSwitch.getCaseRegions())) {
+    lowerRegion(resultVariables, rewriter, std::get<0>(pair),
+                std::get<1>(pair));
+  }
+
+  // Lowering default region.
+  lowerRegion(resultVariables, rewriter, indexSwitchOp.getDefaultRegion(),
+              loweredSwitch.getDefaultRegion());
+
+  rewriter.replaceOp(indexSwitchOp, resultVariables);
+  return success();
+}
+
 void mlir::populateSCFToEmitCConversionPatterns(RewritePatternSet &patterns) {
   patterns.add<ForLowering>(patterns.getContext());
   patterns.add<IfLowering>(patterns.getContext());
+  patterns.add<IndexSwitchOpLowering>(patterns.getContext());
 }
 
 void SCFToEmitCPass::runOnOperation() {
@@ -188,7 +228,7 @@ void SCFToEmitCPass::runOnOperation() {
 
   // Configure conversion to lower out SCF operations.
   ConversionTarget target(getContext());
-  target.addIllegalOp<scf::ForOp, scf::IfOp>();
+  target.addIllegalOp<scf::ForOp, scf::IfOp, scf::IndexSwitchOp>();
   target.markUnknownOpDynamicallyLegal([](Operation *) { return true; });
   if (failed(
           applyPartialConversion(getOperation(), target, std::move(patterns))))

diff  --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
index 70e3e728e01950..d731a6756ff630 100644
--- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
+++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
@@ -1096,6 +1096,138 @@ GetGlobalOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
   return success();
 }
 
+//===----------------------------------------------------------------------===//
+// SwitchOp
+//===----------------------------------------------------------------------===//
+
+/// Parse the case regions and values.
+static ParseResult
+parseSwitchCases(OpAsmParser &parser, DenseI64ArrayAttr &cases,
+                 SmallVectorImpl<std::unique_ptr<Region>> &caseRegions) {
+  SmallVector<int64_t> caseValues;
+  while (succeeded(parser.parseOptionalKeyword("case"))) {
+    int64_t value;
+    Region &region = *caseRegions.emplace_back(std::make_unique<Region>());
+    if (parser.parseInteger(value) ||
+        parser.parseRegion(region, /*arguments=*/{}))
+      return failure();
+    caseValues.push_back(value);
+  }
+  cases = parser.getBuilder().getDenseI64ArrayAttr(caseValues);
+  return success();
+}
+
+/// Print the case regions and values.
+static void printSwitchCases(OpAsmPrinter &p, Operation *op,
+                             DenseI64ArrayAttr cases, RegionRange caseRegions) {
+  for (auto [value, region] : llvm::zip(cases.asArrayRef(), caseRegions)) {
+    p.printNewline();
+    p << "case " << value << ' ';
+    p.printRegion(*region, /*printEntryBlockArgs=*/false);
+  }
+}
+
+static LogicalResult verifyRegion(emitc::SwitchOp op, Region &region,
+                                  const Twine &name) {
+  auto yield = dyn_cast<emitc::YieldOp>(region.front().back());
+  if (!yield)
+    return op.emitOpError("expected region to end with emitc.yield, but got ")
+           << region.front().back().getName();
+
+  if (yield.getNumOperands() != 0) {
+    return (op.emitOpError("expected each region to return ")
+            << "0 values, but " << name << " returns "
+            << yield.getNumOperands())
+               .attachNote(yield.getLoc())
+           << "see yield operation here";
+  }
+
+  return success();
+}
+
+LogicalResult emitc::SwitchOp::verify() {
+  if (!isIntegerIndexOrOpaqueType(getArg().getType()))
+    return emitOpError("unsupported type ") << getArg().getType();
+
+  if (getCases().size() != getCaseRegions().size()) {
+    return emitOpError("has ")
+           << getCaseRegions().size() << " case regions but "
+           << getCases().size() << " case values";
+  }
+
+  DenseSet<int64_t> valueSet;
+  for (int64_t value : getCases())
+    if (!valueSet.insert(value).second)
+      return emitOpError("has duplicate case value: ") << value;
+
+  if (failed(verifyRegion(*this, getDefaultRegion(), "default region")))
+    return failure();
+
+  for (auto [idx, caseRegion] : llvm::enumerate(getCaseRegions()))
+    if (failed(verifyRegion(*this, caseRegion, "case region #" + Twine(idx))))
+      return failure();
+
+  return success();
+}
+
+unsigned emitc::SwitchOp::getNumCases() { return getCases().size(); }
+
+Block &emitc::SwitchOp::getDefaultBlock() { return getDefaultRegion().front(); }
+
+Block &emitc::SwitchOp::getCaseBlock(unsigned idx) {
+  assert(idx < getNumCases() && "case index out-of-bounds");
+  return getCaseRegions()[idx].front();
+}
+
+void SwitchOp::getSuccessorRegions(
+    RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &successors) {
+  llvm::copy(getRegions(), std::back_inserter(successors));
+}
+
+void SwitchOp::getEntrySuccessorRegions(
+    ArrayRef<Attribute> operands,
+    SmallVectorImpl<RegionSuccessor> &successors) {
+  FoldAdaptor adaptor(operands, *this);
+
+  // If a constant was not provided, all regions are possible successors.
+  auto arg = dyn_cast_or_null<IntegerAttr>(adaptor.getArg());
+  if (!arg) {
+    llvm::copy(getRegions(), std::back_inserter(successors));
+    return;
+  }
+
+  // Otherwise, try to find a case with a matching value. If not, the
+  // default region is the only successor.
+  for (auto [caseValue, caseRegion] : llvm::zip(getCases(), getCaseRegions())) {
+    if (caseValue == arg.getInt()) {
+      successors.emplace_back(&caseRegion);
+      return;
+    }
+  }
+  successors.emplace_back(&getDefaultRegion());
+}
+
+void SwitchOp::getRegionInvocationBounds(
+    ArrayRef<Attribute> operands, SmallVectorImpl<InvocationBounds> &bounds) {
+  auto operandValue = llvm::dyn_cast_or_null<IntegerAttr>(operands.front());
+  if (!operandValue) {
+    // All regions are invoked at most once.
+    bounds.append(getNumRegions(), InvocationBounds(/*lb=*/0, /*ub=*/1));
+    return;
+  }
+
+  unsigned liveIndex = getNumRegions() - 1;
+  const auto *iteratorToInt = llvm::find(getCases(), operandValue.getInt());
+
+  liveIndex = iteratorToInt != getCases().end()
+                  ? std::distance(getCases().begin(), iteratorToInt)
+                  : liveIndex;
+
+  for (unsigned regIndex = 0, regNum = getNumRegions(); regIndex < regNum;
+       ++regIndex)
+    bounds.emplace_back(/*lb=*/0, /*ub=*/regIndex == liveIndex);
+}
+
 //===----------------------------------------------------------------------===//
 // TableGen'd op method definitions
 //===----------------------------------------------------------------------===//

diff  --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
index 1dadb9dd691e70..8e112e6f7dda65 100644
--- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp
+++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
@@ -449,6 +449,43 @@ static LogicalResult printOperation(CppEmitter &emitter, emitc::SubOp subOp) {
   return printBinaryOperation(emitter, operation, "-");
 }
 
+static LogicalResult emitSwitchCase(CppEmitter &emitter,
+                                    raw_indented_ostream &os, Region &region) {
+  for (Region::OpIterator iteratorOp = region.op_begin(), end = region.op_end();
+       std::next(iteratorOp) != end; ++iteratorOp) {
+    if (failed(emitter.emitOperation(*iteratorOp, /*trailingSemicolon=*/true)))
+      return failure();
+  }
+  os << "break;\n";
+  return success();
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    emitc::SwitchOp switchOp) {
+  raw_indented_ostream &os = emitter.ostream();
+
+  os << "\nswitch (" << emitter.getOrCreateName(switchOp.getArg()) << ") {";
+
+  for (auto pair : llvm::zip(switchOp.getCases(), switchOp.getCaseRegions())) {
+    os << "\ncase " << std::get<0>(pair) << ": {\n";
+    os.indent();
+
+    if (failed(emitSwitchCase(emitter, os, std::get<1>(pair))))
+      return failure();
+
+    os.unindent() << "}";
+  }
+
+  os << "\ndefault: {\n";
+  os.indent();
+
+  if (failed(emitSwitchCase(emitter, os, switchOp.getDefaultRegion())))
+    return failure();
+
+  os.unindent() << "}";
+  return success();
+}
+
 static LogicalResult printOperation(CppEmitter &emitter, emitc::CmpOp cmpOp) {
   Operation *operation = cmpOp.getOperation();
 
@@ -998,7 +1035,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);
+               emitc::IfOp, emitc::SwitchOp, emitc::VerbatimOp>(op);
 
       if (failed(emitter.emitOperation(
               op, /*trailingSemicolon=*/trailingSemicolon)))
@@ -1509,8 +1546,8 @@ LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) {
                 emitc::GlobalOp, emitc::IfOp, emitc::IncludeOp,
                 emitc::LogicalAndOp, emitc::LogicalNotOp, emitc::LogicalOrOp,
                 emitc::MulOp, emitc::RemOp, emitc::ReturnOp, emitc::SubOp,
-                emitc::UnaryMinusOp, emitc::UnaryPlusOp, emitc::VariableOp,
-                emitc::VerbatimOp>(
+                emitc::SwitchOp, emitc::UnaryMinusOp, emitc::UnaryPlusOp,
+                emitc::VariableOp, emitc::VerbatimOp>(
               [&](auto op) { return printOperation(*this, op); })
           // Func ops.
           .Case<func::CallOp, func::FuncOp, func::ReturnOp>(

diff  --git a/mlir/test/Conversion/SCFToEmitC/switch.mlir b/mlir/test/Conversion/SCFToEmitC/switch.mlir
new file mode 100644
index 00000000000000..659d9f43963efa
--- /dev/null
+++ b/mlir/test/Conversion/SCFToEmitC/switch.mlir
@@ -0,0 +1,117 @@
+// RUN: mlir-opt -allow-unregistered-dialect -convert-scf-to-emitc %s | FileCheck %s
+
+// CHECK-LABEL:   func.func @switch_no_result(
+// CHECK-SAME:                                %[[VAL_0:.*]]: index) {
+// CHECK:           emitc.switch %[[VAL_0]]
+// CHECK:           case 2 {
+// CHECK:             %[[VAL_1:.*]] = arith.constant 10 : i32
+// CHECK:             emitc.yield
+// CHECK:           }
+// CHECK:           case 5 {
+// CHECK:             %[[VAL_2:.*]] = arith.constant 20 : i32
+// CHECK:             emitc.yield
+// CHECK:           }
+// CHECK:           default {
+// CHECK:             %[[VAL_3:.*]] = arith.constant 30 : i32
+// CHECK:           }
+// CHECK:           return
+// CHECK:         }
+func.func @switch_no_result(%arg0 : index) {
+    scf.index_switch %arg0
+    case 2 {
+      %1 = arith.constant 10 : i32
+      scf.yield
+    }
+    case 5 {
+      %2 = arith.constant 20 : i32
+      scf.yield
+    }
+    default {
+      %3 = arith.constant 30 : i32
+    }
+  return
+}
+
+// CHECK-LABEL:   func.func @switch_one_result(
+// CHECK-SAME:                                 %[[VAL_0:.*]]: index) {
+// CHECK:           %[[VAL_1:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> i32
+// CHECK:           emitc.switch %[[VAL_0]]
+// CHECK:           case 2 {
+// CHECK:             %[[VAL_2:.*]] = arith.constant 10 : i32
+// CHECK:             emitc.assign %[[VAL_2]] : i32 to %[[VAL_1]] : i32
+// CHECK:             emitc.yield
+// CHECK:           }
+// CHECK:           case 5 {
+// CHECK:             %[[VAL_3:.*]] = arith.constant 20 : i32
+// CHECK:             emitc.assign %[[VAL_3]] : i32 to %[[VAL_1]] : i32
+// CHECK:             emitc.yield
+// CHECK:           }
+// CHECK:           default {
+// CHECK:             %[[VAL_4:.*]] = arith.constant 30 : i32
+// CHECK:             emitc.assign %[[VAL_4]] : i32 to %[[VAL_1]] : i32
+// CHECK:           }
+// CHECK:           return
+// CHECK:         }
+func.func @switch_one_result(%arg0 : index) {
+    %0 = scf.index_switch %arg0 -> i32
+    case 2 {
+      %1 = arith.constant 10 : i32
+      scf.yield %1 : i32
+    }
+    case 5 {
+      %2 = arith.constant 20 : i32
+      scf.yield %2 : i32
+    }
+    default {
+      %3 = arith.constant 30 : i32
+      scf.yield %3 : i32
+    }
+  return
+}
+
+// CHECK-LABEL:   func.func @switch_two_results(
+// CHECK-SAME:                                  %[[VAL_0:.*]]: index) {
+// CHECK:           %[[VAL_1:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> i32
+// CHECK:           %[[VAL_2:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> f32
+// CHECK:           emitc.switch %[[VAL_0]]
+// CHECK:           case 2 {
+// CHECK:             %[[VAL_3:.*]] = arith.constant 10 : i32
+// CHECK:             %[[VAL_4:.*]] = arith.constant 1.200000e+00 : f32
+// CHECK:             emitc.assign %[[VAL_3]] : i32 to %[[VAL_1]] : i32
+// CHECK:             emitc.assign %[[VAL_4]] : f32 to %[[VAL_2]] : f32
+// CHECK:             emitc.yield
+// CHECK:           }
+// CHECK:           case 5 {
+// CHECK:             %[[VAL_5:.*]] = arith.constant 20 : i32
+// CHECK:             %[[VAL_6:.*]] = arith.constant 2.400000e+00 : f32
+// CHECK:             emitc.assign %[[VAL_5]] : i32 to %[[VAL_1]] : i32
+// CHECK:             emitc.assign %[[VAL_6]] : f32 to %[[VAL_2]] : f32
+// CHECK:             emitc.yield
+// CHECK:           }
+// CHECK:           default {
+// CHECK:             %[[VAL_7:.*]] = arith.constant 30 : i32
+// CHECK:             %[[VAL_8:.*]] = arith.constant 3.600000e+00 : f32
+// CHECK:             emitc.assign %[[VAL_7]] : i32 to %[[VAL_1]] : i32
+// CHECK:             emitc.assign %[[VAL_8]] : f32 to %[[VAL_2]] : f32
+// CHECK:           }
+// CHECK:           return
+// CHECK:         }
+func.func @switch_two_results(%arg0 : index) {
+    %0, %1 = scf.index_switch %arg0 -> i32, f32
+    case 2 {
+      %2 = arith.constant 10 : i32
+      %3 = arith.constant 1.2 : f32
+      scf.yield %2, %3 : i32, f32
+    }
+    case 5 {
+      %4 = arith.constant 20 : i32
+      %5 = arith.constant 2.4 : f32
+      scf.yield %4, %5 : i32, f32
+    }
+    default {
+      %6 = arith.constant 30 : i32
+      %7 = arith.constant 3.6 : f32
+      scf.yield %6, %7 : i32, f32
+    }
+  return
+}

diff  --git a/mlir/test/Dialect/EmitC/invalid_ops.mlir b/mlir/test/Dialect/EmitC/invalid_ops.mlir
index 4181b726593e4a..4b5bcf46c1aab9 100644
--- a/mlir/test/Dialect/EmitC/invalid_ops.mlir
+++ b/mlir/test/Dialect/EmitC/invalid_ops.mlir
@@ -227,7 +227,7 @@ func.func @sub_pointer_pointer(%arg0: !emitc.ptr<f32>, %arg1: !emitc.ptr<f32>) {
 // -----
 
 func.func @test_misplaced_yield() {
-  // expected-error @+1 {{'emitc.yield' op expects parent op to be one of 'emitc.expression, emitc.if, emitc.for'}}
+  // expected-error @+1 {{'emitc.yield' op expects parent op to be one of 'emitc.expression, emitc.if, emitc.for, emitc.switch'}}
   emitc.yield
   return
 }
@@ -466,3 +466,89 @@ func.func @member_of_ptr(%arg0: i32) {
   %0 = "emitc.member_of_ptr" (%arg0) {member = "a"} : (i32) -> i32
   return
 }
+
+// -----
+
+func.func @emitc_switch() {
+  %0 = "emitc.variable"(){value = 1 : i16} : () -> i16
+
+  // expected-error at +1 {{'emitc.switch' op expected region to end with emitc.yield, but got emitc.call_opaque}}
+  emitc.switch %0 : i16
+  case 2 {
+    %1 = emitc.call_opaque "func_b" () : () -> i32
+  }
+  case 5 {
+    %2 = emitc.call_opaque "func_a" () : () -> i32
+    emitc.yield
+  }
+  default {
+    %3 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.yield
+  }
+  return
+}
+
+// -----
+
+func.func @emitc_switch() {
+  %0 = "emitc.variable"(){value = 1 : i32} : () -> i32
+
+  emitc.switch %0 : i32
+  case 2 {
+    %1 = emitc.call_opaque "func_b" () : () -> i32
+    emitc.yield
+  }
+  // expected-error at +1 {{custom op 'emitc.switch' expected integer value}}
+  case {
+    %2 = emitc.call_opaque "func_a" () : () -> i32
+    emitc.yield
+  }
+  default {
+    %3 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.yield
+  }
+  return
+}
+
+// -----
+
+func.func @emitc_switch() {
+  %0 = "emitc.variable"(){value = 1 : i8} : () -> i8
+
+  emitc.switch %0 : i8
+  case 2 {
+    %1 = emitc.call_opaque "func_b" () : () -> i32
+    emitc.yield
+  }
+  case 3 {
+    %2 = emitc.call_opaque "func_a" () : () -> i32
+    emitc.yield
+  }
+  // expected-error at +1 {{custom op 'emitc.switch' expected 'default'}}
+  return
+}
+
+// -----
+
+func.func @emitc_switch() {
+  %0 = "emitc.variable"(){value = 1 : i64} : () -> i64
+
+  // expected-error at +1 {{'emitc.switch' op has duplicate case value: 2}}
+  emitc.switch %0 : i64
+  case 2 {
+    %1 = emitc.call_opaque "func_b" () : () -> i32
+    emitc.yield
+  }
+  case 2 {
+    %2 = emitc.call_opaque "func_a" () : () -> i32
+    emitc.yield
+  }
+  default {
+    %3 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.yield
+  }
+  return
+}

diff  --git a/mlir/test/Dialect/EmitC/ops.mlir b/mlir/test/Dialect/EmitC/ops.mlir
index 20ac077e4402b4..64f22e8ad6b983 100644
--- a/mlir/test/Dialect/EmitC/ops.mlir
+++ b/mlir/test/Dialect/EmitC/ops.mlir
@@ -261,3 +261,23 @@ func.func @member_access(%arg0: !emitc.opaque<"mystruct">, %arg1: !emitc.opaque<
   %2 = "emitc.member_of_ptr" (%arg2) {member = "a"} : (!emitc.ptr<!emitc.opaque<"mystruct">>) -> i32
   return
 }
+
+func.func @switch() {
+  %0 = "emitc.variable"(){value = 1 : index} : () -> !emitc.ptr
diff _t
+
+  emitc.switch %0 : !emitc.ptr
diff _t
+  case 1 {
+    %1 = emitc.call_opaque "func_b" () : () -> i32
+    emitc.yield
+  }
+  case 2 {
+    %2 = emitc.call_opaque "func_a" () : () -> i32
+    emitc.yield
+  }
+  default {
+    %3 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+  }
+
+  return 
+}

diff  --git a/mlir/test/Target/Cpp/invalid.mlir b/mlir/test/Target/Cpp/invalid.mlir
index 513371a09cde1d..4e777239fba662 100644
--- a/mlir/test/Target/Cpp/invalid.mlir
+++ b/mlir/test/Target/Cpp/invalid.mlir
@@ -80,6 +80,7 @@ func.func @array_as_result(%arg: !emitc.array<4xi8>) -> (!emitc.array<4xi8>) {
 }
 
 // -----
+
 func.func @ptr_to_array() {
   // expected-error at +1 {{cannot emit pointer to array type '!emitc.ptr<!emitc.array<9xi16>>'}}
   %v = "emitc.variable"(){value = #emitc.opaque<"NULL">} : () -> !emitc.ptr<!emitc.array<9xi16>>

diff  --git a/mlir/test/Target/Cpp/switch.mlir b/mlir/test/Target/Cpp/switch.mlir
new file mode 100644
index 00000000000000..0f2e716a98f16b
--- /dev/null
+++ b/mlir/test/Target/Cpp/switch.mlir
@@ -0,0 +1,856 @@
+// RUN: mlir-translate -mlir-to-cpp %s | FileCheck %s -check-prefix=CPP-DEFAULT
+// RUN: mlir-translate -mlir-to-cpp -declare-variables-at-top %s | FileCheck %s -check-prefix=CPP-DECLTOP
+
+// CPP-DEFAULT-LABEL: void emitc_switch_ptr
diff _t() {
+// CPP-DEFAULT:         ptr
diff _t v1 = 1;
+// CPP-DEFAULT:         switch (v1) {
+// CPP-DEFAULT:         case 2: {
+// CPP-DEFAULT:           int32_t v2 = func_b();
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         case 5: {
+// CPP-DEFAULT:           int32_t v3 = func_a();
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         default: {
+// CPP-DEFAULT:           float v4 = 4.200000000e+01f;
+// CPP-DEFAULT:           func2(v4);
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         return;
+// CPP-DEFAULT:       }
+
+// CPP-DECLTOP-LABEL: void emitc_switch_ptr
diff _t() {
+// CPP-DECLTOP:         ptr
diff _t v1;
+// CPP-DECLTOP:         float v2;
+// CPP-DECLTOP:         int32_t v3;
+// CPP-DECLTOP:         int32_t v4;
+// CPP-DECLTOP:         v1 = 1;
+// CPP-DECLTOP:         switch (v1) {
+// CPP-DECLTOP:         case 2: {
+// CPP-DECLTOP:           v3 = func_b();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         case 5: {
+// CPP-DECLTOP:           v4 = func_a();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         default: {
+// CPP-DECLTOP:           v2 = 4.200000000e+01f;
+// CPP-DECLTOP:           func2(v2);
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         return;
+// CPP-DECLTOP:       }
+func.func @emitc_switch_ptr
diff _t() {
+  %0 = "emitc.variable"(){value = 1 : index} : () -> !emitc.ptr
diff _t
+
+  emitc.switch %0 : !emitc.ptr
diff _t
+  case 2 {
+    %1 = emitc.call_opaque "func_b" () : () -> i32
+    emitc.yield
+  }
+  case 5 {
+    %2 = emitc.call_opaque "func_a" () : () -> i32
+    emitc.yield
+  }
+  default {
+    %3 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+  }
+  return
+}
+
+// CPP-DEFAULT-LABEL: void emitc_switch_ssize_t() {
+// CPP-DEFAULT:         ssize_t v1 = 1;
+// CPP-DEFAULT:         switch (v1) {
+// CPP-DEFAULT:         case 2: {
+// CPP-DEFAULT:           int32_t v2 = func_b();
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         case 5: {
+// CPP-DEFAULT:           int32_t v3 = func_a();
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         default: {
+// CPP-DEFAULT:           float v4 = 4.200000000e+01f;
+// CPP-DEFAULT:           func2(v4);
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         return;
+// CPP-DEFAULT:       }
+
+// CPP-DECLTOP-LABEL: void emitc_switch_ssize_t() {
+// CPP-DECLTOP:         ssize_t v1;
+// CPP-DECLTOP:         float v2;
+// CPP-DECLTOP:         int32_t v3;
+// CPP-DECLTOP:         int32_t v4;
+// CPP-DECLTOP:         v1 = 1;
+// CPP-DECLTOP:         switch (v1) {
+// CPP-DECLTOP:         case 2: {
+// CPP-DECLTOP:           v3 = func_b();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         case 5: {
+// CPP-DECLTOP:           v4 = func_a();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         default: {
+// CPP-DECLTOP:           v2 = 4.200000000e+01f;
+// CPP-DECLTOP:           func2(v2);
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         return;
+// CPP-DECLTOP:       }
+func.func @emitc_switch_ssize_t() {
+  %0 = "emitc.variable"(){value = 1 : index} : () -> !emitc.ssize_t
+
+  emitc.switch %0 : !emitc.ssize_t
+  case 2 {
+    %1 = emitc.call_opaque "func_b" () : () -> i32
+    emitc.yield
+  }
+  case 5 {
+    %2 = emitc.call_opaque "func_a" () : () -> i32
+    emitc.yield
+  }
+  default {
+    %3 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.yield
+  }
+  return
+}
+
+// CPP-DEFAULT-LABEL: void emitc_switch_size_t() {
+// CPP-DEFAULT:         size_t v1 = 1;
+// CPP-DEFAULT:         switch (v1) {
+// CPP-DEFAULT:         case 2: {
+// CPP-DEFAULT:           int32_t v2 = func_b();
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         case 5: {
+// CPP-DEFAULT:           int32_t v3 = func_a();
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         default: {
+// CPP-DEFAULT:           float v4 = 4.200000000e+01f;
+// CPP-DEFAULT:           func2(v4);
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         return;
+// CPP-DEFAULT:       }
+
+// CPP-DECLTOP-LABEL: void emitc_switch_size_t() {
+// CPP-DECLTOP:         size_t v1;
+// CPP-DECLTOP:         float v2;
+// CPP-DECLTOP:         int32_t v3;
+// CPP-DECLTOP:         int32_t v4;
+// CPP-DECLTOP:         v1 = 1;
+// CPP-DECLTOP:         switch (v1) {
+// CPP-DECLTOP:         case 2: {
+// CPP-DECLTOP:           v3 = func_b();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         case 5: {
+// CPP-DECLTOP:           v4 = func_a();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         default: {
+// CPP-DECLTOP:           v2 = 4.200000000e+01f;
+// CPP-DECLTOP:           func2(v2);
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         return;
+// CPP-DECLTOP:       }
+func.func @emitc_switch_size_t() {
+  %0 = "emitc.variable"(){value = 1 : index} : () -> !emitc.size_t
+
+  emitc.switch %0 : !emitc.size_t
+  case 2 {
+    %1 = emitc.call_opaque "func_b" () : () -> i32
+    emitc.yield
+  }
+  case 5 {
+    %2 = emitc.call_opaque "func_a" () : () -> i32
+    emitc.yield
+  }
+  default {
+    %3 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.yield
+  }
+  return
+}
+
+// CPP-DEFAULT-LABEL: void emitc_switch_index() {
+// CPP-DEFAULT:         size_t v1 = 1;
+// CPP-DEFAULT:         switch (v1) {
+// CPP-DEFAULT:         case 2: {
+// CPP-DEFAULT:           int32_t v2 = func_b();
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         case 5: {
+// CPP-DEFAULT:           int32_t v3 = func_a();
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         default: {
+// CPP-DEFAULT:           float v4 = 4.200000000e+01f;
+// CPP-DEFAULT:           func2(v4);
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         return;
+// CPP-DEFAULT:       }
+
+// CPP-DECLTOP-LABEL: void emitc_switch_index() {
+// CPP-DECLTOP:         size_t v1;
+// CPP-DECLTOP:         float v2;
+// CPP-DECLTOP:         int32_t v3;
+// CPP-DECLTOP:         int32_t v4;
+// CPP-DECLTOP:         v1 = 1;
+// CPP-DECLTOP:         switch (v1) {
+// CPP-DECLTOP:         case 2: {
+// CPP-DECLTOP:           v3 = func_b();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         case 5: {
+// CPP-DECLTOP:           v4 = func_a();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         default: {
+// CPP-DECLTOP:           v2 = 4.200000000e+01f;
+// CPP-DECLTOP:           func2(v2);
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         return;
+// CPP-DECLTOP:       }
+func.func @emitc_switch_index() {
+  %0 = "emitc.variable"(){value = 1 : index} : () -> index
+
+  emitc.switch %0 : index
+  case 2 {
+    %1 = emitc.call_opaque "func_b" () : () -> i32
+    emitc.yield
+  }
+  case 5 {
+    %2 = emitc.call_opaque "func_a" () : () -> i32
+    emitc.yield
+  }
+  default {
+    %3 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.yield
+  }
+  return
+}
+
+// CPP-DEFAULT-LABEL: void emitc_switch_opaque() {
+// CPP-DEFAULT:         size_t v1 = 1;
+// CPP-DEFAULT:         switch (v1) {
+// CPP-DEFAULT:         case 2: {
+// CPP-DEFAULT:           int32_t v2 = func_b();
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         case 5: {
+// CPP-DEFAULT:           int32_t v3 = func_a();
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         default: {
+// CPP-DEFAULT:           float v4 = 4.200000000e+01f;
+// CPP-DEFAULT:           func2(v4);
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         return;
+// CPP-DEFAULT:       }
+
+// CPP-DECLTOP-LABEL: void emitc_switch_opaque() {
+// CPP-DECLTOP:         size_t v1;
+// CPP-DECLTOP:         float v2;
+// CPP-DECLTOP:         int32_t v3;
+// CPP-DECLTOP:         int32_t v4;
+// CPP-DECLTOP:         v1 = 1;
+// CPP-DECLTOP:         switch (v1) {
+// CPP-DECLTOP:         case 2: {
+// CPP-DECLTOP:           v3 = func_b();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         case 5: {
+// CPP-DECLTOP:           v4 = func_a();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         default: {
+// CPP-DECLTOP:           v2 = 4.200000000e+01f;
+// CPP-DECLTOP:           func2(v2);
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         return;
+// CPP-DECLTOP:       }
+func.func @emitc_switch_opaque() {
+  %0 = "emitc.variable"() {value = #emitc.opaque<"1">} 
+  : () -> !emitc.opaque<"size_t">
+
+  emitc.switch %0 : !emitc.opaque<"size_t">
+  case 2 {
+    %1 = emitc.call_opaque "func_b" () : () -> i32
+    emitc.yield
+  }
+  case 5 {
+    %2 = emitc.call_opaque "func_a" () : () -> i32
+    emitc.yield
+  }
+  default {
+    %3 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.yield
+  }
+  return
+}
+
+// CPP-DEFAULT-LABEL: void emitc_switch_i1() {
+// CPP-DEFAULT:         bool v1 = true;
+// CPP-DEFAULT:         switch (v1) {
+// CPP-DEFAULT:         case 2: {
+// CPP-DEFAULT:           int32_t v2 = func_b();
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         case 5: {
+// CPP-DEFAULT:           int32_t v3 = func_a();
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         default: {
+// CPP-DEFAULT:           float v4 = 4.200000000e+01f;
+// CPP-DEFAULT:           func2(v4);
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         return;
+// CPP-DEFAULT:       }
+
+// CPP-DECLTOP-LABEL: void emitc_switch_i1() {
+// CPP-DECLTOP:         bool v1;
+// CPP-DECLTOP:         float v2;
+// CPP-DECLTOP:         int32_t v3;
+// CPP-DECLTOP:         int32_t v4;
+// CPP-DECLTOP:         v1 = true;
+// CPP-DECLTOP:         switch (v1) {
+// CPP-DECLTOP:         case 2: {
+// CPP-DECLTOP:           v3 = func_b();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         case 5: {
+// CPP-DECLTOP:           v4 = func_a();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         default: {
+// CPP-DECLTOP:           v2 = 4.200000000e+01f;
+// CPP-DECLTOP:           func2(v2);
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         return;
+// CPP-DECLTOP:       }
+func.func @emitc_switch_i1() {
+  %0 = "emitc.variable"(){value = 1 : i1} : () -> i1
+
+  emitc.switch %0 : i1
+  case 2 {
+    %1 = emitc.call_opaque "func_b" () : () -> i32
+    emitc.yield
+  }
+  case 5 {
+    %2 = emitc.call_opaque "func_a" () : () -> i32
+    emitc.yield
+  }
+  default {
+    %3 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.yield
+  }
+  return
+}
+
+// CPP-DEFAULT-LABEL: void emitc_switch_i8() {
+// CPP-DEFAULT:         int8_t v1 = 1;
+// CPP-DEFAULT:         switch (v1) {
+// CPP-DEFAULT:         case 2: {
+// CPP-DEFAULT:           int32_t v2 = func_b();
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         case 5: {
+// CPP-DEFAULT:           int32_t v3 = func_a();
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         default: {
+// CPP-DEFAULT:           float v4 = 4.200000000e+01f;
+// CPP-DEFAULT:           func2(v4);
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         return;
+// CPP-DEFAULT:       }
+
+// CPP-DECLTOP-LABEL: void emitc_switch_i8() {
+// CPP-DECLTOP:         int8_t v1;
+// CPP-DECLTOP:         float v2;
+// CPP-DECLTOP:         int32_t v3;
+// CPP-DECLTOP:         int32_t v4;
+// CPP-DECLTOP:         v1 = 1;
+// CPP-DECLTOP:         switch (v1) {
+// CPP-DECLTOP:         case 2: {
+// CPP-DECLTOP:           v3 = func_b();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         case 5: {
+// CPP-DECLTOP:           v4 = func_a();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         default: {
+// CPP-DECLTOP:           v2 = 4.200000000e+01f;
+// CPP-DECLTOP:           func2(v2);
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         return;
+// CPP-DECLTOP:       }
+func.func @emitc_switch_i8() {
+  %0 = "emitc.variable"(){value = 1 : i8} : () -> i8
+
+  emitc.switch %0 : i8
+  case 2 {
+    %1 = emitc.call_opaque "func_b" () : () -> i32
+    emitc.yield
+  }
+  case 5 {
+    %2 = emitc.call_opaque "func_a" () : () -> i32
+    emitc.yield
+  }
+  default {
+    %3 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.yield
+  }
+  return
+}
+
+// CPP-DEFAULT-LABEL: void emitc_switch_ui8() {
+// CPP-DEFAULT:         uint8_t v1 = 1;
+// CPP-DEFAULT:         switch (v1) {
+// CPP-DEFAULT:         case 2: {
+// CPP-DEFAULT:           int32_t v2 = func_b();
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         case 5: {
+// CPP-DEFAULT:           int32_t v3 = func_a();
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         default: {
+// CPP-DEFAULT:           float v4 = 4.200000000e+01f;
+// CPP-DEFAULT:           func2(v4);
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         return;
+// CPP-DEFAULT:       }
+
+// CPP-DECLTOP-LABEL: void emitc_switch_ui8() {
+// CPP-DECLTOP:         uint8_t v1;
+// CPP-DECLTOP:         float v2;
+// CPP-DECLTOP:         int32_t v3;
+// CPP-DECLTOP:         int32_t v4;
+// CPP-DECLTOP:         v1 = 1;
+// CPP-DECLTOP:         switch (v1) {
+// CPP-DECLTOP:         case 2: {
+// CPP-DECLTOP:           v3 = func_b();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         case 5: {
+// CPP-DECLTOP:           v4 = func_a();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         default: {
+// CPP-DECLTOP:           v2 = 4.200000000e+01f;
+// CPP-DECLTOP:           func2(v2);
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         return;
+// CPP-DECLTOP:       }
+func.func @emitc_switch_ui8() {
+  %0 = "emitc.variable"(){value = 1 : ui8} : () -> ui8
+
+  emitc.switch %0 : ui8
+  case 2 {
+    %1 = emitc.call_opaque "func_b" () : () -> i32
+    emitc.yield
+  }
+  case 5 {
+    %2 = emitc.call_opaque "func_a" () : () -> i32
+    emitc.yield
+  }
+  default {
+    %3 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.yield
+  }
+  return
+}
+
+// CPP-DEFAULT-LABEL: void emitc_switch_i16() {
+// CPP-DEFAULT:         int16_t v1 = 1;
+// CPP-DEFAULT:         switch (v1) {
+// CPP-DEFAULT:         case 2: {
+// CPP-DEFAULT:           int32_t v2 = func_b();
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         case 5: {
+// CPP-DEFAULT:           int32_t v3 = func_a();
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         default: {
+// CPP-DEFAULT:           float v4 = 4.200000000e+01f;
+// CPP-DEFAULT:           func2(v4);
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         return;
+// CPP-DEFAULT:       }
+
+// CPP-DECLTOP-LABEL: void emitc_switch_i16() {
+// CPP-DECLTOP:         int16_t v1;
+// CPP-DECLTOP:         float v2;
+// CPP-DECLTOP:         int32_t v3;
+// CPP-DECLTOP:         int32_t v4;
+// CPP-DECLTOP:         v1 = 1;
+// CPP-DECLTOP:         switch (v1) {
+// CPP-DECLTOP:         case 2: {
+// CPP-DECLTOP:           v3 = func_b();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         case 5: {
+// CPP-DECLTOP:           v4 = func_a();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         default: {
+// CPP-DECLTOP:           v2 = 4.200000000e+01f;
+// CPP-DECLTOP:           func2(v2);
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         return;
+// CPP-DECLTOP:       }
+func.func @emitc_switch_i16() {
+  %0 = "emitc.variable"(){value = 1 : i16} : () -> i16
+
+  emitc.switch %0 : i16
+  case 2 {
+    %1 = emitc.call_opaque "func_b" () : () -> i32
+    emitc.yield
+  }
+  case 5 {
+    %2 = emitc.call_opaque "func_a" () : () -> i32
+    emitc.yield
+  }
+  default {
+    %3 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.yield
+  }
+  return
+}
+
+// CPP-DEFAULT-LABEL: void emitc_switch_ui16() {
+// CPP-DEFAULT:         uint16_t v1 = 1;
+// CPP-DEFAULT:         switch (v1) {
+// CPP-DEFAULT:         case 2: {
+// CPP-DEFAULT:           int32_t v2 = func_b();
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         case 5: {
+// CPP-DEFAULT:           int32_t v3 = func_a();
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         default: {
+// CPP-DEFAULT:           float v4 = 4.200000000e+01f;
+// CPP-DEFAULT:           func2(v4);
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         return;
+// CPP-DEFAULT:       }
+
+// CPP-DECLTOP-LABEL: void emitc_switch_ui16() {
+// CPP-DECLTOP:         uint16_t v1;
+// CPP-DECLTOP:         float v2;
+// CPP-DECLTOP:         int32_t v3;
+// CPP-DECLTOP:         int32_t v4;
+// CPP-DECLTOP:         v1 = 1;
+// CPP-DECLTOP:         switch (v1) {
+// CPP-DECLTOP:         case 2: {
+// CPP-DECLTOP:           v3 = func_b();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         case 5: {
+// CPP-DECLTOP:           v4 = func_a();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         default: {
+// CPP-DECLTOP:           v2 = 4.200000000e+01f;
+// CPP-DECLTOP:           func2(v2);
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         return;
+// CPP-DECLTOP:       }
+func.func @emitc_switch_ui16() {
+  %0 = "emitc.variable"(){value = 1 : ui16} : () -> ui16
+
+  emitc.switch %0 : ui16
+  case 2 {
+    %1 = emitc.call_opaque "func_b" () : () -> i32
+    emitc.yield
+  }
+  case 5 {
+    %2 = emitc.call_opaque "func_a" () : () -> i32
+    emitc.yield
+  }
+  default {
+    %3 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.yield
+  }
+  return
+}
+
+// CPP-DEFAULT-LABEL: void emitc_switch_i32() {
+// CPP-DEFAULT:         int32_t v1 = 1;
+// CPP-DEFAULT:         switch (v1) {
+// CPP-DEFAULT:         case 2: {
+// CPP-DEFAULT:           int32_t v2 = func_b();
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         case 5: {
+// CPP-DEFAULT:           int32_t v3 = func_a();
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         default: {
+// CPP-DEFAULT:           float v4 = 4.200000000e+01f;
+// CPP-DEFAULT:           func2(v4);
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         return;
+// CPP-DEFAULT:       }
+
+// CPP-DECLTOP-LABEL: void emitc_switch_i32() {
+// CPP-DECLTOP:         int32_t v1;
+// CPP-DECLTOP:         float v2;
+// CPP-DECLTOP:         int32_t v3;
+// CPP-DECLTOP:         int32_t v4;
+// CPP-DECLTOP:         v1 = 1;
+// CPP-DECLTOP:         switch (v1) {
+// CPP-DECLTOP:         case 2: {
+// CPP-DECLTOP:           v3 = func_b();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         case 5: {
+// CPP-DECLTOP:           v4 = func_a();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         default: {
+// CPP-DECLTOP:           v2 = 4.200000000e+01f;
+// CPP-DECLTOP:           func2(v2);
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         return;
+// CPP-DECLTOP:       }
+func.func @emitc_switch_i32() {
+  %0 = "emitc.variable"(){value = 1 : i32} : () -> i32
+
+  emitc.switch %0 : i32
+  case 2 {
+    %1 = emitc.call_opaque "func_b" () : () -> i32
+    emitc.yield
+  }
+  case 5 {
+    %2 = emitc.call_opaque "func_a" () : () -> i32
+    emitc.yield
+  }
+  default {
+    %3 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.yield
+  }
+  return
+}
+
+// CPP-DEFAULT-LABEL: void emitc_switch_ui32() {
+// CPP-DEFAULT:         uint32_t v1 = 1;
+// CPP-DEFAULT:         switch (v1) {
+// CPP-DEFAULT:         case 2: {
+// CPP-DEFAULT:           int32_t v2 = func_b();
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         case 5: {
+// CPP-DEFAULT:           int32_t v3 = func_a();
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         default: {
+// CPP-DEFAULT:           float v4 = 4.200000000e+01f;
+// CPP-DEFAULT:           func2(v4);
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         return;
+// CPP-DEFAULT:       }
+
+// CPP-DECLTOP-LABEL: void emitc_switch_ui32() {
+// CPP-DECLTOP:         uint32_t v1;
+// CPP-DECLTOP:         float v2;
+// CPP-DECLTOP:         int32_t v3;
+// CPP-DECLTOP:         int32_t v4;
+// CPP-DECLTOP:         v1 = 1;
+// CPP-DECLTOP:         switch (v1) {
+// CPP-DECLTOP:         case 2: {
+// CPP-DECLTOP:           v3 = func_b();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         case 5: {
+// CPP-DECLTOP:           v4 = func_a();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         default: {
+// CPP-DECLTOP:           v2 = 4.200000000e+01f;
+// CPP-DECLTOP:           func2(v2);
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         return;
+// CPP-DECLTOP:       }
+func.func @emitc_switch_ui32() {
+  %0 = "emitc.variable"(){value = 1 : ui32} : () -> ui32
+
+  emitc.switch %0 : ui32
+  case 2 {
+    %1 = emitc.call_opaque "func_b" () : () -> i32
+    emitc.yield
+  }
+  case 5 {
+    %2 = emitc.call_opaque "func_a" () : () -> i32
+    emitc.yield
+  }
+  default {
+    %3 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.yield
+  }
+  return
+}
+
+// CPP-DEFAULT-LABEL: void emitc_switch_i64() {
+// CPP-DEFAULT:         int64_t v1 = 1;
+// CPP-DEFAULT:         switch (v1) {
+// CPP-DEFAULT:         case 2: {
+// CPP-DEFAULT:           int32_t v2 = func_b();
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         case 5: {
+// CPP-DEFAULT:           int32_t v3 = func_a();
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         default: {
+// CPP-DEFAULT:           float v4 = 4.200000000e+01f;
+// CPP-DEFAULT:           func2(v4);
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         return;
+// CPP-DEFAULT:       }
+
+// CPP-DECLTOP-LABEL: void emitc_switch_i64() {
+// CPP-DECLTOP:         int64_t v1;
+// CPP-DECLTOP:         float v2;
+// CPP-DECLTOP:         int32_t v3;
+// CPP-DECLTOP:         int32_t v4;
+// CPP-DECLTOP:         v1 = 1;
+// CPP-DECLTOP:         switch (v1) {
+// CPP-DECLTOP:         case 2: {
+// CPP-DECLTOP:           v3 = func_b();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         case 5: {
+// CPP-DECLTOP:           v4 = func_a();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         default: {
+// CPP-DECLTOP:           v2 = 4.200000000e+01f;
+// CPP-DECLTOP:           func2(v2);
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         return;
+// CPP-DECLTOP:       }
+func.func @emitc_switch_i64() {
+  %0 = "emitc.variable"(){value = 1 : i64} : () -> i64
+
+  emitc.switch %0 : i64
+  case 2 {
+    %1 = emitc.call_opaque "func_b" () : () -> i32
+    emitc.yield
+  }
+  case 5 {
+    %2 = emitc.call_opaque "func_a" () : () -> i32
+    emitc.yield
+  }
+  default {
+    %3 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.yield
+  }
+  return
+}
+
+// CPP-DEFAULT-LABEL: void emitc_switch_ui64() {
+// CPP-DEFAULT:         uint64_t v1 = 1;
+// CPP-DEFAULT:         switch (v1) {
+// CPP-DEFAULT:         case 2: {
+// CPP-DEFAULT:           int32_t v2 = func_b();
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         case 5: {
+// CPP-DEFAULT:           int32_t v3 = func_a();
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         default: {
+// CPP-DEFAULT:           float v4 = 4.200000000e+01f;
+// CPP-DEFAULT:           func2(v4);
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         return;
+// CPP-DEFAULT:       }
+
+// CPP-DECLTOP-LABEL: void emitc_switch_ui64() {
+// CPP-DECLTOP:         uint64_t v1;
+// CPP-DECLTOP:         float v2;
+// CPP-DECLTOP:         int32_t v3;
+// CPP-DECLTOP:         int32_t v4;
+// CPP-DECLTOP:         v1 = 1;
+// CPP-DECLTOP:         switch (v1) {
+// CPP-DECLTOP:         case 2: {
+// CPP-DECLTOP:           v3 = func_b();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         case 5: {
+// CPP-DECLTOP:           v4 = func_a();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         default: {
+// CPP-DECLTOP:           v2 = 4.200000000e+01f;
+// CPP-DECLTOP:           func2(v2);
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         return;
+// CPP-DECLTOP:       }
+func.func @emitc_switch_ui64() {
+  %0 = "emitc.variable"(){value = 1 : ui64} : () -> ui64
+
+  emitc.switch %0 : ui64
+  case 2 {
+    %1 = emitc.call_opaque "func_b" () : () -> i32
+    emitc.yield
+  }
+  case 5 {
+    %2 = emitc.call_opaque "func_a" () : () -> i32
+    emitc.yield
+  }
+  default {
+    %3 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.yield
+  }
+  return
+}


        


More information about the Mlir-commits mailing list