[Mlir-commits] [mlir] [mlir][emitc] Add 'emitc.switch' op to the dialect (PR #102331)
Andrey Timonin
llvmlistbot at llvm.org
Mon Aug 12 01:55:37 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 01/14] [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 ®ion = *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 ®ion,
+ 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 ®ion) {
+ 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 02/14] [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 03/14] [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 04/14] [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 ®ion,
@@ -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 05/14] [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 06/14] [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 07/14] [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 08/14] [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;
}
```
}];
>From b9793a6f04b3ab1ff9ed86a675c93e957ec52f51 Mon Sep 17 00:00:00 2001
From: EtoAndruwa <timonina1909 at gmail.com>
Date: Fri, 9 Aug 2024 15:00:50 +0300
Subject: [PATCH 09/14] [mlir][emitc] NFC
---
mlir/include/mlir/Dialect/EmitC/IR/EmitC.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h
index 8d32e74d68820c..5d623a816ed5a1 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.h
@@ -47,7 +47,8 @@ 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, index 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
>From 07f8db06d4431df1d4c4e95754b184bacd28c637 Mon Sep 17 00:00:00 2001
From: EtoAndruwa <timonina1909 at gmail.com>
Date: Fri, 9 Aug 2024 18:16:15 +0300
Subject: [PATCH 10/14] [mlir][emitc] NFC. Fix 'test_misplaced_yield' test case
---
mlir/test/Dialect/EmitC/invalid_ops.mlir | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mlir/test/Dialect/EmitC/invalid_ops.mlir b/mlir/test/Dialect/EmitC/invalid_ops.mlir
index 4181b726593e4a..66da0a06a1ac95 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
}
>From e7d1c50985185f49d300980d8aa73d26f192fefb Mon Sep 17 00:00:00 2001
From: EtoAndruwa <timonina1909 at gmail.com>
Date: Fri, 9 Aug 2024 19:17:42 +0300
Subject: [PATCH 11/14] [mlir][emitc] Add conversion from 'scf::index_switch'
to 'emitc::switch'
---
mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp | 52 +++++++-
mlir/test/Conversion/SCFToEmitC/switch.mlir | 121 ++++++++++++++++++
2 files changed, 172 insertions(+), 1 deletion(-)
create mode 100644 mlir/test/Conversion/SCFToEmitC/switch.mlir
diff --git a/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp b/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
index 0a892422252554..10ac078145e8bb 100644
--- a/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
+++ b/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
@@ -177,9 +177,59 @@ LogicalResult IfLowering::matchAndRewrite(IfOp ifOp,
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);
+
+ // Utility function to lower the contents of an scf::index_switch regions to
+ // an emitc::switch regions. The contents of the scf::index_switch regions is
+ // moved into the respective emitc::switch 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 ®ion,
+ Region &loweredRegion) {
+ rewriter.inlineRegionBefore(region, loweredRegion, loweredRegion.end());
+ Operation *terminator = loweredRegion.back().getTerminator();
+ lowerYield(resultVariables, rewriter, cast<scf::YieldOp>(terminator));
+ };
+
+ 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(std::get<0>(pair), std::get<1>(pair));
+ }
+
+ // Lowering default region.
+ lowerRegion(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 +238,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/test/Conversion/SCFToEmitC/switch.mlir b/mlir/test/Conversion/SCFToEmitC/switch.mlir
new file mode 100644
index 00000000000000..ef6018e0405618
--- /dev/null
+++ b/mlir/test/Conversion/SCFToEmitC/switch.mlir
@@ -0,0 +1,121 @@
+// 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: emitc.yield
+// 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
+ scf.yield
+ }
+ return
+}
+
+// CHECK-LABEL: func.func @switch_one_result(
+// CHECK-SAME: %[[VAL_0:.*]]: index) {
+// CHECK: %[[VAL_1:.*]] = "emitc.variable"() <{value = #[[?]]<"">}> : () -> 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: emitc.yield
+// 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 = #[[?]]<"">}> : () -> i32
+// CHECK: %[[VAL_2:.*]] = "emitc.variable"() <{value = #[[?]]<"">}> : () -> 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: emitc.yield
+// 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
+}
>From df8e92b1bc90cd9c43fc10c05914a83229919ecb Mon Sep 17 00:00:00 2001
From: EtoAndruwa <timonina1909 at gmail.com>
Date: Fri, 9 Aug 2024 19:27:48 +0300
Subject: [PATCH 12/14] [mlir][emitc] Add test case for the index type
---
mlir/test/Target/Cpp/switch.mlir | 69 ++++++++++++++++++++++++++++++++
1 file changed, 69 insertions(+)
diff --git a/mlir/test/Target/Cpp/switch.mlir b/mlir/test/Target/Cpp/switch.mlir
index 65367d8f819e58..f301b7d2fe17a9 100644
--- a/mlir/test/Target/Cpp/switch.mlir
+++ b/mlir/test/Target/Cpp/switch.mlir
@@ -1,6 +1,75 @@
// 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_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: 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_index() {
+// 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_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
+ %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_opaque() {
// CPP-DEFAULT: size_t v1 = 1;
// CPP-DEFAULT: switch (v1) {
>From dfacff1c0cfd3d47d31e74c046f280d56e135b7c Mon Sep 17 00:00:00 2001
From: EtoAndruwa <timonina1909 at gmail.com>
Date: Sat, 10 Aug 2024 00:09:07 +0300
Subject: [PATCH 13/14] [mlir][emitc] NFC. Fix op's example in the description
---
mlir/include/mlir/Dialect/EmitC/IR/EmitC.td | 162 +++-----------------
1 file changed, 24 insertions(+), 138 deletions(-)
diff --git a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
index 2ba5d64d15f129..ad3f732614ee9e 100644
--- a/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
+++ b/mlir/include/mlir/Dialect/EmitC/IR/EmitC.td
@@ -1327,150 +1327,36 @@ def EmitC_SwitchOp : EmitC_Op<"switch", [RecursiveMemoryEffects,
Example:
```mlir
- // 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
+ // Example:
+ emitc.switch %0 : i32
+ case 2: {
+ %1 = emitc.call_opaque "func_b" () : () -> i32
+ 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;
+ case 5: {
+ %2 = emitc.call_opaque "func_a" () : () -> 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
+ default: {
+ %3 = "emitc.variable"(){value = 42.0 : f32} : () -> f32
+ emitc.call_opaque "func2" (%3) : (f32) -> ()
+ 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;
+ // Output:
+ switch (v1) {
+ case (2): {
+ int32_t v2 = func_b();
+ break;
}
- ...
- // 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
+ case (5): {
+ int32_t v3 = func_a();
+ break;
}
-
- // 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;
+ default: {
+ float v4 = 4.200000000e+01f;
+ func2(v4);
+ break;
}
```
}];
>From 1188105f351201fb67d53b88ef6d2f7a10cb94c7 Mon Sep 17 00:00:00 2001
From: EtoAndruwa <timonina1909 at gmail.com>
Date: Mon, 12 Aug 2024 11:51:27 +0300
Subject: [PATCH 14/14] [emitc][mlir] Refactor 'lowerRegion' lambda function
---
mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp | 37 +++++++------------
1 file changed, 13 insertions(+), 24 deletions(-)
diff --git a/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp b/mlir/lib/Conversion/SCFToEmitC/SCFToEmitC.cpp
index 10ac078145e8bb..63933515810d44 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 regions. 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 ®ion,
+ 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 ®ion,
- 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();
@@ -196,18 +197,6 @@ IndexSwitchOpLowering::matchAndRewrite(IndexSwitchOp indexSwitchOp,
SmallVector<Value> resultVariables =
createVariablesForResults(indexSwitchOp, rewriter);
- // Utility function to lower the contents of an scf::index_switch regions to
- // an emitc::switch regions. The contents of the scf::index_switch regions is
- // moved into the respective emitc::switch 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 ®ion,
- Region &loweredRegion) {
- rewriter.inlineRegionBefore(region, loweredRegion, loweredRegion.end());
- Operation *terminator = loweredRegion.back().getTerminator();
- lowerYield(resultVariables, rewriter, cast<scf::YieldOp>(terminator));
- };
-
auto loweredSwitch = rewriter.create<emitc::SwitchOp>(
loc, indexSwitchOp.getArg(), indexSwitchOp.getCases(),
indexSwitchOp.getNumCases());
More information about the Mlir-commits
mailing list