| //===- TranslateToCpp.cpp - Translating to C++ calls ----------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "emitc/Target/Cpp/CppEmitter.h" |
| #include "mlir/Dialect/EmitC/IR/EmitC.h" |
| #include "mlir/Dialect/SCF/SCF.h" |
| #include "mlir/Dialect/StandardOps/IR/Ops.h" |
| #include "mlir/IR/BuiltinOps.h" |
| #include "mlir/IR/BuiltinTypes.h" |
| #include "mlir/IR/Dialect.h" |
| #include "mlir/IR/Operation.h" |
| #include "mlir/Support/IndentedOstream.h" |
| #include "llvm/ADT/DenseMap.h" |
| #include "llvm/ADT/StringExtras.h" |
| #include "llvm/ADT/StringMap.h" |
| #include "llvm/ADT/TypeSwitch.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/FormatVariadic.h" |
| |
| #define DEBUG_TYPE "translate-to-cpp" |
| |
| using namespace mlir; |
| using namespace mlir::emitc; |
| using llvm::formatv; |
| |
| static LogicalResult printConstantOp(CppEmitter &emitter, Operation *operation, |
| Attribute value) { |
| OpResult result = operation->getResult(0); |
| |
| // Only emit an assignment as the variable was already declared when printing |
| // the FuncOp. |
| if (emitter.shouldDeclareVariablesAtTop()) { |
| // Skip the assignment if the emitc.constant has no value. |
| if (auto oAttr = value.dyn_cast<emitc::OpaqueAttr>()) { |
| if (oAttr.getValue().empty()) |
| return success(); |
| } |
| |
| if (failed(emitter.emitVariableAssignment(result))) |
| return failure(); |
| return emitter.emitAttribute(operation->getLoc(), value); |
| } |
| |
| // Emit a variable declaration for an emitc.constant op without value. |
| if (auto oAttr = value.dyn_cast<emitc::OpaqueAttr>()) { |
| if (oAttr.getValue().empty()) |
| // The semicolon gets printed by the emitOperation function. |
| return emitter.emitVariableDeclaration(result, |
| /*trailingSemicolon=*/false); |
| } |
| |
| // Emit a variable declaration. |
| if (failed(emitter.emitAssignPrefix(*operation))) |
| return failure(); |
| return emitter.emitAttribute(operation->getLoc(), value); |
| } |
| |
| static LogicalResult printOperation(CppEmitter &emitter, |
| emitc::ConstantOp constantOp) { |
| Operation *operation = constantOp.getOperation(); |
| Attribute value = constantOp.value(); |
| |
| return printConstantOp(emitter, operation, value); |
| } |
| |
| static LogicalResult printOperation(CppEmitter &emitter, |
| mlir::ConstantOp constantOp) { |
| Operation *operation = constantOp.getOperation(); |
| Attribute value = constantOp.value(); |
| |
| return printConstantOp(emitter, operation, value); |
| } |
| |
| static LogicalResult printOperation(CppEmitter &emitter, BranchOp branchOp) { |
| raw_ostream &os = emitter.ostream(); |
| Block &successor = *branchOp.getSuccessor(); |
| |
| for (auto pair : |
| llvm::zip(branchOp.getOperands(), successor.getArguments())) { |
| Value &operand = std::get<0>(pair); |
| BlockArgument &argument = std::get<1>(pair); |
| os << emitter.getOrCreateName(argument) << " = " |
| << emitter.getOrCreateName(operand) << ";\n"; |
| } |
| |
| os << "goto "; |
| if (!(emitter.hasBlockLabel(successor))) |
| return branchOp.emitOpError("unable to find label for successor block"); |
| os << emitter.getOrCreateName(successor); |
| return success(); |
| } |
| |
| static LogicalResult printOperation(CppEmitter &emitter, |
| CondBranchOp condBranchOp) { |
| raw_ostream &os = emitter.ostream(); |
| Block &trueSuccessor = *condBranchOp.getTrueDest(); |
| Block &falseSuccessor = *condBranchOp.getFalseDest(); |
| |
| os << "if (" << emitter.getOrCreateName(condBranchOp.getCondition()) |
| << ") {\n"; |
| |
| // If condition is true. |
| for (auto pair : llvm::zip(condBranchOp.getTrueOperands(), |
| trueSuccessor.getArguments())) { |
| Value &operand = std::get<0>(pair); |
| BlockArgument &argument = std::get<1>(pair); |
| os << emitter.getOrCreateName(argument) << " = " |
| << emitter.getOrCreateName(operand) << ";\n"; |
| } |
| |
| os << "goto "; |
| if (!(emitter.hasBlockLabel(trueSuccessor))) { |
| return condBranchOp.emitOpError("unable to find label for successor block"); |
| } |
| os << emitter.getOrCreateName(trueSuccessor) << ";\n"; |
| os << "} else {\n"; |
| // If condition is false. |
| for (auto pair : llvm::zip(condBranchOp.getFalseOperands(), |
| falseSuccessor.getArguments())) { |
| Value &operand = std::get<0>(pair); |
| BlockArgument &argument = std::get<1>(pair); |
| os << emitter.getOrCreateName(argument) << " = " |
| << emitter.getOrCreateName(operand) << ";\n"; |
| } |
| |
| os << "goto "; |
| if (!(emitter.hasBlockLabel(falseSuccessor))) { |
| return condBranchOp.emitOpError() |
| << "unable to find label for successor block"; |
| } |
| os << emitter.getOrCreateName(falseSuccessor) << ";\n"; |
| os << "}"; |
| return success(); |
| } |
| |
| static LogicalResult printOperation(CppEmitter &emitter, mlir::CallOp callOp) { |
| if (failed(emitter.emitAssignPrefix(*callOp.getOperation()))) |
| return failure(); |
| |
| raw_ostream &os = emitter.ostream(); |
| os << callOp.getCallee() << "("; |
| if (failed(emitter.emitOperands(*callOp.getOperation()))) |
| return failure(); |
| os << ")"; |
| return success(); |
| } |
| |
| static LogicalResult printOperation(CppEmitter &emitter, emitc::CallOp callOp) { |
| raw_ostream &os = emitter.ostream(); |
| Operation &op = *callOp.getOperation(); |
| |
| if (failed(emitter.emitAssignPrefix(op))) |
| return failure(); |
| os << callOp.callee(); |
| |
| auto emitArgs = [&](Attribute attr) -> LogicalResult { |
| if (auto t = attr.dyn_cast<IntegerAttr>()) { |
| // Index attributes are treated specially as operand index. |
| if (t.getType().isIndex()) { |
| int64_t idx = t.getInt(); |
| if ((idx < 0) || (idx >= op.getNumOperands())) |
| return op.emitOpError("invalid operand index"); |
| if (!emitter.hasValueInScope(op.getOperand(idx))) |
| return op.emitOpError("operand ") |
| << idx << "'s value not defined in scope"; |
| os << emitter.getOrCreateName(op.getOperand(idx)); |
| return success(); |
| } |
| } |
| if (failed(emitter.emitAttribute(op.getLoc(), attr))) |
| return failure(); |
| |
| return success(); |
| }; |
| |
| if (callOp.template_args()) { |
| os << "<"; |
| if (failed(interleaveCommaWithError(*callOp.template_args(), os, emitArgs))) |
| return failure(); |
| os << ">"; |
| } |
| |
| os << "("; |
| |
| LogicalResult emittedArgs = |
| callOp.args() ? interleaveCommaWithError(*callOp.args(), os, emitArgs) |
| : emitter.emitOperands(op); |
| if (failed(emittedArgs)) |
| return failure(); |
| os << ")"; |
| return success(); |
| } |
| |
| static LogicalResult printOperation(CppEmitter &emitter, |
| emitc::ApplyOp applyOp) { |
| raw_ostream &os = emitter.ostream(); |
| Operation &op = *applyOp.getOperation(); |
| |
| if (failed(emitter.emitAssignPrefix(op))) |
| return failure(); |
| os << applyOp.applicableOperator(); |
| os << emitter.getOrCreateName(applyOp.getOperand()); |
| |
| return success(); |
| } |
| |
| static LogicalResult printOperation(CppEmitter &emitter, |
| emitc::IncludeOp includeOp) { |
| raw_ostream &os = emitter.ostream(); |
| |
| os << "#include "; |
| if (includeOp.is_standard_include()) |
| os << "<" << includeOp.include() << ">"; |
| else |
| os << "\"" << includeOp.include() << "\""; |
| |
| return success(); |
| } |
| |
| static LogicalResult printOperation(CppEmitter &emitter, scf::ForOp forOp) { |
| |
| raw_indented_ostream &os = emitter.ostream(); |
| |
| OperandRange operands = forOp.getIterOperands(); |
| Block::BlockArgListType iterArgs = forOp.getRegionIterArgs(); |
| Operation::result_range results = forOp.getResults(); |
| |
| if (!emitter.shouldDeclareVariablesAtTop()) { |
| for (OpResult result : results) { |
| if (failed(emitter.emitVariableDeclaration(result, |
| /*trailingSemicolon=*/true))) |
| return failure(); |
| } |
| } |
| |
| for (auto pair : llvm::zip(iterArgs, operands)) { |
| if (failed(emitter.emitType(forOp.getLoc(), std::get<0>(pair).getType()))) |
| return failure(); |
| os << " " << emitter.getOrCreateName(std::get<0>(pair)) << " = "; |
| os << emitter.getOrCreateName(std::get<1>(pair)) << ";"; |
| os << "\n"; |
| } |
| |
| os << "for ("; |
| if (failed( |
| emitter.emitType(forOp.getLoc(), forOp.getInductionVar().getType()))) |
| return failure(); |
| os << " "; |
| os << emitter.getOrCreateName(forOp.getInductionVar()); |
| os << " = "; |
| os << emitter.getOrCreateName(forOp.lowerBound()); |
| os << "; "; |
| os << emitter.getOrCreateName(forOp.getInductionVar()); |
| os << " < "; |
| os << emitter.getOrCreateName(forOp.upperBound()); |
| os << "; "; |
| os << emitter.getOrCreateName(forOp.getInductionVar()); |
| os << " += "; |
| os << emitter.getOrCreateName(forOp.step()); |
| os << ") {\n"; |
| os.indent(); |
| |
| Region &forRegion = forOp.region(); |
| auto regionOps = forRegion.getOps(); |
| |
| // We skip the trailing yield op because this updates the result variables |
| // of the for op in the generated code. Instead we update the iterArgs at |
| // the end of a loop iteration and set the result variables after the for |
| // loop. |
| for (auto it = regionOps.begin(); std::next(it) != regionOps.end(); ++it) { |
| if (failed(emitter.emitOperation(*it, /*trailingSemicolon=*/true))) |
| return failure(); |
| } |
| |
| Operation *yieldOp = forRegion.getBlocks().front().getTerminator(); |
| // Copy yield operands into iterArgs at the end of a loop iteration. |
| for (auto pair : llvm::zip(iterArgs, yieldOp->getOperands())) { |
| BlockArgument iterArg = std::get<0>(pair); |
| Value operand = std::get<1>(pair); |
| os << emitter.getOrCreateName(iterArg) << " = " |
| << emitter.getOrCreateName(operand) << ";\n"; |
| } |
| |
| os.unindent() << "}"; |
| |
| // Copy iterArgs into results after the for loop. |
| for (auto pair : llvm::zip(results, iterArgs)) { |
| OpResult result = std::get<0>(pair); |
| BlockArgument iterArg = std::get<1>(pair); |
| os << "\n" |
| << emitter.getOrCreateName(result) << " = " |
| << emitter.getOrCreateName(iterArg) << ";"; |
| } |
| |
| return success(); |
| } |
| |
| static LogicalResult printOperation(CppEmitter &emitter, scf::IfOp ifOp) { |
| raw_indented_ostream &os = emitter.ostream(); |
| |
| if (!emitter.shouldDeclareVariablesAtTop()) { |
| for (OpResult result : ifOp.getResults()) { |
| if (failed(emitter.emitVariableDeclaration(result, |
| /*trailingSemicolon=*/true))) |
| return failure(); |
| } |
| } |
| |
| os << "if ("; |
| if (failed(emitter.emitOperands(*ifOp.getOperation()))) |
| return failure(); |
| os << ") {\n"; |
| os.indent(); |
| |
| Region &thenRegion = ifOp.thenRegion(); |
| for (Operation &op : thenRegion.getOps()) { |
| // Note: This prints a superfluous semicolon if the terminating yield op has |
| // zero results. |
| if (failed(emitter.emitOperation(op, /*trailingSemicolon=*/true))) |
| return failure(); |
| } |
| |
| os.unindent() << "}"; |
| |
| Region &elseRegion = ifOp.elseRegion(); |
| if (!elseRegion.empty()) { |
| os << " else {\n"; |
| os.indent(); |
| |
| for (Operation &op : elseRegion.getOps()) { |
| // Note: This prints a superfluous semicolon if the terminating yield op |
| // has zero results. |
| if (failed(emitter.emitOperation(op, /*trailingSemicolon=*/true))) |
| return failure(); |
| } |
| |
| os.unindent() << "}"; |
| } |
| |
| return success(); |
| } |
| |
| static LogicalResult printOperation(CppEmitter &emitter, scf::YieldOp yieldOp) { |
| raw_ostream &os = emitter.ostream(); |
| Operation &parentOp = *yieldOp.getOperation()->getParentOp(); |
| |
| if (yieldOp.getNumOperands() != parentOp.getNumResults()) { |
| return yieldOp.emitError("number of operands does not to match the number " |
| "of the parent op's results"); |
| } |
| |
| if (failed(interleaveWithError( |
| llvm::zip(parentOp.getResults(), yieldOp.getOperands()), |
| [&](auto pair) -> LogicalResult { |
| auto result = std::get<0>(pair); |
| auto operand = std::get<1>(pair); |
| os << emitter.getOrCreateName(result) << " = "; |
| |
| if (!emitter.hasValueInScope(operand)) |
| return yieldOp.emitError("operand value not in scope"); |
| os << emitter.getOrCreateName(operand); |
| return success(); |
| }, |
| [&]() { os << ";\n"; }))) |
| return failure(); |
| |
| return success(); |
| } |
| |
| static LogicalResult printOperation(CppEmitter &emitter, ReturnOp returnOp) { |
| raw_ostream &os = emitter.ostream(); |
| os << "return"; |
| switch (returnOp.getNumOperands()) { |
| case 0: |
| return success(); |
| case 1: |
| os << " " << emitter.getOrCreateName(returnOp.getOperand(0)); |
| return success(emitter.hasValueInScope(returnOp.getOperand(0))); |
| default: |
| os << " std::make_tuple("; |
| if (failed(emitter.emitOperandsAndAttributes(*returnOp.getOperation()))) |
| return failure(); |
| os << ")"; |
| return success(); |
| } |
| } |
| |
| static LogicalResult printOperation(CppEmitter &emitter, ModuleOp moduleOp) { |
| CppEmitter::Scope scope(emitter); |
| |
| for (Operation &op : moduleOp) { |
| if (failed(emitter.emitOperation(op, /*trailingSemicolon=*/false))) |
| return failure(); |
| } |
| return success(); |
| } |
| |
| static LogicalResult printOperation(CppEmitter &emitter, FuncOp functionOp) { |
| // We need to declare variables at top if the function has multiple blocks. |
| if (!emitter.shouldDeclareVariablesAtTop() && |
| functionOp.getBlocks().size() > 1) { |
| return functionOp.emitOpError( |
| "with multiple blocks needs variables declared at top"); |
| } |
| |
| CppEmitter::Scope scope(emitter); |
| raw_indented_ostream &os = emitter.ostream(); |
| if (failed(emitter.emitTypes(functionOp.getLoc(), |
| functionOp.getType().getResults()))) |
| return failure(); |
| os << " " << functionOp.getName(); |
| |
| os << "("; |
| if (failed(interleaveCommaWithError( |
| functionOp.getArguments(), os, |
| [&](BlockArgument arg) -> LogicalResult { |
| if (failed(emitter.emitType(functionOp.getLoc(), arg.getType()))) |
| return failure(); |
| os << " " << emitter.getOrCreateName(arg); |
| return success(); |
| }))) |
| return failure(); |
| os << ") {\n"; |
| os.indent(); |
| if (emitter.shouldDeclareVariablesAtTop()) { |
| // Declare all variables that hold op results including those from nested |
| // regions. |
| WalkResult result = |
| functionOp.walk<WalkOrder::PreOrder>([&](Operation *op) -> WalkResult { |
| for (OpResult result : op->getResults()) { |
| if (failed(emitter.emitVariableDeclaration( |
| result, /*trailingSemicolon=*/true))) { |
| return WalkResult( |
| op->emitError("unable to declare result variable for op")); |
| } |
| } |
| return WalkResult::advance(); |
| }); |
| if (result.wasInterrupted()) |
| return failure(); |
| } |
| |
| Region::BlockListType &blocks = functionOp.getBlocks(); |
| // Create label names for basic blocks. |
| for (Block &block : blocks) { |
| emitter.getOrCreateName(block); |
| } |
| |
| // Declare variables for basic block arguments. |
| for (auto it = std::next(blocks.begin()); it != blocks.end(); ++it) { |
| Block &block = *it; |
| for (BlockArgument &arg : block.getArguments()) { |
| if (emitter.hasValueInScope(arg)) |
| return functionOp.emitOpError(" block argument #") |
| << arg.getArgNumber() << " is out of scope"; |
| if (failed( |
| emitter.emitType(block.getParentOp()->getLoc(), arg.getType()))) { |
| return failure(); |
| } |
| os << " " << emitter.getOrCreateName(arg) << ";\n"; |
| } |
| } |
| |
| for (Block &block : blocks) { |
| // Only print a label if there is more than one block. |
| if (blocks.size() > 1) { |
| if (failed(emitter.emitLabel(block))) |
| return failure(); |
| } |
| for (Operation &op : block.getOperations()) { |
| // When generating code for an scf.if or std.cond_br op no semicolon needs |
| // to be printed after the closing brace. |
| // When generating code for an scf.for op, printing a trailing semicolon |
| // is handled within the printOperation function. |
| bool trailingSemicolon = !isa<scf::IfOp, scf::ForOp, CondBranchOp>(op); |
| |
| if (failed(emitter.emitOperation( |
| op, /*trailingSemicolon=*/trailingSemicolon))) |
| return failure(); |
| } |
| } |
| os.unindent() << "}\n"; |
| return success(); |
| } |
| |
| CppEmitter::CppEmitter(raw_ostream &os, bool declareVariablesAtTop) |
| : os(os), declareVariablesAtTop(declareVariablesAtTop) { |
| valueInScopeCount.push(0); |
| labelInScopeCount.push(0); |
| } |
| |
| /// Return the existing or a new name for a Value. |
| StringRef CppEmitter::getOrCreateName(Value val) { |
| if (!valueMapper.count(val)) |
| valueMapper.insert(val, formatv("v{0}", ++valueInScopeCount.top())); |
| return *valueMapper.begin(val); |
| } |
| |
| /// Return the existing or a new label for a Block. |
| StringRef CppEmitter::getOrCreateName(Block &block) { |
| if (!blockMapper.count(&block)) |
| blockMapper.insert(&block, formatv("label{0}", ++labelInScopeCount.top())); |
| return *blockMapper.begin(&block); |
| } |
| |
| bool CppEmitter::shouldMapToUnsigned(IntegerType::SignednessSemantics val) { |
| switch (val) { |
| case IntegerType::Signless: |
| return false; |
| case IntegerType::Signed: |
| return false; |
| case IntegerType::Unsigned: |
| return true; |
| default: |
| llvm_unreachable("unsupported IntegerType"); |
| } |
| } |
| |
| bool CppEmitter::hasValueInScope(Value val) { return valueMapper.count(val); } |
| |
| bool CppEmitter::hasBlockLabel(Block &block) { |
| return blockMapper.count(&block); |
| } |
| |
| LogicalResult CppEmitter::emitAttribute(Location loc, Attribute attr) { |
| auto printInt = [&](APInt val, bool isUnsigned) { |
| if (val.getBitWidth() == 1) { |
| if (val.getBoolValue()) |
| os << "true"; |
| else |
| os << "false"; |
| } else { |
| SmallString<128> strValue; |
| val.toString(strValue, 10, !isUnsigned, false); |
| os << strValue; |
| } |
| }; |
| |
| auto printFloat = [&](APFloat val) { |
| if (val.isFinite()) { |
| SmallString<128> strValue; |
| // Use default values of toString except don't truncate zeros. |
| val.toString(strValue, 0, 0, false); |
| switch (llvm::APFloatBase::SemanticsToEnum(val.getSemantics())) { |
| case llvm::APFloatBase::S_IEEEsingle: |
| os << "(float)"; |
| break; |
| case llvm::APFloatBase::S_IEEEdouble: |
| os << "(double)"; |
| break; |
| default: |
| break; |
| }; |
| os << strValue; |
| } else if (val.isNaN()) { |
| os << "NAN"; |
| } else if (val.isInfinity()) { |
| if (val.isNegative()) |
| os << "-"; |
| os << "INFINITY"; |
| } |
| }; |
| |
| // Print floating point attributes. |
| if (auto fAttr = attr.dyn_cast<FloatAttr>()) { |
| printFloat(fAttr.getValue()); |
| return success(); |
| } |
| if (auto dense = attr.dyn_cast<DenseFPElementsAttr>()) { |
| os << '{'; |
| interleaveComma(dense, os, [&](APFloat val) { printFloat(val); }); |
| os << '}'; |
| return success(); |
| } |
| |
| // Print integer attributes. |
| if (auto iAttr = attr.dyn_cast<IntegerAttr>()) { |
| if (auto iType = iAttr.getType().dyn_cast<IntegerType>()) { |
| printInt(iAttr.getValue(), shouldMapToUnsigned(iType.getSignedness())); |
| return success(); |
| } |
| if (auto iType = iAttr.getType().dyn_cast<IndexType>()) { |
| printInt(iAttr.getValue(), false); |
| return success(); |
| } |
| } |
| if (auto dense = attr.dyn_cast<DenseIntElementsAttr>()) { |
| if (auto iType = dense.getType() |
| .cast<TensorType>() |
| .getElementType() |
| .dyn_cast<IntegerType>()) { |
| os << '{'; |
| interleaveComma(dense, os, [&](APInt val) { |
| printInt(val, shouldMapToUnsigned(iType.getSignedness())); |
| }); |
| os << '}'; |
| return success(); |
| } |
| if (auto iType = dense.getType() |
| .cast<TensorType>() |
| .getElementType() |
| .dyn_cast<IndexType>()) { |
| os << '{'; |
| interleaveComma(dense, os, [&](APInt val) { printInt(val, false); }); |
| os << '}'; |
| return success(); |
| } |
| } |
| |
| // Print opaque attributes. |
| if (auto oAttr = attr.dyn_cast<emitc::OpaqueAttr>()) { |
| os << oAttr.getValue(); |
| return success(); |
| } |
| |
| // Print symbolic reference attributes. |
| if (auto sAttr = attr.dyn_cast<SymbolRefAttr>()) { |
| if (sAttr.getNestedReferences().size() > 1) |
| return emitError(loc, "attribute has more than 1 nested reference"); |
| os << sAttr.getRootReference().getValue(); |
| return success(); |
| } |
| |
| // Print type attributes. |
| if (auto type = attr.dyn_cast<TypeAttr>()) |
| return emitType(loc, type.getValue()); |
| |
| return emitError(loc, "cannot emit attribute of type ") << attr.getType(); |
| } |
| |
| LogicalResult CppEmitter::emitOperands(Operation &op) { |
| auto emitOperandName = [&](Value result) -> LogicalResult { |
| if (!hasValueInScope(result)) |
| return op.emitOpError() << "operand value not in scope"; |
| os << getOrCreateName(result); |
| return success(); |
| }; |
| return interleaveCommaWithError(op.getOperands(), os, emitOperandName); |
| } |
| |
| LogicalResult |
| CppEmitter::emitOperandsAndAttributes(Operation &op, |
| ArrayRef<StringRef> exclude) { |
| if (failed(emitOperands(op))) |
| return failure(); |
| // Insert comma in between operands and non-filtered attributes if needed. |
| if (op.getNumOperands() > 0) { |
| for (NamedAttribute attr : op.getAttrs()) { |
| if (!llvm::is_contained(exclude, attr.first.strref())) { |
| os << ", "; |
| break; |
| } |
| } |
| } |
| // Emit attributes. |
| auto emitNamedAttribute = [&](NamedAttribute attr) -> LogicalResult { |
| if (llvm::is_contained(exclude, attr.first.strref())) |
| return success(); |
| os << "/* " << attr.first << " */"; |
| if (failed(emitAttribute(op.getLoc(), attr.second))) |
| return failure(); |
| return success(); |
| }; |
| return interleaveCommaWithError(op.getAttrs(), os, emitNamedAttribute); |
| } |
| |
| LogicalResult CppEmitter::emitVariableAssignment(OpResult result) { |
| if (!hasValueInScope(result)) { |
| return result.getDefiningOp()->emitOpError( |
| "result variable for the operation has not been declared"); |
| } |
| os << getOrCreateName(result) << " = "; |
| return success(); |
| } |
| |
| LogicalResult CppEmitter::emitVariableDeclaration(OpResult result, |
| bool trailingSemicolon) { |
| if (hasValueInScope(result)) { |
| return result.getDefiningOp()->emitError( |
| "result variable for the operation already declared"); |
| } |
| if (failed(emitType(result.getOwner()->getLoc(), result.getType()))) |
| return failure(); |
| os << " " << getOrCreateName(result); |
| if (trailingSemicolon) |
| os << ";\n"; |
| return success(); |
| } |
| |
| LogicalResult CppEmitter::emitAssignPrefix(Operation &op) { |
| switch (op.getNumResults()) { |
| case 0: |
| break; |
| case 1: { |
| OpResult result = op.getResult(0); |
| if (shouldDeclareVariablesAtTop()) { |
| if (failed(emitVariableAssignment(result))) |
| return failure(); |
| } else { |
| if (failed(emitVariableDeclaration(result, /*trailingSemicolon=*/false))) |
| return failure(); |
| os << " = "; |
| } |
| break; |
| } |
| default: |
| if (!shouldDeclareVariablesAtTop()) { |
| for (OpResult result : op.getResults()) { |
| if (failed(emitVariableDeclaration(result, /*trailingSemicolon=*/true))) |
| return failure(); |
| } |
| } |
| os << "std::tie("; |
| interleaveComma(op.getResults(), os, |
| [&](Value result) { os << getOrCreateName(result); }); |
| os << ") = "; |
| } |
| return success(); |
| } |
| |
| LogicalResult CppEmitter::emitLabel(Block &block) { |
| if (!hasBlockLabel(block)) |
| return block.getParentOp()->emitError("label for block not found"); |
| os << getOrCreateName(block) << ":\n"; |
| return success(); |
| } |
| |
| LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) { |
| LogicalResult status = |
| llvm::TypeSwitch<Operation *, LogicalResult>(&op) |
| // EmitC ops. |
| .Case<emitc::ApplyOp, emitc::CallOp, emitc::ConstantOp, |
| emitc::IncludeOp>( |
| [&](auto op) { return printOperation(*this, op); }) |
| // SCF ops. |
| .Case<scf::ForOp, scf::IfOp, scf::YieldOp>( |
| [&](auto op) { return printOperation(*this, op); }) |
| // Standard ops. |
| .Case<BranchOp, mlir::CallOp, CondBranchOp, mlir::ConstantOp, FuncOp, |
| ModuleOp, ReturnOp>( |
| [&](auto op) { return printOperation(*this, op); }) |
| .Default([&](Operation *) { |
| return op.emitOpError("unable to find printer for op"); |
| }); |
| |
| if (failed(status)) |
| return failure(); |
| os << (trailingSemicolon ? ";\n" : "\n"); |
| return success(); |
| } |
| |
| LogicalResult CppEmitter::emitType(Location loc, Type type) { |
| if (auto iType = type.dyn_cast<IntegerType>()) { |
| switch (iType.getWidth()) { |
| case 1: |
| return (os << "bool"), success(); |
| case 8: |
| case 16: |
| case 32: |
| case 64: |
| if (shouldMapToUnsigned(iType.getSignedness())) |
| return (os << "uint" << iType.getWidth() << "_t"), success(); |
| else |
| return (os << "int" << iType.getWidth() << "_t"), success(); |
| default: |
| return emitError(loc, "cannot emit integer type ") << type; |
| } |
| } |
| if (auto fType = type.dyn_cast<FloatType>()) { |
| switch (fType.getWidth()) { |
| case 32: |
| return (os << "float"), success(); |
| case 64: |
| return (os << "double"), success(); |
| default: |
| return emitError(loc, "cannot emit float type ") << type; |
| } |
| } |
| if (auto iType = type.dyn_cast<IndexType>()) |
| return (os << "size_t"), success(); |
| if (auto tType = type.dyn_cast<TensorType>()) { |
| if (!tType.hasRank()) |
| return emitError(loc, "cannot emit unranked tensor type"); |
| if (!tType.hasStaticShape()) |
| return emitError(loc, "cannot emit tensor type with non static shape"); |
| os << "Tensor<"; |
| if (failed(emitType(loc, tType.getElementType()))) |
| return failure(); |
| auto shape = tType.getShape(); |
| for (auto dimSize : shape) { |
| os << ", "; |
| os << dimSize; |
| } |
| os << ">"; |
| return success(); |
| } |
| if (auto tType = type.dyn_cast<TupleType>()) |
| return emitTupleType(loc, tType.getTypes()); |
| if (auto oType = type.dyn_cast<emitc::OpaqueType>()) { |
| os << oType.getValue(); |
| return success(); |
| } |
| return emitError(loc, "cannot emit type ") << type; |
| } |
| |
| LogicalResult CppEmitter::emitTypes(Location loc, ArrayRef<Type> types) { |
| switch (types.size()) { |
| case 0: |
| os << "void"; |
| return success(); |
| case 1: |
| return emitType(loc, types.front()); |
| default: |
| return emitTupleType(loc, types); |
| } |
| } |
| |
| LogicalResult CppEmitter::emitTupleType(Location loc, ArrayRef<Type> types) { |
| os << "std::tuple<"; |
| if (failed(interleaveCommaWithError( |
| types, os, [&](Type type) { return emitType(loc, type); }))) |
| return failure(); |
| os << ">"; |
| return success(); |
| } |
| |
| LogicalResult emitc::translateToCpp(Operation *op, raw_ostream &os, |
| bool declareVariablesAtTop) { |
| CppEmitter emitter(os, declareVariablesAtTop); |
| return emitter.emitOperation(*op, /*trailingSemicolon=*/false); |
| } |