20 #include "llvm/ADT/DenseMap.h" 21 #include "llvm/ADT/StringExtras.h" 22 #include "llvm/ADT/StringMap.h" 23 #include "llvm/ADT/TypeSwitch.h" 24 #include "llvm/Support/Debug.h" 25 #include "llvm/Support/FormatVariadic.h" 28 #define DEBUG_TYPE "translate-to-cpp" 37 template <
typename ForwardIterator,
typename UnaryFunctor,
38 typename NullaryFunctor>
41 UnaryFunctor eachFn, NullaryFunctor betweenFn) {
44 if (
failed(eachFn(*begin)))
47 for (; begin != end; ++begin) {
49 if (
failed(eachFn(*begin)))
55 template <
typename Container,
typename UnaryFunctor,
typename NullaryFunctor>
58 NullaryFunctor betweenFn) {
62 template <
typename Container,
typename UnaryFunctor>
65 UnaryFunctor eachFn) {
72 explicit CppEmitter(raw_ostream &os,
bool declareVariablesAtTop);
98 bool trailingSemicolon);
120 StringRef getOrCreateName(
Value val);
123 StringRef getOrCreateName(
Block &block);
126 bool shouldMapToUnsigned(IntegerType::SignednessSemantics val);
130 Scope(CppEmitter &emitter)
131 : valueMapperScope(emitter.valueMapper),
132 blockMapperScope(emitter.blockMapper), emitter(emitter) {
133 emitter.valueInScopeCount.push(emitter.valueInScopeCount.top());
134 emitter.labelInScopeCount.push(emitter.labelInScopeCount.top());
137 emitter.valueInScopeCount.pop();
138 emitter.labelInScopeCount.pop();
142 llvm::ScopedHashTableScope<Value, std::string> valueMapperScope;
143 llvm::ScopedHashTableScope<Block *, std::string> blockMapperScope;
148 bool hasValueInScope(
Value val);
151 bool hasBlockLabel(
Block &block);
158 bool shouldDeclareVariablesAtTop() {
return declareVariablesAtTop; };
161 using ValueMapper = llvm::ScopedHashTable<Value, std::string>;
162 using BlockMapper = llvm::ScopedHashTable<Block *, std::string>;
170 bool declareVariablesAtTop;
173 ValueMapper valueMapper;
176 BlockMapper blockMapper;
180 std::stack<int64_t> valueInScopeCount;
181 std::stack<int64_t> labelInScopeCount;
191 if (emitter.shouldDeclareVariablesAtTop()) {
193 if (
auto oAttr = value.
dyn_cast<emitc::OpaqueAttr>()) {
194 if (oAttr.getValue().empty())
198 if (
failed(emitter.emitVariableAssignment(result)))
200 return emitter.emitAttribute(operation->
getLoc(),
value);
204 if (
auto oAttr = value.
dyn_cast<emitc::OpaqueAttr>()) {
205 if (oAttr.getValue().empty())
207 return emitter.emitVariableDeclaration(result,
212 if (
failed(emitter.emitAssignPrefix(*operation)))
214 return emitter.emitAttribute(operation->
getLoc(),
value);
218 emitc::ConstantOp constantOp) {
219 Operation *operation = constantOp.getOperation();
226 emitc::VariableOp variableOp) {
227 Operation *operation = variableOp.getOperation();
234 arith::ConstantOp constantOp) {
235 Operation *operation = constantOp.getOperation();
242 func::ConstantOp constantOp) {
243 Operation *operation = constantOp.getOperation();
250 cf::BranchOp branchOp) {
251 raw_ostream &os = emitter.ostream();
255 llvm::zip(branchOp.getOperands(), successor.
getArguments())) {
256 Value &operand = std::get<0>(pair);
258 os << emitter.getOrCreateName(argument) <<
" = " 259 << emitter.getOrCreateName(operand) <<
";\n";
263 if (!(emitter.hasBlockLabel(successor)))
264 return branchOp.emitOpError(
"unable to find label for successor block");
265 os << emitter.getOrCreateName(successor);
270 cf::CondBranchOp condBranchOp) {
272 Block &trueSuccessor = *condBranchOp.getTrueDest();
273 Block &falseSuccessor = *condBranchOp.getFalseDest();
275 os <<
"if (" << emitter.getOrCreateName(condBranchOp.getCondition())
281 for (
auto pair : llvm::zip(condBranchOp.getTrueOperands(),
283 Value &operand = std::get<0>(pair);
285 os << emitter.getOrCreateName(argument) <<
" = " 286 << emitter.getOrCreateName(operand) <<
";\n";
290 if (!(emitter.hasBlockLabel(trueSuccessor))) {
291 return condBranchOp.emitOpError(
"unable to find label for successor block");
293 os << emitter.getOrCreateName(trueSuccessor) <<
";\n";
297 for (
auto pair : llvm::zip(condBranchOp.getFalseOperands(),
299 Value &operand = std::get<0>(pair);
301 os << emitter.getOrCreateName(argument) <<
" = " 302 << emitter.getOrCreateName(operand) <<
";\n";
306 if (!(emitter.hasBlockLabel(falseSuccessor))) {
307 return condBranchOp.emitOpError()
308 <<
"unable to find label for successor block";
310 os << emitter.getOrCreateName(falseSuccessor) <<
";\n";
316 if (
failed(emitter.emitAssignPrefix(*callOp.getOperation())))
319 raw_ostream &os = emitter.ostream();
320 os << callOp.getCallee() <<
"(";
321 if (
failed(emitter.emitOperands(*callOp.getOperation())))
328 raw_ostream &os = emitter.ostream();
331 if (
failed(emitter.emitAssignPrefix(op)))
333 os << callOp.getCallee();
336 if (
auto t = attr.dyn_cast<IntegerAttr>()) {
338 if (t.getType().isIndex()) {
339 int64_t idx = t.getInt();
342 if (!emitter.hasValueInScope(op.
getOperand(idx)))
344 << idx <<
"'s value not defined in scope";
345 os << emitter.getOrCreateName(op.
getOperand(idx));
355 if (callOp.getTemplateArgs()) {
368 : emitter.emitOperands(op);
376 emitc::ApplyOp applyOp) {
377 raw_ostream &os = emitter.ostream();
380 if (
failed(emitter.emitAssignPrefix(op)))
382 os << applyOp.getApplicableOperator();
383 os << emitter.getOrCreateName(applyOp.getOperand());
389 raw_ostream &os = emitter.ostream();
392 if (
failed(emitter.emitAssignPrefix(op)))
398 os << emitter.getOrCreateName(castOp.getOperand());
404 emitc::IncludeOp includeOp) {
405 raw_ostream &os = emitter.ostream();
408 if (includeOp.getIsStandardInclude())
409 os <<
"<" << includeOp.getInclude() <<
">";
411 os <<
"\"" << includeOp.getInclude() <<
"\"";
424 if (!emitter.shouldDeclareVariablesAtTop()) {
426 if (
failed(emitter.emitVariableDeclaration(result,
432 for (
auto pair : llvm::zip(iterArgs, operands)) {
433 if (
failed(emitter.emitType(forOp.getLoc(), std::get<0>(pair).getType())))
435 os <<
" " << emitter.getOrCreateName(std::get<0>(pair)) <<
" = ";
436 os << emitter.getOrCreateName(std::get<1>(pair)) <<
";";
442 emitter.emitType(forOp.getLoc(), forOp.getInductionVar().getType())))
445 os << emitter.getOrCreateName(forOp.getInductionVar());
447 os << emitter.getOrCreateName(forOp.getLowerBound());
449 os << emitter.getOrCreateName(forOp.getInductionVar());
451 os << emitter.getOrCreateName(forOp.getUpperBound());
453 os << emitter.getOrCreateName(forOp.getInductionVar());
455 os << emitter.getOrCreateName(forOp.getStep());
459 Region &forRegion = forOp.getRegion();
460 auto regionOps = forRegion.
getOps();
466 for (
auto it = regionOps.begin(); std::next(it) != regionOps.end(); ++it) {
467 if (
failed(emitter.emitOperation(*it,
true)))
473 for (
auto pair : llvm::zip(iterArgs, yieldOp->
getOperands())) {
475 Value operand = std::get<1>(pair);
476 os << emitter.getOrCreateName(iterArg) <<
" = " 477 << emitter.getOrCreateName(operand) <<
";\n";
483 for (
auto pair : llvm::zip(results, iterArgs)) {
484 OpResult result = std::get<0>(pair);
487 << emitter.getOrCreateName(result) <<
" = " 488 << emitter.getOrCreateName(iterArg) <<
";";
497 if (!emitter.shouldDeclareVariablesAtTop()) {
498 for (
OpResult result : ifOp.getResults()) {
499 if (
failed(emitter.emitVariableDeclaration(result,
506 if (
failed(emitter.emitOperands(*ifOp.getOperation())))
511 Region &thenRegion = ifOp.getThenRegion();
515 if (
failed(emitter.emitOperation(op,
true)))
521 Region &elseRegion = ifOp.getElseRegion();
522 if (!elseRegion.
empty()) {
529 if (
failed(emitter.emitOperation(op,
true)))
540 raw_ostream &os = emitter.ostream();
544 return yieldOp.
emitError(
"number of operands does not to match the number " 545 "of the parent op's results");
549 llvm::zip(parentOp.
getResults(), yieldOp.getOperands()),
551 auto result = std::get<0>(pair);
552 auto operand = std::get<1>(pair);
553 os << emitter.getOrCreateName(result) <<
" = ";
555 if (!emitter.hasValueInScope(operand))
556 return yieldOp.emitError(
"operand value not in scope");
557 os << emitter.getOrCreateName(operand);
560 [&]() { os <<
";\n"; })))
567 func::ReturnOp returnOp) {
568 raw_ostream &os = emitter.ostream();
570 switch (returnOp.getNumOperands()) {
574 os <<
" " << emitter.getOrCreateName(returnOp.getOperand(0));
575 return success(emitter.hasValueInScope(returnOp.getOperand(0)));
577 os <<
" std::make_tuple(";
578 if (
failed(emitter.emitOperandsAndAttributes(*returnOp.getOperation())))
586 CppEmitter::Scope scope(emitter);
589 if (
failed(emitter.emitOperation(op,
false)))
596 func::FuncOp functionOp) {
598 if (!emitter.shouldDeclareVariablesAtTop() &&
599 functionOp.getBlocks().size() > 1) {
600 return functionOp.emitOpError(
601 "with multiple blocks needs variables declared at top");
604 CppEmitter::Scope scope(emitter);
606 if (
failed(emitter.emitTypes(functionOp.getLoc(),
607 functionOp.getFunctionType().getResults())))
609 os <<
" " << functionOp.getName();
613 functionOp.getArguments(), os,
615 if (
failed(emitter.emitType(functionOp.getLoc(), arg.getType())))
617 os <<
" " << emitter.getOrCreateName(arg);
623 if (emitter.shouldDeclareVariablesAtTop()) {
628 for (
OpResult result : op->getResults()) {
629 if (
failed(emitter.emitVariableDeclaration(
632 op->emitError(
"unable to declare result variable for op"));
643 for (
Block &block : blocks) {
644 emitter.getOrCreateName(block);
648 for (
Block &block : llvm::drop_begin(blocks)) {
650 if (emitter.hasValueInScope(arg))
651 return functionOp.emitOpError(
" block argument #")
652 << arg.getArgNumber() <<
" is out of scope";
654 emitter.emitType(block.getParentOp()->getLoc(), arg.getType()))) {
657 os <<
" " << emitter.getOrCreateName(arg) <<
";\n";
661 for (
Block &block : blocks) {
663 if (!block.hasNoPredecessors()) {
664 if (
failed(emitter.emitLabel(block)))
667 for (
Operation &op : block.getOperations()) {
672 bool trailingSemicolon =
673 !isa<scf::IfOp, scf::ForOp, cf::CondBranchOp>(op);
675 if (
failed(emitter.emitOperation(
676 op, trailingSemicolon)))
684 CppEmitter::CppEmitter(raw_ostream &os,
bool declareVariablesAtTop)
685 : os(os), declareVariablesAtTop(declareVariablesAtTop) {
686 valueInScopeCount.push(0);
687 labelInScopeCount.push(0);
691 StringRef CppEmitter::getOrCreateName(
Value val) {
692 if (!valueMapper.count(val))
693 valueMapper.insert(val, formatv(
"v{0}", ++valueInScopeCount.top()));
694 return *valueMapper.begin(val);
698 StringRef CppEmitter::getOrCreateName(
Block &block) {
699 if (!blockMapper.count(&block))
700 blockMapper.insert(&block, formatv(
"label{0}", ++labelInScopeCount.top()));
701 return *blockMapper.begin(&block);
704 bool CppEmitter::shouldMapToUnsigned(IntegerType::SignednessSemantics val) {
706 case IntegerType::Signless:
710 case IntegerType::Unsigned:
713 llvm_unreachable(
"Unexpected IntegerType::SignednessSemantics");
716 bool CppEmitter::hasValueInScope(
Value val) {
return valueMapper.count(val); }
718 bool CppEmitter::hasBlockLabel(
Block &block) {
719 return blockMapper.count(&block);
723 auto printInt = [&](
const APInt &val,
bool isUnsigned) {
724 if (val.getBitWidth() == 1) {
725 if (val.getBoolValue())
731 val.toString(strValue, 10, !isUnsigned,
false);
736 auto printFloat = [&](
const APFloat &val) {
737 if (val.isFinite()) {
740 val.toString(strValue, 0, 0,
false);
741 switch (llvm::APFloatBase::SemanticsToEnum(val.getSemantics())) {
742 case llvm::APFloatBase::S_IEEEsingle:
745 case llvm::APFloatBase::S_IEEEdouble:
752 }
else if (val.isNaN()) {
754 }
else if (val.isInfinity()) {
755 if (val.isNegative())
762 if (
auto fAttr = attr.
dyn_cast<FloatAttr>()) {
763 printFloat(fAttr.getValue());
768 interleaveComma(dense, os, [&](
const APFloat &val) { printFloat(val); });
774 if (
auto iAttr = attr.
dyn_cast<IntegerAttr>()) {
775 if (
auto iType = iAttr.getType().dyn_cast<IntegerType>()) {
776 printInt(iAttr.getValue(), shouldMapToUnsigned(iType.getSignedness()));
779 if (
auto iType = iAttr.getType().dyn_cast<IndexType>()) {
780 printInt(iAttr.getValue(),
false);
785 if (
auto iType = dense.getType()
790 interleaveComma(dense, os, [&](
const APInt &val) {
791 printInt(val, shouldMapToUnsigned(iType.getSignedness()));
796 if (
auto iType = dense.getType()
801 interleaveComma(dense, os,
802 [&](
const APInt &val) { printInt(val,
false); });
809 if (
auto oAttr = attr.
dyn_cast<emitc::OpaqueAttr>()) {
810 os << oAttr.getValue();
815 if (
auto sAttr = attr.
dyn_cast<SymbolRefAttr>()) {
816 if (sAttr.getNestedReferences().size() > 1)
817 return emitError(loc,
"attribute has more than 1 nested reference");
818 os << sAttr.getRootReference().getValue();
823 if (
auto type = attr.
dyn_cast<TypeAttr>())
824 return emitType(loc, type.getValue());
826 return emitError(loc,
"cannot emit attribute: ") << attr;
831 if (!hasValueInScope(result))
832 return op.
emitOpError() <<
"operand value not in scope";
833 os << getOrCreateName(result);
840 CppEmitter::emitOperandsAndAttributes(
Operation &op,
842 if (
failed(emitOperands(op)))
847 if (!llvm::is_contained(exclude, attr.getName().strref())) {
855 if (llvm::is_contained(exclude, attr.getName().strref()))
857 os <<
"/* " << attr.getName().getValue() <<
" */";
858 if (
failed(emitAttribute(op.
getLoc(), attr.getValue())))
866 if (!hasValueInScope(result)) {
868 "result variable for the operation has not been declared");
870 os << getOrCreateName(result) <<
" = ";
875 bool trailingSemicolon) {
876 if (hasValueInScope(result)) {
878 "result variable for the operation already declared");
882 os <<
" " << getOrCreateName(result);
883 if (trailingSemicolon)
894 if (shouldDeclareVariablesAtTop()) {
895 if (
failed(emitVariableAssignment(result)))
898 if (
failed(emitVariableDeclaration(result,
false)))
905 if (!shouldDeclareVariablesAtTop()) {
907 if (
failed(emitVariableDeclaration(result,
true)))
913 [&](
Value result) { os << getOrCreateName(result); });
920 if (!hasBlockLabel(block))
924 os.getOStream() << getOrCreateName(block) <<
":\n";
932 .Case<ModuleOp>([&](
auto op) {
return printOperation(*
this, op); })
934 .Case<cf::BranchOp, cf::CondBranchOp>(
937 .Case<emitc::ApplyOp, emitc::CallOp, emitc::CastOp, emitc::ConstantOp,
938 emitc::IncludeOp, emitc::VariableOp>(
941 .Case<func::CallOp, func::ConstantOp, func::FuncOp, func::ReturnOp>(
944 .Case<scf::ForOp, scf::IfOp, scf::YieldOp>(
947 .Case<arith::ConstantOp>(
950 return op.
emitOpError(
"unable to find printer for op");
955 os << (trailingSemicolon ?
";\n" :
"\n");
960 if (
auto iType = type.
dyn_cast<IntegerType>()) {
961 switch (iType.getWidth()) {
963 return (os <<
"bool"),
success();
968 if (shouldMapToUnsigned(iType.getSignedness()))
969 return (os <<
"uint" << iType.getWidth() <<
"_t"),
success();
971 return (os <<
"int" << iType.getWidth() <<
"_t"),
success();
973 return emitError(loc,
"cannot emit integer type ") << type;
977 switch (fType.getWidth()) {
979 return (os <<
"float"),
success();
981 return (os <<
"double"),
success();
983 return emitError(loc,
"cannot emit float type ") << type;
986 if (
auto iType = type.
dyn_cast<IndexType>())
987 return (os <<
"size_t"),
success();
989 if (!tType.hasRank())
990 return emitError(loc,
"cannot emit unranked tensor type");
991 if (!tType.hasStaticShape())
992 return emitError(loc,
"cannot emit tensor type with non static shape");
994 if (
failed(emitType(loc, tType.getElementType())))
996 auto shape = tType.getShape();
997 for (
auto dimSize : shape) {
1004 if (
auto tType = type.
dyn_cast<TupleType>())
1005 return emitTupleType(loc, tType.getTypes());
1006 if (
auto oType = type.
dyn_cast<emitc::OpaqueType>()) {
1007 os << oType.getValue();
1010 if (
auto pType = type.
dyn_cast<emitc::PointerType>()) {
1011 if (
failed(emitType(loc, pType.getPointee())))
1016 return emitError(loc,
"cannot emit type ") << type;
1020 switch (types.size()) {
1025 return emitType(loc, types.front());
1027 return emitTupleType(loc, types);
1032 os <<
"std::tuple<";
1034 types, os, [&](
Type type) {
return emitType(loc, type); })))
1041 bool declareVariablesAtTop) {
1042 CppEmitter emitter(os, declareVariablesAtTop);
1043 return emitter.emitOperation(*op,
false);
LogicalResult interleaveWithError(ForwardIterator begin, ForwardIterator end, UnaryFunctor eachFn, NullaryFunctor betweenFn)
Convenience functions to produce interleaved output with functions returning a LogicalResult.
Include the generated interface declarations.
Block * getSuccessor(unsigned i)
This class contains a list of basic blocks and a link to the parent operation it is attached to...
An attribute that represents a reference to a dense float vector or tensor object.
Operation is a basic unit of execution within MLIR.
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
BlockListType & getBlocks()
This is a value defined by a result of an operation.
operand_range getOperands()
Returns an iterator on the underlying Value's.
Block represents an ordered list of Operations.
bool wasInterrupted() const
Returns true if the walk was interrupted.
ArrayRef< NamedAttribute > getAttrs()
Return all of the attributes on this operation.
Value getOperand(unsigned idx)
bool failed(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a failure value...
unsigned getNumOperands()
static Type getElementType(Type type, ArrayRef< int32_t > indices, function_ref< InFlightDiagnostic(StringRef)> emitErrorFn)
Walks the given type hierarchy with the given indices, potentially down to component granularity...
This class implements the result iterators for the Operation class.
raw_indented_ostream & indent()
Increases the indent and returning this raw_indented_ostream.
static LogicalResult printOperation(CppEmitter &emitter, emitc::ConstantOp constantOp)
static constexpr const bool value
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Operation * getOwner() const
Returns the operation that owns this result.
LogicalResult translateToCpp(Operation *op, raw_ostream &os, bool declareVariablesAtTop=false)
Translates the given operation to C++ code.
NamedAttribute represents a combination of a name and an Attribute value.
LogicalResult success(bool isSuccess=true)
Utility function to generate a LogicalResult.
This class represents an efficient way to signal success or failure.
LogicalResult failure(bool isFailure=true)
Utility function to generate a LogicalResult.
iterator_range< OpIterator > getOps()
Attributes are known-constant values of operations.
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
static WalkResult advance()
Location getLoc()
The source location the operation was defined or derived from.
BlockArgListType getArguments()
A utility result that is used to signal how to proceed with an ongoing walk:
This class represents an argument of a Block.
Tensor types represent multi-dimensional arrays, and have two variants: RankedTensorType and Unranked...
LogicalResult interleaveCommaWithError(const Container &c, raw_ostream &os, UnaryFunctor eachFn)
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
Type getType() const
Return the type of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
This class implements the operand iterators for the Operation class.
static LogicalResult printConstantOp(CppEmitter &emitter, Operation *operation, Attribute value)
raw_indented_ostream & unindent()
Decreases the indent and returning this raw_indented_ostream.
unsigned getNumResults()
Return the number of results held by this operation.
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers...
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
raw_ostream subclass that simplifies indention a sequence of code.
result_range getResults()
llvm::iplist< Block > BlockListType
An attribute that represents a reference to a dense integer vector or tensor object.