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::BitwiseAndOp op) {
return 7; })
78 .Case([&](emitc::BitwiseLeftShiftOp op) {
return 11; })
79 .Case([&](emitc::BitwiseNotOp op) {
return 15; })
80 .Case([&](emitc::BitwiseOrOp op) {
return 5; })
81 .Case([&](emitc::BitwiseRightShiftOp op) {
return 11; })
82 .Case([&](emitc::BitwiseXorOp op) {
return 6; })
83 .Case([&](emitc::CallOp op) {
return 16; })
84 .Case([&](emitc::CallOpaqueOp op) {
return 16; })
85 .Case([&](emitc::CastOp op) {
return 15; })
86 .Case([&](emitc::CmpOp op) -> FailureOr<int> {
87 switch (op.getPredicate()) {
88 case emitc::CmpPredicate::eq:
89 case emitc::CmpPredicate::ne:
91 case emitc::CmpPredicate::lt:
92 case emitc::CmpPredicate::le:
93 case emitc::CmpPredicate::gt:
94 case emitc::CmpPredicate::ge:
96 case emitc::CmpPredicate::three_way:
99 return op->emitError(
"unsupported cmp predicate");
101 .Case([&](emitc::ConditionalOp op) {
return 2; })
102 .Case([&](emitc::ConstantOp op) {
return 17; })
103 .Case([&](emitc::DereferenceOp op) {
return 15; })
104 .Case([&](emitc::DivOp op) {
return 13; })
105 .Case([&](emitc::GetGlobalOp op) {
return 18; })
106 .Case([&](emitc::GetFieldOp 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 (memberOp.alwaysInline()) {
481 if (!emitter.isPartOfCurrentExpression(memberOp.getOperation()))
484 if (failed(emitter.emitAssignPrefix(*memberOp.getOperation())))
487 if (failed(emitter.emitOperand(memberOp.getOperand())))
489 emitter.ostream() <<
"." << memberOp.getMember();
494 emitc::MemberOfPtrOp memberOfPtrOp) {
495 if (!emitter.isPartOfCurrentExpression(memberOfPtrOp.getOperation()))
498 if (failed(emitter.emitOperand(memberOfPtrOp.getOperand())))
500 emitter.ostream() <<
"->" << memberOfPtrOp.getMember();
505 emitc::SubscriptOp subscriptOp) {
506 if (!emitter.isPartOfCurrentExpression(subscriptOp.getOperation())) {
511 if (failed(emitter.emitOperand(subscriptOp.getValue())))
513 for (
auto index : subscriptOp.getIndices()) {
515 if (failed(emitter.emitOperand(
index,
true)))
528 if (emitter.shouldDeclareVariablesAtTop()) {
530 if (
auto oAttr = dyn_cast<emitc::OpaqueAttr>(value)) {
531 if (oAttr.getValue().empty())
535 if (failed(emitter.emitVariableAssignment(
result)))
537 return emitter.emitAttribute(operation->
getLoc(), value);
541 if (
auto oAttr = dyn_cast<emitc::OpaqueAttr>(value)) {
542 if (oAttr.getValue().empty())
544 return emitter.emitVariableDeclaration(
result,
549 if (failed(emitter.emitAssignPrefix(*operation)))
551 return emitter.emitAttribute(operation->
getLoc(), value);
555 emitc::AddressOfOp addressOfOp) {
557 Operation &op = *addressOfOp.getOperation();
559 if (failed(emitter.emitAssignPrefix(op)))
562 Value operand = addressOfOp.getReference();
569 return emitter.emitOperand(operand);
573 emitc::ConstantOp constantOp) {
574 Operation *operation = constantOp.getOperation();
577 if (emitter.isPartOfCurrentExpression(operation))
578 return emitter.emitAttribute(operation->
getLoc(), value);
584 emitc::VariableOp variableOp) {
585 Operation *operation = variableOp.getOperation();
592 emitc::GlobalOp globalOp) {
594 return emitter.emitGlobalVariable(globalOp);
598 emitc::AssignOp assignOp) {
599 if (failed(emitter.emitOperand(assignOp.getVar())))
602 emitter.ostream() <<
" = ";
604 return emitter.emitOperand(assignOp.getValue());
608 if (failed(emitter.emitAssignPrefix(*loadOp)))
611 return emitter.emitOperand(loadOp.getOperand());
616 StringRef binaryOperator) {
619 if (failed(emitter.emitAssignPrefix(*operation)))
622 if (failed(emitter.emitOperand(operation->
getOperand(0))))
625 os <<
" " << binaryOperator <<
" ";
627 if (failed(emitter.emitOperand(operation->
getOperand(1))))
635 StringRef unaryOperator) {
638 if (failed(emitter.emitAssignPrefix(*operation)))
643 if (failed(emitter.emitOperand(operation->
getOperand(0))))
650 Operation *operation = addOp.getOperation();
656 Operation *operation = divOp.getOperation();
662 Operation *operation = mulOp.getOperation();
668 Operation *operation = remOp.getOperation();
674 Operation *operation = subOp.getOperation();
682 std::next(iteratorOp) != end; ++iteratorOp) {
683 if (failed(emitter.emitOperation(*iteratorOp,
true)))
691 emitc::SwitchOp switchOp) {
695 if (failed(emitter.emitOperand(switchOp.getArg())))
699 for (
auto pair : llvm::zip(switchOp.getCases(), switchOp.getCaseRegions())) {
700 os <<
"\ncase " << std::get<0>(pair) <<
": {\n";
709 os <<
"\ndefault: {\n";
712 if (failed(
emitSwitchCase(emitter, os, switchOp.getDefaultRegion())))
725 Block &bodyBlock = doOp.getBodyRegion().
front();
727 if (failed(emitter.emitOperation(op,
true)))
733 Block &condBlock = doOp.getConditionRegion().
front();
734 auto condYield = cast<emitc::YieldOp>(condBlock.
back());
735 if (failed(emitter.emitExpression(
736 cast<emitc::ExpressionOp>(condYield.getOperand(0).getDefiningOp()))))
744 Operation *operation = cmpOp.getOperation();
746 StringRef binaryOperator;
748 switch (cmpOp.getPredicate()) {
749 case emitc::CmpPredicate::eq:
750 binaryOperator =
"==";
752 case emitc::CmpPredicate::ne:
753 binaryOperator =
"!=";
755 case emitc::CmpPredicate::lt:
756 binaryOperator =
"<";
758 case emitc::CmpPredicate::le:
759 binaryOperator =
"<=";
761 case emitc::CmpPredicate::gt:
762 binaryOperator =
">";
764 case emitc::CmpPredicate::ge:
765 binaryOperator =
">=";
767 case emitc::CmpPredicate::three_way:
768 binaryOperator =
"<=>";
776 emitc::ConditionalOp conditionalOp) {
779 if (failed(emitter.emitAssignPrefix(*conditionalOp)))
782 if (failed(emitter.emitOperand(conditionalOp.getCondition())))
787 if (failed(emitter.emitOperand(conditionalOp.getTrueValue())))
792 if (failed(emitter.emitOperand(conditionalOp.getFalseValue())))
799 emitc::VerbatimOp verbatimOp) {
802 FailureOr<SmallVector<ReplacementItem>> items =
803 verbatimOp.parseFormatString();
807 auto fmtArg = verbatimOp.getFmtArgs().begin();
810 if (
auto *str = std::get_if<StringRef>(&item)) {
813 if (failed(emitter.emitOperand(*fmtArg++)))
822 cf::BranchOp branchOp) {
827 llvm::zip(branchOp.getOperands(), successor.
getArguments())) {
828 Value &operand = std::get<0>(pair);
830 os << emitter.getOrCreateName(argument) <<
" = "
831 << emitter.getOrCreateName(operand) <<
";\n";
835 if (!(emitter.hasBlockLabel(successor)))
836 return branchOp.emitOpError(
"unable to find label for successor block");
837 os << emitter.getOrCreateName(successor);
842 cf::CondBranchOp condBranchOp) {
844 Block &trueSuccessor = *condBranchOp.getTrueDest();
845 Block &falseSuccessor = *condBranchOp.getFalseDest();
848 if (failed(emitter.emitOperand(condBranchOp.getCondition())))
855 for (
auto pair : llvm::zip(condBranchOp.getTrueOperands(),
857 Value &operand = std::get<0>(pair);
859 os << emitter.getOrCreateName(argument) <<
" = "
860 << emitter.getOrCreateName(operand) <<
";\n";
864 if (!(emitter.hasBlockLabel(trueSuccessor))) {
865 return condBranchOp.emitOpError(
"unable to find label for successor block");
867 os << emitter.getOrCreateName(trueSuccessor) <<
";\n";
871 for (
auto pair : llvm::zip(condBranchOp.getFalseOperands(),
873 Value &operand = std::get<0>(pair);
875 os << emitter.getOrCreateName(argument) <<
" = "
876 << emitter.getOrCreateName(operand) <<
";\n";
880 if (!(emitter.hasBlockLabel(falseSuccessor))) {
881 return condBranchOp.emitOpError()
882 <<
"unable to find label for successor block";
884 os << emitter.getOrCreateName(falseSuccessor) <<
";\n";
891 if (failed(emitter.emitAssignPrefix(*callOp)))
896 if (failed(emitter.emitOperands(*callOp)))
903 Operation *operation = callOp.getOperation();
904 StringRef callee = callOp.getCallee();
910 Operation *operation = callOp.getOperation();
911 StringRef callee = callOp.getCallee();
916template <
typename OpTy>
919 std::optional<ArrayAttr> templateArgs,
920 std::optional<ArrayAttr> args,
bool isMemberCall,
921 Value receiver =
nullptr) {
924 if (failed(emitter.emitAssignPrefix(*op.getOperation())))
928 assert(receiver &&
"Expected receiver for member call");
929 if (failed(emitter.emitOperand(receiver)))
932 if (llvm::isa<emitc::PointerType>(receiver.getType()))
944 auto emitTemplateArgs = [&](
Attribute attr) -> LogicalResult {
945 return emitter.emitAttribute(op.getLoc(), attr);
955 auto emitArgs = [&](
Attribute attr) -> LogicalResult {
956 if (
auto t = dyn_cast<IntegerAttr>(attr)) {
957 if (t.getType().isIndex()) {
959 Value operand = op.getArgOperands()[idx];
960 return emitter.emitOperand(operand,
false);
963 if (failed(emitter.emitAttribute(op.getLoc(), attr)))
971 LogicalResult emittedArgs =
success();
977 return emitter.emitOperand(operand, true);
980 if (failed(emittedArgs))
987 emitc::CallOpaqueOp callOpaqueOp) {
989 callOpaqueOp.getTemplateArgs(),
990 callOpaqueOp.getArgs(),
996 emitc::MemberCallOpaqueOp memberCallOpaqueOp) {
998 emitter, memberCallOpaqueOp, memberCallOpaqueOp.getCallee(),
999 memberCallOpaqueOp.getTemplateArgs(), memberCallOpaqueOp.getArgs(),
1000 true, memberCallOpaqueOp.getReceiver());
1004 emitc::BitwiseAndOp bitwiseAndOp) {
1005 Operation *operation = bitwiseAndOp.getOperation();
1011 emitc::BitwiseLeftShiftOp bitwiseLeftShiftOp) {
1012 Operation *operation = bitwiseLeftShiftOp.getOperation();
1017 emitc::BitwiseNotOp bitwiseNotOp) {
1018 Operation *operation = bitwiseNotOp.getOperation();
1023 emitc::BitwiseOrOp bitwiseOrOp) {
1024 Operation *operation = bitwiseOrOp.getOperation();
1030 emitc::BitwiseRightShiftOp bitwiseRightShiftOp) {
1031 Operation *operation = bitwiseRightShiftOp.getOperation();
1036 emitc::BitwiseXorOp bitwiseXorOp) {
1037 Operation *operation = bitwiseXorOp.getOperation();
1042 emitc::UnaryPlusOp unaryPlusOp) {
1043 Operation *operation = unaryPlusOp.getOperation();
1048 emitc::UnaryMinusOp unaryMinusOp) {
1049 Operation *operation = unaryMinusOp.getOperation();
1057 if (failed(emitter.emitAssignPrefix(op)))
1063 return emitter.emitOperand(castOp.getOperand());
1067 emitc::ExpressionOp expressionOp) {
1071 Operation &op = *expressionOp.getOperation();
1073 if (failed(emitter.emitAssignPrefix(op)))
1076 return emitter.emitExpression(expressionOp);
1080 emitc::IncludeOp includeOp) {
1084 if (includeOp.getIsStandardInclude())
1085 os <<
"<" << includeOp.getInclude() <<
">";
1087 os <<
"\"" << includeOp.getInclude() <<
"\"";
1093 emitc::LogicalAndOp logicalAndOp) {
1094 Operation *operation = logicalAndOp.getOperation();
1099 emitc::LogicalNotOp logicalNotOp) {
1100 Operation *operation = logicalNotOp.getOperation();
1105 emitc::LogicalOrOp logicalOrOp) {
1106 Operation *operation = logicalOrOp.getOperation();
1116 auto requiresParentheses = [&](
Value value) {
1125 emitter.emitType(forOp.getLoc(), forOp.getInductionVar().getType())))
1128 os << emitter.getOrCreateInductionVarName(forOp.getInductionVar());
1130 if (failed(emitter.emitOperand(forOp.getLowerBound())))
1133 os << emitter.getOrCreateInductionVarName(forOp.getInductionVar());
1135 Value upperBound = forOp.getUpperBound();
1136 bool upperBoundRequiresParentheses = requiresParentheses(upperBound);
1137 if (upperBoundRequiresParentheses)
1139 if (failed(emitter.emitOperand(upperBound)))
1141 if (upperBoundRequiresParentheses)
1144 os << emitter.getOrCreateInductionVarName(forOp.getInductionVar());
1146 if (failed(emitter.emitOperand(forOp.getStep())))
1151 CppEmitter::LoopScope lScope(emitter);
1153 Region &forRegion = forOp.getRegion();
1154 auto regionOps = forRegion.
getOps();
1157 for (
auto it = regionOps.begin(); std::next(it) != regionOps.end(); ++it) {
1158 if (failed(emitter.emitOperation(*it,
true)))
1172 auto emitAllExceptLast = [&emitter](
Region ®ion) {
1174 for (; std::next(it) != end; ++it) {
1175 if (failed(emitter.emitOperation(*it,
true)))
1178 assert(isa<emitc::YieldOp>(*it) &&
1179 "Expected last operation in the region to be emitc::yield");
1184 if (failed(emitter.emitOperand(ifOp.getCondition())))
1188 if (failed(emitAllExceptLast(ifOp.getThenRegion())))
1192 Region &elseRegion = ifOp.getElseRegion();
1193 if (!elseRegion.
empty()) {
1196 if (failed(emitAllExceptLast(elseRegion)))
1205 func::ReturnOp returnOp) {
1208 switch (returnOp.getNumOperands()) {
1213 if (failed(emitter.emitOperand(returnOp.getOperand(0))))
1217 os <<
" std::make_tuple(";
1218 if (failed(emitter.emitOperandsAndAttributes(*returnOp.getOperation())))
1226 emitc::ReturnOp returnOp) {
1229 if (returnOp.getNumOperands() == 0)
1233 if (failed(emitter.emitOperand(returnOp.getOperand())))
1240 if (failed(emitter.emitOperation(op,
false)))
1248 ClassType classType = classOp.getClassType();
1249 os << stringifyClassType(classType) <<
" " << classOp.getSymName();
1250 if (classOp.getFinalSpecifier())
1254 if (classType == ClassType::class_)
1260 if (failed(emitter.emitOperation(op,
false)))
1271 if (failed(emitter.emitVariableDeclaration(
1272 fieldOp->getLoc(), fieldOp.getType(), fieldOp.getSymName())))
1274 std::optional<Attribute> initialValue = fieldOp.getInitialValue();
1277 if (failed(emitter.emitAttribute(fieldOp->getLoc(), *initialValue)))
1286 if (!emitter.shouldEmitFile(file))
1290 if (failed(emitter.emitOperation(op,
false)))
1303 return emitter.emitType(functionOp->
getLoc(), arg);
1314 return emitter.emitVariableDeclaration(
1315 functionOp->
getLoc(), arg.
getType(), emitter.getOrCreateName(arg));
1325 if (emitter.shouldDeclareVariablesAtTop()) {
1330 if (isa<emitc::ExpressionOp>(op->
getParentOp()) ||
1331 (isa<emitc::ExpressionOp>(op) &&
1335 if (failed(emitter.emitVariableDeclaration(
1338 op->
emitError(
"unable to declare result variable for op"));
1343 if (
result.wasInterrupted())
1348 for (
Block &block : blocks) {
1349 emitter.getOrCreateName(block);
1353 for (
Block &block : llvm::drop_begin(blocks)) {
1355 if (emitter.hasValueInScope(arg))
1356 return functionOp->
emitOpError(
" block argument #")
1357 << arg.getArgNumber() <<
" is out of scope";
1358 if (isa<ArrayType, LValueType>(arg.getType()))
1359 return functionOp->
emitOpError(
"cannot emit block argument #")
1360 << arg.getArgNumber() <<
" with type " << arg.getType();
1362 emitter.emitType(block.getParentOp()->getLoc(), arg.getType()))) {
1365 os <<
" " << emitter.getOrCreateName(arg) <<
";\n";
1369 for (
Block &block : blocks) {
1371 if (!block.hasNoPredecessors()) {
1372 if (failed(emitter.emitLabel(block)))
1375 for (
Operation &op : block.getOperations()) {
1376 if (failed(emitter.emitOperation(op,
true)))
1387 func::FuncOp functionOp) {
1389 if (!emitter.shouldDeclareVariablesAtTop() &&
1390 functionOp.getBlocks().size() > 1) {
1391 return functionOp.emitOpError(
1392 "with multiple blocks needs variables declared at top");
1395 if (llvm::any_of(functionOp.getArgumentTypes(), llvm::IsaPred<LValueType>)) {
1396 return functionOp.emitOpError()
1397 <<
"cannot emit lvalue type as argument type";
1400 if (llvm::any_of(functionOp.getResultTypes(), llvm::IsaPred<ArrayType>)) {
1401 return functionOp.emitOpError() <<
"cannot emit array type as result type";
1404 CppEmitter::FunctionScope scope(emitter);
1406 if (failed(emitter.emitTypes(functionOp.getLoc(),
1407 functionOp.getFunctionType().getResults())))
1409 os <<
" " << functionOp.getName();
1412 Operation *operation = functionOp.getOperation();
1424 emitc::FuncOp functionOp) {
1426 if (!emitter.shouldDeclareVariablesAtTop() &&
1427 functionOp.getBlocks().size() > 1) {
1428 return functionOp.emitOpError(
1429 "with multiple blocks needs variables declared at top");
1432 CppEmitter::FunctionScope scope(emitter);
1434 if (functionOp.getSpecifiers()) {
1435 for (
Attribute specifier : functionOp.getSpecifiersAttr()) {
1436 os << cast<StringAttr>(specifier).str() <<
" ";
1440 if (failed(emitter.emitTypes(functionOp.getLoc(),
1441 functionOp.getFunctionType().getResults())))
1443 os <<
" " << functionOp.getName();
1446 Operation *operation = functionOp.getOperation();
1447 if (functionOp.isExternal()) {
1449 functionOp.getArgumentTypes())))
1465 DeclareFuncOp declareFuncOp) {
1468 CppEmitter::FunctionScope scope(emitter);
1470 declareFuncOp, declareFuncOp.getSymNameAttr());
1475 if (functionOp.getSpecifiers()) {
1476 for (
Attribute specifier : functionOp.getSpecifiersAttr()) {
1477 os << cast<StringAttr>(specifier).str() <<
" ";
1481 if (failed(emitter.emitTypes(functionOp.getLoc(),
1482 functionOp.getFunctionType().getResults())))
1484 os <<
" " << functionOp.getName();
1487 Operation *operation = functionOp.getOperation();
1495CppEmitter::CppEmitter(
raw_ostream &os,
bool declareVariablesAtTop,
1497 : os(os), declareVariablesAtTop(declareVariablesAtTop),
1498 fileId(fileId.str()), defaultValueMapperScope(valueMapper),
1499 defaultBlockMapperScope(blockMapper) {
1500 labelInScopeCount.push(0);
1504StringRef CppEmitter::getOrCreateName(
Value val) {
1505 if (!valueMapper.count(val)) {
1506 valueMapper.insert(val, formatv(
"v{0}", ++valueCount));
1508 return *valueMapper.begin(val);
1513StringRef CppEmitter::getOrCreateInductionVarName(Value val) {
1514 if (!valueMapper.count(val)) {
1516 int64_t identifier =
'i' + loopNestingLevel;
1518 if (identifier >=
'i' && identifier <=
't') {
1519 valueMapper.insert(val,
1520 formatv(
"{0}{1}", (
char)identifier, ++valueCount));
1523 valueMapper.insert(val, formatv(
"u{0}", ++valueCount));
1526 return *valueMapper.begin(val);
1530StringRef CppEmitter::getOrCreateName(
Block &block) {
1531 if (!blockMapper.count(&block))
1532 blockMapper.insert(&block, formatv(
"label{0}", ++labelInScopeCount.top()));
1533 return *blockMapper.begin(&block);
1536bool CppEmitter::shouldMapToUnsigned(IntegerType::SignednessSemantics val) {
1538 case IntegerType::Signless:
1540 case IntegerType::Signed:
1542 case IntegerType::Unsigned:
1545 llvm_unreachable(
"Unexpected IntegerType::SignednessSemantics");
1548bool CppEmitter::hasValueInScope(Value val) {
return valueMapper.count(val); }
1550bool CppEmitter::hasBlockLabel(
Block &block) {
1551 return blockMapper.count(&block);
1554LogicalResult CppEmitter::emitAttribute(Location loc, Attribute attr) {
1555 auto printInt = [&](
const APInt &val,
bool isUnsigned) {
1556 if (val.getBitWidth() == 1) {
1557 if (val.getBoolValue())
1562 SmallString<128> strValue;
1563 val.toString(strValue, 10, !isUnsigned,
false);
1568 auto printFloat = [&](
const APFloat &val) {
1569 if (val.isFinite()) {
1570 SmallString<128> strValue;
1572 val.toString(strValue, 0, 0,
false);
1574 switch (llvm::APFloatBase::SemanticsToEnum(val.getSemantics())) {
1575 case llvm::APFloatBase::S_IEEEhalf:
1578 case llvm::APFloatBase::S_BFloat:
1581 case llvm::APFloatBase::S_IEEEsingle:
1584 case llvm::APFloatBase::S_IEEEdouble:
1587 llvm_unreachable(
"unsupported floating point type");
1589 }
else if (val.isNaN()) {
1591 }
else if (val.isInfinity()) {
1592 if (val.isNegative())
1599 if (
auto fAttr = dyn_cast<FloatAttr>(attr)) {
1600 if (!isa<Float16Type, BFloat16Type, Float32Type, Float64Type>(
1603 loc,
"expected floating point attribute to be f16, bf16, f32 or f64");
1605 printFloat(fAttr.getValue());
1608 if (
auto dense = dyn_cast<DenseFPElementsAttr>(attr)) {
1609 if (!isa<Float16Type, BFloat16Type, Float32Type, Float64Type>(
1610 dense.getElementType())) {
1612 loc,
"expected floating point attribute to be f16, bf16, f32 or f64");
1615 interleaveComma(dense, os, [&](
const APFloat &val) { printFloat(val); });
1621 if (
auto iAttr = dyn_cast<IntegerAttr>(attr)) {
1622 if (
auto iType = dyn_cast<IntegerType>(iAttr.getType())) {
1623 printInt(iAttr.getValue(), shouldMapToUnsigned(iType.getSignedness()));
1626 if (
auto iType = dyn_cast<IndexType>(iAttr.getType())) {
1627 printInt(iAttr.getValue(),
false);
1631 if (
auto dense = dyn_cast<DenseIntElementsAttr>(attr)) {
1632 if (
auto iType = dyn_cast<IntegerType>(
1633 cast<ShapedType>(dense.getType()).getElementType())) {
1635 interleaveComma(dense, os, [&](
const APInt &val) {
1636 printInt(val, shouldMapToUnsigned(iType.getSignedness()));
1641 if (
auto iType = dyn_cast<IndexType>(
1642 cast<ShapedType>(dense.getType()).getElementType())) {
1644 interleaveComma(dense, os,
1645 [&](
const APInt &val) { printInt(val,
false); });
1652 if (
auto oAttr = dyn_cast<emitc::OpaqueAttr>(attr)) {
1653 os << oAttr.getValue();
1658 if (
auto sAttr = dyn_cast<SymbolRefAttr>(attr)) {
1659 if (sAttr.getNestedReferences().size() > 1)
1660 return emitError(loc,
"attribute has more than 1 nested reference");
1661 os << sAttr.getRootReference().getValue();
1666 if (
auto type = dyn_cast<TypeAttr>(attr))
1667 return emitType(loc, type.getValue());
1669 return emitError(loc,
"cannot emit attribute: ") << attr;
1672LogicalResult CppEmitter::emitExpression(Operation *op) {
1673 assert(emittedExpressionPrecedence.empty() &&
1674 "Expected precedence stack to be empty");
1675 Operation *rootOp =
nullptr;
1677 if (
auto expressionOp = dyn_cast<ExpressionOp>(op)) {
1678 rootOp = expressionOp.getRootOp();
1680 assert(cast<CExpressionInterface>(op).alwaysInline() &&
1681 "Expected an always-inline operation");
1683 "Expected operation to have no containing expression");
1689 pushExpressionPrecedence(precedence.value());
1691 if (
failed(emitOperation(*rootOp,
false)))
1694 popExpressionPrecedence();
1695 assert(emittedExpressionPrecedence.empty() &&
1696 "Expected precedence stack to be empty");
1701LogicalResult CppEmitter::emitOperand(Value value,
bool isInBrackets) {
1702 if (isPartOfCurrentExpression(value)) {
1704 assert(def &&
"Expected operand to be defined by an operation");
1705 if (
auto expressionOp = dyn_cast<ExpressionOp>(def))
1706 def = expressionOp.getRootOp();
1714 bool encloseInParenthesis =
1715 !isInBrackets && precedence.value() <= getExpressionPrecedence();
1717 if (encloseInParenthesis)
1719 pushExpressionPrecedence(precedence.value());
1721 if (
failed(emitOperation(*def,
false)))
1724 if (encloseInParenthesis)
1727 popExpressionPrecedence();
1732 return emitExpression(def);
1734 if (BlockArgument arg = dyn_cast<BlockArgument>(value)) {
1737 Operation *argOp = arg.getParentBlock()->getParentOp();
1738 if (
auto expressionOp = dyn_cast<ExpressionOp>(argOp))
1739 return emitOperand(expressionOp->getOperand(arg.getArgNumber()));
1742 os << getOrCreateName(value);
1746LogicalResult CppEmitter::emitOperands(Operation &op) {
1750 return emitOperand(operand, true);
1755CppEmitter::emitOperandsAndAttributes(Operation &op,
1756 ArrayRef<StringRef> exclude) {
1757 if (
failed(emitOperands(op)))
1761 for (NamedAttribute attr : op.
getAttrs()) {
1762 if (!llvm::is_contained(exclude, attr.getName().strref())) {
1769 auto emitNamedAttribute = [&](NamedAttribute attr) -> LogicalResult {
1770 if (llvm::is_contained(exclude, attr.getName().strref()))
1772 os <<
"/* " << attr.getName().getValue() <<
" */";
1773 if (
failed(emitAttribute(op.
getLoc(), attr.getValue())))
1780LogicalResult CppEmitter::emitVariableAssignment(OpResult
result) {
1781 if (!hasValueInScope(
result)) {
1782 return result.getDefiningOp()->emitOpError(
1783 "result variable for the operation has not been declared");
1785 os << getOrCreateName(
result) <<
" = ";
1789LogicalResult CppEmitter::emitVariableDeclaration(OpResult
result,
1790 bool trailingSemicolon) {
1791 if (
auto cExpression =
1792 dyn_cast<CExpressionInterface>(
result.getDefiningOp())) {
1793 if (cExpression.alwaysInline())
1796 if (hasValueInScope(
result)) {
1797 return result.getDefiningOp()->emitError(
1798 "result variable for the operation already declared");
1800 if (
failed(emitVariableDeclaration(
result.getOwner()->getLoc(),
1802 getOrCreateName(
result))))
1804 if (trailingSemicolon)
1809LogicalResult CppEmitter::emitGlobalVariable(GlobalOp op) {
1810 if (op.getExternSpecifier())
1812 else if (op.getStaticSpecifier())
1814 if (op.getConstSpecifier())
1817 if (
failed(emitVariableDeclaration(op->getLoc(), op.getType(),
1818 op.getSymName()))) {
1822 std::optional<Attribute> initialValue = op.getInitialValue();
1825 if (
failed(emitAttribute(op->getLoc(), *initialValue)))
1833LogicalResult CppEmitter::emitAssignPrefix(Operation &op) {
1835 if (isEmittingExpression())
1843 if (shouldDeclareVariablesAtTop()) {
1854 if (!shouldDeclareVariablesAtTop()) {
1862 [&](Value
result) { os << getOrCreateName(result); });
1868LogicalResult CppEmitter::emitLabel(
Block &block) {
1869 if (!hasBlockLabel(block))
1873 os.getOStream() << getOrCreateName(block) <<
":\n";
1877LogicalResult CppEmitter::emitOperation(Operation &op,
bool trailingSemicolon) {
1878 LogicalResult status =
1879 llvm::TypeSwitch<Operation *, LogicalResult>(&op)
1883 .Case<cf::BranchOp, cf::CondBranchOp>(
1886 .Case<emitc::AddressOfOp, emitc::AddOp, emitc::AssignOp,
1887 emitc::BitwiseAndOp, emitc::BitwiseLeftShiftOp,
1888 emitc::BitwiseNotOp, emitc::BitwiseOrOp,
1889 emitc::BitwiseRightShiftOp, emitc::BitwiseXorOp, emitc::CallOp,
1890 emitc::CallOpaqueOp, emitc::CastOp, emitc::ClassOp,
1891 emitc::CmpOp, emitc::ConditionalOp, emitc::ConstantOp,
1892 emitc::DeclareFuncOp, emitc::DereferenceOp, emitc::DivOp,
1893 emitc::DoOp, emitc::ExpressionOp, emitc::FieldOp, emitc::FileOp,
1894 emitc::ForOp, emitc::FuncOp, emitc::GetFieldOp,
1895 emitc::GetGlobalOp, emitc::GlobalOp, emitc::IfOp,
1896 emitc::IncludeOp, emitc::LiteralOp, emitc::LoadOp,
1897 emitc::LogicalAndOp, emitc::LogicalNotOp, emitc::LogicalOrOp,
1898 emitc::MemberCallOpaqueOp, emitc::MemberOfPtrOp,
1899 emitc::MemberOp, emitc::MulOp, emitc::RemOp, emitc::ReturnOp,
1900 emitc::SubscriptOp, emitc::SubOp, emitc::SwitchOp,
1901 emitc::UnaryMinusOp, emitc::UnaryPlusOp, emitc::VariableOp,
1906 .Case<func::CallOp, func::FuncOp, func::ReturnOp>(
1908 .Default([&](Operation *) {
1909 return op.emitOpError(
"unable to find printer for op");
1915 if (
auto cExpression = dyn_cast<CExpressionInterface>(op)) {
1916 if (cExpression.alwaysInline())
1920 if (isEmittingExpression() ||
1921 (isa<emitc::ExpressionOp>(op) &&
1927 trailingSemicolon &=
1928 !isa<cf::CondBranchOp, emitc::DeclareFuncOp, emitc::DoOp, emitc::FileOp,
1929 emitc::ForOp, emitc::IfOp, emitc::IncludeOp, emitc::SwitchOp,
1930 emitc::VerbatimOp>(op);
1932 os << (trailingSemicolon ?
";\n" :
"\n");
1937LogicalResult CppEmitter::emitVariableDeclaration(Location loc, Type type,
1939 if (
auto arrType = dyn_cast<emitc::ArrayType>(type)) {
1940 if (
failed(emitType(loc, arrType.getElementType())))
1943 for (
auto dim : arrType.getShape()) {
1944 os <<
"[" << dim <<
"]";
1948 if (
failed(emitType(loc, type)))
1954LogicalResult CppEmitter::emitType(Location loc, Type type) {
1955 if (
auto iType = dyn_cast<IntegerType>(type)) {
1956 switch (iType.getWidth()) {
1958 return (os <<
"bool"),
success();
1963 if (shouldMapToUnsigned(iType.getSignedness()))
1964 return (os <<
"uint" << iType.getWidth() <<
"_t"),
success();
1966 return (os <<
"int" << iType.getWidth() <<
"_t"),
success();
1968 return emitError(loc,
"cannot emit integer type ") << type;
1971 if (
auto fType = dyn_cast<FloatType>(type)) {
1972 switch (fType.getWidth()) {
1974 if (llvm::isa<Float16Type>(type))
1975 return (os <<
"_Float16"),
success();
1976 if (llvm::isa<BFloat16Type>(type))
1977 return (os <<
"__bf16"),
success();
1979 return emitError(loc,
"cannot emit float type ") << type;
1982 return (os <<
"float"),
success();
1984 return (os <<
"double"),
success();
1986 return emitError(loc,
"cannot emit float type ") << type;
1989 if (
auto iType = dyn_cast<IndexType>(type))
1990 return (os <<
"size_t"),
success();
1991 if (
auto sType = dyn_cast<emitc::SizeTType>(type))
1992 return (os <<
"size_t"),
success();
1993 if (
auto sType = dyn_cast<emitc::SignedSizeTType>(type))
1994 return (os <<
"ssize_t"),
success();
1995 if (
auto pType = dyn_cast<emitc::PtrDiffTType>(type))
1996 return (os <<
"ptrdiff_t"),
success();
1997 if (
auto tType = dyn_cast<TensorType>(type)) {
1998 if (!tType.hasRank())
1999 return emitError(loc,
"cannot emit unranked tensor type");
2000 if (!tType.hasStaticShape())
2001 return emitError(loc,
"cannot emit tensor type with non static shape");
2003 if (isa<ArrayType>(tType.getElementType()))
2004 return emitError(loc,
"cannot emit tensor of array type ") << type;
2005 if (
failed(emitType(loc, tType.getElementType())))
2007 auto shape = tType.getShape();
2008 for (
auto dimSize : shape) {
2015 if (
auto tType = dyn_cast<TupleType>(type))
2016 return emitTupleType(loc, tType.getTypes());
2017 if (
auto oType = dyn_cast<emitc::OpaqueType>(type)) {
2018 os << oType.getValue();
2021 if (
auto aType = dyn_cast<emitc::ArrayType>(type)) {
2022 if (
failed(emitType(loc, aType.getElementType())))
2024 for (
auto dim : aType.getShape())
2025 os <<
"[" << dim <<
"]";
2028 if (
auto lType = dyn_cast<emitc::LValueType>(type))
2029 return emitType(loc, lType.getValueType());
2030 if (
auto pType = dyn_cast<emitc::PointerType>(type)) {
2031 if (isa<ArrayType>(pType.getPointee()))
2032 return emitError(loc,
"cannot emit pointer to array type ") << type;
2033 if (
failed(emitType(loc, pType.getPointee())))
2038 return emitError(loc,
"cannot emit type ") << type;
2041LogicalResult CppEmitter::emitTypes(Location loc, ArrayRef<Type> types) {
2042 switch (types.size()) {
2047 return emitType(loc, types.front());
2049 return emitTupleType(loc, types);
2053LogicalResult CppEmitter::emitTupleType(Location loc, ArrayRef<Type> types) {
2054 if (llvm::any_of(types, llvm::IsaPred<ArrayType>)) {
2055 return emitError(loc,
"cannot emit tuple of array type");
2057 os <<
"std::tuple<";
2059 types, os, [&](Type type) {
return emitType(loc, type); })))
2065void CppEmitter::resetValueCounter() { valueCount = 0; }
2067void CppEmitter::increaseLoopNestingLevel() { loopNestingLevel++; }
2069void CppEmitter::decreaseLoopNestingLevel() { loopNestingLevel--; }
2072 bool declareVariablesAtTop,
2074 CppEmitter emitter(os, declareVariablesAtTop, fileId);
2075 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 LogicalResult printOpaqueCallCommon(CppEmitter &emitter, OpTy op, StringRef callee, std::optional< ArrayAttr > templateArgs, std::optional< ArrayAttr > args, bool isMemberCall, Value receiver=nullptr)
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.