20 #include "llvm/ADT/ScopedHashTable.h"
21 #include "llvm/ADT/StringExtras.h"
22 #include "llvm/ADT/TypeSwitch.h"
23 #include "llvm/Support/Debug.h"
24 #include "llvm/Support/FormatVariadic.h"
27 #define DEBUG_TYPE "translate-to-cpp"
37 typename NullaryFunctor>
40 UnaryFunctor eachFn, NullaryFunctor betweenFn) {
43 if (failed(eachFn(*begin)))
46 for (; begin != end; ++begin) {
48 if (failed(eachFn(*begin)))
54 template <
typename Container,
typename UnaryFunctor,
typename NullaryFunctor>
57 NullaryFunctor betweenFn) {
61 template <
typename Container,
typename UnaryFunctor>
64 UnaryFunctor eachFn) {
72 .Case<emitc::AddOp>([&](
auto op) {
return 12; })
73 .Case<emitc::ApplyOp>([&](
auto op) {
return 15; })
74 .Case<emitc::BitwiseAndOp>([&](
auto op) {
return 7; })
75 .Case<emitc::BitwiseLeftShiftOp>([&](
auto op) {
return 11; })
76 .Case<emitc::BitwiseNotOp>([&](
auto op) {
return 15; })
77 .Case<emitc::BitwiseOrOp>([&](
auto op) {
return 5; })
78 .Case<emitc::BitwiseRightShiftOp>([&](
auto op) {
return 11; })
79 .Case<emitc::BitwiseXorOp>([&](
auto op) {
return 6; })
80 .Case<emitc::CallOp>([&](
auto op) {
return 16; })
81 .Case<emitc::CallOpaqueOp>([&](
auto op) {
return 16; })
82 .Case<emitc::CastOp>([&](
auto op) {
return 15; })
83 .Case<emitc::CmpOp>([&](
auto op) -> FailureOr<int> {
84 switch (op.getPredicate()) {
85 case emitc::CmpPredicate::eq:
86 case emitc::CmpPredicate::ne:
88 case emitc::CmpPredicate::lt:
89 case emitc::CmpPredicate::le:
90 case emitc::CmpPredicate::gt:
91 case emitc::CmpPredicate::ge:
93 case emitc::CmpPredicate::three_way:
96 return op->emitError(
"unsupported cmp predicate");
98 .Case<emitc::ConditionalOp>([&](
auto op) {
return 2; })
99 .Case<emitc::DivOp>([&](
auto op) {
return 13; })
100 .Case<emitc::LoadOp>([&](
auto op) {
return 16; })
101 .Case<emitc::LogicalAndOp>([&](
auto op) {
return 4; })
102 .Case<emitc::LogicalNotOp>([&](
auto op) {
return 15; })
103 .Case<emitc::LogicalOrOp>([&](
auto op) {
return 3; })
104 .Case<emitc::MulOp>([&](
auto op) {
return 13; })
105 .Case<emitc::RemOp>([&](
auto op) {
return 13; })
106 .Case<emitc::SubOp>([&](
auto op) {
return 12; })
107 .Case<emitc::UnaryMinusOp>([&](
auto op) {
return 15; })
108 .Case<emitc::UnaryPlusOp>([&](
auto op) {
return 15; })
109 .Default([](
auto op) {
return op->emitError(
"unsupported operation"); });
115 explicit CppEmitter(raw_ostream &os,
bool declareVariablesAtTop,
126 LogicalResult emitOperation(
Operation &op,
bool trailingSemicolon);
142 LogicalResult emitVariableAssignment(
OpResult result);
145 LogicalResult emitVariableDeclaration(
OpResult result,
146 bool trailingSemicolon);
149 LogicalResult emitVariableDeclaration(
Location loc,
Type type,
158 LogicalResult emitAssignPrefix(
Operation &op);
161 LogicalResult emitGlobalVariable(GlobalOp op);
164 LogicalResult emitLabel(
Block &block);
168 LogicalResult emitOperandsAndAttributes(
Operation &op,
172 LogicalResult emitOperands(
Operation &op);
175 LogicalResult emitOperand(
Value value);
178 LogicalResult emitExpression(ExpressionOp expressionOp);
181 void cacheDeferredOpResult(
Value value, StringRef str);
184 StringRef getOrCreateName(
Value val);
188 StringRef getOrCreateInductionVarName(
Value val);
191 std::string getSubscriptName(emitc::SubscriptOp op);
194 std::string createMemberAccess(emitc::MemberOp op);
197 std::string createMemberAccess(emitc::MemberOfPtrOp op);
200 StringRef getOrCreateName(
Block &block);
203 bool shouldMapToUnsigned(IntegerType::SignednessSemantics val);
207 ~Scope() { emitter.labelInScopeCount.pop(); }
210 llvm::ScopedHashTableScope<Value, std::string> valueMapperScope;
211 llvm::ScopedHashTableScope<Block *, std::string> blockMapperScope;
214 Scope(CppEmitter &emitter)
215 : valueMapperScope(emitter.valueMapper),
216 blockMapperScope(emitter.blockMapper), emitter(emitter) {
217 emitter.labelInScopeCount.push(emitter.labelInScopeCount.top());
224 struct FunctionScope : Scope {
225 FunctionScope(CppEmitter &emitter) : Scope(emitter) {
227 emitter.resetValueCounter();
233 struct LoopScope : Scope {
234 LoopScope(CppEmitter &emitter) : Scope(emitter) {
235 emitter.increaseLoopNestingLevel();
237 ~LoopScope() { emitter.decreaseLoopNestingLevel(); }
241 bool hasValueInScope(
Value val);
244 bool hasBlockLabel(
Block &block);
251 bool shouldDeclareVariablesAtTop() {
return declareVariablesAtTop; };
254 bool shouldEmitFile(FileOp file) {
255 return !fileId.empty() && file.getId() == fileId;
259 ExpressionOp getEmittedExpression() {
return emittedExpression; }
263 bool isPartOfCurrentExpression(
Value value) {
264 if (!emittedExpression)
269 auto operandExpression = dyn_cast<ExpressionOp>(def->
getParentOp());
270 return operandExpression == emittedExpression;
274 void resetValueCounter();
277 void increaseLoopNestingLevel();
280 void decreaseLoopNestingLevel();
283 using ValueMapper = llvm::ScopedHashTable<Value, std::string>;
284 using BlockMapper = llvm::ScopedHashTable<Block *, std::string>;
292 bool declareVariablesAtTop;
298 ValueMapper valueMapper;
301 BlockMapper blockMapper;
304 llvm::ScopedHashTableScope<Value, std::string> defaultValueMapperScope;
305 llvm::ScopedHashTableScope<Block *, std::string> defaultBlockMapperScope;
307 std::stack<int64_t> labelInScopeCount;
311 uint64_t loopNestingLevel{0};
314 unsigned int valueCount{0};
317 ExpressionOp emittedExpression;
320 void pushExpressionPrecedence(
int precedence) {
321 emittedExpressionPrecedence.push_back(precedence);
323 void popExpressionPrecedence() { emittedExpressionPrecedence.pop_back(); }
324 static int lowestPrecedence() {
return 0; }
325 int getExpressionPrecedence() {
326 if (emittedExpressionPrecedence.empty())
327 return lowestPrecedence();
328 return emittedExpressionPrecedence.back();
335 return isa_and_nonnull<emitc::GetGlobalOp, emitc::LiteralOp, emitc::MemberOp,
336 emitc::MemberOfPtrOp, emitc::SubscriptOp,
337 emitc::GetFieldOp>(op);
347 if (expressionOp.getDoNotInline())
352 if (expressionOp.hasSideEffects())
356 Value result = expressionOp.getResult();
369 return !isa<emitc::CExpressionInterface>(*user);
378 if (emitter.shouldDeclareVariablesAtTop()) {
380 if (
auto oAttr = dyn_cast<emitc::OpaqueAttr>(value)) {
381 if (oAttr.getValue().empty())
385 if (failed(emitter.emitVariableAssignment(result)))
387 return emitter.emitAttribute(operation->
getLoc(), value);
391 if (
auto oAttr = dyn_cast<emitc::OpaqueAttr>(value)) {
392 if (oAttr.getValue().empty())
394 return emitter.emitVariableDeclaration(result,
399 if (failed(emitter.emitAssignPrefix(*operation)))
401 return emitter.emitAttribute(operation->
getLoc(), value);
405 emitc::ConstantOp constantOp) {
406 Operation *operation = constantOp.getOperation();
413 emitc::VariableOp variableOp) {
414 Operation *operation = variableOp.getOperation();
421 emitc::GlobalOp globalOp) {
423 return emitter.emitGlobalVariable(globalOp);
427 emitc::AssignOp assignOp) {
430 if (failed(emitter.emitVariableAssignment(result)))
433 return emitter.emitOperand(assignOp.getValue());
437 if (failed(emitter.emitAssignPrefix(*loadOp)))
440 return emitter.emitOperand(loadOp.getOperand());
445 StringRef binaryOperator) {
446 raw_ostream &os = emitter.ostream();
448 if (failed(emitter.emitAssignPrefix(*operation)))
451 if (failed(emitter.emitOperand(operation->
getOperand(0))))
454 os <<
" " << binaryOperator <<
" ";
456 if (failed(emitter.emitOperand(operation->
getOperand(1))))
464 StringRef unaryOperator) {
465 raw_ostream &os = emitter.ostream();
467 if (failed(emitter.emitAssignPrefix(*operation)))
472 if (failed(emitter.emitOperand(operation->
getOperand(0))))
479 Operation *operation = addOp.getOperation();
485 Operation *operation = divOp.getOperation();
491 Operation *operation = mulOp.getOperation();
497 Operation *operation = remOp.getOperation();
503 Operation *operation = subOp.getOperation();
511 std::next(iteratorOp) != end; ++iteratorOp) {
512 if (failed(emitter.emitOperation(*iteratorOp,
true)))
520 emitc::SwitchOp switchOp) {
524 if (failed(emitter.emitOperand(switchOp.getArg())))
528 for (
auto pair : llvm::zip(switchOp.getCases(), switchOp.getCaseRegions())) {
529 os <<
"\ncase " << std::get<0>(pair) <<
": {\n";
538 os <<
"\ndefault: {\n";
541 if (failed(
emitSwitchCase(emitter, os, switchOp.getDefaultRegion())))
549 Operation *operation = cmpOp.getOperation();
551 StringRef binaryOperator;
553 switch (cmpOp.getPredicate()) {
554 case emitc::CmpPredicate::eq:
555 binaryOperator =
"==";
557 case emitc::CmpPredicate::ne:
558 binaryOperator =
"!=";
560 case emitc::CmpPredicate::lt:
561 binaryOperator =
"<";
563 case emitc::CmpPredicate::le:
564 binaryOperator =
"<=";
566 case emitc::CmpPredicate::gt:
567 binaryOperator =
">";
569 case emitc::CmpPredicate::ge:
570 binaryOperator =
">=";
572 case emitc::CmpPredicate::three_way:
573 binaryOperator =
"<=>";
581 emitc::ConditionalOp conditionalOp) {
582 raw_ostream &os = emitter.ostream();
584 if (failed(emitter.emitAssignPrefix(*conditionalOp)))
587 if (failed(emitter.emitOperand(conditionalOp.getCondition())))
592 if (failed(emitter.emitOperand(conditionalOp.getTrueValue())))
597 if (failed(emitter.emitOperand(conditionalOp.getFalseValue())))
604 emitc::VerbatimOp verbatimOp) {
605 raw_ostream &os = emitter.ostream();
607 FailureOr<SmallVector<ReplacementItem>> items =
608 verbatimOp.parseFormatString();
612 auto fmtArg = verbatimOp.getFmtArgs().begin();
615 if (
auto *str = std::get_if<StringRef>(&item)) {
618 if (failed(emitter.emitOperand(*fmtArg++)))
627 cf::BranchOp branchOp) {
628 raw_ostream &os = emitter.ostream();
632 llvm::zip(branchOp.getOperands(), successor.
getArguments())) {
633 Value &operand = std::get<0>(pair);
635 os << emitter.getOrCreateName(argument) <<
" = "
636 << emitter.getOrCreateName(operand) <<
";\n";
640 if (!(emitter.hasBlockLabel(successor)))
641 return branchOp.emitOpError(
"unable to find label for successor block");
642 os << emitter.getOrCreateName(successor);
647 cf::CondBranchOp condBranchOp) {
649 Block &trueSuccessor = *condBranchOp.getTrueDest();
650 Block &falseSuccessor = *condBranchOp.getFalseDest();
653 if (failed(emitter.emitOperand(condBranchOp.getCondition())))
660 for (
auto pair : llvm::zip(condBranchOp.getTrueOperands(),
662 Value &operand = std::get<0>(pair);
664 os << emitter.getOrCreateName(argument) <<
" = "
665 << emitter.getOrCreateName(operand) <<
";\n";
669 if (!(emitter.hasBlockLabel(trueSuccessor))) {
670 return condBranchOp.emitOpError(
"unable to find label for successor block");
672 os << emitter.getOrCreateName(trueSuccessor) <<
";\n";
676 for (
auto pair : llvm::zip(condBranchOp.getFalseOperands(),
678 Value &operand = std::get<0>(pair);
680 os << emitter.getOrCreateName(argument) <<
" = "
681 << emitter.getOrCreateName(operand) <<
";\n";
685 if (!(emitter.hasBlockLabel(falseSuccessor))) {
686 return condBranchOp.emitOpError()
687 <<
"unable to find label for successor block";
689 os << emitter.getOrCreateName(falseSuccessor) <<
";\n";
696 if (failed(emitter.emitAssignPrefix(*callOp)))
699 raw_ostream &os = emitter.ostream();
701 if (failed(emitter.emitOperands(*callOp)))
708 Operation *operation = callOp.getOperation();
709 StringRef callee = callOp.getCallee();
715 Operation *operation = callOp.getOperation();
716 StringRef callee = callOp.getCallee();
722 emitc::CallOpaqueOp callOpaqueOp) {
723 raw_ostream &os = emitter.ostream();
724 Operation &op = *callOpaqueOp.getOperation();
726 if (failed(emitter.emitAssignPrefix(op)))
728 os << callOpaqueOp.getCallee();
734 auto emitTemplateArgs = [&](
Attribute attr) -> LogicalResult {
735 return emitter.emitAttribute(op.
getLoc(), attr);
738 if (callOpaqueOp.getTemplateArgs()) {
746 auto emitArgs = [&](
Attribute attr) -> LogicalResult {
747 if (
auto t = dyn_cast<IntegerAttr>(attr)) {
749 if (t.getType().isIndex()) {
750 int64_t idx = t.getInt();
752 if (!emitter.hasValueInScope(operand))
754 << idx <<
"'s value not defined in scope";
755 os << emitter.getOrCreateName(operand);
759 if (failed(emitter.emitAttribute(op.
getLoc(), attr)))
767 LogicalResult emittedArgs =
768 callOpaqueOp.getArgs()
770 : emitter.emitOperands(op);
771 if (failed(emittedArgs))
778 emitc::ApplyOp applyOp) {
779 raw_ostream &os = emitter.ostream();
782 if (failed(emitter.emitAssignPrefix(op)))
784 os << applyOp.getApplicableOperator();
785 os << emitter.getOrCreateName(applyOp.getOperand());
791 emitc::BitwiseAndOp bitwiseAndOp) {
792 Operation *operation = bitwiseAndOp.getOperation();
798 emitc::BitwiseLeftShiftOp bitwiseLeftShiftOp) {
799 Operation *operation = bitwiseLeftShiftOp.getOperation();
804 emitc::BitwiseNotOp bitwiseNotOp) {
805 Operation *operation = bitwiseNotOp.getOperation();
810 emitc::BitwiseOrOp bitwiseOrOp) {
811 Operation *operation = bitwiseOrOp.getOperation();
817 emitc::BitwiseRightShiftOp bitwiseRightShiftOp) {
818 Operation *operation = bitwiseRightShiftOp.getOperation();
823 emitc::BitwiseXorOp bitwiseXorOp) {
824 Operation *operation = bitwiseXorOp.getOperation();
829 emitc::UnaryPlusOp unaryPlusOp) {
830 Operation *operation = unaryPlusOp.getOperation();
835 emitc::UnaryMinusOp unaryMinusOp) {
836 Operation *operation = unaryMinusOp.getOperation();
841 raw_ostream &os = emitter.ostream();
844 if (failed(emitter.emitAssignPrefix(op)))
850 return emitter.emitOperand(castOp.getOperand());
854 emitc::ExpressionOp expressionOp) {
858 Operation &op = *expressionOp.getOperation();
860 if (failed(emitter.emitAssignPrefix(op)))
863 return emitter.emitExpression(expressionOp);
867 emitc::IncludeOp includeOp) {
868 raw_ostream &os = emitter.ostream();
871 if (includeOp.getIsStandardInclude())
872 os <<
"<" << includeOp.getInclude() <<
">";
874 os <<
"\"" << includeOp.getInclude() <<
"\"";
880 emitc::LogicalAndOp logicalAndOp) {
881 Operation *operation = logicalAndOp.getOperation();
886 emitc::LogicalNotOp logicalNotOp) {
887 Operation *operation = logicalNotOp.getOperation();
892 emitc::LogicalOrOp logicalOrOp) {
893 Operation *operation = logicalOrOp.getOperation();
903 auto requiresParentheses = [&](
Value value) {
912 emitter.emitType(forOp.getLoc(), forOp.getInductionVar().getType())))
915 os << emitter.getOrCreateInductionVarName(forOp.getInductionVar());
917 if (failed(emitter.emitOperand(forOp.getLowerBound())))
920 os << emitter.getOrCreateInductionVarName(forOp.getInductionVar());
922 Value upperBound = forOp.getUpperBound();
923 bool upperBoundRequiresParentheses = requiresParentheses(upperBound);
924 if (upperBoundRequiresParentheses)
926 if (failed(emitter.emitOperand(upperBound)))
928 if (upperBoundRequiresParentheses)
931 os << emitter.getOrCreateInductionVarName(forOp.getInductionVar());
933 if (failed(emitter.emitOperand(forOp.getStep())))
938 CppEmitter::LoopScope lScope(emitter);
940 Region &forRegion = forOp.getRegion();
941 auto regionOps = forRegion.
getOps();
944 for (
auto it = regionOps.begin(); std::next(it) != regionOps.end(); ++it) {
945 if (failed(emitter.emitOperation(*it,
true)))
959 auto emitAllExceptLast = [&emitter](
Region ®ion) {
961 for (; std::next(it) != end; ++it) {
962 if (failed(emitter.emitOperation(*it,
true)))
965 assert(isa<emitc::YieldOp>(*it) &&
966 "Expected last operation in the region to be emitc::yield");
971 if (failed(emitter.emitOperand(ifOp.getCondition())))
975 if (failed(emitAllExceptLast(ifOp.getThenRegion())))
979 Region &elseRegion = ifOp.getElseRegion();
980 if (!elseRegion.
empty()) {
983 if (failed(emitAllExceptLast(elseRegion)))
992 func::ReturnOp returnOp) {
993 raw_ostream &os = emitter.ostream();
995 switch (returnOp.getNumOperands()) {
1000 if (failed(emitter.emitOperand(returnOp.getOperand(0))))
1004 os <<
" std::make_tuple(";
1005 if (failed(emitter.emitOperandsAndAttributes(*returnOp.getOperation())))
1013 emitc::ReturnOp returnOp) {
1014 raw_ostream &os = emitter.ostream();
1016 if (returnOp.getNumOperands() == 0)
1020 if (failed(emitter.emitOperand(returnOp.getOperand())))
1027 if (failed(emitter.emitOperation(op,
false)))
1035 os <<
"class " << classOp.getSymName();
1036 if (classOp.getFinalSpecifier())
1038 os <<
" {\n public:\n";
1042 if (failed(emitter.emitOperation(op,
false)))
1052 raw_ostream &os = emitter.ostream();
1053 if (failed(emitter.emitVariableDeclaration(
1054 fieldOp->getLoc(), fieldOp.getType(), fieldOp.getSymName())))
1056 std::optional<Attribute> initialValue = fieldOp.getInitialValue();
1059 if (failed(emitter.emitAttribute(fieldOp->getLoc(), *initialValue)))
1068 if (!emitter.shouldEmitFile(file))
1072 if (failed(emitter.emitOperation(op,
false)))
1085 return emitter.emitType(functionOp->
getLoc(), arg);
1096 return emitter.emitVariableDeclaration(
1097 functionOp->
getLoc(), arg.
getType(), emitter.getOrCreateName(arg));
1107 if (emitter.shouldDeclareVariablesAtTop()) {
1112 if (isa<emitc::ExpressionOp>(op->
getParentOp()) ||
1113 (isa<emitc::ExpressionOp>(op) &&
1117 if (failed(emitter.emitVariableDeclaration(
1120 op->
emitError(
"unable to declare result variable for op"));
1130 for (
Block &block : blocks) {
1131 emitter.getOrCreateName(block);
1135 for (
Block &block : llvm::drop_begin(blocks)) {
1137 if (emitter.hasValueInScope(arg))
1138 return functionOp->
emitOpError(
" block argument #")
1139 << arg.getArgNumber() <<
" is out of scope";
1140 if (isa<ArrayType, LValueType>(arg.getType()))
1141 return functionOp->
emitOpError(
"cannot emit block argument #")
1142 << arg.getArgNumber() <<
" with type " << arg.getType();
1144 emitter.emitType(block.getParentOp()->getLoc(), arg.getType()))) {
1147 os <<
" " << emitter.getOrCreateName(arg) <<
";\n";
1151 for (
Block &block : blocks) {
1153 if (!block.hasNoPredecessors()) {
1154 if (failed(emitter.emitLabel(block)))
1157 for (
Operation &op : block.getOperations()) {
1158 if (failed(emitter.emitOperation(op,
true)))
1169 func::FuncOp functionOp) {
1171 if (!emitter.shouldDeclareVariablesAtTop() &&
1172 functionOp.getBlocks().size() > 1) {
1173 return functionOp.emitOpError(
1174 "with multiple blocks needs variables declared at top");
1177 if (llvm::any_of(functionOp.getArgumentTypes(), llvm::IsaPred<LValueType>)) {
1178 return functionOp.emitOpError()
1179 <<
"cannot emit lvalue type as argument type";
1182 if (llvm::any_of(functionOp.getResultTypes(), llvm::IsaPred<ArrayType>)) {
1183 return functionOp.emitOpError() <<
"cannot emit array type as result type";
1186 CppEmitter::FunctionScope scope(emitter);
1188 if (failed(emitter.emitTypes(functionOp.getLoc(),
1189 functionOp.getFunctionType().getResults())))
1191 os <<
" " << functionOp.getName();
1194 Operation *operation = functionOp.getOperation();
1206 emitc::FuncOp functionOp) {
1208 if (!emitter.shouldDeclareVariablesAtTop() &&
1209 functionOp.getBlocks().size() > 1) {
1210 return functionOp.emitOpError(
1211 "with multiple blocks needs variables declared at top");
1214 CppEmitter::FunctionScope scope(emitter);
1216 if (functionOp.getSpecifiers()) {
1217 for (
Attribute specifier : functionOp.getSpecifiersAttr()) {
1218 os << cast<StringAttr>(specifier).str() <<
" ";
1222 if (failed(emitter.emitTypes(functionOp.getLoc(),
1223 functionOp.getFunctionType().getResults())))
1225 os <<
" " << functionOp.getName();
1228 Operation *operation = functionOp.getOperation();
1229 if (functionOp.isExternal()) {
1231 functionOp.getArgumentTypes())))
1247 DeclareFuncOp declareFuncOp) {
1250 CppEmitter::FunctionScope scope(emitter);
1251 auto functionOp = SymbolTable::lookupNearestSymbolFrom<emitc::FuncOp>(
1252 declareFuncOp, declareFuncOp.getSymNameAttr());
1257 if (functionOp.getSpecifiers()) {
1258 for (
Attribute specifier : functionOp.getSpecifiersAttr()) {
1259 os << cast<StringAttr>(specifier).str() <<
" ";
1263 if (failed(emitter.emitTypes(functionOp.getLoc(),
1264 functionOp.getFunctionType().getResults())))
1266 os <<
" " << functionOp.getName();
1269 Operation *operation = functionOp.getOperation();
1277 CppEmitter::CppEmitter(raw_ostream &os,
bool declareVariablesAtTop,
1279 : os(os), declareVariablesAtTop(declareVariablesAtTop),
1280 fileId(fileId.str()), defaultValueMapperScope(valueMapper),
1281 defaultBlockMapperScope(blockMapper) {
1282 labelInScopeCount.push(0);
1285 std::string CppEmitter::getSubscriptName(emitc::SubscriptOp op) {
1287 llvm::raw_string_ostream ss(out);
1288 ss << getOrCreateName(op.getValue());
1289 for (
auto index : op.getIndices()) {
1290 ss <<
"[" << getOrCreateName(index) <<
"]";
1295 std::string CppEmitter::createMemberAccess(emitc::MemberOp op) {
1297 llvm::raw_string_ostream ss(out);
1298 ss << getOrCreateName(op.getOperand());
1299 ss <<
"." << op.getMember();
1303 std::string CppEmitter::createMemberAccess(emitc::MemberOfPtrOp op) {
1305 llvm::raw_string_ostream ss(out);
1306 ss << getOrCreateName(op.getOperand());
1307 ss <<
"->" << op.getMember();
1311 void CppEmitter::cacheDeferredOpResult(
Value value, StringRef str) {
1312 if (!valueMapper.count(value))
1313 valueMapper.insert(value, str.str());
1317 StringRef CppEmitter::getOrCreateName(
Value val) {
1318 if (!valueMapper.count(val)) {
1320 "cacheDeferredOpResult should have been called on this value, "
1321 "update the emitOperation function.");
1323 valueMapper.insert(val, formatv(
"v{0}", ++valueCount));
1325 return *valueMapper.begin(val);
1330 StringRef CppEmitter::getOrCreateInductionVarName(
Value val) {
1331 if (!valueMapper.count(val)) {
1333 int64_t identifier =
'i' + loopNestingLevel;
1335 if (identifier >=
'i' && identifier <=
't') {
1336 valueMapper.insert(val,
1337 formatv(
"{0}{1}", (
char)identifier, ++valueCount));
1340 valueMapper.insert(val, formatv(
"u{0}", ++valueCount));
1343 return *valueMapper.begin(val);
1347 StringRef CppEmitter::getOrCreateName(
Block &block) {
1348 if (!blockMapper.count(&block))
1349 blockMapper.insert(&block, formatv(
"label{0}", ++labelInScopeCount.top()));
1350 return *blockMapper.begin(&block);
1353 bool CppEmitter::shouldMapToUnsigned(IntegerType::SignednessSemantics val) {
1355 case IntegerType::Signless:
1359 case IntegerType::Unsigned:
1362 llvm_unreachable(
"Unexpected IntegerType::SignednessSemantics");
1365 bool CppEmitter::hasValueInScope(
Value val) {
return valueMapper.count(val); }
1367 bool CppEmitter::hasBlockLabel(
Block &block) {
1368 return blockMapper.count(&block);
1372 auto printInt = [&](
const APInt &val,
bool isUnsigned) {
1373 if (val.getBitWidth() == 1) {
1374 if (val.getBoolValue())
1380 val.toString(strValue, 10, !isUnsigned,
false);
1385 auto printFloat = [&](
const APFloat &val) {
1386 if (val.isFinite()) {
1389 val.toString(strValue, 0, 0,
false);
1391 switch (llvm::APFloatBase::SemanticsToEnum(val.getSemantics())) {
1392 case llvm::APFloatBase::S_IEEEhalf:
1395 case llvm::APFloatBase::S_BFloat:
1398 case llvm::APFloatBase::S_IEEEsingle:
1401 case llvm::APFloatBase::S_IEEEdouble:
1404 llvm_unreachable(
"unsupported floating point type");
1406 }
else if (val.isNaN()) {
1408 }
else if (val.isInfinity()) {
1409 if (val.isNegative())
1416 if (
auto fAttr = dyn_cast<FloatAttr>(attr)) {
1417 if (!isa<Float16Type, BFloat16Type, Float32Type, Float64Type>(
1420 loc,
"expected floating point attribute to be f16, bf16, f32 or f64");
1422 printFloat(fAttr.getValue());
1425 if (
auto dense = dyn_cast<DenseFPElementsAttr>(attr)) {
1426 if (!isa<Float16Type, BFloat16Type, Float32Type, Float64Type>(
1427 dense.getElementType())) {
1429 loc,
"expected floating point attribute to be f16, bf16, f32 or f64");
1432 interleaveComma(dense, os, [&](
const APFloat &val) { printFloat(val); });
1438 if (
auto iAttr = dyn_cast<IntegerAttr>(attr)) {
1439 if (
auto iType = dyn_cast<IntegerType>(iAttr.getType())) {
1440 printInt(iAttr.getValue(), shouldMapToUnsigned(iType.getSignedness()));
1443 if (
auto iType = dyn_cast<IndexType>(iAttr.getType())) {
1444 printInt(iAttr.getValue(),
false);
1448 if (
auto dense = dyn_cast<DenseIntElementsAttr>(attr)) {
1449 if (
auto iType = dyn_cast<IntegerType>(
1450 cast<TensorType>(dense.getType()).getElementType())) {
1452 interleaveComma(dense, os, [&](
const APInt &val) {
1453 printInt(val, shouldMapToUnsigned(iType.getSignedness()));
1458 if (
auto iType = dyn_cast<IndexType>(
1459 cast<TensorType>(dense.getType()).getElementType())) {
1461 interleaveComma(dense, os,
1462 [&](
const APInt &val) { printInt(val,
false); });
1469 if (
auto oAttr = dyn_cast<emitc::OpaqueAttr>(attr)) {
1470 os << oAttr.getValue();
1475 if (
auto sAttr = dyn_cast<SymbolRefAttr>(attr)) {
1476 if (sAttr.getNestedReferences().size() > 1)
1477 return emitError(loc,
"attribute has more than 1 nested reference");
1478 os << sAttr.getRootReference().getValue();
1483 if (
auto type = dyn_cast<TypeAttr>(attr))
1484 return emitType(loc, type.getValue());
1486 return emitError(loc,
"cannot emit attribute: ") << attr;
1489 LogicalResult CppEmitter::emitExpression(ExpressionOp expressionOp) {
1490 assert(emittedExpressionPrecedence.empty() &&
1491 "Expected precedence stack to be empty");
1492 Operation *rootOp = expressionOp.getRootOp();
1494 emittedExpression = expressionOp;
1496 if (failed(precedence))
1498 pushExpressionPrecedence(precedence.value());
1500 if (failed(emitOperation(*rootOp,
false)))
1503 popExpressionPrecedence();
1504 assert(emittedExpressionPrecedence.empty() &&
1505 "Expected precedence stack to be empty");
1506 emittedExpression =
nullptr;
1511 LogicalResult CppEmitter::emitOperand(
Value value) {
1512 if (isPartOfCurrentExpression(value)) {
1514 assert(def &&
"Expected operand to be defined by an operation");
1516 if (failed(precedence))
1522 bool encloseInParenthesis = precedence.value() <= getExpressionPrecedence();
1523 if (encloseInParenthesis)
1525 pushExpressionPrecedence(precedence.value());
1527 if (failed(emitOperation(*def,
false)))
1530 if (encloseInParenthesis)
1533 popExpressionPrecedence();
1539 return emitExpression(expressionOp);
1541 os << getOrCreateName(value);
1545 LogicalResult CppEmitter::emitOperands(
Operation &op) {
1549 if (getEmittedExpression())
1550 pushExpressionPrecedence(lowestPrecedence());
1551 if (failed(emitOperand(operand)))
1553 if (getEmittedExpression())
1554 popExpressionPrecedence();
1560 CppEmitter::emitOperandsAndAttributes(
Operation &op,
1562 if (failed(emitOperands(op)))
1567 if (!llvm::is_contained(exclude, attr.getName().strref())) {
1574 auto emitNamedAttribute = [&](
NamedAttribute attr) -> LogicalResult {
1575 if (llvm::is_contained(exclude, attr.getName().strref()))
1577 os <<
"/* " << attr.getName().getValue() <<
" */";
1578 if (failed(emitAttribute(op.
getLoc(), attr.getValue())))
1585 LogicalResult CppEmitter::emitVariableAssignment(
OpResult result) {
1586 if (!hasValueInScope(result)) {
1588 "result variable for the operation has not been declared");
1590 os << getOrCreateName(result) <<
" = ";
1594 LogicalResult CppEmitter::emitVariableDeclaration(
OpResult result,
1595 bool trailingSemicolon) {
1598 if (hasValueInScope(result)) {
1600 "result variable for the operation already declared");
1604 getOrCreateName(result))))
1606 if (trailingSemicolon)
1611 LogicalResult CppEmitter::emitGlobalVariable(GlobalOp op) {
1612 if (op.getExternSpecifier())
1614 else if (op.getStaticSpecifier())
1616 if (op.getConstSpecifier())
1619 if (failed(emitVariableDeclaration(op->getLoc(), op.getType(),
1620 op.getSymName()))) {
1624 std::optional<Attribute> initialValue = op.getInitialValue();
1627 if (failed(emitAttribute(op->getLoc(), *initialValue)))
1635 LogicalResult CppEmitter::emitAssignPrefix(
Operation &op) {
1637 if (getEmittedExpression())
1645 if (shouldDeclareVariablesAtTop()) {
1646 if (failed(emitVariableAssignment(result)))
1649 if (failed(emitVariableDeclaration(result,
false)))
1656 if (!shouldDeclareVariablesAtTop()) {
1658 if (failed(emitVariableDeclaration(result,
true)))
1664 [&](
Value result) { os << getOrCreateName(result); });
1670 LogicalResult CppEmitter::emitLabel(
Block &block) {
1671 if (!hasBlockLabel(block))
1675 os.getOStream() << getOrCreateName(block) <<
":\n";
1679 LogicalResult CppEmitter::emitOperation(
Operation &op,
bool trailingSemicolon) {
1680 LogicalResult status =
1683 .Case<ModuleOp>([&](
auto op) {
return printOperation(*
this, op); })
1685 .Case<cf::BranchOp, cf::CondBranchOp>(
1688 .Case<emitc::AddOp, emitc::ApplyOp, emitc::AssignOp,
1689 emitc::BitwiseAndOp, emitc::BitwiseLeftShiftOp,
1690 emitc::BitwiseNotOp, emitc::BitwiseOrOp,
1691 emitc::BitwiseRightShiftOp, emitc::BitwiseXorOp, emitc::CallOp,
1692 emitc::CallOpaqueOp, emitc::CastOp, emitc::ClassOp,
1693 emitc::CmpOp, emitc::ConditionalOp, emitc::ConstantOp,
1694 emitc::DeclareFuncOp, emitc::DivOp, emitc::ExpressionOp,
1695 emitc::FieldOp, emitc::FileOp, emitc::ForOp, emitc::FuncOp,
1696 emitc::GlobalOp, emitc::IfOp, emitc::IncludeOp, emitc::LoadOp,
1697 emitc::LogicalAndOp, emitc::LogicalNotOp, emitc::LogicalOrOp,
1698 emitc::MulOp, emitc::RemOp, emitc::ReturnOp, emitc::SubOp,
1699 emitc::SwitchOp, emitc::UnaryMinusOp, emitc::UnaryPlusOp,
1700 emitc::VariableOp, emitc::VerbatimOp>(
1704 .Case<func::CallOp, func::FuncOp, func::ReturnOp>(
1706 .Case<emitc::GetGlobalOp>([&](
auto op) {
1710 .Case<emitc::GetFieldOp>([&](
auto op) {
1711 cacheDeferredOpResult(op.
getResult(), op.getFieldName());
1714 .Case<emitc::LiteralOp>([&](
auto op) {
1715 cacheDeferredOpResult(op.
getResult(), op.getValue());
1718 .Case<emitc::MemberOp>([&](
auto op) {
1719 cacheDeferredOpResult(op.
getResult(), createMemberAccess(op));
1722 .Case<emitc::MemberOfPtrOp>([&](
auto op) {
1723 cacheDeferredOpResult(op.
getResult(), createMemberAccess(op));
1726 .Case<emitc::SubscriptOp>([&](
auto op) {
1727 cacheDeferredOpResult(op.
getResult(), getSubscriptName(op));
1731 return op.
emitOpError(
"unable to find printer for op");
1740 if (getEmittedExpression() ||
1741 (isa<emitc::ExpressionOp>(op) &&
1747 trailingSemicolon &=
1748 !isa<cf::CondBranchOp, emitc::DeclareFuncOp, emitc::FileOp, emitc::ForOp,
1749 emitc::IfOp, emitc::IncludeOp, emitc::SwitchOp, emitc::VerbatimOp>(
1752 os << (trailingSemicolon ?
";\n" :
"\n");
1757 LogicalResult CppEmitter::emitVariableDeclaration(
Location loc,
Type type,
1759 if (
auto arrType = dyn_cast<emitc::ArrayType>(type)) {
1760 if (failed(emitType(loc, arrType.getElementType())))
1763 for (
auto dim : arrType.getShape()) {
1764 os <<
"[" << dim <<
"]";
1768 if (failed(emitType(loc, type)))
1774 LogicalResult CppEmitter::emitType(
Location loc,
Type type) {
1775 if (
auto iType = dyn_cast<IntegerType>(type)) {
1776 switch (iType.getWidth()) {
1778 return (os <<
"bool"), success();
1783 if (shouldMapToUnsigned(iType.getSignedness()))
1784 return (os <<
"uint" << iType.getWidth() <<
"_t"), success();
1786 return (os <<
"int" << iType.getWidth() <<
"_t"), success();
1788 return emitError(loc,
"cannot emit integer type ") << type;
1791 if (
auto fType = dyn_cast<FloatType>(type)) {
1792 switch (fType.getWidth()) {
1794 if (llvm::isa<Float16Type>(type))
1795 return (os <<
"_Float16"), success();
1796 else if (llvm::isa<BFloat16Type>(type))
1797 return (os <<
"__bf16"), success();
1799 return emitError(loc,
"cannot emit float type ") << type;
1802 return (os <<
"float"), success();
1804 return (os <<
"double"), success();
1806 return emitError(loc,
"cannot emit float type ") << type;
1809 if (
auto iType = dyn_cast<IndexType>(type))
1810 return (os <<
"size_t"), success();
1811 if (
auto sType = dyn_cast<emitc::SizeTType>(type))
1812 return (os <<
"size_t"), success();
1813 if (
auto sType = dyn_cast<emitc::SignedSizeTType>(type))
1814 return (os <<
"ssize_t"), success();
1815 if (
auto pType = dyn_cast<emitc::PtrDiffTType>(type))
1816 return (os <<
"ptrdiff_t"), success();
1817 if (
auto tType = dyn_cast<TensorType>(type)) {
1818 if (!tType.hasRank())
1819 return emitError(loc,
"cannot emit unranked tensor type");
1820 if (!tType.hasStaticShape())
1821 return emitError(loc,
"cannot emit tensor type with non static shape");
1823 if (isa<ArrayType>(tType.getElementType()))
1824 return emitError(loc,
"cannot emit tensor of array type ") << type;
1825 if (failed(emitType(loc, tType.getElementType())))
1827 auto shape = tType.getShape();
1828 for (
auto dimSize : shape) {
1835 if (
auto tType = dyn_cast<TupleType>(type))
1836 return emitTupleType(loc, tType.getTypes());
1837 if (
auto oType = dyn_cast<emitc::OpaqueType>(type)) {
1838 os << oType.getValue();
1841 if (
auto aType = dyn_cast<emitc::ArrayType>(type)) {
1842 if (failed(emitType(loc, aType.getElementType())))
1844 for (
auto dim : aType.getShape())
1845 os <<
"[" << dim <<
"]";
1848 if (
auto lType = dyn_cast<emitc::LValueType>(type))
1849 return emitType(loc, lType.getValueType());
1850 if (
auto pType = dyn_cast<emitc::PointerType>(type)) {
1851 if (isa<ArrayType>(pType.getPointee()))
1852 return emitError(loc,
"cannot emit pointer to array type ") << type;
1853 if (failed(emitType(loc, pType.getPointee())))
1858 return emitError(loc,
"cannot emit type ") << type;
1862 switch (types.size()) {
1867 return emitType(loc, types.front());
1869 return emitTupleType(loc, types);
1874 if (llvm::any_of(types, llvm::IsaPred<ArrayType>)) {
1875 return emitError(loc,
"cannot emit tuple of array type");
1877 os <<
"std::tuple<";
1879 types, os, [&](
Type type) {
return emitType(loc, type); })))
1885 void CppEmitter::resetValueCounter() { valueCount = 0; }
1887 void CppEmitter::increaseLoopNestingLevel() { loopNestingLevel++; }
1889 void CppEmitter::decreaseLoopNestingLevel() { loopNestingLevel--; }
1892 bool declareVariablesAtTop,
1894 CppEmitter emitter(os, declareVariablesAtTop, fileId);
1895 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.
LogicalResult interleaveCommaWithError(const Container &c, raw_ostream &os, UnaryFunctor eachFn)
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)
LogicalResult interleaveWithError(ForwardIterator begin, ForwardIterator end, UnaryFunctor eachFn, NullaryFunctor betweenFn)
Convenience functions to produce interleaved output with functions returning a LogicalResult.
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.
Attributes are known-constant values of operations.
This class represents an argument of a Block.
Block represents an ordered list of Operations.
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.