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

Andrey Timonin llvmlistbot at llvm.org
Fri Aug 9 04:16:57 PDT 2024


https://github.com/EtoAndruwa updated https://github.com/llvm/llvm-project/pull/102331

>From 11a57c346a2f5c88f75d23f292ae017617ecd1a1 Mon Sep 17 00:00:00 2001
From: EtoAndruwa <timonina1909 at gmail.com>
Date: Wed, 7 Aug 2024 19:34:36 +0300
Subject: [PATCH 1/8] [mlir][emitc] Add 'emitc.switch' op to the dialect

---
 mlir/include/mlir/Dialect/EmitC/IR/EmitC.h  |   3 +
 mlir/include/mlir/Dialect/EmitC/IR/EmitC.td |  89 ++++++++-
 mlir/lib/Dialect/EmitC/IR/EmitC.cpp         | 206 ++++++++++++++++++++
 mlir/lib/Target/Cpp/TranslateToCpp.cpp      |  45 ++++-
 mlir/test/Target/Cpp/invalid.mlir           | 173 ++++++++++++++++
 mlir/test/Target/Cpp/switch.mlir            | 129 ++++++++++++
 6 files changed, 641 insertions(+), 4 deletions(-)
 create mode 100644 mlir/test/Target/Cpp/switch.mlir

diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h
index 87a4078f280f65..c7211c9e9b010e 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h
@@ -47,6 +47,9 @@ bool isSupportedFloatType(mlir::Type type);
 /// Determines whether \p type is a emitc.size_t/ssize_t type.
 bool isPointerWideType(mlir::Type type);
 
+/// Determines whether \p type is a valid integer or opaque type for SwitchOp.
+bool isSwitchOperandType(Type type);
+
 } // namespace emitc
 } // namespace mlir
 
diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index 16fa58ce47013d..0f1ab8cee8dfa0 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -54,6 +54,8 @@ def CExpression : NativeOpTrait<"emitc::CExpression">;
 def IntegerIndexOrOpaqueType : Type<CPred<"emitc::isIntegerIndexOrOpaqueType($_self)">,
 "integer, index or opaque type supported by EmitC">;
 def FloatIntegerIndexOrOpaqueType : AnyTypeOf<[EmitCFloatType, IntegerIndexOrOpaqueType]>;
+def SwitchOperandType : Type<CPred<"emitc::isSwitchOperandType($_self)">,
+"integer type for switch operation">;
 
 def EmitC_AddOp : EmitC_BinaryOp<"add", [CExpression]> {
   let summary = "Addition operation";
@@ -1188,7 +1190,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 +1304,90 @@ 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
+    argument is always of type opaque or integer (singed or unsigned), excluding i8 
+    and i1.
+
+    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 and 
+    default region must be terminated using the `emitc.yield` operation.
+
+    Example:
+
+    ```mlir
+    // Cases with i32 type.
+    %0 = "emitc.variable"(){value = 42 : 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
+      %4 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+
+      emitc.call_opaque "func2" (%3) : (f32) -> ()
+      emitc.call_opaque "func3" (%3, %4) { args = [1 : index, 0 : index] } : (f32, f32) -> ()
+      emitc.yield
+    }
+    ...
+    // Cases with i16 type.
+    %0 = "emitc.variable"(){value = 42 : 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
+      %4 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+
+      emitc.call_opaque "func2" (%3) : (f32) -> ()
+      emitc.call_opaque "func3" (%3, %4) { args = [1 : index, 0 : index] } : (f32, f32) -> ()
+      emitc.yield
+    }
+    ```
+  }];
+
+  let arguments = (ins SwitchOperandType:$arg, DenseI64ArrayAttr:$cases);
+  let results = (outs);
+  let regions = (region SizedRegion<1>:$defaultRegion,
+                        VariadicRegion<SizedRegion<1>>:$caseRegions);
+
+  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 hasCustomAssemblyFormat = 1;
+  let hasVerifier = 1;
+}
 
 #endif // MLIR_DIALECT_EMITC_IR_EMITC
diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
index 70e3e728e01950..dc40f3a7cedaa5 100644
--- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
+++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
@@ -131,6 +131,13 @@ bool mlir::emitc::isPointerWideType(Type type) {
       type);
 }
 
+bool mlir::emitc::isSwitchOperandType(Type type) {
+  auto intType = llvm::dyn_cast<IntegerType>(type);
+  return isa<emitc::OpaqueType>(type) ||
+         (isSupportedIntegerType(type) && intType.getWidth() != 1 &&
+          intType.getWidth() != 8);
+}
+
 /// Check that the type of the initial value is compatible with the operations
 /// result type.
 static LogicalResult verifyInitializationAttribute(Operation *op,
@@ -1096,6 +1103,205 @@ 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.parseColon() ||
+        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 &parser, Operation *op,
+                             DenseI64ArrayAttr cases, RegionRange caseRegions) {
+  for (auto [value, region] : llvm::zip(cases.asArrayRef(), caseRegions)) {
+    parser.printNewline();
+    parser << "case " << value << ": ";
+    parser.printRegion(*region, /*printEntryBlockArgs=*/false);
+  }
+  return;
+}
+
+ParseResult SwitchOp::parse(OpAsmParser &parser, OperationState &result) {
+  OpAsmParser::UnresolvedOperand arg;
+  DenseI64ArrayAttr casesAttr;
+  SmallVector<std::unique_ptr<Region>, 2> caseRegionsRegions;
+  std::unique_ptr<Region> defaultRegionRegion = std::make_unique<Region>();
+
+  if (parser.parseOperand(arg))
+    return failure();
+
+  Type argType;
+  // Parse the case's type.
+  if (parser.parseColon() || parser.parseType(argType))
+    return failure();
+
+  auto loc = parser.getCurrentLocation();
+  if (parser.parseOptionalAttrDict(result.attributes))
+    return failure();
+
+  if (failed(verifyInherentAttrs(result.name, result.attributes, [&]() {
+        return parser.emitError(loc)
+               << "'" << result.name.getStringRef() << "' op ";
+      })))
+    return failure();
+
+  auto odsResult = parseSwitchCases(parser, casesAttr, caseRegionsRegions);
+  if (odsResult)
+    return failure();
+
+  result.getOrAddProperties<SwitchOp::Properties>().cases = casesAttr;
+
+  if (parser.parseKeyword("default") || parser.parseColon())
+    return failure();
+
+  if (parser.parseRegion(*defaultRegionRegion))
+    return failure();
+
+  result.addRegion(std::move(defaultRegionRegion));
+  result.addRegions(caseRegionsRegions);
+
+  if (parser.resolveOperand(arg, argType, result.operands))
+    return failure();
+
+  return success();
+}
+
+void SwitchOp::print(OpAsmPrinter &parser) {
+  parser << ' ';
+  parser << getArg();
+  SmallVector<StringRef, 2> elidedAttrs;
+  elidedAttrs.push_back("cases");
+  parser.printOptionalAttrDict((*this)->getAttrs(), elidedAttrs);
+  parser << ' ';
+  printSwitchCases(parser, *this, getCasesAttr(), getCaseRegions());
+  parser.printNewline();
+  parser << "default";
+  parser << ' ';
+  parser.printRegion(getDefaultRegion(), /*printEntryBlockArgs=*/true,
+                     /*printBlockTerminators=*/true);
+
+  return;
+}
+
+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 (!isSwitchOperandType(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));
+  return;
+}
+
+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());
+  return;
+}
+
+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);
+
+  return;
+}
+
 //===----------------------------------------------------------------------===//
 // TableGen'd op method definitions
 //===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
index 1dadb9dd691e70..8831b95a27246a 100644
--- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp
+++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
@@ -449,6 +449,45 @@ 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 +1037,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 +1548,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/Target/Cpp/invalid.mlir b/mlir/test/Target/Cpp/invalid.mlir
index 513371a09cde1d..d2808476f21f84 100644
--- a/mlir/test/Target/Cpp/invalid.mlir
+++ b/mlir/test/Target/Cpp/invalid.mlir
@@ -80,8 +80,181 @@ 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>>
   return
 }
+
+// -----
+
+func.func @emitc_switch() {
+  %0 = "emitc.variable"(){value = 1 : ui16} : () -> ui16
+
+  // expected-error at +1 {{'emitc.switch' op expected region to end with emitc.yield, but got emitc.call_opaque}}
+  emitc.switch %0 : ui16
+  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
+    %4 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.call_opaque "func3" (%3, %4) { args = [1 : index, 0 : index] } : (f32, f32) -> ()
+    emitc.yield
+  }
+  return
+}
+
+// -----
+
+func.func @emitc_switch() {
+  %0 = "emitc.variable"(){value = 1 : ui16} : () -> ui16
+
+  // expected-error at +1 {{'emitc.switch' op expected region to end with emitc.yield, but got emitc.call_opaque}}
+  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
+    %4 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.call_opaque "func3" (%3, %4) { args = [1 : index, 0 : index] } : (f32, f32) -> ()
+  }
+  return
+}
+
+// -----
+
+func.func @emitc_switch() {
+  %0 = "emitc.variable"(){value = 1 : i8} : () -> i8
+
+  // expected-error at +1 {{'emitc.switch' op operand #0 must be integer type for switch operation, but got '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
+    %4 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.call_opaque "func3" (%3, %4) { args = [1 : index, 0 : index] } : (f32, f32) -> ()
+    emitc.yield
+  }
+  return
+}
+
+// -----
+
+func.func @emitc_switch() {
+  %0 = "emitc.variable"(){value = 1 : i1} : () -> i1
+
+  // expected-error at +1 {{'emitc.switch' op operand #0 must be integer type for switch operation, but got '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
+    %4 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.call_opaque "func3" (%3, %4) { args = [1 : index, 0 : index] } : (f32, f32) -> ()
+    emitc.yield
+  }
+  return
+}
+
+// -----
+
+func.func @emitc_switch() {
+  %0 = "emitc.variable"(){value = 1 : i16} : () -> i16
+
+  emitc.switch %0 : i16
+  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
+    %4 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.call_opaque "func3" (%3, %4) { args = [1 : index, 0 : index] } : (f32, f32) -> ()
+    emitc.yield
+  }
+  return
+}
+
+// -----
+
+func.func @emitc_switch() {
+  %0 = "emitc.variable"(){value = 1 : i16} : () -> i16
+
+  emitc.switch %0 : i16
+  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 : i16} : () -> i16
+
+  // expected-error at +1 {{'emitc.switch' op has duplicate case value: 2}}
+  emitc.switch %0 : i16
+  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
+    %4 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.call_opaque "func3" (%3, %4) { args = [1 : index, 0 : index] } : (f32, f32) -> ()
+    emitc.yield
+  }
+  return
+}
diff --git a/mlir/test/Target/Cpp/switch.mlir b/mlir/test/Target/Cpp/switch.mlir
new file mode 100644
index 00000000000000..878a7e4863856c
--- /dev/null
+++ b/mlir/test/Target/Cpp/switch.mlir
@@ -0,0 +1,129 @@
+// RUN: mlir-translate -mlir-to-cpp %s | FileCheck %s
+
+// CHECK-LABEL:   int32_t v1 = 1;
+// CHECK-LABEL:   switch(v1) {
+// CHECK:         case (2): {
+// CHECK:           int32_t v2 = func_b();
+// CHECK:           break;
+// CHECK:         }
+// CHECK:         case (5): {
+// CHECK:           int32_t v3 = func_a();
+// CHECK:           break;
+// CHECK:         }
+// CHECK:         default: {
+// CHECK:           float v4 = 4.200000000e+01f;
+// CHECK:           float v5 = 4.200000000e+01f;
+// CHECK:           func2(v4);
+// CHECK:           func3(v5, v4);
+// CHECK:           break;
+// CHECK:         }
+// CHECK:         return;
+// CHECK:       }
+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
+    %4 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.call_opaque "func3" (%3, %4) { args = [1 : index, 0 : index] } : (f32, f32) -> ()
+    emitc.yield
+  }
+  return
+}
+
+// CHECK-LABEL: void emitc_switch_i16() {
+// CHECK:         int16_t v1 = 1;
+// CHECK:         switch(v1) {
+// CHECK:         case (2): {
+// CHECK:           int32_t v2 = func_b();
+// CHECK:           break;
+// CHECK:         }
+// CHECK:         case (5): {
+// CHECK:           int32_t v3 = func_a();
+// CHECK:           break;
+// CHECK:         }
+// CHECK:         default: {
+// CHECK:           float v4 = 4.200000000e+01f;
+// CHECK:           float v5 = 4.200000000e+01f;
+// CHECK:           func2(v4);
+// CHECK:           func3(v5, v4);
+// CHECK:           break;
+// CHECK:         }
+// CHECK:         return;
+// CHECK:       }
+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
+    %4 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.call_opaque "func3" (%3, %4) { args = [1 : index, 0 : index] } : (f32, f32) -> ()
+    emitc.yield
+  }
+  return
+}
+
+// CHECK-LABEL: void emitc_switch_ui16() {
+// CHECK:         uint16_t v1 = 1;
+// CHECK:         switch(v1) {
+// CHECK:         case (2): {
+// CHECK:           int32_t v2 = func_b();
+// CHECK:           break;
+// CHECK:         }
+// CHECK:         case (5): {
+// CHECK:           int32_t v3 = func_a();
+// CHECK:           break;
+// CHECK:         }
+// CHECK:         default: {
+// CHECK:           float v4 = 4.200000000e+01f;
+// CHECK:           float v5 = 4.200000000e+01f;
+// CHECK:           func2(v4);
+// CHECK:           func3(v5, v4);
+// CHECK:           break;
+// CHECK:         }
+// CHECK:         return;
+// CHECK:       }
+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
+    %4 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.call_opaque "func3" (%3, %4) { args = [1 : index, 0 : index] } : (f32, f32) -> ()
+    emitc.yield
+  }
+  return
+}

>From 82b8c25b92e347c0dac90ace4058c04f86daf64a Mon Sep 17 00:00:00 2001
From: EtoAndruwa <timonina1909 at gmail.com>
Date: Thu, 8 Aug 2024 16:03:51 +0300
Subject: [PATCH 2/8] [mlir][emitc] Add support of opaque type and all integral
 types

---
 mlir/lib/Dialect/EmitC/IR/EmitC.cpp |   4 +-
 mlir/test/Target/Cpp/invalid.mlir   |  52 -------
 mlir/test/Target/Cpp/switch.mlir    | 224 +++++++++++++++++++++++++++-
 3 files changed, 221 insertions(+), 59 deletions(-)

diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
index dc40f3a7cedaa5..8f3f5f0be7263f 100644
--- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
+++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
@@ -133,9 +133,7 @@ bool mlir::emitc::isPointerWideType(Type type) {
 
 bool mlir::emitc::isSwitchOperandType(Type type) {
   auto intType = llvm::dyn_cast<IntegerType>(type);
-  return isa<emitc::OpaqueType>(type) ||
-         (isSupportedIntegerType(type) && intType.getWidth() != 1 &&
-          intType.getWidth() != 8);
+  return isa<emitc::OpaqueType>(type) || isSupportedIntegerType(type);
 }
 
 /// Check that the type of the initial value is compatible with the operations
diff --git a/mlir/test/Target/Cpp/invalid.mlir b/mlir/test/Target/Cpp/invalid.mlir
index d2808476f21f84..8db4b7e5ed8f01 100644
--- a/mlir/test/Target/Cpp/invalid.mlir
+++ b/mlir/test/Target/Cpp/invalid.mlir
@@ -139,58 +139,6 @@ func.func @emitc_switch() {
 
 // -----
 
-func.func @emitc_switch() {
-  %0 = "emitc.variable"(){value = 1 : i8} : () -> i8
-
-  // expected-error at +1 {{'emitc.switch' op operand #0 must be integer type for switch operation, but got '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
-    %4 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
-
-    emitc.call_opaque "func2" (%3) : (f32) -> ()
-    emitc.call_opaque "func3" (%3, %4) { args = [1 : index, 0 : index] } : (f32, f32) -> ()
-    emitc.yield
-  }
-  return
-}
-
-// -----
-
-func.func @emitc_switch() {
-  %0 = "emitc.variable"(){value = 1 : i1} : () -> i1
-
-  // expected-error at +1 {{'emitc.switch' op operand #0 must be integer type for switch operation, but got '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
-    %4 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
-
-    emitc.call_opaque "func2" (%3) : (f32) -> ()
-    emitc.call_opaque "func3" (%3, %4) { args = [1 : index, 0 : index] } : (f32, f32) -> ()
-    emitc.yield
-  }
-  return
-}
-
-// -----
-
 func.func @emitc_switch() {
   %0 = "emitc.variable"(){value = 1 : i16} : () -> i16
 
diff --git a/mlir/test/Target/Cpp/switch.mlir b/mlir/test/Target/Cpp/switch.mlir
index 878a7e4863856c..92228504c20eb2 100644
--- a/mlir/test/Target/Cpp/switch.mlir
+++ b/mlir/test/Target/Cpp/switch.mlir
@@ -1,6 +1,6 @@
 // RUN: mlir-translate -mlir-to-cpp %s | FileCheck %s
 
-// CHECK-LABEL:   int32_t v1 = 1;
+// CHECK-LABEL:   int8_t v1 = 1;
 // CHECK-LABEL:   switch(v1) {
 // CHECK:         case (2): {
 // CHECK:           int32_t v2 = func_b();
@@ -19,10 +19,54 @@
 // CHECK:         }
 // CHECK:         return;
 // CHECK:       }
-func.func @emitc_switch_i32() {
-  %0 = "emitc.variable"(){value = 1 : i32} : () -> i32
+func.func @emitc_switch_i8() {
+  %0 = "emitc.variable"(){value = 1 : i8} : () -> i8
 
-  emitc.switch %0 : i32
+  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
+    %4 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.call_opaque "func3" (%3, %4) { args = [1 : index, 0 : index] } : (f32, f32) -> ()
+    emitc.yield
+  }
+  return
+}
+
+
+// CHECK-LABEL: void emitc_switch_ui8() {
+// CHECK:         uint8_t v1 = 1;
+// CHECK:         switch(v1) {
+// CHECK:         case (2): {
+// CHECK:           int32_t v2 = func_b();
+// CHECK:           break;
+// CHECK:         }
+// CHECK:         case (5): {
+// CHECK:           int32_t v3 = func_a();
+// CHECK:           break;
+// CHECK:         }
+// CHECK:         default: {
+// CHECK:           float v4 = 4.200000000e+01f;
+// CHECK:           float v5 = 4.200000000e+01f;
+// CHECK:           func2(v4);
+// CHECK:           func3(v5, v4);
+// CHECK:           break;
+// CHECK:         }
+// CHECK:         return;
+// CHECK:       }
+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
@@ -127,3 +171,175 @@ func.func @emitc_switch_ui16() {
   }
   return
 }
+
+// CHECK-LABEL: void emitc_switch_i32() {
+// CHECK:         int32_t v1 = 1;
+// CHECK:         switch(v1) {
+// CHECK:         case (2): {
+// CHECK:           int32_t v2 = func_b();
+// CHECK:           break;
+// CHECK:         }
+// CHECK:         case (5): {
+// CHECK:           int32_t v3 = func_a();
+// CHECK:           break;
+// CHECK:         }
+// CHECK:         default: {
+// CHECK:           float v4 = 4.200000000e+01f;
+// CHECK:           float v5 = 4.200000000e+01f;
+// CHECK:           func2(v4);
+// CHECK:           func3(v5, v4);
+// CHECK:           break;
+// CHECK:         }
+// CHECK:         return;
+// CHECK:       }
+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
+    %4 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.call_opaque "func3" (%3, %4) { args = [1 : index, 0 : index] } : (f32, f32) -> ()
+    emitc.yield
+  }
+  return
+}
+
+// CHECK-LABEL: void emitc_switch_ui32() {
+// CHECK:         uint32_t v1 = 1;
+// CHECK:         switch(v1) {
+// CHECK:         case (2): {
+// CHECK:           int32_t v2 = func_b();
+// CHECK:           break;
+// CHECK:         }
+// CHECK:         case (5): {
+// CHECK:           int32_t v3 = func_a();
+// CHECK:           break;
+// CHECK:         }
+// CHECK:         default: {
+// CHECK:           float v4 = 4.200000000e+01f;
+// CHECK:           float v5 = 4.200000000e+01f;
+// CHECK:           func2(v4);
+// CHECK:           func3(v5, v4);
+// CHECK:           break;
+// CHECK:         }
+// CHECK:         return;
+// CHECK:       }
+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
+    %4 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.call_opaque "func3" (%3, %4) { args = [1 : index, 0 : index] } : (f32, f32) -> ()
+    emitc.yield
+  }
+  return
+}
+
+// CHECK-LABEL: void emitc_switch_i64() {
+// CHECK:         int64_t v1 = 1;
+// CHECK:         switch(v1) {
+// CHECK:         case (2): {
+// CHECK:           int32_t v2 = func_b();
+// CHECK:           break;
+// CHECK:         }
+// CHECK:         case (5): {
+// CHECK:           int32_t v3 = func_a();
+// CHECK:           break;
+// CHECK:         }
+// CHECK:         default: {
+// CHECK:           float v4 = 4.200000000e+01f;
+// CHECK:           float v5 = 4.200000000e+01f;
+// CHECK:           func2(v4);
+// CHECK:           func3(v5, v4);
+// CHECK:           break;
+// CHECK:         }
+// CHECK:         return;
+// CHECK:       }
+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
+    %4 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.call_opaque "func3" (%3, %4) { args = [1 : index, 0 : index] } : (f32, f32) -> ()
+    emitc.yield
+  }
+  return
+}
+
+// CHECK-LABEL: void emitc_switch_ui64() {
+// CHECK:         uint64_t v1 = 1;
+// CHECK:         switch(v1) {
+// CHECK:         case (2): {
+// CHECK:           int32_t v2 = func_b();
+// CHECK:           break;
+// CHECK:         }
+// CHECK:         case (5): {
+// CHECK:           int32_t v3 = func_a();
+// CHECK:           break;
+// CHECK:         }
+// CHECK:         default: {
+// CHECK:           float v4 = 4.200000000e+01f;
+// CHECK:           float v5 = 4.200000000e+01f;
+// CHECK:           func2(v4);
+// CHECK:           func3(v5, v4);
+// CHECK:           break;
+// CHECK:         }
+// CHECK:         return;
+// CHECK:       }
+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
+    %4 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.call_opaque "func3" (%3, %4) { args = [1 : index, 0 : index] } : (f32, f32) -> ()
+    emitc.yield
+  }
+  return
+}

>From 89b62040c209f97b0cadbc184212c2f54ada7a44 Mon Sep 17 00:00:00 2001
From: EtoAndruwa <timonina1909 at gmail.com>
Date: Thu, 8 Aug 2024 16:16:33 +0300
Subject: [PATCH 3/8] [mlir][emict] Fix description of the switch op

---
 mlir/include/mlir/Dialect/EmitC/IR/EmitC.td | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index 0f1ab8cee8dfa0..f0c65548ed396c 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -1312,9 +1312,8 @@ def EmitC_SwitchOp : EmitC_Op<"switch", [RecursiveMemoryEffects,
   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
-    argument is always of type opaque or integer (singed or unsigned), excluding i8 
-    and i1.
+    the given regions based on the values of the argument and the cases.
+    The operand to a switch operation is a opaque or integral value.
 
     The operation always has a "default" region and any number of case regions
     denoted by integer constants. Control-flow transfers to the case region
@@ -1323,7 +1322,7 @@ def EmitC_SwitchOp : EmitC_Op<"switch", [RecursiveMemoryEffects,
     region.
 
     The operation does not return any value. Moreover, case regions and 
-    default region must be terminated using the `emitc.yield` operation.
+    default region must be explicitly terminated using the `emitc.yield` operation.
 
     Example:
 

>From e99904d1c8b0410accd7908d62f8fbbc08db2560 Mon Sep 17 00:00:00 2001
From: EtoAndruwa <timonina1909 at gmail.com>
Date: Fri, 9 Aug 2024 13:04:56 +0300
Subject: [PATCH 4/8] [mlir][emitc] NFC

---
 mlir/lib/Dialect/EmitC/IR/EmitC.cpp    | 29 ++++++++++----------------
 mlir/lib/Target/Cpp/TranslateToCpp.cpp |  2 +-
 2 files changed, 12 insertions(+), 19 deletions(-)

diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
index 8f3f5f0be7263f..608199ce9fffe0 100644
--- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
+++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
@@ -1131,7 +1131,6 @@ static void printSwitchCases(OpAsmPrinter &parser, Operation *op,
     parser << "case " << value << ": ";
     parser.printRegion(*region, /*printEntryBlockArgs=*/false);
   }
-  return;
 }
 
 ParseResult SwitchOp::parse(OpAsmParser &parser, OperationState &result) {
@@ -1179,21 +1178,19 @@ ParseResult SwitchOp::parse(OpAsmParser &parser, OperationState &result) {
   return success();
 }
 
-void SwitchOp::print(OpAsmPrinter &parser) {
-  parser << ' ';
-  parser << getArg();
+void SwitchOp::print(OpAsmPrinter &p) {
+  p << ' ';
+  p << getArg();
   SmallVector<StringRef, 2> elidedAttrs;
   elidedAttrs.push_back("cases");
-  parser.printOptionalAttrDict((*this)->getAttrs(), elidedAttrs);
-  parser << ' ';
-  printSwitchCases(parser, *this, getCasesAttr(), getCaseRegions());
-  parser.printNewline();
-  parser << "default";
-  parser << ' ';
-  parser.printRegion(getDefaultRegion(), /*printEntryBlockArgs=*/true,
-                     /*printBlockTerminators=*/true);
-
-  return;
+  p.printOptionalAttrDict((*this)->getAttrs(), elidedAttrs);
+  p << ' ';
+  printSwitchCases(p, *this, getCasesAttr(), getCaseRegions());
+  p.printNewline();
+  p << "default";
+  p << ' ';
+  p.printRegion(getDefaultRegion(), /*printEntryBlockArgs=*/true,
+                /*printBlockTerminators=*/true);
 }
 
 static LogicalResult verifyRegion(emitc::SwitchOp op, Region &region,
@@ -1250,7 +1247,6 @@ Block &emitc::SwitchOp::getCaseBlock(unsigned idx) {
 void SwitchOp::getSuccessorRegions(
     RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> &successors) {
   llvm::copy(getRegions(), std::back_inserter(successors));
-  return;
 }
 
 void SwitchOp::getEntrySuccessorRegions(
@@ -1274,7 +1270,6 @@ void SwitchOp::getEntrySuccessorRegions(
     }
   }
   successors.emplace_back(&getDefaultRegion());
-  return;
 }
 
 void SwitchOp::getRegionInvocationBounds(
@@ -1296,8 +1291,6 @@ void SwitchOp::getRegionInvocationBounds(
   for (unsigned regIndex = 0, regNum = getNumRegions(); regIndex < regNum;
        ++regIndex)
     bounds.emplace_back(/*lb=*/0, /*ub=*/regIndex == liveIndex);
-
-  return;
 }
 
 //===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
index 8831b95a27246a..ff5515a3bf7c4a 100644
--- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp
+++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
@@ -464,7 +464,7 @@ static LogicalResult printOperation(CppEmitter &emitter,
                                     emitc::SwitchOp switchOp) {
   raw_indented_ostream &os = emitter.ostream();
 
-  os << "\nswitch(" << emitter.getOrCreateName(switchOp.getArg()) << ") {";
+  os << "\nswitch (" << emitter.getOrCreateName(switchOp.getArg()) << ") {";
 
   for (auto pair : llvm::zip(switchOp.getCases(), switchOp.getCaseRegions())) {
     os << "\ncase "

>From 5c44e22c90875906eb2318ddf608fdc185e8291a Mon Sep 17 00:00:00 2001
From: EtoAndruwa <timonina1909 at gmail.com>
Date: Fri, 9 Aug 2024 13:50:43 +0300
Subject: [PATCH 5/8] [mlir][emitc] Add test cases for
 '-declare-variables-at-top'

---
 mlir/test/Target/Cpp/switch.mlir | 670 +++++++++++++++++++++++--------
 1 file changed, 509 insertions(+), 161 deletions(-)

diff --git a/mlir/test/Target/Cpp/switch.mlir b/mlir/test/Target/Cpp/switch.mlir
index 92228504c20eb2..65367d8f819e58 100644
--- a/mlir/test/Target/Cpp/switch.mlir
+++ b/mlir/test/Target/Cpp/switch.mlir
@@ -1,24 +1,191 @@
-// RUN: mlir-translate -mlir-to-cpp %s | FileCheck %s
-
-// CHECK-LABEL:   int8_t v1 = 1;
-// CHECK-LABEL:   switch(v1) {
-// CHECK:         case (2): {
-// CHECK:           int32_t v2 = func_b();
-// CHECK:           break;
-// CHECK:         }
-// CHECK:         case (5): {
-// CHECK:           int32_t v3 = func_a();
-// CHECK:           break;
-// CHECK:         }
-// CHECK:         default: {
-// CHECK:           float v4 = 4.200000000e+01f;
-// CHECK:           float v5 = 4.200000000e+01f;
-// CHECK:           func2(v4);
-// CHECK:           func3(v5, v4);
-// CHECK:           break;
-// CHECK:         }
-// CHECK:         return;
-// CHECK:       }
+// 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_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:           float v5 = 4.200000000e+01f;
+// CPP-DEFAULT:           func2(v4);
+// CPP-DEFAULT:           func3(v5, 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:         float v3;
+// CPP-DECLTOP:         int32_t v4;
+// CPP-DECLTOP:         int32_t v5;
+// CPP-DECLTOP:         v1 = 1;
+// CPP-DECLTOP:         switch (v1) {
+// CPP-DECLTOP:         case (2): {
+// CPP-DECLTOP:           v4 = func_b();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         case (5): {
+// CPP-DECLTOP:           v5 = func_a();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         default: {
+// CPP-DECLTOP:           v2 = 4.200000000e+01f;
+// CPP-DECLTOP:           v3 = 4.200000000e+01f;
+// CPP-DECLTOP:           func2(v2);
+// CPP-DECLTOP:           func3(v3, 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
+    %4 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.call_opaque "func3" (%3, %4) { args = [1 : index, 0 : index] } : (f32, 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:           float v5 = 4.200000000e+01f;
+// CPP-DEFAULT:           func2(v4);
+// CPP-DEFAULT:           func3(v5, 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:         float v3;
+// CPP-DECLTOP:         int32_t v4;
+// CPP-DECLTOP:         int32_t v5;
+// CPP-DECLTOP:         v1 = true;
+// CPP-DECLTOP:         switch (v1) {
+// CPP-DECLTOP:         case (2): {
+// CPP-DECLTOP:           v4 = func_b();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         case (5): {
+// CPP-DECLTOP:           v5 = func_a();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         default: {
+// CPP-DECLTOP:           v2 = 4.200000000e+01f;
+// CPP-DECLTOP:           v3 = 4.200000000e+01f;
+// CPP-DECLTOP:           func2(v2);
+// CPP-DECLTOP:           func3(v3, 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
+    %4 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+
+    emitc.call_opaque "func2" (%3) : (f32) -> ()
+    emitc.call_opaque "func3" (%3, %4) { args = [1 : index, 0 : index] } : (f32, 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:           float v5 = 4.200000000e+01f;
+// CPP-DEFAULT:           func2(v4);
+// CPP-DEFAULT:           func3(v5, 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:         float v3;
+// CPP-DECLTOP:         int32_t v4;
+// CPP-DECLTOP:         int32_t v5;
+// CPP-DECLTOP:         v1 = 1;
+// CPP-DECLTOP:         switch (v1) {
+// CPP-DECLTOP:         case (2): {
+// CPP-DECLTOP:           v4 = func_b();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         case (5): {
+// CPP-DECLTOP:           v5 = func_a();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         default: {
+// CPP-DECLTOP:           v2 = 4.200000000e+01f;
+// CPP-DECLTOP:           v3 = 4.200000000e+01f;
+// CPP-DECLTOP:           func2(v2);
+// CPP-DECLTOP:           func3(v3, v2);
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         return;
+// CPP-DECLTOP:       }
 func.func @emitc_switch_i8() {
   %0 = "emitc.variable"(){value = 1 : i8} : () -> i8
 
@@ -42,27 +209,52 @@ func.func @emitc_switch_i8() {
   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:           float v5 = 4.200000000e+01f;
+// CPP-DEFAULT:           func2(v4);
+// CPP-DEFAULT:           func3(v5, v4);
+// CPP-DEFAULT:           break;
+// CPP-DEFAULT:         }
+// CPP-DEFAULT:         return;
+// CPP-DEFAULT:       }
 
-// CHECK-LABEL: void emitc_switch_ui8() {
-// CHECK:         uint8_t v1 = 1;
-// CHECK:         switch(v1) {
-// CHECK:         case (2): {
-// CHECK:           int32_t v2 = func_b();
-// CHECK:           break;
-// CHECK:         }
-// CHECK:         case (5): {
-// CHECK:           int32_t v3 = func_a();
-// CHECK:           break;
-// CHECK:         }
-// CHECK:         default: {
-// CHECK:           float v4 = 4.200000000e+01f;
-// CHECK:           float v5 = 4.200000000e+01f;
-// CHECK:           func2(v4);
-// CHECK:           func3(v5, v4);
-// CHECK:           break;
-// CHECK:         }
-// CHECK:         return;
-// CHECK:       }
+// CPP-DECLTOP-LABEL: void emitc_switch_ui8() {
+// CPP-DECLTOP:         uint8_t v1;
+// CPP-DECLTOP:         float v2;
+// CPP-DECLTOP:         float v3;
+// CPP-DECLTOP:         int32_t v4;
+// CPP-DECLTOP:         int32_t v5;
+// CPP-DECLTOP:         v1 = 1;
+// CPP-DECLTOP:         switch (v1) {
+// CPP-DECLTOP:         case (2): {
+// CPP-DECLTOP:           v4 = func_b();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         case (5): {
+// CPP-DECLTOP:           v5 = func_a();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         default: {
+// CPP-DECLTOP:           v2 = 4.200000000e+01f;
+// CPP-DECLTOP:           v3 = 4.200000000e+01f;
+// CPP-DECLTOP:           func2(v2);
+// CPP-DECLTOP:           func3(v3, v2);
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         return;
+// CPP-DECLTOP:       }
 func.func @emitc_switch_ui8() {
   %0 = "emitc.variable"(){value = 1 : ui8} : () -> ui8
 
@@ -86,26 +278,52 @@ func.func @emitc_switch_ui8() {
   return
 }
 
-// CHECK-LABEL: void emitc_switch_i16() {
-// CHECK:         int16_t v1 = 1;
-// CHECK:         switch(v1) {
-// CHECK:         case (2): {
-// CHECK:           int32_t v2 = func_b();
-// CHECK:           break;
-// CHECK:         }
-// CHECK:         case (5): {
-// CHECK:           int32_t v3 = func_a();
-// CHECK:           break;
-// CHECK:         }
-// CHECK:         default: {
-// CHECK:           float v4 = 4.200000000e+01f;
-// CHECK:           float v5 = 4.200000000e+01f;
-// CHECK:           func2(v4);
-// CHECK:           func3(v5, v4);
-// CHECK:           break;
-// CHECK:         }
-// CHECK:         return;
-// CHECK:       }
+// 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:           float v5 = 4.200000000e+01f;
+// CPP-DEFAULT:           func2(v4);
+// CPP-DEFAULT:           func3(v5, 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:         float v3;
+// CPP-DECLTOP:         int32_t v4;
+// CPP-DECLTOP:         int32_t v5;
+// CPP-DECLTOP:         v1 = 1;
+// CPP-DECLTOP:         switch (v1) {
+// CPP-DECLTOP:         case (2): {
+// CPP-DECLTOP:           v4 = func_b();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         case (5): {
+// CPP-DECLTOP:           v5 = func_a();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         default: {
+// CPP-DECLTOP:           v2 = 4.200000000e+01f;
+// CPP-DECLTOP:           v3 = 4.200000000e+01f;
+// CPP-DECLTOP:           func2(v2);
+// CPP-DECLTOP:           func3(v3, v2);
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         return;
+// CPP-DECLTOP:       }
 func.func @emitc_switch_i16() {
   %0 = "emitc.variable"(){value = 1 : i16} : () -> i16
 
@@ -129,26 +347,52 @@ func.func @emitc_switch_i16() {
   return
 }
 
-// CHECK-LABEL: void emitc_switch_ui16() {
-// CHECK:         uint16_t v1 = 1;
-// CHECK:         switch(v1) {
-// CHECK:         case (2): {
-// CHECK:           int32_t v2 = func_b();
-// CHECK:           break;
-// CHECK:         }
-// CHECK:         case (5): {
-// CHECK:           int32_t v3 = func_a();
-// CHECK:           break;
-// CHECK:         }
-// CHECK:         default: {
-// CHECK:           float v4 = 4.200000000e+01f;
-// CHECK:           float v5 = 4.200000000e+01f;
-// CHECK:           func2(v4);
-// CHECK:           func3(v5, v4);
-// CHECK:           break;
-// CHECK:         }
-// CHECK:         return;
-// CHECK:       }
+// 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:           float v5 = 4.200000000e+01f;
+// CPP-DEFAULT:           func2(v4);
+// CPP-DEFAULT:           func3(v5, 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:         float v3;
+// CPP-DECLTOP:         int32_t v4;
+// CPP-DECLTOP:         int32_t v5;
+// CPP-DECLTOP:         v1 = 1;
+// CPP-DECLTOP:         switch (v1) {
+// CPP-DECLTOP:         case (2): {
+// CPP-DECLTOP:           v4 = func_b();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         case (5): {
+// CPP-DECLTOP:           v5 = func_a();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         default: {
+// CPP-DECLTOP:           v2 = 4.200000000e+01f;
+// CPP-DECLTOP:           v3 = 4.200000000e+01f;
+// CPP-DECLTOP:           func2(v2);
+// CPP-DECLTOP:           func3(v3, v2);
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         return;
+// CPP-DECLTOP:       }
 func.func @emitc_switch_ui16() {
   %0 = "emitc.variable"(){value = 1 : ui16} : () -> ui16
 
@@ -172,26 +416,52 @@ func.func @emitc_switch_ui16() {
   return
 }
 
-// CHECK-LABEL: void emitc_switch_i32() {
-// CHECK:         int32_t v1 = 1;
-// CHECK:         switch(v1) {
-// CHECK:         case (2): {
-// CHECK:           int32_t v2 = func_b();
-// CHECK:           break;
-// CHECK:         }
-// CHECK:         case (5): {
-// CHECK:           int32_t v3 = func_a();
-// CHECK:           break;
-// CHECK:         }
-// CHECK:         default: {
-// CHECK:           float v4 = 4.200000000e+01f;
-// CHECK:           float v5 = 4.200000000e+01f;
-// CHECK:           func2(v4);
-// CHECK:           func3(v5, v4);
-// CHECK:           break;
-// CHECK:         }
-// CHECK:         return;
-// CHECK:       }
+// 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:           float v5 = 4.200000000e+01f;
+// CPP-DEFAULT:           func2(v4);
+// CPP-DEFAULT:           func3(v5, 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:         float v3;
+// CPP-DECLTOP:         int32_t v4;
+// CPP-DECLTOP:         int32_t v5;
+// CPP-DECLTOP:         v1 = 1;
+// CPP-DECLTOP:         switch (v1) {
+// CPP-DECLTOP:         case (2): {
+// CPP-DECLTOP:           v4 = func_b();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         case (5): {
+// CPP-DECLTOP:           v5 = func_a();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         default: {
+// CPP-DECLTOP:           v2 = 4.200000000e+01f;
+// CPP-DECLTOP:           v3 = 4.200000000e+01f;
+// CPP-DECLTOP:           func2(v2);
+// CPP-DECLTOP:           func3(v3, v2);
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         return;
+// CPP-DECLTOP:       }
 func.func @emitc_switch_i32() {
   %0 = "emitc.variable"(){value = 1 : i32} : () -> i32
 
@@ -215,26 +485,52 @@ func.func @emitc_switch_i32() {
   return
 }
 
-// CHECK-LABEL: void emitc_switch_ui32() {
-// CHECK:         uint32_t v1 = 1;
-// CHECK:         switch(v1) {
-// CHECK:         case (2): {
-// CHECK:           int32_t v2 = func_b();
-// CHECK:           break;
-// CHECK:         }
-// CHECK:         case (5): {
-// CHECK:           int32_t v3 = func_a();
-// CHECK:           break;
-// CHECK:         }
-// CHECK:         default: {
-// CHECK:           float v4 = 4.200000000e+01f;
-// CHECK:           float v5 = 4.200000000e+01f;
-// CHECK:           func2(v4);
-// CHECK:           func3(v5, v4);
-// CHECK:           break;
-// CHECK:         }
-// CHECK:         return;
-// CHECK:       }
+// 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:           float v5 = 4.200000000e+01f;
+// CPP-DEFAULT:           func2(v4);
+// CPP-DEFAULT:           func3(v5, 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:         float v3;
+// CPP-DECLTOP:         int32_t v4;
+// CPP-DECLTOP:         int32_t v5;
+// CPP-DECLTOP:         v1 = 1;
+// CPP-DECLTOP:         switch (v1) {
+// CPP-DECLTOP:         case (2): {
+// CPP-DECLTOP:           v4 = func_b();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         case (5): {
+// CPP-DECLTOP:           v5 = func_a();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         default: {
+// CPP-DECLTOP:           v2 = 4.200000000e+01f;
+// CPP-DECLTOP:           v3 = 4.200000000e+01f;
+// CPP-DECLTOP:           func2(v2);
+// CPP-DECLTOP:           func3(v3, v2);
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         return;
+// CPP-DECLTOP:       }
 func.func @emitc_switch_ui32() {
   %0 = "emitc.variable"(){value = 1 : ui32} : () -> ui32
 
@@ -258,26 +554,52 @@ func.func @emitc_switch_ui32() {
   return
 }
 
-// CHECK-LABEL: void emitc_switch_i64() {
-// CHECK:         int64_t v1 = 1;
-// CHECK:         switch(v1) {
-// CHECK:         case (2): {
-// CHECK:           int32_t v2 = func_b();
-// CHECK:           break;
-// CHECK:         }
-// CHECK:         case (5): {
-// CHECK:           int32_t v3 = func_a();
-// CHECK:           break;
-// CHECK:         }
-// CHECK:         default: {
-// CHECK:           float v4 = 4.200000000e+01f;
-// CHECK:           float v5 = 4.200000000e+01f;
-// CHECK:           func2(v4);
-// CHECK:           func3(v5, v4);
-// CHECK:           break;
-// CHECK:         }
-// CHECK:         return;
-// CHECK:       }
+// 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:           float v5 = 4.200000000e+01f;
+// CPP-DEFAULT:           func2(v4);
+// CPP-DEFAULT:           func3(v5, 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:         float v3;
+// CPP-DECLTOP:         int32_t v4;
+// CPP-DECLTOP:         int32_t v5;
+// CPP-DECLTOP:         v1 = 1;
+// CPP-DECLTOP:         switch (v1) {
+// CPP-DECLTOP:         case (2): {
+// CPP-DECLTOP:           v4 = func_b();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         case (5): {
+// CPP-DECLTOP:           v5 = func_a();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         default: {
+// CPP-DECLTOP:           v2 = 4.200000000e+01f;
+// CPP-DECLTOP:           v3 = 4.200000000e+01f;
+// CPP-DECLTOP:           func2(v2);
+// CPP-DECLTOP:           func3(v3, v2);
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         return;
+// CPP-DECLTOP:       }
 func.func @emitc_switch_i64() {
   %0 = "emitc.variable"(){value = 1 : i64} : () -> i64
 
@@ -301,26 +623,52 @@ func.func @emitc_switch_i64() {
   return
 }
 
-// CHECK-LABEL: void emitc_switch_ui64() {
-// CHECK:         uint64_t v1 = 1;
-// CHECK:         switch(v1) {
-// CHECK:         case (2): {
-// CHECK:           int32_t v2 = func_b();
-// CHECK:           break;
-// CHECK:         }
-// CHECK:         case (5): {
-// CHECK:           int32_t v3 = func_a();
-// CHECK:           break;
-// CHECK:         }
-// CHECK:         default: {
-// CHECK:           float v4 = 4.200000000e+01f;
-// CHECK:           float v5 = 4.200000000e+01f;
-// CHECK:           func2(v4);
-// CHECK:           func3(v5, v4);
-// CHECK:           break;
-// CHECK:         }
-// CHECK:         return;
-// CHECK:       }
+// 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:           float v5 = 4.200000000e+01f;
+// CPP-DEFAULT:           func2(v4);
+// CPP-DEFAULT:           func3(v5, 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:         float v3;
+// CPP-DECLTOP:         int32_t v4;
+// CPP-DECLTOP:         int32_t v5;
+// CPP-DECLTOP:         v1 = 1;
+// CPP-DECLTOP:         switch (v1) {
+// CPP-DECLTOP:         case (2): {
+// CPP-DECLTOP:           v4 = func_b();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         case (5): {
+// CPP-DECLTOP:           v5 = func_a();
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         default: {
+// CPP-DECLTOP:           v2 = 4.200000000e+01f;
+// CPP-DECLTOP:           v3 = 4.200000000e+01f;
+// CPP-DECLTOP:           func2(v2);
+// CPP-DECLTOP:           func3(v3, v2);
+// CPP-DECLTOP:           break;
+// CPP-DECLTOP:         }
+// CPP-DECLTOP:         return;
+// CPP-DECLTOP:       }
 func.func @emitc_switch_ui64() {
   %0 = "emitc.variable"(){value = 1 : ui64} : () -> ui64
 

>From 246e9f710254be4c47de4b41b54737a42fc89c20 Mon Sep 17 00:00:00 2001
From: EtoAndruwa <timonina1909 at gmail.com>
Date: Fri, 9 Aug 2024 13:55:46 +0300
Subject: [PATCH 6/8] [mlir][emitc] Fix 'isSwitchOperandType' function

---
 mlir/lib/Dialect/EmitC/IR/EmitC.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
index 608199ce9fffe0..1839d6621af389 100644
--- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
+++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
@@ -132,8 +132,8 @@ bool mlir::emitc::isPointerWideType(Type type) {
 }
 
 bool mlir::emitc::isSwitchOperandType(Type type) {
-  auto intType = llvm::dyn_cast<IntegerType>(type);
-  return isa<emitc::OpaqueType>(type) || isSupportedIntegerType(type);
+  return isa<emitc::OpaqueType, IndexType>(type) ||
+         isSupportedIntegerType(type);
 }
 
 /// Check that the type of the initial value is compatible with the operations

>From 6f53683cea3dc1bd6787a03ea2743ddf89ece649 Mon Sep 17 00:00:00 2001
From: EtoAndruwa <timonina1909 at gmail.com>
Date: Fri, 9 Aug 2024 14:14:00 +0300
Subject: [PATCH 7/8] [mlir][emitc] NFC

---
 mlir/include/mlir/Dialect/EmitC/IR/EmitC.h | 2 +-
 mlir/lib/Dialect/EmitC/IR/EmitC.cpp        | 8 ++++----
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h
index c7211c9e9b010e..8d32e74d68820c 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h
@@ -47,7 +47,7 @@ bool isSupportedFloatType(mlir::Type type);
 /// Determines whether \p type is a emitc.size_t/ssize_t type.
 bool isPointerWideType(mlir::Type type);
 
-/// Determines whether \p type is a valid integer or opaque type for SwitchOp.
+/// Determines whether \p type is a valid integer, index or opaque type for SwitchOp.
 bool isSwitchOperandType(Type type);
 
 } // namespace emitc
diff --git a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
index 1839d6621af389..7ce03d9f5bed94 100644
--- a/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
+++ b/mlir/lib/Dialect/EmitC/IR/EmitC.cpp
@@ -1124,12 +1124,12 @@ parseSwitchCases(OpAsmParser &parser, DenseI64ArrayAttr &cases,
 }
 
 /// Print the case regions and values.
-static void printSwitchCases(OpAsmPrinter &parser, Operation *op,
+static void printSwitchCases(OpAsmPrinter &p, Operation *op,
                              DenseI64ArrayAttr cases, RegionRange caseRegions) {
   for (auto [value, region] : llvm::zip(cases.asArrayRef(), caseRegions)) {
-    parser.printNewline();
-    parser << "case " << value << ": ";
-    parser.printRegion(*region, /*printEntryBlockArgs=*/false);
+    p.printNewline();
+    p << "case " << value << ": ";
+    p.printRegion(*region, /*printEntryBlockArgs=*/false);
   }
 }
 

>From 99903071992922c8b8aec1f50c56ce3ca39e42d7 Mon Sep 17 00:00:00 2001
From: EtoAndruwa <timonina1909 at gmail.com>
Date: Fri, 9 Aug 2024 14:15:29 +0300
Subject: [PATCH 8/8] [mlir][emitc] Add example of emitted code to the op's
 description

---
 mlir/include/mlir/Dialect/EmitC/IR/EmitC.td | 168 ++++++++++++++++----
 1 file changed, 137 insertions(+), 31 deletions(-)

diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index f0c65548ed396c..2ba5d64d15f129 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -1327,44 +1327,150 @@ def EmitC_SwitchOp : EmitC_Op<"switch", [RecursiveMemoryEffects,
     Example:
 
     ```mlir
-    // Cases with i32 type.
-    %0 = "emitc.variable"(){value = 42 : 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
+    // Example for i32:
+    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
+        %4 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+
+        emitc.call_opaque "func2" (%3) : (f32) -> ()
+        emitc.call_opaque "func3" (%3, %4) { args = [1 : index, 0 : index] } : (f32, f32) -> ()
+        emitc.yield
+      }
+      return
     }
-    default : {
-      %3 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
-      %4 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
 
-      emitc.call_opaque "func2" (%3) : (f32) -> ()
-      emitc.call_opaque "func3" (%3, %4) { args = [1 : index, 0 : index] } : (f32, f32) -> ()
-      emitc.yield
+    // Output for i32:
+    void emitc_switch_i32() {
+      int32_t v1 = 1;
+
+      switch (v1) {
+      case (2): {
+        int32_t v2 = func_b();
+        break;
+      }
+      case (5): {
+        int32_t v3 = func_a();
+        break;
+      }
+      default: {
+        float v4 = 4.200000000e+01f;
+        float v5 = 4.200000000e+01f;
+        func2(v4);
+        func3(v5, v4);
+        break;
+      }
+      return;
     }
     ...
-    // Cases with i16 type.
-    %0 = "emitc.variable"(){value = 42 : i16} : () -> i16
-    emitc.switch %0 : i16
-    case 2: {
-      %1 = emitc.call_opaque "func_b" () : () -> i32
-      emitc.yield
+    // Example for ui16:
+    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
+        %4 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+
+        emitc.call_opaque "func2" (%3) : (f32) -> ()
+        emitc.call_opaque "func3" (%3, %4) { args = [1 : index, 0 : index] } : (f32, f32) -> ()
+        emitc.yield
+      }
+      return
     }
-    case 5: {
-      %2 = emitc.call_opaque "func_a" () : () -> i32
-      emitc.yield
+
+    // Output for ui16:
+    void emitc_switch_ui16() {
+      uint16_t v1 = 1;
+
+      switch (v1) {
+      case (2): {
+        int32_t v2 = func_b();
+        break;
+      }
+      case (5): {
+        int32_t v3 = func_a();
+        break;
+      }
+      default: {
+        float v4 = 4.200000000e+01f;
+        float v5 = 4.200000000e+01f;
+        func2(v4);
+        func3(v5, v4);
+        break;
+      }
+      return;
+    }
+    ...
+    // Example for i64 with `-declare-variables-at-top` option: 
+    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
+        %4 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+
+        emitc.call_opaque "func2" (%3) : (f32) -> ()
+        emitc.call_opaque "func3" (%3, %4) { args = [1 : index, 0 : index] } : (f32, f32) -> ()
+        emitc.yield
+      }
+      return
     }
-    default : {
-      %3 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
-      %4 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
 
-      emitc.call_opaque "func2" (%3) : (f32) -> ()
-      emitc.call_opaque "func3" (%3, %4) { args = [1 : index, 0 : index] } : (f32, f32) -> ()
-      emitc.yield
+    // Output for i64 with `-declare-variables-at-top` option:
+    void emitc_switch_i64() {
+      int64_t v1;
+      float v2;
+      float v3;
+      int32_t v4;
+      int32_t v5;
+      v1 = 1;
+
+      switch (v1) {
+      case (2): {
+        v4 = func_b();
+        break;
+      }
+      case (5): {
+        v5 = func_a();
+        break;
+      }
+      default: {
+        v2 = 4.200000000e+01f;
+        v3 = 4.200000000e+01f;
+        func2(v2);
+        func3(v3, v2);
+        break;
+      }
+      return;
     }
     ```
   }];



More information about the Mlir-commits mailing list