21 #include "llvm/ADT/ScopedHashTable.h"
22 #include "llvm/ADT/StringExtras.h"
23 #include "llvm/ADT/TypeSwitch.h"
24 #include "llvm/Support/Debug.h"
25 #include "llvm/Support/FormatVariadic.h"
28 #define DEBUG_TYPE "translate-to-cpp"
38 typename NullaryFunctor>
39 static inline LogicalResult
41 UnaryFunctor eachFn, NullaryFunctor betweenFn) {
44 if (
failed(eachFn(*begin)))
47 for (; begin != end; ++begin) {
49 if (
failed(eachFn(*begin)))
55 template <
typename Container,
typename UnaryFunctor,
typename NullaryFunctor>
58 NullaryFunctor betweenFn) {
62 template <
typename Container,
typename UnaryFunctor>
65 UnaryFunctor eachFn) {
73 .Case<emitc::AddOp>([&](
auto op) {
return 12; })
74 .Case<emitc::ApplyOp>([&](
auto op) {
return 15; })
75 .Case<emitc::BitwiseAndOp>([&](
auto op) {
return 7; })
76 .Case<emitc::BitwiseLeftShiftOp>([&](
auto op) {
return 11; })
77 .Case<emitc::BitwiseNotOp>([&](
auto op) {
return 15; })
78 .Case<emitc::BitwiseOrOp>([&](
auto op) {
return 5; })
79 .Case<emitc::BitwiseRightShiftOp>([&](
auto op) {
return 11; })
80 .Case<emitc::BitwiseXorOp>([&](
auto op) {
return 6; })
81 .Case<emitc::CallOp>([&](
auto op) {
return 16; })
82 .Case<emitc::CallOpaqueOp>([&](
auto op) {
return 16; })
83 .Case<emitc::CastOp>([&](
auto op) {
return 15; })
84 .Case<emitc::CmpOp>([&](
auto op) -> FailureOr<int> {
85 switch (op.getPredicate()) {
86 case emitc::CmpPredicate::eq:
87 case emitc::CmpPredicate::ne:
89 case emitc::CmpPredicate::lt:
90 case emitc::CmpPredicate::le:
91 case emitc::CmpPredicate::gt:
92 case emitc::CmpPredicate::ge:
94 case emitc::CmpPredicate::three_way:
97 return op->emitError(
"unsupported cmp predicate");
99 .Case<emitc::ConditionalOp>([&](
auto op) {
return 2; })
100 .Case<emitc::ConstantOp>([&](
auto op) {
return 17; })
101 .Case<emitc::DivOp>([&](
auto op) {
return 13; })
102 .Case<emitc::LoadOp>([&](
auto op) {
return 16; })
103 .Case<emitc::LogicalAndOp>([&](
auto op) {
return 4; })
104 .Case<emitc::LogicalNotOp>([&](
auto op) {
return 15; })
105 .Case<emitc::LogicalOrOp>([&](
auto op) {
return 3; })
106 .Case<emitc::MulOp>([&](
auto op) {
return 13; })
107 .Case<emitc::RemOp>([&](
auto op) {
return 13; })
108 .Case<emitc::SubOp>([&](
auto op) {
return 12; })
109 .Case<emitc::UnaryMinusOp>([&](
auto op) {
return 15; })
110 .Case<emitc::UnaryPlusOp>([&](
auto op) {
return 15; })
111 .Default([](
auto op) {
return op->emitError(
"unsupported operation"); });
117 explicit CppEmitter(raw_ostream &os,
bool declareVariablesAtTop,
128 LogicalResult emitOperation(
Operation &op,
bool trailingSemicolon);
144 LogicalResult emitVariableAssignment(
OpResult result);
147 LogicalResult emitVariableDeclaration(
OpResult result,
148 bool trailingSemicolon);
151 LogicalResult emitVariableDeclaration(
Location loc,
Type type,
160 LogicalResult emitAssignPrefix(
Operation &op);
163 LogicalResult emitGlobalVariable(GlobalOp op);
166 LogicalResult emitLabel(
Block &block);
170 LogicalResult emitOperandsAndAttributes(
Operation &op,
174 LogicalResult emitOperands(
Operation &op);
177 LogicalResult emitOperand(
Value value);
180 LogicalResult emitExpression(ExpressionOp expressionOp);
183 void cacheDeferredOpResult(
Value value, StringRef str);
186 StringRef getOrCreateName(
Value val);
190 StringRef getOrCreateInductionVarName(
Value val);
193 std::string getSubscriptName(emitc::SubscriptOp op);
196 std::string createMemberAccess(emitc::MemberOp op);
199 std::string createMemberAccess(emitc::MemberOfPtrOp op);
202 StringRef getOrCreateName(
Block &block);
204 LogicalResult emitInlinedExpression(
Value value);
207 bool shouldMapToUnsigned(IntegerType::SignednessSemantics val);
211 ~Scope() { emitter.labelInScopeCount.pop(); }
214 llvm::ScopedHashTableScope<Value, std::string> valueMapperScope;
215 llvm::ScopedHashTableScope<Block *, std::string> blockMapperScope;
218 Scope(CppEmitter &emitter)
219 : valueMapperScope(emitter.valueMapper),
220 blockMapperScope(emitter.blockMapper), emitter(emitter) {
221 emitter.labelInScopeCount.push(emitter.labelInScopeCount.top());
228 struct FunctionScope : Scope {
229 FunctionScope(CppEmitter &emitter) : Scope(emitter) {
231 emitter.resetValueCounter();
237 struct LoopScope : Scope {
238 LoopScope(CppEmitter &emitter) : Scope(emitter) {
239 emitter.increaseLoopNestingLevel();
241 ~LoopScope() { emitter.decreaseLoopNestingLevel(); }
245 bool hasValueInScope(
Value val);
248 bool hasBlockLabel(
Block &block);
255 bool shouldDeclareVariablesAtTop() {
return declareVariablesAtTop; };
258 bool shouldEmitFile(FileOp file) {
259 return !fileId.empty() && file.getId() == fileId;
263 ExpressionOp getEmittedExpression() {
return emittedExpression; }
267 bool isPartOfCurrentExpression(
Value value) {
268 if (!emittedExpression)
273 return isPartOfCurrentExpression(def);
278 bool isPartOfCurrentExpression(
Operation *def) {
279 auto operandExpression = dyn_cast<ExpressionOp>(def->
getParentOp());
280 return operandExpression && operandExpression == emittedExpression;
284 void resetValueCounter();
287 void increaseLoopNestingLevel();
290 void decreaseLoopNestingLevel();
293 using ValueMapper = llvm::ScopedHashTable<Value, std::string>;
294 using BlockMapper = llvm::ScopedHashTable<Block *, std::string>;
302 bool declareVariablesAtTop;
308 ValueMapper valueMapper;
311 BlockMapper blockMapper;
314 llvm::ScopedHashTableScope<Value, std::string> defaultValueMapperScope;
315 llvm::ScopedHashTableScope<Block *, std::string> defaultBlockMapperScope;
317 std::stack<int64_t> labelInScopeCount;
321 uint64_t loopNestingLevel{0};
324 unsigned int valueCount{0};
327 ExpressionOp emittedExpression;
330 void pushExpressionPrecedence(
int precedence) {
331 emittedExpressionPrecedence.push_back(precedence);
333 void popExpressionPrecedence() { emittedExpressionPrecedence.pop_back(); }
334 static int lowestPrecedence() {
return 0; }
335 int getExpressionPrecedence() {
336 if (emittedExpressionPrecedence.empty())
337 return lowestPrecedence();
338 return emittedExpressionPrecedence.back();
345 return isa_and_nonnull<emitc::GetGlobalOp, emitc::LiteralOp, emitc::MemberOp,
346 emitc::MemberOfPtrOp, emitc::SubscriptOp,
347 emitc::GetFieldOp>(op);
357 if (expressionOp.getDoNotInline())
361 Value result = expressionOp.getResult();
375 if (isa<emitc::ExpressionOp, emitc::CExpressionInterface>(*user))
379 if (!expressionOp.hasSideEffects())
390 if (isa<emitc::IfOp, emitc::SwitchOp, emitc::ReturnOp>(user))
395 if (
auto assignOp = dyn_cast<emitc::AssignOp>(user)) {
397 if (expressionOp.getResult() == assignOp.getValue() &&
398 isa_and_present<VariableOp>(assignOp.getVar().getDefiningOp()))
411 if (emitter.shouldDeclareVariablesAtTop()) {
413 if (
auto oAttr = dyn_cast<emitc::OpaqueAttr>(value)) {
414 if (oAttr.getValue().empty())
418 if (
failed(emitter.emitVariableAssignment(result)))
420 return emitter.emitAttribute(operation->
getLoc(), value);
424 if (
auto oAttr = dyn_cast<emitc::OpaqueAttr>(value)) {
425 if (oAttr.getValue().empty())
427 return emitter.emitVariableDeclaration(result,
432 if (
failed(emitter.emitAssignPrefix(*operation)))
434 return emitter.emitAttribute(operation->
getLoc(), value);
438 emitc::ConstantOp constantOp) {
439 Operation *operation = constantOp.getOperation();
442 if (emitter.isPartOfCurrentExpression(operation))
443 return emitter.emitAttribute(operation->
getLoc(), value);
449 emitc::VariableOp variableOp) {
450 Operation *operation = variableOp.getOperation();
457 emitc::GlobalOp globalOp) {
459 return emitter.emitGlobalVariable(globalOp);
463 emitc::AssignOp assignOp) {
466 if (
failed(emitter.emitVariableAssignment(result)))
469 return emitter.emitOperand(assignOp.getValue());
473 if (
failed(emitter.emitAssignPrefix(*loadOp)))
476 return emitter.emitOperand(loadOp.getOperand());
481 StringRef binaryOperator) {
482 raw_ostream &os = emitter.ostream();
484 if (
failed(emitter.emitAssignPrefix(*operation)))
490 os <<
" " << binaryOperator <<
" ";
500 StringRef unaryOperator) {
501 raw_ostream &os = emitter.ostream();
503 if (
failed(emitter.emitAssignPrefix(*operation)))
515 Operation *operation = addOp.getOperation();
521 Operation *operation = divOp.getOperation();
527 Operation *operation = mulOp.getOperation();
533 Operation *operation = remOp.getOperation();
539 Operation *operation = subOp.getOperation();
547 std::next(iteratorOp) != end; ++iteratorOp) {
548 if (
failed(emitter.emitOperation(*iteratorOp,
true)))
556 emitc::SwitchOp switchOp) {
560 if (
failed(emitter.emitOperand(switchOp.getArg())))
564 for (
auto pair : llvm::zip(switchOp.getCases(), switchOp.getCaseRegions())) {
565 os <<
"\ncase " << std::get<0>(pair) <<
": {\n";
574 os <<
"\ndefault: {\n";
590 Block &bodyBlock = doOp.getBodyRegion().
front();
592 if (
failed(emitter.emitOperation(op,
true)))
598 Block &condBlock = doOp.getConditionRegion().
front();
599 auto condYield = cast<emitc::YieldOp>(condBlock.
back());
600 if (
failed(emitter.emitExpression(
601 cast<emitc::ExpressionOp>(condYield.getOperand(0).getDefiningOp()))))
609 Operation *operation = cmpOp.getOperation();
611 StringRef binaryOperator;
613 switch (cmpOp.getPredicate()) {
614 case emitc::CmpPredicate::eq:
615 binaryOperator =
"==";
617 case emitc::CmpPredicate::ne:
618 binaryOperator =
"!=";
620 case emitc::CmpPredicate::lt:
621 binaryOperator =
"<";
623 case emitc::CmpPredicate::le:
624 binaryOperator =
"<=";
626 case emitc::CmpPredicate::gt:
627 binaryOperator =
">";
629 case emitc::CmpPredicate::ge:
630 binaryOperator =
">=";
632 case emitc::CmpPredicate::three_way:
633 binaryOperator =
"<=>";
641 emitc::ConditionalOp conditionalOp) {
642 raw_ostream &os = emitter.ostream();
644 if (
failed(emitter.emitAssignPrefix(*conditionalOp)))
647 if (
failed(emitter.emitOperand(conditionalOp.getCondition())))
652 if (
failed(emitter.emitOperand(conditionalOp.getTrueValue())))
657 if (
failed(emitter.emitOperand(conditionalOp.getFalseValue())))
664 emitc::VerbatimOp verbatimOp) {
665 raw_ostream &os = emitter.ostream();
667 FailureOr<SmallVector<ReplacementItem>> items =
668 verbatimOp.parseFormatString();
672 auto fmtArg = verbatimOp.getFmtArgs().begin();
675 if (
auto *str = std::get_if<StringRef>(&item)) {
678 if (
failed(emitter.emitOperand(*fmtArg++)))
687 cf::BranchOp branchOp) {
688 raw_ostream &os = emitter.ostream();
692 llvm::zip(branchOp.getOperands(), successor.
getArguments())) {
693 Value &operand = std::get<0>(pair);
695 os << emitter.getOrCreateName(argument) <<
" = "
696 << emitter.getOrCreateName(operand) <<
";\n";
700 if (!(emitter.hasBlockLabel(successor)))
701 return branchOp.emitOpError(
"unable to find label for successor block");
702 os << emitter.getOrCreateName(successor);
707 cf::CondBranchOp condBranchOp) {
709 Block &trueSuccessor = *condBranchOp.getTrueDest();
710 Block &falseSuccessor = *condBranchOp.getFalseDest();
713 if (
failed(emitter.emitOperand(condBranchOp.getCondition())))
720 for (
auto pair : llvm::zip(condBranchOp.getTrueOperands(),
722 Value &operand = std::get<0>(pair);
724 os << emitter.getOrCreateName(argument) <<
" = "
725 << emitter.getOrCreateName(operand) <<
";\n";
729 if (!(emitter.hasBlockLabel(trueSuccessor))) {
730 return condBranchOp.emitOpError(
"unable to find label for successor block");
732 os << emitter.getOrCreateName(trueSuccessor) <<
";\n";
736 for (
auto pair : llvm::zip(condBranchOp.getFalseOperands(),
738 Value &operand = std::get<0>(pair);
740 os << emitter.getOrCreateName(argument) <<
" = "
741 << emitter.getOrCreateName(operand) <<
";\n";
745 if (!(emitter.hasBlockLabel(falseSuccessor))) {
746 return condBranchOp.emitOpError()
747 <<
"unable to find label for successor block";
749 os << emitter.getOrCreateName(falseSuccessor) <<
";\n";
756 if (
failed(emitter.emitAssignPrefix(*callOp)))
759 raw_ostream &os = emitter.ostream();
761 if (
failed(emitter.emitOperands(*callOp)))
768 Operation *operation = callOp.getOperation();
769 StringRef callee = callOp.getCallee();
775 Operation *operation = callOp.getOperation();
776 StringRef callee = callOp.getCallee();
782 emitc::CallOpaqueOp callOpaqueOp) {
783 raw_ostream &os = emitter.ostream();
784 Operation &op = *callOpaqueOp.getOperation();
786 if (
failed(emitter.emitAssignPrefix(op)))
788 os << callOpaqueOp.getCallee();
794 auto emitTemplateArgs = [&](
Attribute attr) -> LogicalResult {
795 return emitter.emitAttribute(op.
getLoc(), attr);
798 if (callOpaqueOp.getTemplateArgs()) {
806 auto emitArgs = [&](
Attribute attr) -> LogicalResult {
807 if (
auto t = dyn_cast<IntegerAttr>(attr)) {
809 if (t.getType().isIndex()) {
810 int64_t idx = t.getInt();
812 return emitter.emitOperand(operand);
823 LogicalResult emittedArgs =
824 callOpaqueOp.getArgs()
826 : emitter.emitOperands(op);
834 emitc::ApplyOp applyOp) {
835 raw_ostream &os = emitter.ostream();
838 if (
failed(emitter.emitAssignPrefix(op)))
840 os << applyOp.getApplicableOperator();
841 return emitter.emitOperand(applyOp.getOperand());
845 emitc::BitwiseAndOp bitwiseAndOp) {
846 Operation *operation = bitwiseAndOp.getOperation();
852 emitc::BitwiseLeftShiftOp bitwiseLeftShiftOp) {
853 Operation *operation = bitwiseLeftShiftOp.getOperation();
858 emitc::BitwiseNotOp bitwiseNotOp) {
859 Operation *operation = bitwiseNotOp.getOperation();
864 emitc::BitwiseOrOp bitwiseOrOp) {
865 Operation *operation = bitwiseOrOp.getOperation();
871 emitc::BitwiseRightShiftOp bitwiseRightShiftOp) {
872 Operation *operation = bitwiseRightShiftOp.getOperation();
877 emitc::BitwiseXorOp bitwiseXorOp) {
878 Operation *operation = bitwiseXorOp.getOperation();
883 emitc::UnaryPlusOp unaryPlusOp) {
884 Operation *operation = unaryPlusOp.getOperation();
889 emitc::UnaryMinusOp unaryMinusOp) {
890 Operation *operation = unaryMinusOp.getOperation();
895 raw_ostream &os = emitter.ostream();
898 if (
failed(emitter.emitAssignPrefix(op)))
904 return emitter.emitOperand(castOp.getOperand());
908 emitc::ExpressionOp expressionOp) {
912 Operation &op = *expressionOp.getOperation();
914 if (
failed(emitter.emitAssignPrefix(op)))
917 return emitter.emitExpression(expressionOp);
921 emitc::IncludeOp includeOp) {
922 raw_ostream &os = emitter.ostream();
925 if (includeOp.getIsStandardInclude())
926 os <<
"<" << includeOp.getInclude() <<
">";
928 os <<
"\"" << includeOp.getInclude() <<
"\"";
934 emitc::LogicalAndOp logicalAndOp) {
935 Operation *operation = logicalAndOp.getOperation();
940 emitc::LogicalNotOp logicalNotOp) {
941 Operation *operation = logicalNotOp.getOperation();
946 emitc::LogicalOrOp logicalOrOp) {
947 Operation *operation = logicalOrOp.getOperation();
957 auto requiresParentheses = [&](
Value value) {
966 emitter.emitType(forOp.getLoc(), forOp.getInductionVar().getType())))
969 os << emitter.getOrCreateInductionVarName(forOp.getInductionVar());
971 if (
failed(emitter.emitOperand(forOp.getLowerBound())))
974 os << emitter.getOrCreateInductionVarName(forOp.getInductionVar());
976 Value upperBound = forOp.getUpperBound();
977 bool upperBoundRequiresParentheses = requiresParentheses(upperBound);
978 if (upperBoundRequiresParentheses)
980 if (
failed(emitter.emitOperand(upperBound)))
982 if (upperBoundRequiresParentheses)
985 os << emitter.getOrCreateInductionVarName(forOp.getInductionVar());
987 if (
failed(emitter.emitOperand(forOp.getStep())))
992 CppEmitter::LoopScope lScope(emitter);
994 Region &forRegion = forOp.getRegion();
995 auto regionOps = forRegion.
getOps();
998 for (
auto it = regionOps.begin(); std::next(it) != regionOps.end(); ++it) {
999 if (
failed(emitter.emitOperation(*it,
true)))
1013 auto emitAllExceptLast = [&emitter](
Region ®ion) {
1015 for (; std::next(it) != end; ++it) {
1016 if (
failed(emitter.emitOperation(*it,
true)))
1019 assert(isa<emitc::YieldOp>(*it) &&
1020 "Expected last operation in the region to be emitc::yield");
1025 if (
failed(emitter.emitOperand(ifOp.getCondition())))
1029 if (
failed(emitAllExceptLast(ifOp.getThenRegion())))
1033 Region &elseRegion = ifOp.getElseRegion();
1034 if (!elseRegion.
empty()) {
1037 if (
failed(emitAllExceptLast(elseRegion)))
1046 func::ReturnOp returnOp) {
1047 raw_ostream &os = emitter.ostream();
1049 switch (returnOp.getNumOperands()) {
1054 if (
failed(emitter.emitOperand(returnOp.getOperand(0))))
1058 os <<
" std::make_tuple(";
1059 if (
failed(emitter.emitOperandsAndAttributes(*returnOp.getOperation())))
1067 emitc::ReturnOp returnOp) {
1068 raw_ostream &os = emitter.ostream();
1070 if (returnOp.getNumOperands() == 0)
1074 if (
failed(emitter.emitOperand(returnOp.getOperand())))
1081 if (
failed(emitter.emitOperation(op,
false)))
1089 os <<
"class " << classOp.getSymName();
1090 if (classOp.getFinalSpecifier())
1092 os <<
" {\n public:\n";
1096 if (
failed(emitter.emitOperation(op,
false)))
1106 raw_ostream &os = emitter.ostream();
1107 if (
failed(emitter.emitVariableDeclaration(
1108 fieldOp->getLoc(), fieldOp.getType(), fieldOp.getSymName())))
1110 std::optional<Attribute> initialValue = fieldOp.getInitialValue();
1113 if (
failed(emitter.emitAttribute(fieldOp->getLoc(), *initialValue)))
1122 if (!emitter.shouldEmitFile(file))
1126 if (
failed(emitter.emitOperation(op,
false)))
1139 return emitter.emitType(functionOp->
getLoc(), arg);
1150 return emitter.emitVariableDeclaration(
1151 functionOp->
getLoc(), arg.
getType(), emitter.getOrCreateName(arg));
1161 if (emitter.shouldDeclareVariablesAtTop()) {
1166 if (isa<emitc::ExpressionOp>(op->
getParentOp()) ||
1167 (isa<emitc::ExpressionOp>(op) &&
1171 if (
failed(emitter.emitVariableDeclaration(
1174 op->
emitError(
"unable to declare result variable for op"));
1184 for (
Block &block : blocks) {
1185 emitter.getOrCreateName(block);
1189 for (
Block &block : llvm::drop_begin(blocks)) {
1191 if (emitter.hasValueInScope(arg))
1192 return functionOp->
emitOpError(
" block argument #")
1193 << arg.getArgNumber() <<
" is out of scope";
1194 if (isa<ArrayType, LValueType>(arg.getType()))
1195 return functionOp->
emitOpError(
"cannot emit block argument #")
1196 << arg.getArgNumber() <<
" with type " << arg.getType();
1198 emitter.emitType(block.getParentOp()->getLoc(), arg.getType()))) {
1201 os <<
" " << emitter.getOrCreateName(arg) <<
";\n";
1205 for (
Block &block : blocks) {
1207 if (!block.hasNoPredecessors()) {
1208 if (
failed(emitter.emitLabel(block)))
1211 for (
Operation &op : block.getOperations()) {
1212 if (
failed(emitter.emitOperation(op,
true)))
1223 func::FuncOp functionOp) {
1225 if (!emitter.shouldDeclareVariablesAtTop() &&
1226 functionOp.getBlocks().size() > 1) {
1227 return functionOp.emitOpError(
1228 "with multiple blocks needs variables declared at top");
1231 if (llvm::any_of(functionOp.getArgumentTypes(), llvm::IsaPred<LValueType>)) {
1232 return functionOp.emitOpError()
1233 <<
"cannot emit lvalue type as argument type";
1236 if (llvm::any_of(functionOp.getResultTypes(), llvm::IsaPred<ArrayType>)) {
1237 return functionOp.emitOpError() <<
"cannot emit array type as result type";
1240 CppEmitter::FunctionScope scope(emitter);
1242 if (
failed(emitter.emitTypes(functionOp.getLoc(),
1243 functionOp.getFunctionType().getResults())))
1245 os <<
" " << functionOp.getName();
1248 Operation *operation = functionOp.getOperation();
1260 emitc::FuncOp functionOp) {
1262 if (!emitter.shouldDeclareVariablesAtTop() &&
1263 functionOp.getBlocks().size() > 1) {
1264 return functionOp.emitOpError(
1265 "with multiple blocks needs variables declared at top");
1268 CppEmitter::FunctionScope scope(emitter);
1270 if (functionOp.getSpecifiers()) {
1271 for (
Attribute specifier : functionOp.getSpecifiersAttr()) {
1272 os << cast<StringAttr>(specifier).str() <<
" ";
1276 if (
failed(emitter.emitTypes(functionOp.getLoc(),
1277 functionOp.getFunctionType().getResults())))
1279 os <<
" " << functionOp.getName();
1282 Operation *operation = functionOp.getOperation();
1283 if (functionOp.isExternal()) {
1285 functionOp.getArgumentTypes())))
1301 DeclareFuncOp declareFuncOp) {
1304 CppEmitter::FunctionScope scope(emitter);
1305 auto functionOp = SymbolTable::lookupNearestSymbolFrom<emitc::FuncOp>(
1306 declareFuncOp, declareFuncOp.getSymNameAttr());
1311 if (functionOp.getSpecifiers()) {
1312 for (
Attribute specifier : functionOp.getSpecifiersAttr()) {
1313 os << cast<StringAttr>(specifier).str() <<
" ";
1317 if (
failed(emitter.emitTypes(functionOp.getLoc(),
1318 functionOp.getFunctionType().getResults())))
1320 os <<
" " << functionOp.getName();
1323 Operation *operation = functionOp.getOperation();
1331 CppEmitter::CppEmitter(raw_ostream &os,
bool declareVariablesAtTop,
1333 : os(os), declareVariablesAtTop(declareVariablesAtTop),
1334 fileId(fileId.str()), defaultValueMapperScope(valueMapper),
1335 defaultBlockMapperScope(blockMapper) {
1336 labelInScopeCount.push(0);
1339 std::string CppEmitter::getSubscriptName(emitc::SubscriptOp op) {
1341 llvm::raw_string_ostream ss(out);
1342 ss << getOrCreateName(op.getValue());
1343 for (
auto index : op.getIndices()) {
1344 ss <<
"[" << getOrCreateName(index) <<
"]";
1349 std::string CppEmitter::createMemberAccess(emitc::MemberOp op) {
1351 llvm::raw_string_ostream ss(out);
1352 ss << getOrCreateName(op.getOperand());
1353 ss <<
"." << op.getMember();
1357 std::string CppEmitter::createMemberAccess(emitc::MemberOfPtrOp op) {
1359 llvm::raw_string_ostream ss(out);
1360 ss << getOrCreateName(op.getOperand());
1361 ss <<
"->" << op.getMember();
1365 void CppEmitter::cacheDeferredOpResult(
Value value, StringRef str) {
1366 if (!valueMapper.count(value))
1367 valueMapper.insert(value, str.str());
1371 StringRef CppEmitter::getOrCreateName(
Value val) {
1372 if (!valueMapper.count(val)) {
1374 "cacheDeferredOpResult should have been called on this value, "
1375 "update the emitOperation function.");
1377 valueMapper.insert(val, formatv(
"v{0}", ++valueCount));
1379 return *valueMapper.begin(val);
1384 StringRef CppEmitter::getOrCreateInductionVarName(
Value val) {
1385 if (!valueMapper.count(val)) {
1387 int64_t identifier =
'i' + loopNestingLevel;
1389 if (identifier >=
'i' && identifier <=
't') {
1390 valueMapper.insert(val,
1391 formatv(
"{0}{1}", (
char)identifier, ++valueCount));
1394 valueMapper.insert(val, formatv(
"u{0}", ++valueCount));
1397 return *valueMapper.begin(val);
1401 StringRef CppEmitter::getOrCreateName(
Block &block) {
1402 if (!blockMapper.count(&block))
1403 blockMapper.insert(&block, formatv(
"label{0}", ++labelInScopeCount.top()));
1404 return *blockMapper.begin(&block);
1407 bool CppEmitter::shouldMapToUnsigned(IntegerType::SignednessSemantics val) {
1409 case IntegerType::Signless:
1413 case IntegerType::Unsigned:
1416 llvm_unreachable(
"Unexpected IntegerType::SignednessSemantics");
1419 bool CppEmitter::hasValueInScope(
Value val) {
return valueMapper.count(val); }
1421 bool CppEmitter::hasBlockLabel(
Block &block) {
1422 return blockMapper.count(&block);
1426 auto printInt = [&](
const APInt &val,
bool isUnsigned) {
1427 if (val.getBitWidth() == 1) {
1428 if (val.getBoolValue())
1434 val.toString(strValue, 10, !isUnsigned,
false);
1439 auto printFloat = [&](
const APFloat &val) {
1440 if (val.isFinite()) {
1443 val.toString(strValue, 0, 0,
false);
1445 switch (llvm::APFloatBase::SemanticsToEnum(val.getSemantics())) {
1446 case llvm::APFloatBase::S_IEEEhalf:
1449 case llvm::APFloatBase::S_BFloat:
1452 case llvm::APFloatBase::S_IEEEsingle:
1455 case llvm::APFloatBase::S_IEEEdouble:
1458 llvm_unreachable(
"unsupported floating point type");
1460 }
else if (val.isNaN()) {
1462 }
else if (val.isInfinity()) {
1463 if (val.isNegative())
1470 if (
auto fAttr = dyn_cast<FloatAttr>(attr)) {
1471 if (!isa<Float16Type, BFloat16Type, Float32Type, Float64Type>(
1474 loc,
"expected floating point attribute to be f16, bf16, f32 or f64");
1476 printFloat(fAttr.getValue());
1479 if (
auto dense = dyn_cast<DenseFPElementsAttr>(attr)) {
1480 if (!isa<Float16Type, BFloat16Type, Float32Type, Float64Type>(
1481 dense.getElementType())) {
1483 loc,
"expected floating point attribute to be f16, bf16, f32 or f64");
1486 interleaveComma(dense, os, [&](
const APFloat &val) { printFloat(val); });
1492 if (
auto iAttr = dyn_cast<IntegerAttr>(attr)) {
1493 if (
auto iType = dyn_cast<IntegerType>(iAttr.getType())) {
1494 printInt(iAttr.getValue(), shouldMapToUnsigned(iType.getSignedness()));
1497 if (
auto iType = dyn_cast<IndexType>(iAttr.getType())) {
1498 printInt(iAttr.getValue(),
false);
1502 if (
auto dense = dyn_cast<DenseIntElementsAttr>(attr)) {
1503 if (
auto iType = dyn_cast<IntegerType>(
1504 cast<ShapedType>(dense.getType()).getElementType())) {
1506 interleaveComma(dense, os, [&](
const APInt &val) {
1507 printInt(val, shouldMapToUnsigned(iType.getSignedness()));
1512 if (
auto iType = dyn_cast<IndexType>(
1513 cast<ShapedType>(dense.getType()).getElementType())) {
1515 interleaveComma(dense, os,
1516 [&](
const APInt &val) { printInt(val,
false); });
1523 if (
auto oAttr = dyn_cast<emitc::OpaqueAttr>(attr)) {
1524 os << oAttr.getValue();
1529 if (
auto sAttr = dyn_cast<SymbolRefAttr>(attr)) {
1530 if (sAttr.getNestedReferences().size() > 1)
1531 return emitError(loc,
"attribute has more than 1 nested reference");
1532 os << sAttr.getRootReference().getValue();
1537 if (
auto type = dyn_cast<TypeAttr>(attr))
1538 return emitType(loc, type.getValue());
1540 return emitError(loc,
"cannot emit attribute: ") << attr;
1543 LogicalResult CppEmitter::emitExpression(ExpressionOp expressionOp) {
1544 assert(emittedExpressionPrecedence.empty() &&
1545 "Expected precedence stack to be empty");
1546 Operation *rootOp = expressionOp.getRootOp();
1548 emittedExpression = expressionOp;
1552 pushExpressionPrecedence(precedence.value());
1554 if (
failed(emitOperation(*rootOp,
false)))
1557 popExpressionPrecedence();
1558 assert(emittedExpressionPrecedence.empty() &&
1559 "Expected precedence stack to be empty");
1560 emittedExpression =
nullptr;
1565 LogicalResult CppEmitter::emitOperand(
Value value) {
1566 if (isPartOfCurrentExpression(value)) {
1568 assert(def &&
"Expected operand to be defined by an operation");
1576 bool encloseInParenthesis = precedence.value() <= getExpressionPrecedence();
1577 if (encloseInParenthesis)
1579 pushExpressionPrecedence(precedence.value());
1581 if (
failed(emitOperation(*def,
false)))
1584 if (encloseInParenthesis)
1587 popExpressionPrecedence();
1593 return emitExpression(expressionOp);
1599 if (
auto expressionOp = dyn_cast<ExpressionOp>(argOp)) {
1603 assert(expressionOp == emittedExpression &&
1604 "Expected expression being emitted");
1605 value = expressionOp->getOperand(arg.getArgNumber());
1609 os << getOrCreateName(value);
1613 LogicalResult CppEmitter::emitOperands(
Operation &op) {
1617 if (getEmittedExpression())
1618 pushExpressionPrecedence(lowestPrecedence());
1619 if (failed(emitOperand(operand)))
1621 if (getEmittedExpression())
1622 popExpressionPrecedence();
1628 CppEmitter::emitOperandsAndAttributes(
Operation &op,
1630 if (
failed(emitOperands(op)))
1635 if (!llvm::is_contained(exclude, attr.getName().strref())) {
1642 auto emitNamedAttribute = [&](
NamedAttribute attr) -> LogicalResult {
1643 if (llvm::is_contained(exclude, attr.getName().strref()))
1645 os <<
"/* " << attr.getName().getValue() <<
" */";
1646 if (
failed(emitAttribute(op.
getLoc(), attr.getValue())))
1653 LogicalResult CppEmitter::emitVariableAssignment(
OpResult result) {
1654 if (!hasValueInScope(result)) {
1656 "result variable for the operation has not been declared");
1658 os << getOrCreateName(result) <<
" = ";
1662 LogicalResult CppEmitter::emitVariableDeclaration(
OpResult result,
1663 bool trailingSemicolon) {
1666 if (hasValueInScope(result)) {
1668 "result variable for the operation already declared");
1672 getOrCreateName(result))))
1674 if (trailingSemicolon)
1679 LogicalResult CppEmitter::emitGlobalVariable(GlobalOp op) {
1680 if (op.getExternSpecifier())
1682 else if (op.getStaticSpecifier())
1684 if (op.getConstSpecifier())
1687 if (
failed(emitVariableDeclaration(op->getLoc(), op.getType(),
1688 op.getSymName()))) {
1692 std::optional<Attribute> initialValue = op.getInitialValue();
1695 if (
failed(emitAttribute(op->getLoc(), *initialValue)))
1703 LogicalResult CppEmitter::emitAssignPrefix(
Operation &op) {
1705 if (getEmittedExpression())
1713 if (shouldDeclareVariablesAtTop()) {
1714 if (
failed(emitVariableAssignment(result)))
1717 if (
failed(emitVariableDeclaration(result,
false)))
1724 if (!shouldDeclareVariablesAtTop()) {
1726 if (
failed(emitVariableDeclaration(result,
true)))
1732 [&](
Value result) { os << getOrCreateName(result); });
1738 LogicalResult CppEmitter::emitLabel(
Block &block) {
1739 if (!hasBlockLabel(block))
1743 os.getOStream() << getOrCreateName(block) <<
":\n";
1747 LogicalResult CppEmitter::emitOperation(
Operation &op,
bool trailingSemicolon) {
1748 LogicalResult status =
1751 .Case<ModuleOp>([&](
auto op) {
return printOperation(*
this, op); })
1753 .Case<cf::BranchOp, cf::CondBranchOp>(
1756 .Case<emitc::AddOp, emitc::ApplyOp, emitc::AssignOp,
1757 emitc::BitwiseAndOp, emitc::BitwiseLeftShiftOp,
1758 emitc::BitwiseNotOp, emitc::BitwiseOrOp,
1759 emitc::BitwiseRightShiftOp, emitc::BitwiseXorOp, emitc::CallOp,
1760 emitc::CallOpaqueOp, emitc::CastOp, emitc::ClassOp,
1761 emitc::CmpOp, emitc::ConditionalOp, emitc::ConstantOp,
1762 emitc::DeclareFuncOp, emitc::DivOp, emitc::DoOp,
1763 emitc::ExpressionOp, emitc::FieldOp, emitc::FileOp,
1764 emitc::ForOp, emitc::FuncOp, emitc::GlobalOp, emitc::IfOp,
1765 emitc::IncludeOp, emitc::LoadOp, emitc::LogicalAndOp,
1766 emitc::LogicalNotOp, emitc::LogicalOrOp, emitc::MulOp,
1767 emitc::RemOp, emitc::ReturnOp, emitc::SubOp, emitc::SwitchOp,
1768 emitc::UnaryMinusOp, emitc::UnaryPlusOp, emitc::VariableOp,
1773 .Case<func::CallOp, func::FuncOp, func::ReturnOp>(
1775 .Case<emitc::GetGlobalOp>([&](
auto op) {
1779 .Case<emitc::GetFieldOp>([&](
auto op) {
1780 cacheDeferredOpResult(op.
getResult(), op.getFieldName());
1783 .Case<emitc::LiteralOp>([&](
auto op) {
1784 cacheDeferredOpResult(op.
getResult(), op.getValue());
1787 .Case<emitc::MemberOp>([&](
auto op) {
1788 cacheDeferredOpResult(op.
getResult(), createMemberAccess(op));
1791 .Case<emitc::MemberOfPtrOp>([&](
auto op) {
1792 cacheDeferredOpResult(op.
getResult(), createMemberAccess(op));
1795 .Case<emitc::SubscriptOp>([&](
auto op) {
1796 cacheDeferredOpResult(op.
getResult(), getSubscriptName(op));
1800 return op.
emitOpError(
"unable to find printer for op");
1809 if (getEmittedExpression() ||
1810 (isa<emitc::ExpressionOp>(op) &&
1816 trailingSemicolon &=
1817 !isa<cf::CondBranchOp, emitc::DeclareFuncOp, emitc::DoOp, emitc::FileOp,
1818 emitc::ForOp, emitc::IfOp, emitc::IncludeOp, emitc::SwitchOp,
1819 emitc::VerbatimOp>(op);
1821 os << (trailingSemicolon ?
";\n" :
"\n");
1826 LogicalResult CppEmitter::emitVariableDeclaration(
Location loc,
Type type,
1828 if (
auto arrType = dyn_cast<emitc::ArrayType>(type)) {
1829 if (
failed(emitType(loc, arrType.getElementType())))
1832 for (
auto dim : arrType.getShape()) {
1833 os <<
"[" << dim <<
"]";
1837 if (
failed(emitType(loc, type)))
1843 LogicalResult CppEmitter::emitType(
Location loc,
Type type) {
1844 if (
auto iType = dyn_cast<IntegerType>(type)) {
1845 switch (iType.getWidth()) {
1847 return (os <<
"bool"), success();
1852 if (shouldMapToUnsigned(iType.getSignedness()))
1853 return (os <<
"uint" << iType.getWidth() <<
"_t"), success();
1855 return (os <<
"int" << iType.getWidth() <<
"_t"), success();
1857 return emitError(loc,
"cannot emit integer type ") << type;
1860 if (
auto fType = dyn_cast<FloatType>(type)) {
1861 switch (fType.getWidth()) {
1863 if (llvm::isa<Float16Type>(type))
1864 return (os <<
"_Float16"), success();
1865 if (llvm::isa<BFloat16Type>(type))
1866 return (os <<
"__bf16"), success();
1868 return emitError(loc,
"cannot emit float type ") << type;
1871 return (os <<
"float"), success();
1873 return (os <<
"double"), success();
1875 return emitError(loc,
"cannot emit float type ") << type;
1878 if (
auto iType = dyn_cast<IndexType>(type))
1879 return (os <<
"size_t"), success();
1880 if (
auto sType = dyn_cast<emitc::SizeTType>(type))
1881 return (os <<
"size_t"), success();
1882 if (
auto sType = dyn_cast<emitc::SignedSizeTType>(type))
1883 return (os <<
"ssize_t"), success();
1884 if (
auto pType = dyn_cast<emitc::PtrDiffTType>(type))
1885 return (os <<
"ptrdiff_t"), success();
1886 if (
auto tType = dyn_cast<TensorType>(type)) {
1887 if (!tType.hasRank())
1888 return emitError(loc,
"cannot emit unranked tensor type");
1889 if (!tType.hasStaticShape())
1890 return emitError(loc,
"cannot emit tensor type with non static shape");
1892 if (isa<ArrayType>(tType.getElementType()))
1893 return emitError(loc,
"cannot emit tensor of array type ") << type;
1894 if (
failed(emitType(loc, tType.getElementType())))
1896 auto shape = tType.getShape();
1897 for (
auto dimSize : shape) {
1904 if (
auto tType = dyn_cast<TupleType>(type))
1905 return emitTupleType(loc, tType.getTypes());
1906 if (
auto oType = dyn_cast<emitc::OpaqueType>(type)) {
1907 os << oType.getValue();
1910 if (
auto aType = dyn_cast<emitc::ArrayType>(type)) {
1911 if (
failed(emitType(loc, aType.getElementType())))
1913 for (
auto dim : aType.getShape())
1914 os <<
"[" << dim <<
"]";
1917 if (
auto lType = dyn_cast<emitc::LValueType>(type))
1918 return emitType(loc, lType.getValueType());
1919 if (
auto pType = dyn_cast<emitc::PointerType>(type)) {
1920 if (isa<ArrayType>(pType.getPointee()))
1921 return emitError(loc,
"cannot emit pointer to array type ") << type;
1922 if (
failed(emitType(loc, pType.getPointee())))
1927 return emitError(loc,
"cannot emit type ") << type;
1931 switch (types.size()) {
1936 return emitType(loc, types.front());
1938 return emitTupleType(loc, types);
1943 if (llvm::any_of(types, llvm::IsaPred<ArrayType>)) {
1944 return emitError(loc,
"cannot emit tuple of array type");
1946 os <<
"std::tuple<";
1948 types, os, [&](
Type type) {
return emitType(loc, type); })))
1954 void CppEmitter::resetValueCounter() { valueCount = 0; }
1956 void CppEmitter::increaseLoopNestingLevel() { loopNestingLevel++; }
1958 void CppEmitter::decreaseLoopNestingLevel() { loopNestingLevel--; }
1961 bool declareVariablesAtTop,
1963 CppEmitter emitter(os, declareVariablesAtTop, fileId);
1964 return emitter.emitOperation(*op,
false);
static LogicalResult printCallOperation(CppEmitter &emitter, Operation *callOp, StringRef callee)
static bool shouldBeInlined(ExpressionOp expressionOp)
Determine whether expression expressionOp should be emitted inline, i.e.
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 LogicalResult printOperation(CppEmitter &emitter, emitc::ConstantOp constantOp)
static LogicalResult printUnaryOperation(CppEmitter &emitter, Operation *operation, StringRef unaryOperator)
static bool hasDeferredEmission(Operation *op)
Determine whether expression op should be emitted in a deferred way.
static LogicalResult interleaveWithError(ForwardIterator begin, ForwardIterator end, UnaryFunctor eachFn, NullaryFunctor betweenFn)
Convenience functions to produce interleaved output with functions returning a LogicalResult.
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 class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
NamedAttribute represents a combination of a name and an Attribute value.
This is a value defined by a result of an operation.
Operation * getOwner() const
Returns the operation that owns this result.
Operation is the basic unit of execution within MLIR.
Value getOperand(unsigned idx)
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
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),...
Location getLoc()
The source location the operation was defined or derived from.
unsigned getNumOperands()
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
ArrayRef< NamedAttribute > getAttrs()
Return all of the attributes on this operation.
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
OperationName getName()
The name of an operation is the key identifier for it.
operand_range getOperands()
Returns an iterator on the underlying Value's.
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.
iterator_range< OpIterator > getOps()
llvm::iplist< Block > BlockListType
OpIterator op_begin()
Return iterators that walk the operations nested directly within this region.
MutableArrayRef< BlockArgument > BlockArgListType
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.
user_range getUsers() const
bool hasOneUse() const
Returns true if this value has exactly one use.
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()
bool wasInterrupted() const
Returns true if the walk was interrupted.
raw_ostream subclass that simplifies indention a sequence of code.
raw_indented_ostream & unindent()
Decreases the indent and returning this raw_indented_ostream.
raw_indented_ostream & indent()
Increases 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.