22#include "llvm/ADT/ScopedHashTable.h"
23#include "llvm/ADT/StringExtras.h"
24#include "llvm/ADT/TypeSwitch.h"
25#include "llvm/Support/Casting.h"
26#include "llvm/Support/Debug.h"
27#include "llvm/Support/FormatVariadic.h"
30#define DEBUG_TYPE "translate-to-cpp"
40 typename NullaryFunctor>
41static inline LogicalResult
43 UnaryFunctor eachFn, NullaryFunctor betweenFn) {
46 if (failed(eachFn(*begin)))
49 for (; begin != end; ++begin) {
51 if (failed(eachFn(*begin)))
57template <
typename Container,
typename UnaryFunctor,
typename NullaryFunctor>
60 NullaryFunctor betweenFn) {
64template <
typename Container,
typename UnaryFunctor>
67 UnaryFunctor eachFn) {
75 .Case([&](emitc::AddressOfOp op) {
return 15; })
76 .Case([&](emitc::AddOp op) {
return 12; })
77 .Case([&](emitc::ApplyOp op) {
return 15; })
78 .Case([&](emitc::BitwiseAndOp op) {
return 7; })
79 .Case([&](emitc::BitwiseLeftShiftOp op) {
return 11; })
80 .Case([&](emitc::BitwiseNotOp op) {
return 15; })
81 .Case([&](emitc::BitwiseOrOp op) {
return 5; })
82 .Case([&](emitc::BitwiseRightShiftOp op) {
return 11; })
83 .Case([&](emitc::BitwiseXorOp op) {
return 6; })
84 .Case([&](emitc::CallOp op) {
return 16; })
85 .Case([&](emitc::CallOpaqueOp op) {
return 16; })
86 .Case([&](emitc::CastOp op) {
return 15; })
87 .Case([&](emitc::CmpOp op) -> FailureOr<int> {
88 switch (op.getPredicate()) {
89 case emitc::CmpPredicate::eq:
90 case emitc::CmpPredicate::ne:
92 case emitc::CmpPredicate::lt:
93 case emitc::CmpPredicate::le:
94 case emitc::CmpPredicate::gt:
95 case emitc::CmpPredicate::ge:
97 case emitc::CmpPredicate::three_way:
100 return op->emitError(
"unsupported cmp predicate");
102 .Case([&](emitc::ConditionalOp op) {
return 2; })
103 .Case([&](emitc::ConstantOp op) {
return 17; })
104 .Case([&](emitc::DereferenceOp op) {
return 15; })
105 .Case([&](emitc::DivOp op) {
return 13; })
106 .Case([&](emitc::GetGlobalOp op) {
return 18; })
107 .Case([&](emitc::LiteralOp op) {
return 18; })
108 .Case([&](emitc::LoadOp op) {
return 16; })
109 .Case([&](emitc::LogicalAndOp op) {
return 4; })
110 .Case([&](emitc::LogicalNotOp op) {
return 15; })
111 .Case([&](emitc::LogicalOrOp op) {
return 3; })
112 .Case([&](emitc::MemberOfPtrOp op) {
return 17; })
113 .Case([&](emitc::MemberOp op) {
return 17; })
114 .Case([&](emitc::MulOp op) {
return 13; })
115 .Case([&](emitc::RemOp op) {
return 13; })
116 .Case([&](emitc::SubOp op) {
return 12; })
117 .Case([&](emitc::SubscriptOp op) {
return 17; })
118 .Case([&](emitc::UnaryMinusOp op) {
return 15; })
119 .Case([&](emitc::UnaryPlusOp op) {
return 15; })
120 .Default([](
auto op) {
return op->emitError(
"unsupported operation"); });
128 explicit CppEmitter(raw_ostream &os,
bool declareVariablesAtTop,
132 LogicalResult emitAttribute(Location loc, Attribute attr);
139 LogicalResult emitOperation(Operation &op,
bool trailingSemicolon);
142 LogicalResult emitType(Location loc, Type type);
148 LogicalResult emitTypes(Location loc, ArrayRef<Type> types);
152 LogicalResult emitTupleType(Location loc, ArrayRef<Type> types);
155 LogicalResult emitVariableAssignment(OpResult
result);
158 LogicalResult emitVariableDeclaration(OpResult
result,
159 bool trailingSemicolon);
162 LogicalResult emitVariableDeclaration(Location loc, Type type,
171 LogicalResult emitAssignPrefix(Operation &op);
174 LogicalResult emitGlobalVariable(GlobalOp op);
177 LogicalResult emitLabel(
Block &block);
181 LogicalResult emitOperandsAndAttributes(Operation &op,
182 ArrayRef<StringRef> exclude = {});
185 LogicalResult emitOperands(Operation &op);
191 LogicalResult emitOperand(Value value,
bool isInBrackets =
false);
194 LogicalResult emitExpression(Operation *op);
197 StringRef getOrCreateName(Value val);
201 StringRef getOrCreateInductionVarName(Value val);
204 StringRef getOrCreateName(
Block &block);
206 LogicalResult emitInlinedExpression(Value value);
209 bool shouldMapToUnsigned(IntegerType::SignednessSemantics val);
213 ~Scope() { emitter.labelInScopeCount.pop(); }
216 llvm::ScopedHashTableScope<Value, std::string> valueMapperScope;
217 llvm::ScopedHashTableScope<Block *, std::string> blockMapperScope;
220 Scope(CppEmitter &emitter)
221 : valueMapperScope(emitter.valueMapper),
222 blockMapperScope(emitter.blockMapper), emitter(emitter) {
223 emitter.labelInScopeCount.push(emitter.labelInScopeCount.top());
230 struct FunctionScope : Scope {
231 FunctionScope(CppEmitter &emitter) : Scope(emitter) {
233 emitter.resetValueCounter();
239 struct LoopScope : Scope {
240 LoopScope(CppEmitter &emitter) : Scope(emitter) {
241 emitter.increaseLoopNestingLevel();
243 ~LoopScope() { emitter.decreaseLoopNestingLevel(); }
247 bool hasValueInScope(Value val);
250 bool hasBlockLabel(
Block &block);
253 raw_indented_ostream &ostream() {
return os; };
257 bool shouldDeclareVariablesAtTop() {
return declareVariablesAtTop; };
260 bool shouldEmitFile(FileOp file) {
261 return !fileId.empty() && file.getId() == fileId;
265 bool isEmittingExpression() {
return !emittedExpressionPrecedence.empty(); }
269 bool isPartOfCurrentExpression(Value value) {
271 return def ? isPartOfCurrentExpression(def) :
false;
276 bool isPartOfCurrentExpression(Operation *def) {
281 void resetValueCounter();
284 void increaseLoopNestingLevel();
287 void decreaseLoopNestingLevel();
290 using ValueMapper = llvm::ScopedHashTable<Value, std::string>;
291 using BlockMapper = llvm::ScopedHashTable<Block *, std::string>;
294 raw_indented_ostream os;
299 bool declareVariablesAtTop;
305 ValueMapper valueMapper;
308 BlockMapper blockMapper;
311 llvm::ScopedHashTableScope<Value, std::string> defaultValueMapperScope;
312 llvm::ScopedHashTableScope<Block *, std::string> defaultBlockMapperScope;
314 std::stack<int64_t> labelInScopeCount;
318 uint64_t loopNestingLevel{0};
321 unsigned int valueCount{0};
324 SmallVector<int> emittedExpressionPrecedence;
326 void pushExpressionPrecedence(
int precedence) {
327 emittedExpressionPrecedence.push_back(precedence);
329 void popExpressionPrecedence() { emittedExpressionPrecedence.pop_back(); }
330 static int lowestPrecedence() {
return 0; }
331 int getExpressionPrecedence() {
332 if (emittedExpressionPrecedence.empty())
333 return lowestPrecedence();
334 return emittedExpressionPrecedence.back();
347 if (
auto cExpression = dyn_cast<CExpressionInterface>(op))
348 return cExpression.alwaysInline() || isa<ExpressionOp>(op->
getParentOp());
351 ExpressionOp expressionOp = dyn_cast<ExpressionOp>(op);
356 if (cast<CExpressionInterface>(expressionOp.getRootOp()).alwaysInline())
360 if (expressionOp.getDoNotInline())
373 if (isa<emitc::ExpressionOp, emitc::CExpressionInterface>(*user))
377 if (!expressionOp.hasSideEffects())
388 if (isa<emitc::IfOp, emitc::SwitchOp, emitc::ReturnOp>(user))
393 if (
auto assignOp = dyn_cast<emitc::AssignOp>(user)) {
395 if (expressionOp.getResult() == assignOp.getValue() &&
396 isa_and_present<VariableOp>(assignOp.getVar().getDefiningOp()))
407 while (
auto subscriptOp = value.
getDefiningOp<emitc::SubscriptOp>()) {
408 value = subscriptOp.getValue();
411 auto getGlobalOp = value.
getDefiningOp<emitc::GetGlobalOp>();
417 fromOp, getGlobalOp.getNameAttr());
419 if (globalOp && globalOp.getConstSpecifier())
434 if (failed(emitter.emitOperand(operand)))
441 emitc::DereferenceOp dereferenceOp) {
443 Operation &op = *dereferenceOp.getOperation();
445 if (failed(emitter.emitAssignPrefix(op)))
448 return emitter.emitOperand(dereferenceOp.getPointer());
452 emitc::GetFieldOp getFieldOp) {
453 if (!emitter.isPartOfCurrentExpression(getFieldOp.getOperation()))
456 emitter.ostream() << getFieldOp.getFieldName();
461 emitc::GetGlobalOp getGlobalOp) {
462 if (!emitter.isPartOfCurrentExpression(getGlobalOp.getOperation()))
465 emitter.ostream() << getGlobalOp.getName();
470 emitc::LiteralOp literalOp) {
471 if (!emitter.isPartOfCurrentExpression(literalOp.getOperation()))
474 emitter.ostream() << literalOp.getValue();
479 emitc::MemberOp memberOp) {
480 if (!emitter.isPartOfCurrentExpression(memberOp.getOperation()))
483 if (failed(emitter.emitOperand(memberOp.getOperand())))
485 emitter.ostream() <<
"." << memberOp.getMember();
490 emitc::MemberOfPtrOp memberOfPtrOp) {
491 if (!emitter.isPartOfCurrentExpression(memberOfPtrOp.getOperation()))
494 if (failed(emitter.emitOperand(memberOfPtrOp.getOperand())))
496 emitter.ostream() <<
"->" << memberOfPtrOp.getMember();
501 emitc::SubscriptOp subscriptOp) {
502 if (!emitter.isPartOfCurrentExpression(subscriptOp.getOperation())) {
507 if (failed(emitter.emitOperand(subscriptOp.getValue())))
509 for (
auto index : subscriptOp.getIndices()) {
511 if (failed(emitter.emitOperand(
index,
true)))
524 if (emitter.shouldDeclareVariablesAtTop()) {
526 if (
auto oAttr = dyn_cast<emitc::OpaqueAttr>(value)) {
527 if (oAttr.getValue().empty())
531 if (failed(emitter.emitVariableAssignment(
result)))
533 return emitter.emitAttribute(operation->
getLoc(), value);
537 if (
auto oAttr = dyn_cast<emitc::OpaqueAttr>(value)) {
538 if (oAttr.getValue().empty())
540 return emitter.emitVariableDeclaration(
result,
545 if (failed(emitter.emitAssignPrefix(*operation)))
547 return emitter.emitAttribute(operation->
getLoc(), value);
551 emitc::AddressOfOp addressOfOp) {
553 Operation &op = *addressOfOp.getOperation();
555 if (failed(emitter.emitAssignPrefix(op)))
558 Value operand = addressOfOp.getReference();
565 return emitter.emitOperand(operand);
569 emitc::ConstantOp constantOp) {
570 Operation *operation = constantOp.getOperation();
573 if (emitter.isPartOfCurrentExpression(operation))
574 return emitter.emitAttribute(operation->
getLoc(), value);
580 emitc::VariableOp variableOp) {
581 Operation *operation = variableOp.getOperation();
588 emitc::GlobalOp globalOp) {
590 return emitter.emitGlobalVariable(globalOp);
594 emitc::AssignOp assignOp) {
595 if (failed(emitter.emitOperand(assignOp.getVar())))
598 emitter.ostream() <<
" = ";
600 return emitter.emitOperand(assignOp.getValue());
604 if (failed(emitter.emitAssignPrefix(*loadOp)))
607 return emitter.emitOperand(loadOp.getOperand());
612 StringRef binaryOperator) {
615 if (failed(emitter.emitAssignPrefix(*operation)))
618 if (failed(emitter.emitOperand(operation->
getOperand(0))))
621 os <<
" " << binaryOperator <<
" ";
623 if (failed(emitter.emitOperand(operation->
getOperand(1))))
631 StringRef unaryOperator) {
634 if (failed(emitter.emitAssignPrefix(*operation)))
639 if (failed(emitter.emitOperand(operation->
getOperand(0))))
646 Operation *operation = addOp.getOperation();
652 Operation *operation = divOp.getOperation();
658 Operation *operation = mulOp.getOperation();
664 Operation *operation = remOp.getOperation();
670 Operation *operation = subOp.getOperation();
678 std::next(iteratorOp) != end; ++iteratorOp) {
679 if (failed(emitter.emitOperation(*iteratorOp,
true)))
687 emitc::SwitchOp switchOp) {
691 if (failed(emitter.emitOperand(switchOp.getArg())))
695 for (
auto pair : llvm::zip(switchOp.getCases(), switchOp.getCaseRegions())) {
696 os <<
"\ncase " << std::get<0>(pair) <<
": {\n";
705 os <<
"\ndefault: {\n";
708 if (failed(
emitSwitchCase(emitter, os, switchOp.getDefaultRegion())))
721 Block &bodyBlock = doOp.getBodyRegion().
front();
723 if (failed(emitter.emitOperation(op,
true)))
729 Block &condBlock = doOp.getConditionRegion().
front();
730 auto condYield = cast<emitc::YieldOp>(condBlock.
back());
731 if (failed(emitter.emitExpression(
732 cast<emitc::ExpressionOp>(condYield.getOperand(0).getDefiningOp()))))
740 Operation *operation = cmpOp.getOperation();
742 StringRef binaryOperator;
744 switch (cmpOp.getPredicate()) {
745 case emitc::CmpPredicate::eq:
746 binaryOperator =
"==";
748 case emitc::CmpPredicate::ne:
749 binaryOperator =
"!=";
751 case emitc::CmpPredicate::lt:
752 binaryOperator =
"<";
754 case emitc::CmpPredicate::le:
755 binaryOperator =
"<=";
757 case emitc::CmpPredicate::gt:
758 binaryOperator =
">";
760 case emitc::CmpPredicate::ge:
761 binaryOperator =
">=";
763 case emitc::CmpPredicate::three_way:
764 binaryOperator =
"<=>";
772 emitc::ConditionalOp conditionalOp) {
775 if (failed(emitter.emitAssignPrefix(*conditionalOp)))
778 if (failed(emitter.emitOperand(conditionalOp.getCondition())))
783 if (failed(emitter.emitOperand(conditionalOp.getTrueValue())))
788 if (failed(emitter.emitOperand(conditionalOp.getFalseValue())))
795 emitc::VerbatimOp verbatimOp) {
798 FailureOr<SmallVector<ReplacementItem>> items =
799 verbatimOp.parseFormatString();
803 auto fmtArg = verbatimOp.getFmtArgs().begin();
806 if (
auto *str = std::get_if<StringRef>(&item)) {
809 if (failed(emitter.emitOperand(*fmtArg++)))
818 cf::BranchOp branchOp) {
823 llvm::zip(branchOp.getOperands(), successor.
getArguments())) {
824 Value &operand = std::get<0>(pair);
826 os << emitter.getOrCreateName(argument) <<
" = "
827 << emitter.getOrCreateName(operand) <<
";\n";
831 if (!(emitter.hasBlockLabel(successor)))
832 return branchOp.emitOpError(
"unable to find label for successor block");
833 os << emitter.getOrCreateName(successor);
838 cf::CondBranchOp condBranchOp) {
840 Block &trueSuccessor = *condBranchOp.getTrueDest();
841 Block &falseSuccessor = *condBranchOp.getFalseDest();
844 if (failed(emitter.emitOperand(condBranchOp.getCondition())))
851 for (
auto pair : llvm::zip(condBranchOp.getTrueOperands(),
853 Value &operand = std::get<0>(pair);
855 os << emitter.getOrCreateName(argument) <<
" = "
856 << emitter.getOrCreateName(operand) <<
";\n";
860 if (!(emitter.hasBlockLabel(trueSuccessor))) {
861 return condBranchOp.emitOpError(
"unable to find label for successor block");
863 os << emitter.getOrCreateName(trueSuccessor) <<
";\n";
867 for (
auto pair : llvm::zip(condBranchOp.getFalseOperands(),
869 Value &operand = std::get<0>(pair);
871 os << emitter.getOrCreateName(argument) <<
" = "
872 << emitter.getOrCreateName(operand) <<
";\n";
876 if (!(emitter.hasBlockLabel(falseSuccessor))) {
877 return condBranchOp.emitOpError()
878 <<
"unable to find label for successor block";
880 os << emitter.getOrCreateName(falseSuccessor) <<
";\n";
887 if (failed(emitter.emitAssignPrefix(*callOp)))
892 if (failed(emitter.emitOperands(*callOp)))
899 Operation *operation = callOp.getOperation();
900 StringRef callee = callOp.getCallee();
906 Operation *operation = callOp.getOperation();
907 StringRef callee = callOp.getCallee();
913 emitc::CallOpaqueOp callOpaqueOp) {
915 Operation &op = *callOpaqueOp.getOperation();
917 if (failed(emitter.emitAssignPrefix(op)))
919 os << callOpaqueOp.getCallee();
925 auto emitTemplateArgs = [&](
Attribute attr) -> LogicalResult {
926 return emitter.emitAttribute(op.
getLoc(), attr);
929 if (callOpaqueOp.getTemplateArgs()) {
937 auto emitArgs = [&](
Attribute attr) -> LogicalResult {
938 if (
auto t = dyn_cast<IntegerAttr>(attr)) {
940 if (t.getType().isIndex()) {
943 return emitter.emitOperand(operand);
946 if (failed(emitter.emitAttribute(op.
getLoc(), attr)))
954 LogicalResult emittedArgs =
955 callOpaqueOp.getArgs()
957 : emitter.emitOperands(op);
958 if (failed(emittedArgs))
965 emitc::ApplyOp applyOp) {
969 if (failed(emitter.emitAssignPrefix(op)))
972 StringRef applicableOperator = applyOp.getApplicableOperator();
973 Value operand = applyOp.getOperand();
979 os << applicableOperator;
980 return emitter.emitOperand(operand);
984 emitc::BitwiseAndOp bitwiseAndOp) {
985 Operation *operation = bitwiseAndOp.getOperation();
991 emitc::BitwiseLeftShiftOp bitwiseLeftShiftOp) {
992 Operation *operation = bitwiseLeftShiftOp.getOperation();
997 emitc::BitwiseNotOp bitwiseNotOp) {
998 Operation *operation = bitwiseNotOp.getOperation();
1003 emitc::BitwiseOrOp bitwiseOrOp) {
1004 Operation *operation = bitwiseOrOp.getOperation();
1010 emitc::BitwiseRightShiftOp bitwiseRightShiftOp) {
1011 Operation *operation = bitwiseRightShiftOp.getOperation();
1016 emitc::BitwiseXorOp bitwiseXorOp) {
1017 Operation *operation = bitwiseXorOp.getOperation();
1022 emitc::UnaryPlusOp unaryPlusOp) {
1023 Operation *operation = unaryPlusOp.getOperation();
1028 emitc::UnaryMinusOp unaryMinusOp) {
1029 Operation *operation = unaryMinusOp.getOperation();
1037 if (failed(emitter.emitAssignPrefix(op)))
1043 return emitter.emitOperand(castOp.getOperand());
1047 emitc::ExpressionOp expressionOp) {
1051 Operation &op = *expressionOp.getOperation();
1053 if (failed(emitter.emitAssignPrefix(op)))
1056 return emitter.emitExpression(expressionOp);
1060 emitc::IncludeOp includeOp) {
1064 if (includeOp.getIsStandardInclude())
1065 os <<
"<" << includeOp.getInclude() <<
">";
1067 os <<
"\"" << includeOp.getInclude() <<
"\"";
1073 emitc::LogicalAndOp logicalAndOp) {
1074 Operation *operation = logicalAndOp.getOperation();
1079 emitc::LogicalNotOp logicalNotOp) {
1080 Operation *operation = logicalNotOp.getOperation();
1085 emitc::LogicalOrOp logicalOrOp) {
1086 Operation *operation = logicalOrOp.getOperation();
1096 auto requiresParentheses = [&](
Value value) {
1105 emitter.emitType(forOp.getLoc(), forOp.getInductionVar().getType())))
1108 os << emitter.getOrCreateInductionVarName(forOp.getInductionVar());
1110 if (failed(emitter.emitOperand(forOp.getLowerBound())))
1113 os << emitter.getOrCreateInductionVarName(forOp.getInductionVar());
1115 Value upperBound = forOp.getUpperBound();
1116 bool upperBoundRequiresParentheses = requiresParentheses(upperBound);
1117 if (upperBoundRequiresParentheses)
1119 if (failed(emitter.emitOperand(upperBound)))
1121 if (upperBoundRequiresParentheses)
1124 os << emitter.getOrCreateInductionVarName(forOp.getInductionVar());
1126 if (failed(emitter.emitOperand(forOp.getStep())))
1131 CppEmitter::LoopScope lScope(emitter);
1133 Region &forRegion = forOp.getRegion();
1134 auto regionOps = forRegion.
getOps();
1137 for (
auto it = regionOps.begin(); std::next(it) != regionOps.end(); ++it) {
1138 if (failed(emitter.emitOperation(*it,
true)))
1152 auto emitAllExceptLast = [&emitter](
Region ®ion) {
1154 for (; std::next(it) != end; ++it) {
1155 if (failed(emitter.emitOperation(*it,
true)))
1158 assert(isa<emitc::YieldOp>(*it) &&
1159 "Expected last operation in the region to be emitc::yield");
1164 if (failed(emitter.emitOperand(ifOp.getCondition())))
1168 if (failed(emitAllExceptLast(ifOp.getThenRegion())))
1172 Region &elseRegion = ifOp.getElseRegion();
1173 if (!elseRegion.
empty()) {
1176 if (failed(emitAllExceptLast(elseRegion)))
1185 func::ReturnOp returnOp) {
1188 switch (returnOp.getNumOperands()) {
1193 if (failed(emitter.emitOperand(returnOp.getOperand(0))))
1197 os <<
" std::make_tuple(";
1198 if (failed(emitter.emitOperandsAndAttributes(*returnOp.getOperation())))
1206 emitc::ReturnOp returnOp) {
1209 if (returnOp.getNumOperands() == 0)
1213 if (failed(emitter.emitOperand(returnOp.getOperand())))
1220 if (failed(emitter.emitOperation(op,
false)))
1228 ClassType classType = classOp.getClassType();
1229 os << stringifyClassType(classType) <<
" " << classOp.getSymName();
1230 if (classOp.getFinalSpecifier())
1234 if (classType == ClassType::class_)
1240 if (failed(emitter.emitOperation(op,
false)))
1251 if (failed(emitter.emitVariableDeclaration(
1252 fieldOp->getLoc(), fieldOp.getType(), fieldOp.getSymName())))
1254 std::optional<Attribute> initialValue = fieldOp.getInitialValue();
1257 if (failed(emitter.emitAttribute(fieldOp->getLoc(), *initialValue)))
1266 if (!emitter.shouldEmitFile(file))
1270 if (failed(emitter.emitOperation(op,
false)))
1283 return emitter.emitType(functionOp->
getLoc(), arg);
1294 return emitter.emitVariableDeclaration(
1295 functionOp->
getLoc(), arg.
getType(), emitter.getOrCreateName(arg));
1305 if (emitter.shouldDeclareVariablesAtTop()) {
1310 if (isa<emitc::ExpressionOp>(op->
getParentOp()) ||
1311 (isa<emitc::ExpressionOp>(op) &&
1315 if (failed(emitter.emitVariableDeclaration(
1318 op->
emitError(
"unable to declare result variable for op"));
1323 if (
result.wasInterrupted())
1328 for (
Block &block : blocks) {
1329 emitter.getOrCreateName(block);
1333 for (
Block &block : llvm::drop_begin(blocks)) {
1335 if (emitter.hasValueInScope(arg))
1336 return functionOp->
emitOpError(
" block argument #")
1337 << arg.getArgNumber() <<
" is out of scope";
1338 if (isa<ArrayType, LValueType>(arg.getType()))
1339 return functionOp->
emitOpError(
"cannot emit block argument #")
1340 << arg.getArgNumber() <<
" with type " << arg.getType();
1342 emitter.emitType(block.getParentOp()->getLoc(), arg.getType()))) {
1345 os <<
" " << emitter.getOrCreateName(arg) <<
";\n";
1349 for (
Block &block : blocks) {
1351 if (!block.hasNoPredecessors()) {
1352 if (failed(emitter.emitLabel(block)))
1355 for (
Operation &op : block.getOperations()) {
1356 if (failed(emitter.emitOperation(op,
true)))
1367 func::FuncOp functionOp) {
1369 if (!emitter.shouldDeclareVariablesAtTop() &&
1370 functionOp.getBlocks().size() > 1) {
1371 return functionOp.emitOpError(
1372 "with multiple blocks needs variables declared at top");
1375 if (llvm::any_of(functionOp.getArgumentTypes(), llvm::IsaPred<LValueType>)) {
1376 return functionOp.emitOpError()
1377 <<
"cannot emit lvalue type as argument type";
1380 if (llvm::any_of(functionOp.getResultTypes(), llvm::IsaPred<ArrayType>)) {
1381 return functionOp.emitOpError() <<
"cannot emit array type as result type";
1384 CppEmitter::FunctionScope scope(emitter);
1386 if (failed(emitter.emitTypes(functionOp.getLoc(),
1387 functionOp.getFunctionType().getResults())))
1389 os <<
" " << functionOp.getName();
1392 Operation *operation = functionOp.getOperation();
1404 emitc::FuncOp functionOp) {
1406 if (!emitter.shouldDeclareVariablesAtTop() &&
1407 functionOp.getBlocks().size() > 1) {
1408 return functionOp.emitOpError(
1409 "with multiple blocks needs variables declared at top");
1412 CppEmitter::FunctionScope scope(emitter);
1414 if (functionOp.getSpecifiers()) {
1415 for (
Attribute specifier : functionOp.getSpecifiersAttr()) {
1416 os << cast<StringAttr>(specifier).str() <<
" ";
1420 if (failed(emitter.emitTypes(functionOp.getLoc(),
1421 functionOp.getFunctionType().getResults())))
1423 os <<
" " << functionOp.getName();
1426 Operation *operation = functionOp.getOperation();
1427 if (functionOp.isExternal()) {
1429 functionOp.getArgumentTypes())))
1445 DeclareFuncOp declareFuncOp) {
1448 CppEmitter::FunctionScope scope(emitter);
1450 declareFuncOp, declareFuncOp.getSymNameAttr());
1455 if (functionOp.getSpecifiers()) {
1456 for (
Attribute specifier : functionOp.getSpecifiersAttr()) {
1457 os << cast<StringAttr>(specifier).str() <<
" ";
1461 if (failed(emitter.emitTypes(functionOp.getLoc(),
1462 functionOp.getFunctionType().getResults())))
1464 os <<
" " << functionOp.getName();
1467 Operation *operation = functionOp.getOperation();
1475CppEmitter::CppEmitter(
raw_ostream &os,
bool declareVariablesAtTop,
1477 : os(os), declareVariablesAtTop(declareVariablesAtTop),
1478 fileId(fileId.str()), defaultValueMapperScope(valueMapper),
1479 defaultBlockMapperScope(blockMapper) {
1480 labelInScopeCount.push(0);
1484StringRef CppEmitter::getOrCreateName(
Value val) {
1485 if (!valueMapper.count(val)) {
1486 valueMapper.insert(val, formatv(
"v{0}", ++valueCount));
1488 return *valueMapper.begin(val);
1493StringRef CppEmitter::getOrCreateInductionVarName(Value val) {
1494 if (!valueMapper.count(val)) {
1496 int64_t identifier =
'i' + loopNestingLevel;
1498 if (identifier >=
'i' && identifier <=
't') {
1499 valueMapper.insert(val,
1500 formatv(
"{0}{1}", (
char)identifier, ++valueCount));
1503 valueMapper.insert(val, formatv(
"u{0}", ++valueCount));
1506 return *valueMapper.begin(val);
1510StringRef CppEmitter::getOrCreateName(
Block &block) {
1511 if (!blockMapper.count(&block))
1512 blockMapper.insert(&block, formatv(
"label{0}", ++labelInScopeCount.top()));
1513 return *blockMapper.begin(&block);
1516bool CppEmitter::shouldMapToUnsigned(IntegerType::SignednessSemantics val) {
1518 case IntegerType::Signless:
1520 case IntegerType::Signed:
1522 case IntegerType::Unsigned:
1525 llvm_unreachable(
"Unexpected IntegerType::SignednessSemantics");
1528bool CppEmitter::hasValueInScope(Value val) {
return valueMapper.count(val); }
1530bool CppEmitter::hasBlockLabel(
Block &block) {
1531 return blockMapper.count(&block);
1534LogicalResult CppEmitter::emitAttribute(Location loc, Attribute attr) {
1535 auto printInt = [&](
const APInt &val,
bool isUnsigned) {
1536 if (val.getBitWidth() == 1) {
1537 if (val.getBoolValue())
1542 SmallString<128> strValue;
1543 val.toString(strValue, 10, !isUnsigned,
false);
1548 auto printFloat = [&](
const APFloat &val) {
1549 if (val.isFinite()) {
1550 SmallString<128> strValue;
1552 val.toString(strValue, 0, 0,
false);
1554 switch (llvm::APFloatBase::SemanticsToEnum(val.getSemantics())) {
1555 case llvm::APFloatBase::S_IEEEhalf:
1558 case llvm::APFloatBase::S_BFloat:
1561 case llvm::APFloatBase::S_IEEEsingle:
1564 case llvm::APFloatBase::S_IEEEdouble:
1567 llvm_unreachable(
"unsupported floating point type");
1569 }
else if (val.isNaN()) {
1571 }
else if (val.isInfinity()) {
1572 if (val.isNegative())
1579 if (
auto fAttr = dyn_cast<FloatAttr>(attr)) {
1580 if (!isa<Float16Type, BFloat16Type, Float32Type, Float64Type>(
1583 loc,
"expected floating point attribute to be f16, bf16, f32 or f64");
1585 printFloat(fAttr.getValue());
1588 if (
auto dense = dyn_cast<DenseFPElementsAttr>(attr)) {
1589 if (!isa<Float16Type, BFloat16Type, Float32Type, Float64Type>(
1590 dense.getElementType())) {
1592 loc,
"expected floating point attribute to be f16, bf16, f32 or f64");
1595 interleaveComma(dense, os, [&](
const APFloat &val) { printFloat(val); });
1601 if (
auto iAttr = dyn_cast<IntegerAttr>(attr)) {
1602 if (
auto iType = dyn_cast<IntegerType>(iAttr.getType())) {
1603 printInt(iAttr.getValue(), shouldMapToUnsigned(iType.getSignedness()));
1606 if (
auto iType = dyn_cast<IndexType>(iAttr.getType())) {
1607 printInt(iAttr.getValue(),
false);
1611 if (
auto dense = dyn_cast<DenseIntElementsAttr>(attr)) {
1612 if (
auto iType = dyn_cast<IntegerType>(
1613 cast<ShapedType>(dense.getType()).getElementType())) {
1615 interleaveComma(dense, os, [&](
const APInt &val) {
1616 printInt(val, shouldMapToUnsigned(iType.getSignedness()));
1621 if (
auto iType = dyn_cast<IndexType>(
1622 cast<ShapedType>(dense.getType()).getElementType())) {
1624 interleaveComma(dense, os,
1625 [&](
const APInt &val) { printInt(val,
false); });
1632 if (
auto oAttr = dyn_cast<emitc::OpaqueAttr>(attr)) {
1633 os << oAttr.getValue();
1638 if (
auto sAttr = dyn_cast<SymbolRefAttr>(attr)) {
1639 if (sAttr.getNestedReferences().size() > 1)
1640 return emitError(loc,
"attribute has more than 1 nested reference");
1641 os << sAttr.getRootReference().getValue();
1646 if (
auto type = dyn_cast<TypeAttr>(attr))
1647 return emitType(loc, type.getValue());
1649 return emitError(loc,
"cannot emit attribute: ") << attr;
1652LogicalResult CppEmitter::emitExpression(Operation *op) {
1653 assert(emittedExpressionPrecedence.empty() &&
1654 "Expected precedence stack to be empty");
1655 Operation *rootOp =
nullptr;
1657 if (
auto expressionOp = dyn_cast<ExpressionOp>(op)) {
1658 rootOp = expressionOp.getRootOp();
1660 assert(cast<CExpressionInterface>(op).alwaysInline() &&
1661 "Expected an always-inline operation");
1663 "Expected operation to have no containing expression");
1669 pushExpressionPrecedence(precedence.value());
1671 if (
failed(emitOperation(*rootOp,
false)))
1674 popExpressionPrecedence();
1675 assert(emittedExpressionPrecedence.empty() &&
1676 "Expected precedence stack to be empty");
1681LogicalResult CppEmitter::emitOperand(Value value,
bool isInBrackets) {
1682 if (isPartOfCurrentExpression(value)) {
1684 assert(def &&
"Expected operand to be defined by an operation");
1685 if (
auto expressionOp = dyn_cast<ExpressionOp>(def))
1686 def = expressionOp.getRootOp();
1694 bool encloseInParenthesis =
1695 !isInBrackets && precedence.value() <= getExpressionPrecedence();
1697 if (encloseInParenthesis)
1699 pushExpressionPrecedence(precedence.value());
1701 if (
failed(emitOperation(*def,
false)))
1704 if (encloseInParenthesis)
1707 popExpressionPrecedence();
1712 return emitExpression(def);
1714 if (BlockArgument arg = dyn_cast<BlockArgument>(value)) {
1717 Operation *argOp = arg.getParentBlock()->getParentOp();
1718 if (
auto expressionOp = dyn_cast<ExpressionOp>(argOp))
1719 return emitOperand(expressionOp->getOperand(arg.getArgNumber()));
1722 os << getOrCreateName(value);
1726LogicalResult CppEmitter::emitOperands(Operation &op) {
1730 return emitOperand(operand, true);
1735CppEmitter::emitOperandsAndAttributes(Operation &op,
1736 ArrayRef<StringRef> exclude) {
1737 if (
failed(emitOperands(op)))
1741 for (NamedAttribute attr : op.
getAttrs()) {
1742 if (!llvm::is_contained(exclude, attr.getName().strref())) {
1749 auto emitNamedAttribute = [&](NamedAttribute attr) -> LogicalResult {
1750 if (llvm::is_contained(exclude, attr.getName().strref()))
1752 os <<
"/* " << attr.getName().getValue() <<
" */";
1753 if (
failed(emitAttribute(op.
getLoc(), attr.getValue())))
1760LogicalResult CppEmitter::emitVariableAssignment(OpResult
result) {
1761 if (!hasValueInScope(
result)) {
1762 return result.getDefiningOp()->emitOpError(
1763 "result variable for the operation has not been declared");
1765 os << getOrCreateName(
result) <<
" = ";
1769LogicalResult CppEmitter::emitVariableDeclaration(OpResult
result,
1770 bool trailingSemicolon) {
1771 if (
auto cExpression =
1772 dyn_cast<CExpressionInterface>(
result.getDefiningOp())) {
1773 if (cExpression.alwaysInline())
1776 if (hasValueInScope(
result)) {
1777 return result.getDefiningOp()->emitError(
1778 "result variable for the operation already declared");
1780 if (
failed(emitVariableDeclaration(
result.getOwner()->getLoc(),
1782 getOrCreateName(
result))))
1784 if (trailingSemicolon)
1789LogicalResult CppEmitter::emitGlobalVariable(GlobalOp op) {
1790 if (op.getExternSpecifier())
1792 else if (op.getStaticSpecifier())
1794 if (op.getConstSpecifier())
1797 if (
failed(emitVariableDeclaration(op->getLoc(), op.getType(),
1798 op.getSymName()))) {
1802 std::optional<Attribute> initialValue = op.getInitialValue();
1805 if (
failed(emitAttribute(op->getLoc(), *initialValue)))
1813LogicalResult CppEmitter::emitAssignPrefix(Operation &op) {
1815 if (isEmittingExpression())
1823 if (shouldDeclareVariablesAtTop()) {
1834 if (!shouldDeclareVariablesAtTop()) {
1842 [&](Value
result) { os << getOrCreateName(result); });
1848LogicalResult CppEmitter::emitLabel(
Block &block) {
1849 if (!hasBlockLabel(block))
1853 os.getOStream() << getOrCreateName(block) <<
":\n";
1857LogicalResult CppEmitter::emitOperation(Operation &op,
bool trailingSemicolon) {
1858 LogicalResult status =
1859 llvm::TypeSwitch<Operation *, LogicalResult>(&op)
1863 .Case<cf::BranchOp, cf::CondBranchOp>(
1866 .Case<emitc::AddressOfOp, emitc::AddOp, emitc::ApplyOp,
1867 emitc::AssignOp, emitc::BitwiseAndOp, emitc::BitwiseLeftShiftOp,
1868 emitc::BitwiseNotOp, emitc::BitwiseOrOp,
1869 emitc::BitwiseRightShiftOp, emitc::BitwiseXorOp, emitc::CallOp,
1870 emitc::CallOpaqueOp, emitc::CastOp, emitc::ClassOp,
1871 emitc::CmpOp, emitc::ConditionalOp, emitc::ConstantOp,
1872 emitc::DeclareFuncOp, emitc::DereferenceOp, emitc::DivOp,
1873 emitc::DoOp, emitc::ExpressionOp, emitc::FieldOp, emitc::FileOp,
1874 emitc::ForOp, emitc::FuncOp, emitc::GetFieldOp,
1875 emitc::GetGlobalOp, emitc::GlobalOp, emitc::IfOp,
1876 emitc::IncludeOp, emitc::LiteralOp, emitc::LoadOp,
1877 emitc::LogicalAndOp, emitc::LogicalNotOp, emitc::LogicalOrOp,
1878 emitc::MemberOfPtrOp, emitc::MemberOp, emitc::MulOp,
1879 emitc::RemOp, emitc::ReturnOp, emitc::SubscriptOp, emitc::SubOp,
1880 emitc::SwitchOp, emitc::UnaryMinusOp, emitc::UnaryPlusOp,
1881 emitc::VariableOp, emitc::VerbatimOp>(
1885 .Case<func::CallOp, func::FuncOp, func::ReturnOp>(
1887 .Default([&](Operation *) {
1888 return op.emitOpError(
"unable to find printer for op");
1894 if (
auto cExpression = dyn_cast<CExpressionInterface>(op)) {
1895 if (cExpression.alwaysInline())
1899 if (isEmittingExpression() ||
1900 (isa<emitc::ExpressionOp>(op) &&
1906 trailingSemicolon &=
1907 !isa<cf::CondBranchOp, emitc::DeclareFuncOp, emitc::DoOp, emitc::FileOp,
1908 emitc::ForOp, emitc::IfOp, emitc::IncludeOp, emitc::SwitchOp,
1909 emitc::VerbatimOp>(op);
1911 os << (trailingSemicolon ?
";\n" :
"\n");
1916LogicalResult CppEmitter::emitVariableDeclaration(Location loc, Type type,
1918 if (
auto arrType = dyn_cast<emitc::ArrayType>(type)) {
1919 if (
failed(emitType(loc, arrType.getElementType())))
1922 for (
auto dim : arrType.getShape()) {
1923 os <<
"[" << dim <<
"]";
1927 if (
failed(emitType(loc, type)))
1933LogicalResult CppEmitter::emitType(Location loc, Type type) {
1934 if (
auto iType = dyn_cast<IntegerType>(type)) {
1935 switch (iType.getWidth()) {
1937 return (os <<
"bool"),
success();
1942 if (shouldMapToUnsigned(iType.getSignedness()))
1943 return (os <<
"uint" << iType.getWidth() <<
"_t"),
success();
1945 return (os <<
"int" << iType.getWidth() <<
"_t"),
success();
1947 return emitError(loc,
"cannot emit integer type ") << type;
1950 if (
auto fType = dyn_cast<FloatType>(type)) {
1951 switch (fType.getWidth()) {
1953 if (llvm::isa<Float16Type>(type))
1954 return (os <<
"_Float16"),
success();
1955 if (llvm::isa<BFloat16Type>(type))
1956 return (os <<
"__bf16"),
success();
1958 return emitError(loc,
"cannot emit float type ") << type;
1961 return (os <<
"float"),
success();
1963 return (os <<
"double"),
success();
1965 return emitError(loc,
"cannot emit float type ") << type;
1968 if (
auto iType = dyn_cast<IndexType>(type))
1969 return (os <<
"size_t"),
success();
1970 if (
auto sType = dyn_cast<emitc::SizeTType>(type))
1971 return (os <<
"size_t"),
success();
1972 if (
auto sType = dyn_cast<emitc::SignedSizeTType>(type))
1973 return (os <<
"ssize_t"),
success();
1974 if (
auto pType = dyn_cast<emitc::PtrDiffTType>(type))
1975 return (os <<
"ptrdiff_t"),
success();
1976 if (
auto tType = dyn_cast<TensorType>(type)) {
1977 if (!tType.hasRank())
1978 return emitError(loc,
"cannot emit unranked tensor type");
1979 if (!tType.hasStaticShape())
1980 return emitError(loc,
"cannot emit tensor type with non static shape");
1982 if (isa<ArrayType>(tType.getElementType()))
1983 return emitError(loc,
"cannot emit tensor of array type ") << type;
1984 if (
failed(emitType(loc, tType.getElementType())))
1986 auto shape = tType.getShape();
1987 for (
auto dimSize : shape) {
1994 if (
auto tType = dyn_cast<TupleType>(type))
1995 return emitTupleType(loc, tType.getTypes());
1996 if (
auto oType = dyn_cast<emitc::OpaqueType>(type)) {
1997 os << oType.getValue();
2000 if (
auto aType = dyn_cast<emitc::ArrayType>(type)) {
2001 if (
failed(emitType(loc, aType.getElementType())))
2003 for (
auto dim : aType.getShape())
2004 os <<
"[" << dim <<
"]";
2007 if (
auto lType = dyn_cast<emitc::LValueType>(type))
2008 return emitType(loc, lType.getValueType());
2009 if (
auto pType = dyn_cast<emitc::PointerType>(type)) {
2010 if (isa<ArrayType>(pType.getPointee()))
2011 return emitError(loc,
"cannot emit pointer to array type ") << type;
2012 if (
failed(emitType(loc, pType.getPointee())))
2017 return emitError(loc,
"cannot emit type ") << type;
2020LogicalResult CppEmitter::emitTypes(Location loc, ArrayRef<Type> types) {
2021 switch (types.size()) {
2026 return emitType(loc, types.front());
2028 return emitTupleType(loc, types);
2032LogicalResult CppEmitter::emitTupleType(Location loc, ArrayRef<Type> types) {
2033 if (llvm::any_of(types, llvm::IsaPred<ArrayType>)) {
2034 return emitError(loc,
"cannot emit tuple of array type");
2036 os <<
"std::tuple<";
2038 types, os, [&](Type type) {
return emitType(loc, type); })))
2044void CppEmitter::resetValueCounter() { valueCount = 0; }
2046void CppEmitter::increaseLoopNestingLevel() { loopNestingLevel++; }
2048void CppEmitter::decreaseLoopNestingLevel() { loopNestingLevel--; }
2051 bool declareVariablesAtTop,
2053 CppEmitter emitter(os, declareVariablesAtTop, fileId);
2054 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 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.