21#include "llvm/ADT/ScopedHashTable.h"
22#include "llvm/ADT/StringExtras.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"
38 typename NullaryFunctor>
39static inline LogicalResult
41 UnaryFunctor eachFn, NullaryFunctor betweenFn) {
44 if (failed(eachFn(*begin)))
47 for (; begin != end; ++begin) {
49 if (failed(eachFn(*begin)))
55template <
typename Container,
typename UnaryFunctor,
typename NullaryFunctor>
58 NullaryFunctor betweenFn) {
62template <
typename Container,
typename UnaryFunctor>
65 UnaryFunctor eachFn) {
73 .Case<emitc::AddressOfOp>([&](
auto op) {
return 15; })
74 .Case<emitc::AddOp>([&](
auto op) {
return 12; })
75 .Case<emitc::ApplyOp>([&](
auto op) {
return 15; })
76 .Case<emitc::BitwiseAndOp>([&](
auto op) {
return 7; })
77 .Case<emitc::BitwiseLeftShiftOp>([&](
auto op) {
return 11; })
78 .Case<emitc::BitwiseNotOp>([&](
auto op) {
return 15; })
79 .Case<emitc::BitwiseOrOp>([&](
auto op) {
return 5; })
80 .Case<emitc::BitwiseRightShiftOp>([&](
auto op) {
return 11; })
81 .Case<emitc::BitwiseXorOp>([&](
auto op) {
return 6; })
82 .Case<emitc::CallOp>([&](
auto op) {
return 16; })
83 .Case<emitc::CallOpaqueOp>([&](
auto op) {
return 16; })
84 .Case<emitc::CastOp>([&](
auto op) {
return 15; })
85 .Case<emitc::CmpOp>([&](
auto op) -> FailureOr<int> {
86 switch (op.getPredicate()) {
87 case emitc::CmpPredicate::eq:
88 case emitc::CmpPredicate::ne:
90 case emitc::CmpPredicate::lt:
91 case emitc::CmpPredicate::le:
92 case emitc::CmpPredicate::gt:
93 case emitc::CmpPredicate::ge:
95 case emitc::CmpPredicate::three_way:
98 return op->emitError(
"unsupported cmp predicate");
100 .Case<emitc::ConditionalOp>([&](
auto op) {
return 2; })
101 .Case<emitc::ConstantOp>([&](
auto op) {
return 17; })
102 .Case<emitc::DivOp>([&](
auto op) {
return 13; })
103 .Case<emitc::LoadOp>([&](
auto op) {
return 16; })
104 .Case<emitc::LogicalAndOp>([&](
auto op) {
return 4; })
105 .Case<emitc::LogicalNotOp>([&](
auto op) {
return 15; })
106 .Case<emitc::LogicalOrOp>([&](
auto op) {
return 3; })
107 .Case<emitc::MulOp>([&](
auto op) {
return 13; })
108 .Case<emitc::RemOp>([&](
auto op) {
return 13; })
109 .Case<emitc::SubOp>([&](
auto op) {
return 12; })
110 .Case<emitc::UnaryMinusOp>([&](
auto op) {
return 15; })
111 .Case<emitc::UnaryPlusOp>([&](
auto op) {
return 15; })
112 .Default([](
auto op) {
return op->emitError(
"unsupported operation"); });
120 explicit CppEmitter(raw_ostream &os,
bool declareVariablesAtTop,
124 LogicalResult emitAttribute(Location loc, Attribute attr);
131 LogicalResult emitOperation(Operation &op,
bool trailingSemicolon);
134 LogicalResult emitType(Location loc, Type type);
140 LogicalResult emitTypes(Location loc, ArrayRef<Type> types);
144 LogicalResult emitTupleType(Location loc, ArrayRef<Type> types);
147 LogicalResult emitVariableAssignment(OpResult
result);
150 LogicalResult emitVariableDeclaration(OpResult
result,
151 bool trailingSemicolon);
154 LogicalResult emitVariableDeclaration(Location loc, Type type,
163 LogicalResult emitAssignPrefix(Operation &op);
166 LogicalResult emitGlobalVariable(GlobalOp op);
169 LogicalResult emitLabel(
Block &block);
173 LogicalResult emitOperandsAndAttributes(Operation &op,
174 ArrayRef<StringRef> exclude = {});
177 LogicalResult emitOperands(Operation &op);
183 LogicalResult emitOperand(Value value,
bool isInBrackets =
false);
186 LogicalResult emitExpression(ExpressionOp expressionOp);
189 void cacheDeferredOpResult(Value value, StringRef str);
192 StringRef getOrCreateName(Value val);
196 StringRef getOrCreateInductionVarName(Value val);
199 StringRef getOrCreateName(
Block &block);
201 LogicalResult emitInlinedExpression(Value value);
204 bool shouldMapToUnsigned(IntegerType::SignednessSemantics val);
208 ~Scope() { emitter.labelInScopeCount.pop(); }
211 llvm::ScopedHashTableScope<Value, std::string> valueMapperScope;
212 llvm::ScopedHashTableScope<Block *, std::string> blockMapperScope;
215 Scope(CppEmitter &emitter)
216 : valueMapperScope(emitter.valueMapper),
217 blockMapperScope(emitter.blockMapper), emitter(emitter) {
218 emitter.labelInScopeCount.push(emitter.labelInScopeCount.top());
225 struct FunctionScope : Scope {
226 FunctionScope(CppEmitter &emitter) : Scope(emitter) {
228 emitter.resetValueCounter();
234 struct LoopScope : Scope {
235 LoopScope(CppEmitter &emitter) : Scope(emitter) {
236 emitter.increaseLoopNestingLevel();
238 ~LoopScope() { emitter.decreaseLoopNestingLevel(); }
242 bool hasValueInScope(Value val);
245 bool hasBlockLabel(
Block &block);
248 raw_indented_ostream &ostream() {
return os; };
252 bool shouldDeclareVariablesAtTop() {
return declareVariablesAtTop; };
255 bool shouldEmitFile(FileOp file) {
256 return !fileId.empty() && file.getId() == fileId;
260 bool isEmittingExpression() {
return !emittedExpressionPrecedence.empty(); }
264 bool isPartOfCurrentExpression(Value value) {
266 return def ? isPartOfCurrentExpression(def) :
false;
271 bool isPartOfCurrentExpression(Operation *def) {
276 void resetValueCounter();
279 void increaseLoopNestingLevel();
282 void decreaseLoopNestingLevel();
285 using ValueMapper = llvm::ScopedHashTable<Value, std::string>;
286 using BlockMapper = llvm::ScopedHashTable<Block *, std::string>;
289 raw_indented_ostream os;
294 bool declareVariablesAtTop;
300 ValueMapper valueMapper;
303 BlockMapper blockMapper;
306 llvm::ScopedHashTableScope<Value, std::string> defaultValueMapperScope;
307 llvm::ScopedHashTableScope<Block *, std::string> defaultBlockMapperScope;
309 std::stack<int64_t> labelInScopeCount;
313 uint64_t loopNestingLevel{0};
316 unsigned int valueCount{0};
319 SmallVector<int> emittedExpressionPrecedence;
321 void pushExpressionPrecedence(
int precedence) {
322 emittedExpressionPrecedence.push_back(precedence);
324 void popExpressionPrecedence() { emittedExpressionPrecedence.pop_back(); }
325 static int lowestPrecedence() {
return 0; }
326 int getExpressionPrecedence() {
327 if (emittedExpressionPrecedence.empty())
328 return lowestPrecedence();
329 return emittedExpressionPrecedence.back();
336 return isa_and_nonnull<emitc::DereferenceOp, emitc::GetGlobalOp,
337 emitc::LiteralOp, emitc::MemberOp,
338 emitc::MemberOfPtrOp, emitc::SubscriptOp,
339 emitc::GetFieldOp>(op);
350 if (isa<CExpressionInterface>(op))
354 ExpressionOp expressionOp = dyn_cast<ExpressionOp>(op);
359 if (expressionOp.getDoNotInline())
377 if (isa<emitc::ExpressionOp, emitc::CExpressionInterface>(*user))
381 if (!expressionOp.hasSideEffects())
392 if (isa<emitc::IfOp, emitc::SwitchOp, emitc::ReturnOp>(user))
397 if (
auto assignOp = dyn_cast<emitc::AssignOp>(user)) {
399 if (expressionOp.getResult() == assignOp.getValue() &&
400 isa_and_present<VariableOp>(assignOp.getVar().getDefiningOp()))
411 while (
auto subscriptOp = value.
getDefiningOp<emitc::SubscriptOp>()) {
412 value = subscriptOp.getValue();
415 auto getGlobalOp = value.
getDefiningOp<emitc::GetGlobalOp>();
421 fromOp, getGlobalOp.getNameAttr());
423 if (globalOp && globalOp.getConstSpecifier())
438 if (failed(emitter.emitOperand(operand)))
445 emitc::DereferenceOp dereferenceOp) {
447 llvm::raw_string_ostream ss(out);
448 ss <<
"*" << emitter.getOrCreateName(dereferenceOp.getPointer());
449 emitter.cacheDeferredOpResult(dereferenceOp.getResult(), out);
454 emitc::GetFieldOp getFieldOp) {
455 emitter.cacheDeferredOpResult(getFieldOp.getResult(),
456 getFieldOp.getFieldName());
461 emitc::GetGlobalOp getGlobalOp) {
462 emitter.cacheDeferredOpResult(getGlobalOp.getResult(), getGlobalOp.getName());
467 emitc::LiteralOp literalOp) {
468 emitter.cacheDeferredOpResult(literalOp.getResult(), literalOp.getValue());
473 emitc::MemberOp memberOp) {
475 llvm::raw_string_ostream ss(out);
476 ss << emitter.getOrCreateName(memberOp.getOperand());
477 ss <<
"." << memberOp.getMember();
478 emitter.cacheDeferredOpResult(memberOp.getResult(), out);
483 emitc::MemberOfPtrOp memberOfPtrOp) {
485 llvm::raw_string_ostream ss(out);
486 ss << emitter.getOrCreateName(memberOfPtrOp.getOperand());
487 ss <<
"->" << memberOfPtrOp.getMember();
488 emitter.cacheDeferredOpResult(memberOfPtrOp.getResult(), out);
493 emitc::SubscriptOp subscriptOp) {
495 llvm::raw_string_ostream ss(out);
496 ss << emitter.getOrCreateName(subscriptOp.getValue());
497 for (
auto index : subscriptOp.getIndices()) {
498 ss <<
"[" << emitter.getOrCreateName(
index) <<
"]";
500 emitter.cacheDeferredOpResult(subscriptOp.getResult(), out);
510 if (emitter.shouldDeclareVariablesAtTop()) {
512 if (
auto oAttr = dyn_cast<emitc::OpaqueAttr>(value)) {
513 if (oAttr.getValue().empty())
517 if (failed(emitter.emitVariableAssignment(
result)))
519 return emitter.emitAttribute(operation->
getLoc(), value);
523 if (
auto oAttr = dyn_cast<emitc::OpaqueAttr>(value)) {
524 if (oAttr.getValue().empty())
526 return emitter.emitVariableDeclaration(
result,
531 if (failed(emitter.emitAssignPrefix(*operation)))
533 return emitter.emitAttribute(operation->
getLoc(), value);
537 emitc::AddressOfOp addressOfOp) {
539 Operation &op = *addressOfOp.getOperation();
541 if (failed(emitter.emitAssignPrefix(op)))
544 Value operand = addressOfOp.getReference();
551 return emitter.emitOperand(operand);
555 emitc::ConstantOp constantOp) {
556 Operation *operation = constantOp.getOperation();
559 if (emitter.isPartOfCurrentExpression(operation))
560 return emitter.emitAttribute(operation->
getLoc(), value);
566 emitc::VariableOp variableOp) {
567 Operation *operation = variableOp.getOperation();
574 emitc::GlobalOp globalOp) {
576 return emitter.emitGlobalVariable(globalOp);
580 emitc::AssignOp assignOp) {
581 OpResult result = assignOp.getVar().getDefiningOp()->getResult(0);
583 if (failed(emitter.emitVariableAssignment(
result)))
586 return emitter.emitOperand(assignOp.getValue());
590 if (failed(emitter.emitAssignPrefix(*loadOp)))
593 return emitter.emitOperand(loadOp.getOperand());
598 StringRef binaryOperator) {
601 if (failed(emitter.emitAssignPrefix(*operation)))
604 if (failed(emitter.emitOperand(operation->
getOperand(0))))
607 os <<
" " << binaryOperator <<
" ";
609 if (failed(emitter.emitOperand(operation->
getOperand(1))))
617 StringRef unaryOperator) {
620 if (failed(emitter.emitAssignPrefix(*operation)))
625 if (failed(emitter.emitOperand(operation->
getOperand(0))))
632 Operation *operation = addOp.getOperation();
638 Operation *operation = divOp.getOperation();
644 Operation *operation = mulOp.getOperation();
650 Operation *operation = remOp.getOperation();
656 Operation *operation = subOp.getOperation();
664 std::next(iteratorOp) != end; ++iteratorOp) {
665 if (failed(emitter.emitOperation(*iteratorOp,
true)))
673 emitc::SwitchOp switchOp) {
677 if (failed(emitter.emitOperand(switchOp.getArg())))
681 for (
auto pair : llvm::zip(switchOp.getCases(), switchOp.getCaseRegions())) {
682 os <<
"\ncase " << std::get<0>(pair) <<
": {\n";
691 os <<
"\ndefault: {\n";
694 if (failed(
emitSwitchCase(emitter, os, switchOp.getDefaultRegion())))
707 Block &bodyBlock = doOp.getBodyRegion().
front();
709 if (failed(emitter.emitOperation(op,
true)))
715 Block &condBlock = doOp.getConditionRegion().
front();
716 auto condYield = cast<emitc::YieldOp>(condBlock.
back());
717 if (failed(emitter.emitExpression(
718 cast<emitc::ExpressionOp>(condYield.getOperand(0).getDefiningOp()))))
726 Operation *operation = cmpOp.getOperation();
728 StringRef binaryOperator;
730 switch (cmpOp.getPredicate()) {
731 case emitc::CmpPredicate::eq:
732 binaryOperator =
"==";
734 case emitc::CmpPredicate::ne:
735 binaryOperator =
"!=";
737 case emitc::CmpPredicate::lt:
738 binaryOperator =
"<";
740 case emitc::CmpPredicate::le:
741 binaryOperator =
"<=";
743 case emitc::CmpPredicate::gt:
744 binaryOperator =
">";
746 case emitc::CmpPredicate::ge:
747 binaryOperator =
">=";
749 case emitc::CmpPredicate::three_way:
750 binaryOperator =
"<=>";
758 emitc::ConditionalOp conditionalOp) {
761 if (failed(emitter.emitAssignPrefix(*conditionalOp)))
764 if (failed(emitter.emitOperand(conditionalOp.getCondition())))
769 if (failed(emitter.emitOperand(conditionalOp.getTrueValue())))
774 if (failed(emitter.emitOperand(conditionalOp.getFalseValue())))
781 emitc::VerbatimOp verbatimOp) {
784 FailureOr<SmallVector<ReplacementItem>> items =
785 verbatimOp.parseFormatString();
789 auto fmtArg = verbatimOp.getFmtArgs().begin();
792 if (
auto *str = std::get_if<StringRef>(&item)) {
795 if (failed(emitter.emitOperand(*fmtArg++)))
804 cf::BranchOp branchOp) {
809 llvm::zip(branchOp.getOperands(), successor.
getArguments())) {
810 Value &operand = std::get<0>(pair);
812 os << emitter.getOrCreateName(argument) <<
" = "
813 << emitter.getOrCreateName(operand) <<
";\n";
817 if (!(emitter.hasBlockLabel(successor)))
818 return branchOp.emitOpError(
"unable to find label for successor block");
819 os << emitter.getOrCreateName(successor);
824 cf::CondBranchOp condBranchOp) {
826 Block &trueSuccessor = *condBranchOp.getTrueDest();
827 Block &falseSuccessor = *condBranchOp.getFalseDest();
830 if (failed(emitter.emitOperand(condBranchOp.getCondition())))
837 for (
auto pair : llvm::zip(condBranchOp.getTrueOperands(),
839 Value &operand = std::get<0>(pair);
841 os << emitter.getOrCreateName(argument) <<
" = "
842 << emitter.getOrCreateName(operand) <<
";\n";
846 if (!(emitter.hasBlockLabel(trueSuccessor))) {
847 return condBranchOp.emitOpError(
"unable to find label for successor block");
849 os << emitter.getOrCreateName(trueSuccessor) <<
";\n";
853 for (
auto pair : llvm::zip(condBranchOp.getFalseOperands(),
855 Value &operand = std::get<0>(pair);
857 os << emitter.getOrCreateName(argument) <<
" = "
858 << emitter.getOrCreateName(operand) <<
";\n";
862 if (!(emitter.hasBlockLabel(falseSuccessor))) {
863 return condBranchOp.emitOpError()
864 <<
"unable to find label for successor block";
866 os << emitter.getOrCreateName(falseSuccessor) <<
";\n";
873 if (failed(emitter.emitAssignPrefix(*callOp)))
878 if (failed(emitter.emitOperands(*callOp)))
885 Operation *operation = callOp.getOperation();
886 StringRef callee = callOp.getCallee();
892 Operation *operation = callOp.getOperation();
893 StringRef callee = callOp.getCallee();
899 emitc::CallOpaqueOp callOpaqueOp) {
901 Operation &op = *callOpaqueOp.getOperation();
903 if (failed(emitter.emitAssignPrefix(op)))
905 os << callOpaqueOp.getCallee();
911 auto emitTemplateArgs = [&](
Attribute attr) -> LogicalResult {
912 return emitter.emitAttribute(op.
getLoc(), attr);
915 if (callOpaqueOp.getTemplateArgs()) {
923 auto emitArgs = [&](
Attribute attr) -> LogicalResult {
924 if (
auto t = dyn_cast<IntegerAttr>(attr)) {
926 if (t.getType().isIndex()) {
929 return emitter.emitOperand(operand);
932 if (failed(emitter.emitAttribute(op.
getLoc(), attr)))
940 LogicalResult emittedArgs =
941 callOpaqueOp.getArgs()
943 : emitter.emitOperands(op);
944 if (failed(emittedArgs))
951 emitc::ApplyOp applyOp) {
955 if (failed(emitter.emitAssignPrefix(op)))
958 StringRef applicableOperator = applyOp.getApplicableOperator();
959 Value operand = applyOp.getOperand();
965 os << applicableOperator;
966 return emitter.emitOperand(operand);
970 emitc::BitwiseAndOp bitwiseAndOp) {
971 Operation *operation = bitwiseAndOp.getOperation();
977 emitc::BitwiseLeftShiftOp bitwiseLeftShiftOp) {
978 Operation *operation = bitwiseLeftShiftOp.getOperation();
983 emitc::BitwiseNotOp bitwiseNotOp) {
984 Operation *operation = bitwiseNotOp.getOperation();
989 emitc::BitwiseOrOp bitwiseOrOp) {
990 Operation *operation = bitwiseOrOp.getOperation();
996 emitc::BitwiseRightShiftOp bitwiseRightShiftOp) {
997 Operation *operation = bitwiseRightShiftOp.getOperation();
1002 emitc::BitwiseXorOp bitwiseXorOp) {
1003 Operation *operation = bitwiseXorOp.getOperation();
1008 emitc::UnaryPlusOp unaryPlusOp) {
1009 Operation *operation = unaryPlusOp.getOperation();
1014 emitc::UnaryMinusOp unaryMinusOp) {
1015 Operation *operation = unaryMinusOp.getOperation();
1023 if (failed(emitter.emitAssignPrefix(op)))
1029 return emitter.emitOperand(castOp.getOperand());
1033 emitc::ExpressionOp expressionOp) {
1037 Operation &op = *expressionOp.getOperation();
1039 if (failed(emitter.emitAssignPrefix(op)))
1042 return emitter.emitExpression(expressionOp);
1046 emitc::IncludeOp includeOp) {
1050 if (includeOp.getIsStandardInclude())
1051 os <<
"<" << includeOp.getInclude() <<
">";
1053 os <<
"\"" << includeOp.getInclude() <<
"\"";
1059 emitc::LogicalAndOp logicalAndOp) {
1060 Operation *operation = logicalAndOp.getOperation();
1065 emitc::LogicalNotOp logicalNotOp) {
1066 Operation *operation = logicalNotOp.getOperation();
1071 emitc::LogicalOrOp logicalOrOp) {
1072 Operation *operation = logicalOrOp.getOperation();
1082 auto requiresParentheses = [&](
Value value) {
1091 emitter.emitType(forOp.getLoc(), forOp.getInductionVar().getType())))
1094 os << emitter.getOrCreateInductionVarName(forOp.getInductionVar());
1096 if (failed(emitter.emitOperand(forOp.getLowerBound())))
1099 os << emitter.getOrCreateInductionVarName(forOp.getInductionVar());
1101 Value upperBound = forOp.getUpperBound();
1102 bool upperBoundRequiresParentheses = requiresParentheses(upperBound);
1103 if (upperBoundRequiresParentheses)
1105 if (failed(emitter.emitOperand(upperBound)))
1107 if (upperBoundRequiresParentheses)
1110 os << emitter.getOrCreateInductionVarName(forOp.getInductionVar());
1112 if (failed(emitter.emitOperand(forOp.getStep())))
1117 CppEmitter::LoopScope lScope(emitter);
1119 Region &forRegion = forOp.getRegion();
1120 auto regionOps = forRegion.
getOps();
1123 for (
auto it = regionOps.begin(); std::next(it) != regionOps.end(); ++it) {
1124 if (failed(emitter.emitOperation(*it,
true)))
1138 auto emitAllExceptLast = [&emitter](
Region ®ion) {
1140 for (; std::next(it) != end; ++it) {
1141 if (failed(emitter.emitOperation(*it,
true)))
1144 assert(isa<emitc::YieldOp>(*it) &&
1145 "Expected last operation in the region to be emitc::yield");
1150 if (failed(emitter.emitOperand(ifOp.getCondition())))
1154 if (failed(emitAllExceptLast(ifOp.getThenRegion())))
1158 Region &elseRegion = ifOp.getElseRegion();
1159 if (!elseRegion.
empty()) {
1162 if (failed(emitAllExceptLast(elseRegion)))
1171 func::ReturnOp returnOp) {
1174 switch (returnOp.getNumOperands()) {
1179 if (failed(emitter.emitOperand(returnOp.getOperand(0))))
1183 os <<
" std::make_tuple(";
1184 if (failed(emitter.emitOperandsAndAttributes(*returnOp.getOperation())))
1192 emitc::ReturnOp returnOp) {
1195 if (returnOp.getNumOperands() == 0)
1199 if (failed(emitter.emitOperand(returnOp.getOperand())))
1206 if (failed(emitter.emitOperation(op,
false)))
1214 os <<
"class " << classOp.getSymName();
1215 if (classOp.getFinalSpecifier())
1217 os <<
" {\n public:\n";
1221 if (failed(emitter.emitOperation(op,
false)))
1232 if (failed(emitter.emitVariableDeclaration(
1233 fieldOp->getLoc(), fieldOp.getType(), fieldOp.getSymName())))
1235 std::optional<Attribute> initialValue = fieldOp.getInitialValue();
1238 if (failed(emitter.emitAttribute(fieldOp->getLoc(), *initialValue)))
1247 if (!emitter.shouldEmitFile(file))
1251 if (failed(emitter.emitOperation(op,
false)))
1264 return emitter.emitType(functionOp->
getLoc(), arg);
1275 return emitter.emitVariableDeclaration(
1276 functionOp->
getLoc(), arg.
getType(), emitter.getOrCreateName(arg));
1286 if (emitter.shouldDeclareVariablesAtTop()) {
1291 if (isa<emitc::ExpressionOp>(op->
getParentOp()) ||
1292 (isa<emitc::ExpressionOp>(op) &&
1296 if (failed(emitter.emitVariableDeclaration(
1299 op->
emitError(
"unable to declare result variable for op"));
1304 if (
result.wasInterrupted())
1309 for (
Block &block : blocks) {
1310 emitter.getOrCreateName(block);
1314 for (
Block &block : llvm::drop_begin(blocks)) {
1316 if (emitter.hasValueInScope(arg))
1317 return functionOp->
emitOpError(
" block argument #")
1318 << arg.getArgNumber() <<
" is out of scope";
1319 if (isa<ArrayType, LValueType>(arg.getType()))
1320 return functionOp->
emitOpError(
"cannot emit block argument #")
1321 << arg.getArgNumber() <<
" with type " << arg.getType();
1323 emitter.emitType(block.getParentOp()->getLoc(), arg.getType()))) {
1326 os <<
" " << emitter.getOrCreateName(arg) <<
";\n";
1330 for (
Block &block : blocks) {
1332 if (!block.hasNoPredecessors()) {
1333 if (failed(emitter.emitLabel(block)))
1336 for (
Operation &op : block.getOperations()) {
1337 if (failed(emitter.emitOperation(op,
true)))
1348 func::FuncOp functionOp) {
1350 if (!emitter.shouldDeclareVariablesAtTop() &&
1351 functionOp.getBlocks().size() > 1) {
1352 return functionOp.emitOpError(
1353 "with multiple blocks needs variables declared at top");
1356 if (llvm::any_of(functionOp.getArgumentTypes(), llvm::IsaPred<LValueType>)) {
1357 return functionOp.emitOpError()
1358 <<
"cannot emit lvalue type as argument type";
1361 if (llvm::any_of(functionOp.getResultTypes(), llvm::IsaPred<ArrayType>)) {
1362 return functionOp.emitOpError() <<
"cannot emit array type as result type";
1365 CppEmitter::FunctionScope scope(emitter);
1367 if (failed(emitter.emitTypes(functionOp.getLoc(),
1368 functionOp.getFunctionType().getResults())))
1370 os <<
" " << functionOp.getName();
1373 Operation *operation = functionOp.getOperation();
1385 emitc::FuncOp functionOp) {
1387 if (!emitter.shouldDeclareVariablesAtTop() &&
1388 functionOp.getBlocks().size() > 1) {
1389 return functionOp.emitOpError(
1390 "with multiple blocks needs variables declared at top");
1393 CppEmitter::FunctionScope scope(emitter);
1395 if (functionOp.getSpecifiers()) {
1396 for (
Attribute specifier : functionOp.getSpecifiersAttr()) {
1397 os << cast<StringAttr>(specifier).str() <<
" ";
1401 if (failed(emitter.emitTypes(functionOp.getLoc(),
1402 functionOp.getFunctionType().getResults())))
1404 os <<
" " << functionOp.getName();
1407 Operation *operation = functionOp.getOperation();
1408 if (functionOp.isExternal()) {
1410 functionOp.getArgumentTypes())))
1426 DeclareFuncOp declareFuncOp) {
1429 CppEmitter::FunctionScope scope(emitter);
1431 declareFuncOp, declareFuncOp.getSymNameAttr());
1436 if (functionOp.getSpecifiers()) {
1437 for (
Attribute specifier : functionOp.getSpecifiersAttr()) {
1438 os << cast<StringAttr>(specifier).str() <<
" ";
1442 if (failed(emitter.emitTypes(functionOp.getLoc(),
1443 functionOp.getFunctionType().getResults())))
1445 os <<
" " << functionOp.getName();
1448 Operation *operation = functionOp.getOperation();
1456CppEmitter::CppEmitter(
raw_ostream &os,
bool declareVariablesAtTop,
1458 : os(os), declareVariablesAtTop(declareVariablesAtTop),
1459 fileId(fileId.str()), defaultValueMapperScope(valueMapper),
1460 defaultBlockMapperScope(blockMapper) {
1461 labelInScopeCount.push(0);
1464void CppEmitter::cacheDeferredOpResult(
Value value, StringRef str) {
1465 if (!valueMapper.count(value))
1466 valueMapper.insert(value, str.str());
1470StringRef CppEmitter::getOrCreateName(Value val) {
1471 if (!valueMapper.count(val)) {
1473 "cacheDeferredOpResult should have been called on this value, "
1474 "update the emitOperation function.");
1476 valueMapper.insert(val, formatv(
"v{0}", ++valueCount));
1478 return *valueMapper.begin(val);
1483StringRef CppEmitter::getOrCreateInductionVarName(Value val) {
1484 if (!valueMapper.count(val)) {
1486 int64_t identifier =
'i' + loopNestingLevel;
1488 if (identifier >=
'i' && identifier <=
't') {
1489 valueMapper.insert(val,
1490 formatv(
"{0}{1}", (
char)identifier, ++valueCount));
1493 valueMapper.insert(val, formatv(
"u{0}", ++valueCount));
1496 return *valueMapper.begin(val);
1500StringRef CppEmitter::getOrCreateName(
Block &block) {
1501 if (!blockMapper.count(&block))
1502 blockMapper.insert(&block, formatv(
"label{0}", ++labelInScopeCount.top()));
1503 return *blockMapper.begin(&block);
1506bool CppEmitter::shouldMapToUnsigned(IntegerType::SignednessSemantics val) {
1508 case IntegerType::Signless:
1510 case IntegerType::Signed:
1512 case IntegerType::Unsigned:
1515 llvm_unreachable(
"Unexpected IntegerType::SignednessSemantics");
1518bool CppEmitter::hasValueInScope(Value val) {
return valueMapper.count(val); }
1520bool CppEmitter::hasBlockLabel(
Block &block) {
1521 return blockMapper.count(&block);
1524LogicalResult CppEmitter::emitAttribute(Location loc, Attribute attr) {
1525 auto printInt = [&](
const APInt &val,
bool isUnsigned) {
1526 if (val.getBitWidth() == 1) {
1527 if (val.getBoolValue())
1532 SmallString<128> strValue;
1533 val.toString(strValue, 10, !isUnsigned,
false);
1538 auto printFloat = [&](
const APFloat &val) {
1539 if (val.isFinite()) {
1540 SmallString<128> strValue;
1542 val.toString(strValue, 0, 0,
false);
1544 switch (llvm::APFloatBase::SemanticsToEnum(val.getSemantics())) {
1545 case llvm::APFloatBase::S_IEEEhalf:
1548 case llvm::APFloatBase::S_BFloat:
1551 case llvm::APFloatBase::S_IEEEsingle:
1554 case llvm::APFloatBase::S_IEEEdouble:
1557 llvm_unreachable(
"unsupported floating point type");
1559 }
else if (val.isNaN()) {
1561 }
else if (val.isInfinity()) {
1562 if (val.isNegative())
1569 if (
auto fAttr = dyn_cast<FloatAttr>(attr)) {
1570 if (!isa<Float16Type, BFloat16Type, Float32Type, Float64Type>(
1573 loc,
"expected floating point attribute to be f16, bf16, f32 or f64");
1575 printFloat(fAttr.getValue());
1578 if (
auto dense = dyn_cast<DenseFPElementsAttr>(attr)) {
1579 if (!isa<Float16Type, BFloat16Type, Float32Type, Float64Type>(
1580 dense.getElementType())) {
1582 loc,
"expected floating point attribute to be f16, bf16, f32 or f64");
1585 interleaveComma(dense, os, [&](
const APFloat &val) { printFloat(val); });
1591 if (
auto iAttr = dyn_cast<IntegerAttr>(attr)) {
1592 if (
auto iType = dyn_cast<IntegerType>(iAttr.getType())) {
1593 printInt(iAttr.getValue(), shouldMapToUnsigned(iType.getSignedness()));
1596 if (
auto iType = dyn_cast<IndexType>(iAttr.getType())) {
1597 printInt(iAttr.getValue(),
false);
1601 if (
auto dense = dyn_cast<DenseIntElementsAttr>(attr)) {
1602 if (
auto iType = dyn_cast<IntegerType>(
1603 cast<ShapedType>(dense.getType()).getElementType())) {
1605 interleaveComma(dense, os, [&](
const APInt &val) {
1606 printInt(val, shouldMapToUnsigned(iType.getSignedness()));
1611 if (
auto iType = dyn_cast<IndexType>(
1612 cast<ShapedType>(dense.getType()).getElementType())) {
1614 interleaveComma(dense, os,
1615 [&](
const APInt &val) { printInt(val,
false); });
1622 if (
auto oAttr = dyn_cast<emitc::OpaqueAttr>(attr)) {
1623 os << oAttr.getValue();
1628 if (
auto sAttr = dyn_cast<SymbolRefAttr>(attr)) {
1629 if (sAttr.getNestedReferences().size() > 1)
1630 return emitError(loc,
"attribute has more than 1 nested reference");
1631 os << sAttr.getRootReference().getValue();
1636 if (
auto type = dyn_cast<TypeAttr>(attr))
1637 return emitType(loc, type.getValue());
1639 return emitError(loc,
"cannot emit attribute: ") << attr;
1642LogicalResult CppEmitter::emitExpression(ExpressionOp expressionOp) {
1643 assert(emittedExpressionPrecedence.empty() &&
1644 "Expected precedence stack to be empty");
1645 Operation *rootOp = expressionOp.getRootOp();
1650 pushExpressionPrecedence(precedence.value());
1652 if (
failed(emitOperation(*rootOp,
false)))
1655 popExpressionPrecedence();
1656 assert(emittedExpressionPrecedence.empty() &&
1657 "Expected precedence stack to be empty");
1662LogicalResult CppEmitter::emitOperand(Value value,
bool isInBrackets) {
1663 if (isPartOfCurrentExpression(value)) {
1665 assert(def &&
"Expected operand to be defined by an operation");
1673 bool encloseInParenthesis =
1674 !isInBrackets && precedence.value() <= getExpressionPrecedence();
1676 if (encloseInParenthesis)
1678 pushExpressionPrecedence(precedence.value());
1680 if (
failed(emitOperation(*def,
false)))
1683 if (encloseInParenthesis)
1686 popExpressionPrecedence();
1692 return emitExpression(expressionOp);
1694 if (BlockArgument arg = dyn_cast<BlockArgument>(value)) {
1697 Operation *argOp = arg.getParentBlock()->getParentOp();
1698 if (
auto expressionOp = dyn_cast<ExpressionOp>(argOp))
1699 return emitOperand(expressionOp->getOperand(arg.getArgNumber()));
1702 os << getOrCreateName(value);
1706LogicalResult CppEmitter::emitOperands(Operation &op) {
1710 return emitOperand(operand, true);
1715CppEmitter::emitOperandsAndAttributes(Operation &op,
1716 ArrayRef<StringRef> exclude) {
1717 if (
failed(emitOperands(op)))
1721 for (NamedAttribute attr : op.
getAttrs()) {
1722 if (!llvm::is_contained(exclude, attr.getName().strref())) {
1729 auto emitNamedAttribute = [&](NamedAttribute attr) -> LogicalResult {
1730 if (llvm::is_contained(exclude, attr.getName().strref()))
1732 os <<
"/* " << attr.getName().getValue() <<
" */";
1733 if (
failed(emitAttribute(op.
getLoc(), attr.getValue())))
1740LogicalResult CppEmitter::emitVariableAssignment(OpResult
result) {
1741 if (!hasValueInScope(
result)) {
1742 return result.getDefiningOp()->emitOpError(
1743 "result variable for the operation has not been declared");
1745 os << getOrCreateName(
result) <<
" = ";
1749LogicalResult CppEmitter::emitVariableDeclaration(OpResult
result,
1750 bool trailingSemicolon) {
1753 if (hasValueInScope(
result)) {
1754 return result.getDefiningOp()->emitError(
1755 "result variable for the operation already declared");
1757 if (
failed(emitVariableDeclaration(
result.getOwner()->getLoc(),
1759 getOrCreateName(
result))))
1761 if (trailingSemicolon)
1766LogicalResult CppEmitter::emitGlobalVariable(GlobalOp op) {
1767 if (op.getExternSpecifier())
1769 else if (op.getStaticSpecifier())
1771 if (op.getConstSpecifier())
1774 if (
failed(emitVariableDeclaration(op->getLoc(), op.getType(),
1775 op.getSymName()))) {
1779 std::optional<Attribute> initialValue = op.getInitialValue();
1782 if (
failed(emitAttribute(op->getLoc(), *initialValue)))
1790LogicalResult CppEmitter::emitAssignPrefix(Operation &op) {
1792 if (isEmittingExpression())
1800 if (shouldDeclareVariablesAtTop()) {
1811 if (!shouldDeclareVariablesAtTop()) {
1819 [&](Value
result) { os << getOrCreateName(result); });
1825LogicalResult CppEmitter::emitLabel(
Block &block) {
1826 if (!hasBlockLabel(block))
1830 os.getOStream() << getOrCreateName(block) <<
":\n";
1834LogicalResult CppEmitter::emitOperation(Operation &op,
bool trailingSemicolon) {
1835 LogicalResult status =
1836 llvm::TypeSwitch<Operation *, LogicalResult>(&op)
1838 .Case<ModuleOp>([&](
auto op) {
return printOperation(*
this, op); })
1840 .Case<cf::BranchOp, cf::CondBranchOp>(
1843 .Case<emitc::AddressOfOp, emitc::AddOp, emitc::ApplyOp,
1844 emitc::AssignOp, emitc::BitwiseAndOp, emitc::BitwiseLeftShiftOp,
1845 emitc::BitwiseNotOp, emitc::BitwiseOrOp,
1846 emitc::BitwiseRightShiftOp, emitc::BitwiseXorOp, emitc::CallOp,
1847 emitc::CallOpaqueOp, emitc::CastOp, emitc::ClassOp,
1848 emitc::CmpOp, emitc::ConditionalOp, emitc::ConstantOp,
1849 emitc::DeclareFuncOp, emitc::DereferenceOp, emitc::DivOp,
1850 emitc::DoOp, emitc::ExpressionOp, emitc::FieldOp, emitc::FileOp,
1851 emitc::ForOp, emitc::FuncOp, emitc::GetFieldOp,
1852 emitc::GetGlobalOp, emitc::GlobalOp, emitc::IfOp,
1853 emitc::IncludeOp, emitc::LiteralOp, emitc::LoadOp,
1854 emitc::LogicalAndOp, emitc::LogicalNotOp, emitc::LogicalOrOp,
1855 emitc::MemberOfPtrOp, emitc::MemberOp, emitc::MulOp,
1856 emitc::RemOp, emitc::ReturnOp, emitc::SubscriptOp, emitc::SubOp,
1857 emitc::SwitchOp, emitc::UnaryMinusOp, emitc::UnaryPlusOp,
1858 emitc::VariableOp, emitc::VerbatimOp>(
1862 .Case<func::CallOp, func::FuncOp, func::ReturnOp>(
1864 .Default([&](Operation *) {
1865 return op.
emitOpError(
"unable to find printer for op");
1874 if (isEmittingExpression() ||
1875 (isa<emitc::ExpressionOp>(op) &&
1881 trailingSemicolon &=
1882 !isa<cf::CondBranchOp, emitc::DeclareFuncOp, emitc::DoOp, emitc::FileOp,
1883 emitc::ForOp, emitc::IfOp, emitc::IncludeOp, emitc::SwitchOp,
1884 emitc::VerbatimOp>(op);
1886 os << (trailingSemicolon ?
";\n" :
"\n");
1891LogicalResult CppEmitter::emitVariableDeclaration(Location loc, Type type,
1893 if (
auto arrType = dyn_cast<emitc::ArrayType>(type)) {
1894 if (
failed(emitType(loc, arrType.getElementType())))
1897 for (
auto dim : arrType.getShape()) {
1898 os <<
"[" << dim <<
"]";
1902 if (
failed(emitType(loc, type)))
1908LogicalResult CppEmitter::emitType(Location loc, Type type) {
1909 if (
auto iType = dyn_cast<IntegerType>(type)) {
1910 switch (iType.getWidth()) {
1912 return (os <<
"bool"),
success();
1917 if (shouldMapToUnsigned(iType.getSignedness()))
1918 return (os <<
"uint" << iType.getWidth() <<
"_t"),
success();
1920 return (os <<
"int" << iType.getWidth() <<
"_t"),
success();
1922 return emitError(loc,
"cannot emit integer type ") << type;
1925 if (
auto fType = dyn_cast<FloatType>(type)) {
1926 switch (fType.getWidth()) {
1928 if (llvm::isa<Float16Type>(type))
1929 return (os <<
"_Float16"),
success();
1930 if (llvm::isa<BFloat16Type>(type))
1931 return (os <<
"__bf16"),
success();
1933 return emitError(loc,
"cannot emit float type ") << type;
1936 return (os <<
"float"),
success();
1938 return (os <<
"double"),
success();
1940 return emitError(loc,
"cannot emit float type ") << type;
1943 if (
auto iType = dyn_cast<IndexType>(type))
1944 return (os <<
"size_t"),
success();
1945 if (
auto sType = dyn_cast<emitc::SizeTType>(type))
1946 return (os <<
"size_t"),
success();
1947 if (
auto sType = dyn_cast<emitc::SignedSizeTType>(type))
1948 return (os <<
"ssize_t"),
success();
1949 if (
auto pType = dyn_cast<emitc::PtrDiffTType>(type))
1950 return (os <<
"ptrdiff_t"),
success();
1951 if (
auto tType = dyn_cast<TensorType>(type)) {
1952 if (!tType.hasRank())
1953 return emitError(loc,
"cannot emit unranked tensor type");
1954 if (!tType.hasStaticShape())
1955 return emitError(loc,
"cannot emit tensor type with non static shape");
1957 if (isa<ArrayType>(tType.getElementType()))
1958 return emitError(loc,
"cannot emit tensor of array type ") << type;
1959 if (
failed(emitType(loc, tType.getElementType())))
1961 auto shape = tType.getShape();
1962 for (
auto dimSize : shape) {
1969 if (
auto tType = dyn_cast<TupleType>(type))
1970 return emitTupleType(loc, tType.getTypes());
1971 if (
auto oType = dyn_cast<emitc::OpaqueType>(type)) {
1972 os << oType.getValue();
1975 if (
auto aType = dyn_cast<emitc::ArrayType>(type)) {
1976 if (
failed(emitType(loc, aType.getElementType())))
1978 for (
auto dim : aType.getShape())
1979 os <<
"[" << dim <<
"]";
1982 if (
auto lType = dyn_cast<emitc::LValueType>(type))
1983 return emitType(loc, lType.getValueType());
1984 if (
auto pType = dyn_cast<emitc::PointerType>(type)) {
1985 if (isa<ArrayType>(pType.getPointee()))
1986 return emitError(loc,
"cannot emit pointer to array type ") << type;
1987 if (
failed(emitType(loc, pType.getPointee())))
1992 return emitError(loc,
"cannot emit type ") << type;
1995LogicalResult CppEmitter::emitTypes(Location loc, ArrayRef<Type> types) {
1996 switch (types.size()) {
2001 return emitType(loc, types.front());
2003 return emitTupleType(loc, types);
2007LogicalResult CppEmitter::emitTupleType(Location loc, ArrayRef<Type> types) {
2008 if (llvm::any_of(types, llvm::IsaPred<ArrayType>)) {
2009 return emitError(loc,
"cannot emit tuple of array type");
2011 os <<
"std::tuple<";
2013 types, os, [&](Type type) {
return emitType(loc, type); })))
2019void CppEmitter::resetValueCounter() { valueCount = 0; }
2021void CppEmitter::increaseLoopNestingLevel() { loopNestingLevel++; }
2023void CppEmitter::decreaseLoopNestingLevel() { loopNestingLevel--; }
2026 bool declareVariablesAtTop,
2028 CppEmitter emitter(os, declareVariablesAtTop, fileId);
2029 return emitter.emitOperation(*op,
false);
false
Parses a map_entries map type from a string format back into its numeric value.
static LogicalResult printCallOperation(CppEmitter &emitter, Operation *callOp, StringRef callee)
static FailureOr< int > getOperatorPrecedence(Operation *operation)
Return the precedence of a operator as an integer, higher values imply higher precedence.
static LogicalResult printFunctionArgs(CppEmitter &emitter, Operation *functionOp, ArrayRef< Type > arguments)
static LogicalResult printFunctionBody(CppEmitter &emitter, Operation *functionOp, Region::BlockListType &blocks)
static LogicalResult printConstantOp(CppEmitter &emitter, Operation *operation, Attribute value)
static LogicalResult emitSwitchCase(CppEmitter &emitter, raw_indented_ostream &os, Region ®ion)
static LogicalResult interleaveCommaWithError(const Container &c, raw_ostream &os, UnaryFunctor eachFn)
static LogicalResult printBinaryOperation(CppEmitter &emitter, Operation *operation, StringRef binaryOperator)
static bool shouldBeInlined(Operation *op)
Determine whether operation op should be emitted inline, i.e.
static LogicalResult printOperation(CppEmitter &emitter, emitc::DereferenceOp dereferenceOp)
static LogicalResult printUnaryOperation(CppEmitter &emitter, Operation *operation, StringRef unaryOperator)
static LogicalResult emitAddressOfWithConstCast(CppEmitter &emitter, Operation &op, Value operand)
Emit address-of with a cast to strip const qualification.
static bool hasDeferredEmission(Operation *op)
Determine whether expression op should be emitted in a deferred way.
static LogicalResult interleaveWithError(ForwardIterator begin, ForwardIterator end, UnaryFunctor eachFn, NullaryFunctor betweenFn)
Convenience functions to produce interleaved output with functions returning a LogicalResult.
static emitc::GlobalOp getConstGlobal(Value value, Operation *fromOp)
Helper function to check if a value traces back to a const global.
Attributes are known-constant values of operations.
This class represents an argument of a Block.
Block represents an ordered list of Operations.
OpListType::iterator iterator
BlockArgListType getArguments()
Block * getSuccessor(unsigned i)
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
This is a value defined by a result of an operation.
Operation is the basic unit of execution within MLIR.
Value getOperand(unsigned idx)
ArrayRef< NamedAttribute > getAttrs()
Return all of the attributes on this operation.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Location getLoc()
The source location the operation was defined or derived from.
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
unsigned getNumOperands()
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
operand_range getOperands()
Returns an iterator on the underlying Value's.
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
result_range getResults()
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
unsigned getNumResults()
Return the number of results held by this operation.
This class provides iteration over the held operations of blocks directly within a region.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
llvm::iplist< Block > BlockListType
OpIterator op_begin()
Return iterators that walk the operations nested directly within this region.
iterator_range< OpIterator > getOps()
MutableArrayRef< BlockArgument > BlockArgListType
static Operation * lookupNearestSymbolFrom(Operation *from, StringAttr symbol)
Returns the operation registered with the given symbol name within the closest parent operation of,...
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...
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.
A utility result that is used to signal how to proceed with an ongoing walk:
static WalkResult advance()
raw_ostream subclass that simplifies indention a sequence of code.
raw_indented_ostream & indent()
Increases the indent and returning this raw_indented_ostream.
raw_indented_ostream & unindent()
Decreases the indent and returning this raw_indented_ostream.
LogicalResult translateToCpp(Operation *op, raw_ostream &os, bool declareVariablesAtTop=false, StringRef fileId={})
Translates the given operation to C++ code.
std::variant< StringRef, Placeholder > ReplacementItem
Include the generated interface declarations.
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
This iterator enumerates the elements in "forward" order.