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::LiteralOp op) {
return 18; })
107 .Case([&](emitc::LoadOp op) {
return 16; })
108 .Case([&](emitc::LogicalAndOp op) {
return 4; })
109 .Case([&](emitc::LogicalNotOp op) {
return 15; })
110 .Case([&](emitc::LogicalOrOp op) {
return 3; })
111 .Case([&](emitc::MemberOfPtrOp op) {
return 17; })
112 .Case([&](emitc::MemberOp op) {
return 17; })
113 .Case([&](emitc::MulOp op) {
return 13; })
114 .Case([&](emitc::RemOp op) {
return 13; })
115 .Case([&](emitc::SubOp op) {
return 12; })
116 .Case([&](emitc::SubscriptOp op) {
return 17; })
117 .Case([&](emitc::UnaryMinusOp op) {
return 15; })
118 .Case([&](emitc::UnaryPlusOp op) {
return 15; })
119 .Default([](
auto op) {
return op->emitError(
"unsupported operation"); });
127 explicit CppEmitter(raw_ostream &os,
bool declareVariablesAtTop,
131 LogicalResult emitAttribute(Location loc, Attribute attr);
138 LogicalResult emitOperation(Operation &op,
bool trailingSemicolon);
141 LogicalResult emitType(Location loc, Type type);
147 LogicalResult emitTypes(Location loc, ArrayRef<Type> types);
151 LogicalResult emitTupleType(Location loc, ArrayRef<Type> types);
154 LogicalResult emitVariableAssignment(OpResult
result);
157 LogicalResult emitVariableDeclaration(OpResult
result,
158 bool trailingSemicolon);
161 LogicalResult emitVariableDeclaration(Location loc, Type type,
170 LogicalResult emitAssignPrefix(Operation &op);
173 LogicalResult emitGlobalVariable(GlobalOp op);
176 LogicalResult emitLabel(
Block &block);
180 LogicalResult emitOperandsAndAttributes(Operation &op,
181 ArrayRef<StringRef> exclude = {});
184 LogicalResult emitOperands(Operation &op);
190 LogicalResult emitOperand(Value value,
bool isInBrackets =
false);
193 LogicalResult emitExpression(Operation *op);
196 StringRef getOrCreateName(Value val);
200 StringRef getOrCreateInductionVarName(Value val);
203 StringRef getOrCreateName(
Block &block);
205 LogicalResult emitInlinedExpression(Value value);
208 bool shouldMapToUnsigned(IntegerType::SignednessSemantics val);
212 ~Scope() { emitter.labelInScopeCount.pop(); }
215 llvm::ScopedHashTableScope<Value, std::string> valueMapperScope;
216 llvm::ScopedHashTableScope<Block *, std::string> blockMapperScope;
219 Scope(CppEmitter &emitter)
220 : valueMapperScope(emitter.valueMapper),
221 blockMapperScope(emitter.blockMapper), emitter(emitter) {
222 emitter.labelInScopeCount.push(emitter.labelInScopeCount.top());
229 struct FunctionScope : Scope {
230 FunctionScope(CppEmitter &emitter) : Scope(emitter) {
232 emitter.resetValueCounter();
238 struct LoopScope : Scope {
239 LoopScope(CppEmitter &emitter) : Scope(emitter) {
240 emitter.increaseLoopNestingLevel();
242 ~LoopScope() { emitter.decreaseLoopNestingLevel(); }
246 bool hasValueInScope(Value val);
249 bool hasBlockLabel(
Block &block);
252 raw_indented_ostream &ostream() {
return os; };
256 bool shouldDeclareVariablesAtTop() {
return declareVariablesAtTop; };
259 bool shouldEmitFile(FileOp file) {
260 return !fileId.empty() && file.getId() == fileId;
264 bool isEmittingExpression() {
return !emittedExpressionPrecedence.empty(); }
268 bool isPartOfCurrentExpression(Value value) {
270 return def ? isPartOfCurrentExpression(def) :
false;
275 bool isPartOfCurrentExpression(Operation *def) {
280 void resetValueCounter();
283 void increaseLoopNestingLevel();
286 void decreaseLoopNestingLevel();
289 using ValueMapper = llvm::ScopedHashTable<Value, std::string>;
290 using BlockMapper = llvm::ScopedHashTable<Block *, std::string>;
293 raw_indented_ostream os;
298 bool declareVariablesAtTop;
304 ValueMapper valueMapper;
307 BlockMapper blockMapper;
310 llvm::ScopedHashTableScope<Value, std::string> defaultValueMapperScope;
311 llvm::ScopedHashTableScope<Block *, std::string> defaultBlockMapperScope;
313 std::stack<int64_t> labelInScopeCount;
317 uint64_t loopNestingLevel{0};
320 unsigned int valueCount{0};
323 SmallVector<int> emittedExpressionPrecedence;
325 void pushExpressionPrecedence(
int precedence) {
326 emittedExpressionPrecedence.push_back(precedence);
328 void popExpressionPrecedence() { emittedExpressionPrecedence.pop_back(); }
329 static int lowestPrecedence() {
return 0; }
330 int getExpressionPrecedence() {
331 if (emittedExpressionPrecedence.empty())
332 return lowestPrecedence();
333 return emittedExpressionPrecedence.back();
346 if (
auto cExpression = dyn_cast<CExpressionInterface>(op))
347 return cExpression.alwaysInline() || isa<ExpressionOp>(op->
getParentOp());
350 ExpressionOp expressionOp = dyn_cast<ExpressionOp>(op);
355 if (cast<CExpressionInterface>(expressionOp.getRootOp()).alwaysInline())
359 if (expressionOp.getDoNotInline())
372 if (isa<emitc::ExpressionOp, emitc::CExpressionInterface>(*user))
376 if (!expressionOp.hasSideEffects())
387 if (isa<emitc::IfOp, emitc::SwitchOp, emitc::ReturnOp>(user))
392 if (
auto assignOp = dyn_cast<emitc::AssignOp>(user)) {
394 if (expressionOp.getResult() == assignOp.getValue() &&
395 isa_and_present<VariableOp>(assignOp.getVar().getDefiningOp()))
406 while (
auto subscriptOp = value.
getDefiningOp<emitc::SubscriptOp>()) {
407 value = subscriptOp.getValue();
410 auto getGlobalOp = value.
getDefiningOp<emitc::GetGlobalOp>();
416 fromOp, getGlobalOp.getNameAttr());
418 if (globalOp && globalOp.getConstSpecifier())
433 if (failed(emitter.emitOperand(operand)))
440 emitc::DereferenceOp dereferenceOp) {
442 Operation &op = *dereferenceOp.getOperation();
444 if (failed(emitter.emitAssignPrefix(op)))
447 return emitter.emitOperand(dereferenceOp.getPointer());
451 emitc::GetFieldOp getFieldOp) {
452 if (!emitter.isPartOfCurrentExpression(getFieldOp.getOperation()))
455 emitter.ostream() << getFieldOp.getFieldName();
460 emitc::GetGlobalOp getGlobalOp) {
461 if (!emitter.isPartOfCurrentExpression(getGlobalOp.getOperation()))
464 emitter.ostream() << getGlobalOp.getName();
469 emitc::LiteralOp literalOp) {
470 if (!emitter.isPartOfCurrentExpression(literalOp.getOperation()))
473 emitter.ostream() << literalOp.getValue();
478 emitc::MemberOp memberOp) {
479 if (memberOp.alwaysInline()) {
480 if (!emitter.isPartOfCurrentExpression(memberOp.getOperation()))
483 if (failed(emitter.emitAssignPrefix(*memberOp.getOperation())))
486 if (failed(emitter.emitOperand(memberOp.getOperand())))
488 emitter.ostream() <<
"." << memberOp.getMember();
493 emitc::MemberOfPtrOp memberOfPtrOp) {
494 if (!emitter.isPartOfCurrentExpression(memberOfPtrOp.getOperation()))
497 if (failed(emitter.emitOperand(memberOfPtrOp.getOperand())))
499 emitter.ostream() <<
"->" << memberOfPtrOp.getMember();
504 emitc::SubscriptOp subscriptOp) {
505 if (!emitter.isPartOfCurrentExpression(subscriptOp.getOperation())) {
510 if (failed(emitter.emitOperand(subscriptOp.getValue())))
512 for (
auto index : subscriptOp.getIndices()) {
514 if (failed(emitter.emitOperand(
index,
true)))
527 if (emitter.shouldDeclareVariablesAtTop()) {
529 if (
auto oAttr = dyn_cast<emitc::OpaqueAttr>(value)) {
530 if (oAttr.getValue().empty())
534 if (failed(emitter.emitVariableAssignment(
result)))
536 return emitter.emitAttribute(operation->
getLoc(), value);
540 if (
auto oAttr = dyn_cast<emitc::OpaqueAttr>(value)) {
541 if (oAttr.getValue().empty())
543 return emitter.emitVariableDeclaration(
result,
548 if (failed(emitter.emitAssignPrefix(*operation)))
550 return emitter.emitAttribute(operation->
getLoc(), value);
554 emitc::AddressOfOp addressOfOp) {
556 Operation &op = *addressOfOp.getOperation();
558 if (failed(emitter.emitAssignPrefix(op)))
561 Value operand = addressOfOp.getReference();
568 return emitter.emitOperand(operand);
572 emitc::ConstantOp constantOp) {
573 Operation *operation = constantOp.getOperation();
576 if (emitter.isPartOfCurrentExpression(operation))
577 return emitter.emitAttribute(operation->
getLoc(), value);
583 emitc::VariableOp variableOp) {
584 Operation *operation = variableOp.getOperation();
591 emitc::GlobalOp globalOp) {
593 return emitter.emitGlobalVariable(globalOp);
597 emitc::AssignOp assignOp) {
598 if (failed(emitter.emitOperand(assignOp.getVar())))
601 emitter.ostream() <<
" = ";
603 return emitter.emitOperand(assignOp.getValue());
607 if (failed(emitter.emitAssignPrefix(*loadOp)))
610 return emitter.emitOperand(loadOp.getOperand());
615 StringRef binaryOperator) {
618 if (failed(emitter.emitAssignPrefix(*operation)))
621 if (failed(emitter.emitOperand(operation->
getOperand(0))))
624 os <<
" " << binaryOperator <<
" ";
626 if (failed(emitter.emitOperand(operation->
getOperand(1))))
634 StringRef unaryOperator) {
637 if (failed(emitter.emitAssignPrefix(*operation)))
642 if (failed(emitter.emitOperand(operation->
getOperand(0))))
649 Operation *operation = addOp.getOperation();
655 Operation *operation = divOp.getOperation();
661 Operation *operation = mulOp.getOperation();
667 Operation *operation = remOp.getOperation();
673 Operation *operation = subOp.getOperation();
681 std::next(iteratorOp) != end; ++iteratorOp) {
682 if (failed(emitter.emitOperation(*iteratorOp,
true)))
690 emitc::SwitchOp switchOp) {
694 if (failed(emitter.emitOperand(switchOp.getArg())))
698 for (
auto pair : llvm::zip(switchOp.getCases(), switchOp.getCaseRegions())) {
699 os <<
"\ncase " << std::get<0>(pair) <<
": {\n";
708 os <<
"\ndefault: {\n";
711 if (failed(
emitSwitchCase(emitter, os, switchOp.getDefaultRegion())))
724 Block &bodyBlock = doOp.getBodyRegion().
front();
726 if (failed(emitter.emitOperation(op,
true)))
732 Block &condBlock = doOp.getConditionRegion().
front();
733 auto condYield = cast<emitc::YieldOp>(condBlock.
back());
734 if (failed(emitter.emitExpression(
735 cast<emitc::ExpressionOp>(condYield.getOperand(0).getDefiningOp()))))
743 Operation *operation = cmpOp.getOperation();
745 StringRef binaryOperator;
747 switch (cmpOp.getPredicate()) {
748 case emitc::CmpPredicate::eq:
749 binaryOperator =
"==";
751 case emitc::CmpPredicate::ne:
752 binaryOperator =
"!=";
754 case emitc::CmpPredicate::lt:
755 binaryOperator =
"<";
757 case emitc::CmpPredicate::le:
758 binaryOperator =
"<=";
760 case emitc::CmpPredicate::gt:
761 binaryOperator =
">";
763 case emitc::CmpPredicate::ge:
764 binaryOperator =
">=";
766 case emitc::CmpPredicate::three_way:
767 binaryOperator =
"<=>";
775 emitc::ConditionalOp conditionalOp) {
778 if (failed(emitter.emitAssignPrefix(*conditionalOp)))
781 if (failed(emitter.emitOperand(conditionalOp.getCondition())))
786 if (failed(emitter.emitOperand(conditionalOp.getTrueValue())))
791 if (failed(emitter.emitOperand(conditionalOp.getFalseValue())))
798 emitc::VerbatimOp verbatimOp) {
801 FailureOr<SmallVector<ReplacementItem>> items =
802 verbatimOp.parseFormatString();
806 auto fmtArg = verbatimOp.getFmtArgs().begin();
809 if (
auto *str = std::get_if<StringRef>(&item)) {
812 if (failed(emitter.emitOperand(*fmtArg++)))
821 cf::BranchOp branchOp) {
826 llvm::zip(branchOp.getOperands(), successor.
getArguments())) {
827 Value &operand = std::get<0>(pair);
829 os << emitter.getOrCreateName(argument) <<
" = "
830 << emitter.getOrCreateName(operand) <<
";\n";
834 if (!(emitter.hasBlockLabel(successor)))
835 return branchOp.emitOpError(
"unable to find label for successor block");
836 os << emitter.getOrCreateName(successor);
841 cf::CondBranchOp condBranchOp) {
843 Block &trueSuccessor = *condBranchOp.getTrueDest();
844 Block &falseSuccessor = *condBranchOp.getFalseDest();
847 if (failed(emitter.emitOperand(condBranchOp.getCondition())))
854 for (
auto pair : llvm::zip(condBranchOp.getTrueOperands(),
856 Value &operand = std::get<0>(pair);
858 os << emitter.getOrCreateName(argument) <<
" = "
859 << emitter.getOrCreateName(operand) <<
";\n";
863 if (!(emitter.hasBlockLabel(trueSuccessor))) {
864 return condBranchOp.emitOpError(
"unable to find label for successor block");
866 os << emitter.getOrCreateName(trueSuccessor) <<
";\n";
870 for (
auto pair : llvm::zip(condBranchOp.getFalseOperands(),
872 Value &operand = std::get<0>(pair);
874 os << emitter.getOrCreateName(argument) <<
" = "
875 << emitter.getOrCreateName(operand) <<
";\n";
879 if (!(emitter.hasBlockLabel(falseSuccessor))) {
880 return condBranchOp.emitOpError()
881 <<
"unable to find label for successor block";
883 os << emitter.getOrCreateName(falseSuccessor) <<
";\n";
890 if (failed(emitter.emitAssignPrefix(*callOp)))
895 if (failed(emitter.emitOperands(*callOp)))
902 Operation *operation = callOp.getOperation();
903 StringRef callee = callOp.getCallee();
909 Operation *operation = callOp.getOperation();
910 StringRef callee = callOp.getCallee();
915template <
typename OpTy>
918 std::optional<ArrayAttr> templateArgs,
919 std::optional<ArrayAttr> args,
bool isMemberCall,
920 Value receiver =
nullptr) {
923 if (failed(emitter.emitAssignPrefix(*op.getOperation())))
927 assert(receiver &&
"Expected receiver for member call");
928 if (failed(emitter.emitOperand(receiver)))
931 if (llvm::isa<emitc::PointerType>(receiver.getType()))
943 auto emitTemplateArgs = [&](
Attribute attr) -> LogicalResult {
944 return emitter.emitAttribute(op.getLoc(), attr);
954 auto emitArgs = [&](
Attribute attr) -> LogicalResult {
955 if (
auto t = dyn_cast<IntegerAttr>(attr)) {
956 if (t.getType().isIndex()) {
958 Value operand = op.getArgOperands()[idx];
959 return emitter.emitOperand(operand,
false);
962 if (failed(emitter.emitAttribute(op.getLoc(), attr)))
970 LogicalResult emittedArgs =
success();
976 return emitter.emitOperand(operand, true);
979 if (failed(emittedArgs))
986 emitc::CallOpaqueOp callOpaqueOp) {
988 callOpaqueOp.getTemplateArgs(),
989 callOpaqueOp.getArgs(),
995 emitc::MemberCallOpaqueOp memberCallOpaqueOp) {
997 emitter, memberCallOpaqueOp, memberCallOpaqueOp.getCallee(),
998 memberCallOpaqueOp.getTemplateArgs(), memberCallOpaqueOp.getArgs(),
999 true, memberCallOpaqueOp.getReceiver());
1003 emitc::BitwiseAndOp bitwiseAndOp) {
1004 Operation *operation = bitwiseAndOp.getOperation();
1010 emitc::BitwiseLeftShiftOp bitwiseLeftShiftOp) {
1011 Operation *operation = bitwiseLeftShiftOp.getOperation();
1016 emitc::BitwiseNotOp bitwiseNotOp) {
1017 Operation *operation = bitwiseNotOp.getOperation();
1022 emitc::BitwiseOrOp bitwiseOrOp) {
1023 Operation *operation = bitwiseOrOp.getOperation();
1029 emitc::BitwiseRightShiftOp bitwiseRightShiftOp) {
1030 Operation *operation = bitwiseRightShiftOp.getOperation();
1035 emitc::BitwiseXorOp bitwiseXorOp) {
1036 Operation *operation = bitwiseXorOp.getOperation();
1041 emitc::UnaryPlusOp unaryPlusOp) {
1042 Operation *operation = unaryPlusOp.getOperation();
1047 emitc::UnaryMinusOp unaryMinusOp) {
1048 Operation *operation = unaryMinusOp.getOperation();
1056 if (failed(emitter.emitAssignPrefix(op)))
1062 return emitter.emitOperand(castOp.getOperand());
1066 emitc::ExpressionOp expressionOp) {
1070 Operation &op = *expressionOp.getOperation();
1072 if (failed(emitter.emitAssignPrefix(op)))
1075 return emitter.emitExpression(expressionOp);
1079 emitc::IncludeOp includeOp) {
1083 if (includeOp.getIsStandardInclude())
1084 os <<
"<" << includeOp.getInclude() <<
">";
1086 os <<
"\"" << includeOp.getInclude() <<
"\"";
1092 emitc::LogicalAndOp logicalAndOp) {
1093 Operation *operation = logicalAndOp.getOperation();
1098 emitc::LogicalNotOp logicalNotOp) {
1099 Operation *operation = logicalNotOp.getOperation();
1104 emitc::LogicalOrOp logicalOrOp) {
1105 Operation *operation = logicalOrOp.getOperation();
1115 auto requiresParentheses = [&](
Value value) {
1124 emitter.emitType(forOp.getLoc(), forOp.getInductionVar().getType())))
1127 os << emitter.getOrCreateInductionVarName(forOp.getInductionVar());
1129 if (failed(emitter.emitOperand(forOp.getLowerBound())))
1132 os << emitter.getOrCreateInductionVarName(forOp.getInductionVar());
1134 Value upperBound = forOp.getUpperBound();
1135 bool upperBoundRequiresParentheses = requiresParentheses(upperBound);
1136 if (upperBoundRequiresParentheses)
1138 if (failed(emitter.emitOperand(upperBound)))
1140 if (upperBoundRequiresParentheses)
1143 os << emitter.getOrCreateInductionVarName(forOp.getInductionVar());
1145 if (failed(emitter.emitOperand(forOp.getStep())))
1150 CppEmitter::LoopScope lScope(emitter);
1152 Region &forRegion = forOp.getRegion();
1153 auto regionOps = forRegion.
getOps();
1156 for (
auto it = regionOps.begin(); std::next(it) != regionOps.end(); ++it) {
1157 if (failed(emitter.emitOperation(*it,
true)))
1171 auto emitAllExceptLast = [&emitter](
Region ®ion) {
1173 for (; std::next(it) != end; ++it) {
1174 if (failed(emitter.emitOperation(*it,
true)))
1177 assert(isa<emitc::YieldOp>(*it) &&
1178 "Expected last operation in the region to be emitc::yield");
1183 if (failed(emitter.emitOperand(ifOp.getCondition())))
1187 if (failed(emitAllExceptLast(ifOp.getThenRegion())))
1191 Region &elseRegion = ifOp.getElseRegion();
1192 if (!elseRegion.
empty()) {
1195 if (failed(emitAllExceptLast(elseRegion)))
1204 func::ReturnOp returnOp) {
1207 switch (returnOp.getNumOperands()) {
1212 if (failed(emitter.emitOperand(returnOp.getOperand(0))))
1216 os <<
" std::make_tuple(";
1217 if (failed(emitter.emitOperandsAndAttributes(*returnOp.getOperation())))
1225 emitc::ReturnOp returnOp) {
1228 if (returnOp.getNumOperands() == 0)
1232 if (failed(emitter.emitOperand(returnOp.getOperand())))
1239 if (failed(emitter.emitOperation(op,
false)))
1247 ClassType classType = classOp.getClassType();
1248 os << stringifyClassType(classType) <<
" " << classOp.getSymName();
1249 if (classOp.getFinalSpecifier())
1253 if (classType == ClassType::class_)
1259 if (failed(emitter.emitOperation(op,
false)))
1270 if (failed(emitter.emitVariableDeclaration(
1271 fieldOp->getLoc(), fieldOp.getType(), fieldOp.getSymName())))
1273 std::optional<Attribute> initialValue = fieldOp.getInitialValue();
1276 if (failed(emitter.emitAttribute(fieldOp->getLoc(), *initialValue)))
1285 if (!emitter.shouldEmitFile(file))
1289 if (failed(emitter.emitOperation(op,
false)))
1302 return emitter.emitType(functionOp->
getLoc(), arg);
1313 return emitter.emitVariableDeclaration(
1314 functionOp->
getLoc(), arg.
getType(), emitter.getOrCreateName(arg));
1324 if (emitter.shouldDeclareVariablesAtTop()) {
1329 if (isa<emitc::ExpressionOp>(op->
getParentOp()) ||
1330 (isa<emitc::ExpressionOp>(op) &&
1334 if (failed(emitter.emitVariableDeclaration(
1337 op->
emitError(
"unable to declare result variable for op"));
1342 if (
result.wasInterrupted())
1347 for (
Block &block : blocks) {
1348 emitter.getOrCreateName(block);
1352 for (
Block &block : llvm::drop_begin(blocks)) {
1354 if (emitter.hasValueInScope(arg))
1355 return functionOp->
emitOpError(
" block argument #")
1356 << arg.getArgNumber() <<
" is out of scope";
1357 if (isa<ArrayType, LValueType>(arg.getType()))
1358 return functionOp->
emitOpError(
"cannot emit block argument #")
1359 << arg.getArgNumber() <<
" with type " << arg.getType();
1361 emitter.emitType(block.getParentOp()->getLoc(), arg.getType()))) {
1364 os <<
" " << emitter.getOrCreateName(arg) <<
";\n";
1368 for (
Block &block : blocks) {
1370 if (!block.hasNoPredecessors()) {
1371 if (failed(emitter.emitLabel(block)))
1374 for (
Operation &op : block.getOperations()) {
1375 if (failed(emitter.emitOperation(op,
true)))
1386 func::FuncOp functionOp) {
1388 if (!emitter.shouldDeclareVariablesAtTop() &&
1389 functionOp.getBlocks().size() > 1) {
1390 return functionOp.emitOpError(
1391 "with multiple blocks needs variables declared at top");
1394 if (llvm::any_of(functionOp.getArgumentTypes(), llvm::IsaPred<LValueType>)) {
1395 return functionOp.emitOpError()
1396 <<
"cannot emit lvalue type as argument type";
1399 if (llvm::any_of(functionOp.getResultTypes(), llvm::IsaPred<ArrayType>)) {
1400 return functionOp.emitOpError() <<
"cannot emit array type as result type";
1403 CppEmitter::FunctionScope scope(emitter);
1405 if (failed(emitter.emitTypes(functionOp.getLoc(),
1406 functionOp.getFunctionType().getResults())))
1408 os <<
" " << functionOp.getName();
1411 Operation *operation = functionOp.getOperation();
1423 emitc::FuncOp functionOp) {
1425 if (!emitter.shouldDeclareVariablesAtTop() &&
1426 functionOp.getBlocks().size() > 1) {
1427 return functionOp.emitOpError(
1428 "with multiple blocks needs variables declared at top");
1431 CppEmitter::FunctionScope scope(emitter);
1433 if (functionOp.getSpecifiers()) {
1434 for (
Attribute specifier : functionOp.getSpecifiersAttr()) {
1435 os << cast<StringAttr>(specifier).str() <<
" ";
1439 if (failed(emitter.emitTypes(functionOp.getLoc(),
1440 functionOp.getFunctionType().getResults())))
1442 os <<
" " << functionOp.getName();
1445 Operation *operation = functionOp.getOperation();
1446 if (functionOp.isExternal()) {
1448 functionOp.getArgumentTypes())))
1464 DeclareFuncOp declareFuncOp) {
1467 CppEmitter::FunctionScope scope(emitter);
1469 declareFuncOp, declareFuncOp.getSymNameAttr());
1474 if (functionOp.getSpecifiers()) {
1475 for (
Attribute specifier : functionOp.getSpecifiersAttr()) {
1476 os << cast<StringAttr>(specifier).str() <<
" ";
1480 if (failed(emitter.emitTypes(functionOp.getLoc(),
1481 functionOp.getFunctionType().getResults())))
1483 os <<
" " << functionOp.getName();
1486 Operation *operation = functionOp.getOperation();
1494CppEmitter::CppEmitter(
raw_ostream &os,
bool declareVariablesAtTop,
1496 : os(os), declareVariablesAtTop(declareVariablesAtTop),
1497 fileId(fileId.str()), defaultValueMapperScope(valueMapper),
1498 defaultBlockMapperScope(blockMapper) {
1499 labelInScopeCount.push(0);
1503StringRef CppEmitter::getOrCreateName(
Value val) {
1504 if (!valueMapper.count(val)) {
1505 valueMapper.insert(val, formatv(
"v{0}", ++valueCount));
1507 return *valueMapper.begin(val);
1512StringRef CppEmitter::getOrCreateInductionVarName(Value val) {
1513 if (!valueMapper.count(val)) {
1515 int64_t identifier =
'i' + loopNestingLevel;
1517 if (identifier >=
'i' && identifier <=
't') {
1518 valueMapper.insert(val,
1519 formatv(
"{0}{1}", (
char)identifier, ++valueCount));
1522 valueMapper.insert(val, formatv(
"u{0}", ++valueCount));
1525 return *valueMapper.begin(val);
1529StringRef CppEmitter::getOrCreateName(
Block &block) {
1530 if (!blockMapper.count(&block))
1531 blockMapper.insert(&block, formatv(
"label{0}", ++labelInScopeCount.top()));
1532 return *blockMapper.begin(&block);
1535bool CppEmitter::shouldMapToUnsigned(IntegerType::SignednessSemantics val) {
1537 case IntegerType::Signless:
1539 case IntegerType::Signed:
1541 case IntegerType::Unsigned:
1544 llvm_unreachable(
"Unexpected IntegerType::SignednessSemantics");
1547bool CppEmitter::hasValueInScope(Value val) {
return valueMapper.count(val); }
1549bool CppEmitter::hasBlockLabel(
Block &block) {
1550 return blockMapper.count(&block);
1553LogicalResult CppEmitter::emitAttribute(Location loc, Attribute attr) {
1554 auto printInt = [&](
const APInt &val,
bool isUnsigned) {
1555 if (val.getBitWidth() == 1) {
1556 if (val.getBoolValue())
1561 SmallString<128> strValue;
1562 val.toString(strValue, 10, !isUnsigned,
false);
1567 auto printFloat = [&](
const APFloat &val) {
1568 if (val.isFinite()) {
1569 SmallString<128> strValue;
1571 val.toString(strValue, 0, 0,
false);
1573 switch (llvm::APFloatBase::SemanticsToEnum(val.getSemantics())) {
1574 case llvm::APFloatBase::S_IEEEhalf:
1577 case llvm::APFloatBase::S_BFloat:
1580 case llvm::APFloatBase::S_IEEEsingle:
1583 case llvm::APFloatBase::S_IEEEdouble:
1586 llvm_unreachable(
"unsupported floating point type");
1588 }
else if (val.isNaN()) {
1590 }
else if (val.isInfinity()) {
1591 if (val.isNegative())
1598 if (
auto fAttr = dyn_cast<FloatAttr>(attr)) {
1599 if (!isa<Float16Type, BFloat16Type, Float32Type, Float64Type>(
1602 loc,
"expected floating point attribute to be f16, bf16, f32 or f64");
1604 printFloat(fAttr.getValue());
1607 if (
auto dense = dyn_cast<DenseFPElementsAttr>(attr)) {
1608 if (!isa<Float16Type, BFloat16Type, Float32Type, Float64Type>(
1609 dense.getElementType())) {
1611 loc,
"expected floating point attribute to be f16, bf16, f32 or f64");
1614 interleaveComma(dense, os, [&](
const APFloat &val) { printFloat(val); });
1620 if (
auto iAttr = dyn_cast<IntegerAttr>(attr)) {
1621 if (
auto iType = dyn_cast<IntegerType>(iAttr.getType())) {
1622 printInt(iAttr.getValue(), shouldMapToUnsigned(iType.getSignedness()));
1625 if (
auto iType = dyn_cast<IndexType>(iAttr.getType())) {
1626 printInt(iAttr.getValue(),
false);
1630 if (
auto dense = dyn_cast<DenseIntElementsAttr>(attr)) {
1631 if (
auto iType = dyn_cast<IntegerType>(
1632 cast<ShapedType>(dense.getType()).getElementType())) {
1634 interleaveComma(dense, os, [&](
const APInt &val) {
1635 printInt(val, shouldMapToUnsigned(iType.getSignedness()));
1640 if (
auto iType = dyn_cast<IndexType>(
1641 cast<ShapedType>(dense.getType()).getElementType())) {
1643 interleaveComma(dense, os,
1644 [&](
const APInt &val) { printInt(val,
false); });
1651 if (
auto oAttr = dyn_cast<emitc::OpaqueAttr>(attr)) {
1652 os << oAttr.getValue();
1657 if (
auto sAttr = dyn_cast<SymbolRefAttr>(attr)) {
1658 if (sAttr.getNestedReferences().size() > 1)
1659 return emitError(loc,
"attribute has more than 1 nested reference");
1660 os << sAttr.getRootReference().getValue();
1665 if (
auto type = dyn_cast<TypeAttr>(attr))
1666 return emitType(loc, type.getValue());
1668 return emitError(loc,
"cannot emit attribute: ") << attr;
1671LogicalResult CppEmitter::emitExpression(Operation *op) {
1672 assert(emittedExpressionPrecedence.empty() &&
1673 "Expected precedence stack to be empty");
1674 Operation *rootOp =
nullptr;
1676 if (
auto expressionOp = dyn_cast<ExpressionOp>(op)) {
1677 rootOp = expressionOp.getRootOp();
1679 assert(cast<CExpressionInterface>(op).alwaysInline() &&
1680 "Expected an always-inline operation");
1682 "Expected operation to have no containing expression");
1688 pushExpressionPrecedence(precedence.value());
1690 if (
failed(emitOperation(*rootOp,
false)))
1693 popExpressionPrecedence();
1694 assert(emittedExpressionPrecedence.empty() &&
1695 "Expected precedence stack to be empty");
1700LogicalResult CppEmitter::emitOperand(Value value,
bool isInBrackets) {
1701 if (isPartOfCurrentExpression(value)) {
1703 assert(def &&
"Expected operand to be defined by an operation");
1704 if (
auto expressionOp = dyn_cast<ExpressionOp>(def))
1705 def = expressionOp.getRootOp();
1713 bool encloseInParenthesis =
1714 !isInBrackets && precedence.value() <= getExpressionPrecedence();
1716 if (encloseInParenthesis)
1718 pushExpressionPrecedence(precedence.value());
1720 if (
failed(emitOperation(*def,
false)))
1723 if (encloseInParenthesis)
1726 popExpressionPrecedence();
1731 return emitExpression(def);
1733 if (BlockArgument arg = dyn_cast<BlockArgument>(value)) {
1736 Operation *argOp = arg.getParentBlock()->getParentOp();
1737 if (
auto expressionOp = dyn_cast<ExpressionOp>(argOp))
1738 return emitOperand(expressionOp->getOperand(arg.getArgNumber()));
1741 os << getOrCreateName(value);
1745LogicalResult CppEmitter::emitOperands(Operation &op) {
1749 return emitOperand(operand, true);
1754CppEmitter::emitOperandsAndAttributes(Operation &op,
1755 ArrayRef<StringRef> exclude) {
1756 if (
failed(emitOperands(op)))
1760 for (NamedAttribute attr : op.
getAttrs()) {
1761 if (!llvm::is_contained(exclude, attr.getName().strref())) {
1768 auto emitNamedAttribute = [&](NamedAttribute attr) -> LogicalResult {
1769 if (llvm::is_contained(exclude, attr.getName().strref()))
1771 os <<
"/* " << attr.getName().getValue() <<
" */";
1772 if (
failed(emitAttribute(op.
getLoc(), attr.getValue())))
1779LogicalResult CppEmitter::emitVariableAssignment(OpResult
result) {
1780 if (!hasValueInScope(
result)) {
1781 return result.getDefiningOp()->emitOpError(
1782 "result variable for the operation has not been declared");
1784 os << getOrCreateName(
result) <<
" = ";
1788LogicalResult CppEmitter::emitVariableDeclaration(OpResult
result,
1789 bool trailingSemicolon) {
1790 if (
auto cExpression =
1791 dyn_cast<CExpressionInterface>(
result.getDefiningOp())) {
1792 if (cExpression.alwaysInline())
1795 if (hasValueInScope(
result)) {
1796 return result.getDefiningOp()->emitError(
1797 "result variable for the operation already declared");
1799 if (
failed(emitVariableDeclaration(
result.getOwner()->getLoc(),
1801 getOrCreateName(
result))))
1803 if (trailingSemicolon)
1808LogicalResult CppEmitter::emitGlobalVariable(GlobalOp op) {
1809 if (op.getExternSpecifier())
1811 else if (op.getStaticSpecifier())
1813 if (op.getConstSpecifier())
1816 if (
failed(emitVariableDeclaration(op->getLoc(), op.getType(),
1817 op.getSymName()))) {
1821 std::optional<Attribute> initialValue = op.getInitialValue();
1824 if (
failed(emitAttribute(op->getLoc(), *initialValue)))
1832LogicalResult CppEmitter::emitAssignPrefix(Operation &op) {
1834 if (isEmittingExpression())
1842 if (shouldDeclareVariablesAtTop()) {
1853 if (!shouldDeclareVariablesAtTop()) {
1861 [&](Value
result) { os << getOrCreateName(result); });
1867LogicalResult CppEmitter::emitLabel(
Block &block) {
1868 if (!hasBlockLabel(block))
1872 os.getOStream() << getOrCreateName(block) <<
":\n";
1876LogicalResult CppEmitter::emitOperation(Operation &op,
bool trailingSemicolon) {
1877 LogicalResult status =
1878 llvm::TypeSwitch<Operation *, LogicalResult>(&op)
1882 .Case<cf::BranchOp, cf::CondBranchOp>(
1885 .Case<emitc::AddressOfOp, emitc::AddOp, emitc::AssignOp,
1886 emitc::BitwiseAndOp, emitc::BitwiseLeftShiftOp,
1887 emitc::BitwiseNotOp, emitc::BitwiseOrOp,
1888 emitc::BitwiseRightShiftOp, emitc::BitwiseXorOp, emitc::CallOp,
1889 emitc::CallOpaqueOp, emitc::CastOp, emitc::ClassOp,
1890 emitc::CmpOp, emitc::ConditionalOp, emitc::ConstantOp,
1891 emitc::DeclareFuncOp, emitc::DereferenceOp, emitc::DivOp,
1892 emitc::DoOp, emitc::ExpressionOp, emitc::FieldOp, emitc::FileOp,
1893 emitc::ForOp, emitc::FuncOp, emitc::GetFieldOp,
1894 emitc::GetGlobalOp, emitc::GlobalOp, emitc::IfOp,
1895 emitc::IncludeOp, emitc::LiteralOp, emitc::LoadOp,
1896 emitc::LogicalAndOp, emitc::LogicalNotOp, emitc::LogicalOrOp,
1897 emitc::MemberCallOpaqueOp, emitc::MemberOfPtrOp,
1898 emitc::MemberOp, emitc::MulOp, emitc::RemOp, emitc::ReturnOp,
1899 emitc::SubscriptOp, emitc::SubOp, emitc::SwitchOp,
1900 emitc::UnaryMinusOp, emitc::UnaryPlusOp, emitc::VariableOp,
1905 .Case<func::CallOp, func::FuncOp, func::ReturnOp>(
1907 .Default([&](Operation *) {
1908 return op.emitOpError(
"unable to find printer for op");
1914 if (
auto cExpression = dyn_cast<CExpressionInterface>(op)) {
1915 if (cExpression.alwaysInline())
1919 if (isEmittingExpression() ||
1920 (isa<emitc::ExpressionOp>(op) &&
1926 trailingSemicolon &=
1927 !isa<cf::CondBranchOp, emitc::DeclareFuncOp, emitc::DoOp, emitc::FileOp,
1928 emitc::ForOp, emitc::IfOp, emitc::IncludeOp, emitc::SwitchOp,
1929 emitc::VerbatimOp>(op);
1931 os << (trailingSemicolon ?
";\n" :
"\n");
1936LogicalResult CppEmitter::emitVariableDeclaration(Location loc, Type type,
1938 if (
auto arrType = dyn_cast<emitc::ArrayType>(type)) {
1939 if (
failed(emitType(loc, arrType.getElementType())))
1942 for (
auto dim : arrType.getShape()) {
1943 os <<
"[" << dim <<
"]";
1947 if (
failed(emitType(loc, type)))
1953LogicalResult CppEmitter::emitType(Location loc, Type type) {
1954 if (
auto iType = dyn_cast<IntegerType>(type)) {
1955 switch (iType.getWidth()) {
1957 return (os <<
"bool"),
success();
1962 if (shouldMapToUnsigned(iType.getSignedness()))
1963 return (os <<
"uint" << iType.getWidth() <<
"_t"),
success();
1965 return (os <<
"int" << iType.getWidth() <<
"_t"),
success();
1967 return emitError(loc,
"cannot emit integer type ") << type;
1970 if (
auto fType = dyn_cast<FloatType>(type)) {
1971 switch (fType.getWidth()) {
1973 if (llvm::isa<Float16Type>(type))
1974 return (os <<
"_Float16"),
success();
1975 if (llvm::isa<BFloat16Type>(type))
1976 return (os <<
"__bf16"),
success();
1978 return emitError(loc,
"cannot emit float type ") << type;
1981 return (os <<
"float"),
success();
1983 return (os <<
"double"),
success();
1985 return emitError(loc,
"cannot emit float type ") << type;
1988 if (
auto iType = dyn_cast<IndexType>(type))
1989 return (os <<
"size_t"),
success();
1990 if (
auto sType = dyn_cast<emitc::SizeTType>(type))
1991 return (os <<
"size_t"),
success();
1992 if (
auto sType = dyn_cast<emitc::SignedSizeTType>(type))
1993 return (os <<
"ssize_t"),
success();
1994 if (
auto pType = dyn_cast<emitc::PtrDiffTType>(type))
1995 return (os <<
"ptrdiff_t"),
success();
1996 if (
auto tType = dyn_cast<TensorType>(type)) {
1997 if (!tType.hasRank())
1998 return emitError(loc,
"cannot emit unranked tensor type");
1999 if (!tType.hasStaticShape())
2000 return emitError(loc,
"cannot emit tensor type with non static shape");
2002 if (isa<ArrayType>(tType.getElementType()))
2003 return emitError(loc,
"cannot emit tensor of array type ") << type;
2004 if (
failed(emitType(loc, tType.getElementType())))
2006 auto shape = tType.getShape();
2007 for (
auto dimSize : shape) {
2014 if (
auto tType = dyn_cast<TupleType>(type))
2015 return emitTupleType(loc, tType.getTypes());
2016 if (
auto oType = dyn_cast<emitc::OpaqueType>(type)) {
2017 os << oType.getValue();
2020 if (
auto aType = dyn_cast<emitc::ArrayType>(type)) {
2021 if (
failed(emitType(loc, aType.getElementType())))
2023 for (
auto dim : aType.getShape())
2024 os <<
"[" << dim <<
"]";
2027 if (
auto lType = dyn_cast<emitc::LValueType>(type))
2028 return emitType(loc, lType.getValueType());
2029 if (
auto pType = dyn_cast<emitc::PointerType>(type)) {
2030 if (isa<ArrayType>(pType.getPointee()))
2031 return emitError(loc,
"cannot emit pointer to array type ") << type;
2032 if (
failed(emitType(loc, pType.getPointee())))
2037 return emitError(loc,
"cannot emit type ") << type;
2040LogicalResult CppEmitter::emitTypes(Location loc, ArrayRef<Type> types) {
2041 switch (types.size()) {
2046 return emitType(loc, types.front());
2048 return emitTupleType(loc, types);
2052LogicalResult CppEmitter::emitTupleType(Location loc, ArrayRef<Type> types) {
2053 if (llvm::any_of(types, llvm::IsaPred<ArrayType>)) {
2054 return emitError(loc,
"cannot emit tuple of array type");
2056 os <<
"std::tuple<";
2058 types, os, [&](Type type) {
return emitType(loc, type); })))
2064void CppEmitter::resetValueCounter() { valueCount = 0; }
2066void CppEmitter::increaseLoopNestingLevel() { loopNestingLevel++; }
2068void CppEmitter::decreaseLoopNestingLevel() { loopNestingLevel--; }
2071 bool declareVariablesAtTop,
2073 CppEmitter emitter(os, declareVariablesAtTop, fileId);
2074 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.