Mercurial > hg > CbC > CbC_llvm
view mlir/lib/Target/Cpp/TranslateToCpp.cpp @ 252:1f2b6ac9f198 llvm-original
LLVM16-1
author | Shinji KONO <kono@ie.u-ryukyu.ac.jp> |
---|---|
date | Fri, 18 Aug 2023 09:04:13 +0900 |
parents | c4bab56944e8 |
children |
line wrap: on
line source
//===- 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 "mlir/Dialect/Arith/IR/Arith.h" #include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h" #include "mlir/Dialect/EmitC/IR/EmitC.h" #include "mlir/Dialect/Func/IR/FuncOps.h" #include "mlir/Dialect/SCF/IR/SCF.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 "mlir/Target/Cpp/CppEmitter.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" #include <utility> #define DEBUG_TYPE "translate-to-cpp" using namespace mlir; using namespace mlir::emitc; using llvm::formatv; /// Convenience functions to produce interleaved output with functions returning /// a LogicalResult. This is different than those in STLExtras as functions used /// on each element doesn't return a string. template <typename ForwardIterator, typename UnaryFunctor, typename NullaryFunctor> inline LogicalResult interleaveWithError(ForwardIterator begin, ForwardIterator end, UnaryFunctor eachFn, NullaryFunctor betweenFn) { if (begin == end) return success(); if (failed(eachFn(*begin))) return failure(); ++begin; for (; begin != end; ++begin) { betweenFn(); if (failed(eachFn(*begin))) return failure(); } return success(); } template <typename Container, typename UnaryFunctor, typename NullaryFunctor> inline LogicalResult interleaveWithError(const Container &c, UnaryFunctor eachFn, NullaryFunctor betweenFn) { return interleaveWithError(c.begin(), c.end(), eachFn, betweenFn); } template <typename Container, typename UnaryFunctor> inline LogicalResult interleaveCommaWithError(const Container &c, raw_ostream &os, UnaryFunctor eachFn) { return interleaveWithError(c.begin(), c.end(), eachFn, [&]() { os << ", "; }); } namespace { /// Emitter that uses dialect specific emitters to emit C++ code. struct CppEmitter { explicit CppEmitter(raw_ostream &os, bool declareVariablesAtTop); /// Emits attribute or returns failure. LogicalResult emitAttribute(Location loc, Attribute attr); /// Emits operation 'op' with/without training semicolon or returns failure. LogicalResult emitOperation(Operation &op, bool trailingSemicolon); /// Emits type 'type' or returns failure. LogicalResult emitType(Location loc, Type type); /// Emits array of types as a std::tuple of the emitted types. /// - emits void for an empty array; /// - emits the type of the only element for arrays of size one; /// - emits a std::tuple otherwise; LogicalResult emitTypes(Location loc, ArrayRef<Type> types); /// Emits array of types as a std::tuple of the emitted types independently of /// the array size. LogicalResult emitTupleType(Location loc, ArrayRef<Type> types); /// Emits an assignment for a variable which has been declared previously. LogicalResult emitVariableAssignment(OpResult result); /// Emits a variable declaration for a result of an operation. LogicalResult emitVariableDeclaration(OpResult result, bool trailingSemicolon); /// Emits the variable declaration and assignment prefix for 'op'. /// - emits separate variable followed by std::tie for multi-valued operation; /// - emits single type followed by variable for single result; /// - emits nothing if no value produced by op; /// Emits final '=' operator where a type is produced. Returns failure if /// any result type could not be converted. LogicalResult emitAssignPrefix(Operation &op); /// Emits a label for the block. LogicalResult emitLabel(Block &block); /// Emits the operands and atttributes of the operation. All operands are /// emitted first and then all attributes in alphabetical order. LogicalResult emitOperandsAndAttributes(Operation &op, ArrayRef<StringRef> exclude = {}); /// Emits the operands of the operation. All operands are emitted in order. LogicalResult emitOperands(Operation &op); /// Return the existing or a new name for a Value. StringRef getOrCreateName(Value val); /// Return the existing or a new label of a Block. StringRef getOrCreateName(Block &block); /// Whether to map an mlir integer to a unsigned integer in C++. bool shouldMapToUnsigned(IntegerType::SignednessSemantics val); /// RAII helper function to manage entering/exiting C++ scopes. struct Scope { Scope(CppEmitter &emitter) : valueMapperScope(emitter.valueMapper), blockMapperScope(emitter.blockMapper), emitter(emitter) { emitter.valueInScopeCount.push(emitter.valueInScopeCount.top()); emitter.labelInScopeCount.push(emitter.labelInScopeCount.top()); } ~Scope() { emitter.valueInScopeCount.pop(); emitter.labelInScopeCount.pop(); } private: llvm::ScopedHashTableScope<Value, std::string> valueMapperScope; llvm::ScopedHashTableScope<Block *, std::string> blockMapperScope; CppEmitter &emitter; }; /// Returns wether the Value is assigned to a C++ variable in the scope. bool hasValueInScope(Value val); // Returns whether a label is assigned to the block. bool hasBlockLabel(Block &block); /// Returns the output stream. raw_indented_ostream &ostream() { return os; }; /// Returns if all variables for op results and basic block arguments need to /// be declared at the beginning of a function. bool shouldDeclareVariablesAtTop() { return declareVariablesAtTop; }; private: using ValueMapper = llvm::ScopedHashTable<Value, std::string>; using BlockMapper = llvm::ScopedHashTable<Block *, std::string>; /// Output stream to emit to. raw_indented_ostream os; /// Boolean to enforce that all variables for op results and block /// arguments are declared at the beginning of the function. This also /// includes results from ops located in nested regions. bool declareVariablesAtTop; /// Map from value to name of C++ variable that contain the name. ValueMapper valueMapper; /// Map from block to name of C++ label. BlockMapper blockMapper; /// The number of values in the current scope. This is used to declare the /// names of values in a scope. std::stack<int64_t> valueInScopeCount; std::stack<int64_t> labelInScopeCount; }; } // namespace 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 = dyn_cast<emitc::OpaqueAttr>(value)) { 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 = dyn_cast<emitc::OpaqueAttr>(value)) { 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.getValue(); return printConstantOp(emitter, operation, value); } static LogicalResult printOperation(CppEmitter &emitter, emitc::VariableOp variableOp) { Operation *operation = variableOp.getOperation(); Attribute value = variableOp.getValue(); return printConstantOp(emitter, operation, value); } static LogicalResult printOperation(CppEmitter &emitter, arith::ConstantOp constantOp) { Operation *operation = constantOp.getOperation(); Attribute value = constantOp.getValue(); return printConstantOp(emitter, operation, value); } static LogicalResult printOperation(CppEmitter &emitter, func::ConstantOp constantOp) { Operation *operation = constantOp.getOperation(); Attribute value = constantOp.getValueAttr(); return printConstantOp(emitter, operation, value); } static LogicalResult printBinaryArithOperation(CppEmitter &emitter, Operation *operation, StringRef binaryArithOperator) { raw_ostream &os = emitter.ostream(); if (failed(emitter.emitAssignPrefix(*operation))) return failure(); os << emitter.getOrCreateName(operation->getOperand(0)); os << " " << binaryArithOperator; os << " " << emitter.getOrCreateName(operation->getOperand(1)); return success(); } static LogicalResult printOperation(CppEmitter &emitter, emitc::AddOp addOp) { Operation *operation = addOp.getOperation(); return printBinaryArithOperation(emitter, operation, "+"); } static LogicalResult printOperation(CppEmitter &emitter, emitc::DivOp divOp) { Operation *operation = divOp.getOperation(); return printBinaryArithOperation(emitter, operation, "/"); } static LogicalResult printOperation(CppEmitter &emitter, emitc::MulOp mulOp) { Operation *operation = mulOp.getOperation(); return printBinaryArithOperation(emitter, operation, "*"); } static LogicalResult printOperation(CppEmitter &emitter, emitc::RemOp remOp) { Operation *operation = remOp.getOperation(); return printBinaryArithOperation(emitter, operation, "%"); } static LogicalResult printOperation(CppEmitter &emitter, emitc::SubOp subOp) { Operation *operation = subOp.getOperation(); return printBinaryArithOperation(emitter, operation, "-"); } static LogicalResult printOperation(CppEmitter &emitter, cf::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, cf::CondBranchOp condBranchOp) { raw_indented_ostream &os = emitter.ostream(); Block &trueSuccessor = *condBranchOp.getTrueDest(); Block &falseSuccessor = *condBranchOp.getFalseDest(); os << "if (" << emitter.getOrCreateName(condBranchOp.getCondition()) << ") {\n"; os.indent(); // 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.unindent() << "} else {\n"; os.indent(); // 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.unindent() << "}"; return success(); } static LogicalResult printOperation(CppEmitter &emitter, func::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.getCallee(); auto emitArgs = [&](Attribute attr) -> LogicalResult { if (auto t = dyn_cast<IntegerAttr>(attr)) { // 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.getTemplateArgs()) { os << "<"; if (failed( interleaveCommaWithError(*callOp.getTemplateArgs(), os, emitArgs))) return failure(); os << ">"; } os << "("; LogicalResult emittedArgs = callOp.getArgs() ? interleaveCommaWithError(*callOp.getArgs(), 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.getApplicableOperator(); os << emitter.getOrCreateName(applyOp.getOperand()); return success(); } static LogicalResult printOperation(CppEmitter &emitter, emitc::CastOp castOp) { raw_ostream &os = emitter.ostream(); Operation &op = *castOp.getOperation(); if (failed(emitter.emitAssignPrefix(op))) return failure(); os << "("; if (failed(emitter.emitType(op.getLoc(), op.getResult(0).getType()))) return failure(); os << ") "; os << emitter.getOrCreateName(castOp.getOperand()); return success(); } static LogicalResult printOperation(CppEmitter &emitter, emitc::IncludeOp includeOp) { raw_ostream &os = emitter.ostream(); os << "#include "; if (includeOp.getIsStandardInclude()) os << "<" << includeOp.getInclude() << ">"; else os << "\"" << includeOp.getInclude() << "\""; 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.getLowerBound()); os << "; "; os << emitter.getOrCreateName(forOp.getInductionVar()); os << " < "; os << emitter.getOrCreateName(forOp.getUpperBound()); os << "; "; os << emitter.getOrCreateName(forOp.getInductionVar()); os << " += "; os << emitter.getOrCreateName(forOp.getStep()); os << ") {\n"; os.indent(); Region &forRegion = forOp.getRegion(); 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.getThenRegion(); 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.getElseRegion(); 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, func::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, func::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.getFunctionType().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 (Block &block : llvm::drop_begin(blocks)) { 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 the block has predecessors. if (!block.hasNoPredecessors()) { if (failed(emitter.emitLabel(block))) return failure(); } for (Operation &op : block.getOperations()) { // When generating code for an scf.if or cf.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<cf::CondBranchOp, emitc::LiteralOp, scf::IfOp, scf::ForOp>(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 (auto literal = dyn_cast_if_present<emitc::LiteralOp>(val.getDefiningOp())) return literal.getValue(); 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; } llvm_unreachable("Unexpected IntegerType::SignednessSemantics"); } 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 = [&](const 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 = [&](const 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 = dyn_cast<FloatAttr>(attr)) { printFloat(fAttr.getValue()); return success(); } if (auto dense = dyn_cast<DenseFPElementsAttr>(attr)) { os << '{'; interleaveComma(dense, os, [&](const APFloat &val) { printFloat(val); }); os << '}'; return success(); } // Print integer attributes. if (auto iAttr = dyn_cast<IntegerAttr>(attr)) { if (auto iType = dyn_cast<IntegerType>(iAttr.getType())) { printInt(iAttr.getValue(), shouldMapToUnsigned(iType.getSignedness())); return success(); } if (auto iType = dyn_cast<IndexType>(iAttr.getType())) { printInt(iAttr.getValue(), false); return success(); } } if (auto dense = dyn_cast<DenseIntElementsAttr>(attr)) { if (auto iType = dyn_cast<IntegerType>( cast<TensorType>(dense.getType()).getElementType())) { os << '{'; interleaveComma(dense, os, [&](const APInt &val) { printInt(val, shouldMapToUnsigned(iType.getSignedness())); }); os << '}'; return success(); } if (auto iType = dyn_cast<IndexType>( cast<TensorType>(dense.getType()).getElementType())) { os << '{'; interleaveComma(dense, os, [&](const APInt &val) { printInt(val, false); }); os << '}'; return success(); } } // Print opaque attributes. if (auto oAttr = dyn_cast<emitc::OpaqueAttr>(attr)) { os << oAttr.getValue(); return success(); } // Print symbolic reference attributes. if (auto sAttr = dyn_cast<SymbolRefAttr>(attr)) { 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 = dyn_cast<TypeAttr>(attr)) return emitType(loc, type.getValue()); return emitError(loc, "cannot emit attribute: ") << attr; } 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.getName().strref())) { os << ", "; break; } } } // Emit attributes. auto emitNamedAttribute = [&](NamedAttribute attr) -> LogicalResult { if (llvm::is_contained(exclude, attr.getName().strref())) return success(); os << "/* " << attr.getName().getValue() << " */"; if (failed(emitAttribute(op.getLoc(), attr.getValue()))) 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"); // FIXME: Add feature in `raw_indented_ostream` to ignore indent for block // label instead of using `getOStream`. os.getOStream() << getOrCreateName(block) << ":\n"; return success(); } LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) { LogicalResult status = llvm::TypeSwitch<Operation *, LogicalResult>(&op) // Builtin ops. .Case<ModuleOp>([&](auto op) { return printOperation(*this, op); }) // CF ops. .Case<cf::BranchOp, cf::CondBranchOp>( [&](auto op) { return printOperation(*this, op); }) // EmitC ops. .Case<emitc::AddOp, emitc::ApplyOp, emitc::CallOp, emitc::CastOp, emitc::ConstantOp, emitc::DivOp, emitc::IncludeOp, emitc::MulOp, emitc::RemOp, emitc::SubOp, emitc::VariableOp>( [&](auto op) { return printOperation(*this, op); }) // Func ops. .Case<func::CallOp, func::ConstantOp, func::FuncOp, func::ReturnOp>( [&](auto op) { return printOperation(*this, op); }) // SCF ops. .Case<scf::ForOp, scf::IfOp, scf::YieldOp>( [&](auto op) { return printOperation(*this, op); }) // Arithmetic ops. .Case<arith::ConstantOp>( [&](auto op) { return printOperation(*this, op); }) .Case<emitc::LiteralOp>([&](auto op) { return success(); }) .Default([&](Operation *) { return op.emitOpError("unable to find printer for op"); }); if (failed(status)) return failure(); if (isa<emitc::LiteralOp>(op)) return success(); os << (trailingSemicolon ? ";\n" : "\n"); return success(); } LogicalResult CppEmitter::emitType(Location loc, Type type) { if (auto iType = dyn_cast<IntegerType>(type)) { 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 = dyn_cast<FloatType>(type)) { 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 = dyn_cast<IndexType>(type)) return (os << "size_t"), success(); if (auto tType = dyn_cast<TensorType>(type)) { 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 = dyn_cast<TupleType>(type)) return emitTupleType(loc, tType.getTypes()); if (auto oType = dyn_cast<emitc::OpaqueType>(type)) { os << oType.getValue(); return success(); } if (auto pType = dyn_cast<emitc::PointerType>(type)) { if (failed(emitType(loc, pType.getPointee()))) return failure(); os << "*"; 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); }