20 #include "llvm/ADT/DenseMap.h"
21 #include "llvm/ADT/ScopedHashTable.h"
22 #include "llvm/ADT/StringExtras.h"
23 #include "llvm/ADT/StringMap.h"
24 #include "llvm/ADT/TypeSwitch.h"
25 #include "llvm/Support/Debug.h"
26 #include "llvm/Support/FormatVariadic.h"
30 #define DEBUG_TYPE "translate-to-cpp"
40 typename NullaryFunctor>
43 UnaryFunctor eachFn, NullaryFunctor betweenFn) {
46 if (failed(eachFn(*begin)))
49 for (; begin != end; ++begin) {
51 if (failed(eachFn(*begin)))
57 template <
typename Container,
typename UnaryFunctor,
typename NullaryFunctor>
60 NullaryFunctor betweenFn) {
64 template <
typename Container,
typename UnaryFunctor>
67 UnaryFunctor eachFn) {
75 .Case<emitc::AddOp>([&](
auto op) {
return 12; })
76 .Case<emitc::ApplyOp>([&](
auto op) {
return 15; })
77 .Case<emitc::BitwiseAndOp>([&](
auto op) {
return 7; })
78 .Case<emitc::BitwiseLeftShiftOp>([&](
auto op) {
return 11; })
79 .Case<emitc::BitwiseNotOp>([&](
auto op) {
return 15; })
80 .Case<emitc::BitwiseOrOp>([&](
auto op) {
return 5; })
81 .Case<emitc::BitwiseRightShiftOp>([&](
auto op) {
return 11; })
82 .Case<emitc::BitwiseXorOp>([&](
auto op) {
return 6; })
83 .Case<emitc::CallOp>([&](
auto op) {
return 16; })
84 .Case<emitc::CallOpaqueOp>([&](
auto op) {
return 16; })
85 .Case<emitc::CastOp>([&](
auto op) {
return 15; })
86 .Case<emitc::CmpOp>([&](
auto 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>([&](
auto op) {
return 2; })
102 .Case<emitc::DivOp>([&](
auto op) {
return 13; })
103 .Case<emitc::LoadOp>([&](
auto op) {
return 16; })
104 .Case<emitc::LogicalAndOp>([&](
auto op) {
return 4; })
105 .Case<emitc::LogicalNotOp>([&](
auto op) {
return 15; })
106 .Case<emitc::LogicalOrOp>([&](
auto op) {
return 3; })
107 .Case<emitc::MulOp>([&](
auto op) {
return 13; })
108 .Case<emitc::RemOp>([&](
auto op) {
return 13; })
109 .Case<emitc::SubOp>([&](
auto op) {
return 12; })
110 .Case<emitc::UnaryMinusOp>([&](
auto op) {
return 15; })
111 .Case<emitc::UnaryPlusOp>([&](
auto op) {
return 15; })
112 .Default([](
auto op) {
return op->emitError(
"unsupported operation"); });
118 explicit CppEmitter(raw_ostream &os,
bool declareVariablesAtTop,
129 LogicalResult emitOperation(
Operation &op,
bool trailingSemicolon);
145 LogicalResult emitVariableAssignment(
OpResult result);
148 LogicalResult emitVariableDeclaration(
OpResult result,
149 bool trailingSemicolon);
152 LogicalResult emitVariableDeclaration(
Location loc,
Type type,
161 LogicalResult emitAssignPrefix(
Operation &op);
164 LogicalResult emitGlobalVariable(GlobalOp op);
167 LogicalResult emitLabel(
Block &block);
171 LogicalResult emitOperandsAndAttributes(
Operation &op,
175 LogicalResult emitOperands(
Operation &op);
178 LogicalResult emitOperand(
Value value);
181 LogicalResult emitExpression(ExpressionOp expressionOp);
184 void cacheDeferredOpResult(
Value value, StringRef str);
187 StringRef getOrCreateName(
Value val);
190 std::string getSubscriptName(emitc::SubscriptOp op);
193 std::string createMemberAccess(emitc::MemberOp op);
196 std::string createMemberAccess(emitc::MemberOfPtrOp op);
199 StringRef getOrCreateName(
Block &block);
202 bool shouldMapToUnsigned(IntegerType::SignednessSemantics val);
206 Scope(CppEmitter &emitter)
207 : valueMapperScope(emitter.valueMapper),
208 blockMapperScope(emitter.blockMapper), emitter(emitter) {
209 emitter.valueInScopeCount.push(emitter.valueInScopeCount.top());
210 emitter.labelInScopeCount.push(emitter.labelInScopeCount.top());
213 emitter.valueInScopeCount.pop();
214 emitter.labelInScopeCount.pop();
218 llvm::ScopedHashTableScope<Value, std::string> valueMapperScope;
219 llvm::ScopedHashTableScope<Block *, std::string> blockMapperScope;
224 bool hasValueInScope(
Value val);
227 bool hasBlockLabel(
Block &block);
234 bool shouldDeclareVariablesAtTop() {
return declareVariablesAtTop; };
237 bool shouldEmitFile(FileOp file) {
238 return !fileId.empty() && file.getId() == fileId;
242 ExpressionOp getEmittedExpression() {
return emittedExpression; }
246 bool isPartOfCurrentExpression(
Value value) {
247 if (!emittedExpression)
252 auto operandExpression = dyn_cast<ExpressionOp>(def->
getParentOp());
253 return operandExpression == emittedExpression;
257 using ValueMapper = llvm::ScopedHashTable<Value, std::string>;
258 using BlockMapper = llvm::ScopedHashTable<Block *, std::string>;
266 bool declareVariablesAtTop;
272 ValueMapper valueMapper;
275 BlockMapper blockMapper;
279 std::stack<int64_t> valueInScopeCount;
280 std::stack<int64_t> labelInScopeCount;
283 ExpressionOp emittedExpression;
286 void pushExpressionPrecedence(
int precedence) {
287 emittedExpressionPrecedence.push_back(precedence);
289 void popExpressionPrecedence() { emittedExpressionPrecedence.pop_back(); }
290 static int lowestPrecedence() {
return 0; }
291 int getExpressionPrecedence() {
292 if (emittedExpressionPrecedence.empty())
293 return lowestPrecedence();
294 return emittedExpressionPrecedence.back();
301 return isa_and_nonnull<emitc::GetGlobalOp, emitc::LiteralOp, emitc::MemberOp,
302 emitc::MemberOfPtrOp, emitc::SubscriptOp>(op);
312 if (expressionOp.getDoNotInline())
317 if (expressionOp.hasSideEffects())
321 Value result = expressionOp.getResult();
334 return !isa<emitc::CExpressionInterface>(*user);
343 if (emitter.shouldDeclareVariablesAtTop()) {
345 if (
auto oAttr = dyn_cast<emitc::OpaqueAttr>(value)) {
346 if (oAttr.getValue().empty())
350 if (failed(emitter.emitVariableAssignment(result)))
352 return emitter.emitAttribute(operation->
getLoc(), value);
356 if (
auto oAttr = dyn_cast<emitc::OpaqueAttr>(value)) {
357 if (oAttr.getValue().empty())
359 return emitter.emitVariableDeclaration(result,
364 if (failed(emitter.emitAssignPrefix(*operation)))
366 return emitter.emitAttribute(operation->
getLoc(), value);
370 emitc::ConstantOp constantOp) {
371 Operation *operation = constantOp.getOperation();
378 emitc::VariableOp variableOp) {
379 Operation *operation = variableOp.getOperation();
386 emitc::GlobalOp globalOp) {
388 return emitter.emitGlobalVariable(globalOp);
392 emitc::AssignOp assignOp) {
395 if (failed(emitter.emitVariableAssignment(result)))
398 return emitter.emitOperand(assignOp.getValue());
402 if (failed(emitter.emitAssignPrefix(*loadOp)))
405 return emitter.emitOperand(loadOp.getOperand());
410 StringRef binaryOperator) {
411 raw_ostream &os = emitter.ostream();
413 if (failed(emitter.emitAssignPrefix(*operation)))
416 if (failed(emitter.emitOperand(operation->
getOperand(0))))
419 os <<
" " << binaryOperator <<
" ";
421 if (failed(emitter.emitOperand(operation->
getOperand(1))))
429 StringRef unaryOperator) {
430 raw_ostream &os = emitter.ostream();
432 if (failed(emitter.emitAssignPrefix(*operation)))
437 if (failed(emitter.emitOperand(operation->
getOperand(0))))
444 Operation *operation = addOp.getOperation();
450 Operation *operation = divOp.getOperation();
456 Operation *operation = mulOp.getOperation();
462 Operation *operation = remOp.getOperation();
468 Operation *operation = subOp.getOperation();
476 std::next(iteratorOp) != end; ++iteratorOp) {
477 if (failed(emitter.emitOperation(*iteratorOp,
true)))
485 emitc::SwitchOp switchOp) {
489 if (failed(emitter.emitOperand(switchOp.getArg())))
493 for (
auto pair : llvm::zip(switchOp.getCases(), switchOp.getCaseRegions())) {
494 os <<
"\ncase " << std::get<0>(pair) <<
": {\n";
503 os <<
"\ndefault: {\n";
506 if (failed(
emitSwitchCase(emitter, os, switchOp.getDefaultRegion())))
514 Operation *operation = cmpOp.getOperation();
516 StringRef binaryOperator;
518 switch (cmpOp.getPredicate()) {
519 case emitc::CmpPredicate::eq:
520 binaryOperator =
"==";
522 case emitc::CmpPredicate::ne:
523 binaryOperator =
"!=";
525 case emitc::CmpPredicate::lt:
526 binaryOperator =
"<";
528 case emitc::CmpPredicate::le:
529 binaryOperator =
"<=";
531 case emitc::CmpPredicate::gt:
532 binaryOperator =
">";
534 case emitc::CmpPredicate::ge:
535 binaryOperator =
">=";
537 case emitc::CmpPredicate::three_way:
538 binaryOperator =
"<=>";
546 emitc::ConditionalOp conditionalOp) {
547 raw_ostream &os = emitter.ostream();
549 if (failed(emitter.emitAssignPrefix(*conditionalOp)))
552 if (failed(emitter.emitOperand(conditionalOp.getCondition())))
557 if (failed(emitter.emitOperand(conditionalOp.getTrueValue())))
562 if (failed(emitter.emitOperand(conditionalOp.getFalseValue())))
569 emitc::VerbatimOp verbatimOp) {
570 raw_ostream &os = emitter.ostream();
572 FailureOr<SmallVector<ReplacementItem>> items =
573 verbatimOp.parseFormatString();
577 auto fmtArg = verbatimOp.getFmtArgs().begin();
580 if (
auto *str = std::get_if<StringRef>(&item)) {
583 if (failed(emitter.emitOperand(*fmtArg++)))
592 cf::BranchOp branchOp) {
593 raw_ostream &os = emitter.ostream();
597 llvm::zip(branchOp.getOperands(), successor.
getArguments())) {
598 Value &operand = std::get<0>(pair);
600 os << emitter.getOrCreateName(argument) <<
" = "
601 << emitter.getOrCreateName(operand) <<
";\n";
605 if (!(emitter.hasBlockLabel(successor)))
606 return branchOp.emitOpError(
"unable to find label for successor block");
607 os << emitter.getOrCreateName(successor);
612 cf::CondBranchOp condBranchOp) {
614 Block &trueSuccessor = *condBranchOp.getTrueDest();
615 Block &falseSuccessor = *condBranchOp.getFalseDest();
618 if (failed(emitter.emitOperand(condBranchOp.getCondition())))
625 for (
auto pair : llvm::zip(condBranchOp.getTrueOperands(),
627 Value &operand = std::get<0>(pair);
629 os << emitter.getOrCreateName(argument) <<
" = "
630 << emitter.getOrCreateName(operand) <<
";\n";
634 if (!(emitter.hasBlockLabel(trueSuccessor))) {
635 return condBranchOp.emitOpError(
"unable to find label for successor block");
637 os << emitter.getOrCreateName(trueSuccessor) <<
";\n";
641 for (
auto pair : llvm::zip(condBranchOp.getFalseOperands(),
643 Value &operand = std::get<0>(pair);
645 os << emitter.getOrCreateName(argument) <<
" = "
646 << emitter.getOrCreateName(operand) <<
";\n";
650 if (!(emitter.hasBlockLabel(falseSuccessor))) {
651 return condBranchOp.emitOpError()
652 <<
"unable to find label for successor block";
654 os << emitter.getOrCreateName(falseSuccessor) <<
";\n";
661 if (failed(emitter.emitAssignPrefix(*callOp)))
664 raw_ostream &os = emitter.ostream();
666 if (failed(emitter.emitOperands(*callOp)))
673 Operation *operation = callOp.getOperation();
674 StringRef callee = callOp.getCallee();
680 Operation *operation = callOp.getOperation();
681 StringRef callee = callOp.getCallee();
687 emitc::CallOpaqueOp callOpaqueOp) {
688 raw_ostream &os = emitter.ostream();
689 Operation &op = *callOpaqueOp.getOperation();
691 if (failed(emitter.emitAssignPrefix(op)))
693 os << callOpaqueOp.getCallee();
699 auto emitTemplateArgs = [&](
Attribute attr) -> LogicalResult {
700 return emitter.emitAttribute(op.
getLoc(), attr);
703 if (callOpaqueOp.getTemplateArgs()) {
711 auto emitArgs = [&](
Attribute attr) -> LogicalResult {
712 if (
auto t = dyn_cast<IntegerAttr>(attr)) {
714 if (t.getType().isIndex()) {
715 int64_t idx = t.getInt();
717 if (!emitter.hasValueInScope(operand))
719 << idx <<
"'s value not defined in scope";
720 os << emitter.getOrCreateName(operand);
724 if (failed(emitter.emitAttribute(op.
getLoc(), attr)))
732 LogicalResult emittedArgs =
733 callOpaqueOp.getArgs()
735 : emitter.emitOperands(op);
736 if (failed(emittedArgs))
743 emitc::ApplyOp applyOp) {
744 raw_ostream &os = emitter.ostream();
747 if (failed(emitter.emitAssignPrefix(op)))
749 os << applyOp.getApplicableOperator();
750 os << emitter.getOrCreateName(applyOp.getOperand());
756 emitc::BitwiseAndOp bitwiseAndOp) {
757 Operation *operation = bitwiseAndOp.getOperation();
763 emitc::BitwiseLeftShiftOp bitwiseLeftShiftOp) {
764 Operation *operation = bitwiseLeftShiftOp.getOperation();
769 emitc::BitwiseNotOp bitwiseNotOp) {
770 Operation *operation = bitwiseNotOp.getOperation();
775 emitc::BitwiseOrOp bitwiseOrOp) {
776 Operation *operation = bitwiseOrOp.getOperation();
782 emitc::BitwiseRightShiftOp bitwiseRightShiftOp) {
783 Operation *operation = bitwiseRightShiftOp.getOperation();
788 emitc::BitwiseXorOp bitwiseXorOp) {
789 Operation *operation = bitwiseXorOp.getOperation();
794 emitc::UnaryPlusOp unaryPlusOp) {
795 Operation *operation = unaryPlusOp.getOperation();
800 emitc::UnaryMinusOp unaryMinusOp) {
801 Operation *operation = unaryMinusOp.getOperation();
806 raw_ostream &os = emitter.ostream();
809 if (failed(emitter.emitAssignPrefix(op)))
815 return emitter.emitOperand(castOp.getOperand());
819 emitc::ExpressionOp expressionOp) {
823 Operation &op = *expressionOp.getOperation();
825 if (failed(emitter.emitAssignPrefix(op)))
828 return emitter.emitExpression(expressionOp);
832 emitc::IncludeOp includeOp) {
833 raw_ostream &os = emitter.ostream();
836 if (includeOp.getIsStandardInclude())
837 os <<
"<" << includeOp.getInclude() <<
">";
839 os <<
"\"" << includeOp.getInclude() <<
"\"";
845 emitc::LogicalAndOp logicalAndOp) {
846 Operation *operation = logicalAndOp.getOperation();
851 emitc::LogicalNotOp logicalNotOp) {
852 Operation *operation = logicalNotOp.getOperation();
857 emitc::LogicalOrOp logicalOrOp) {
858 Operation *operation = logicalOrOp.getOperation();
869 auto requiresParentheses = [&](
Value value) {
879 emitter.emitType(forOp.getLoc(), forOp.getInductionVar().getType())))
882 os << emitter.getOrCreateName(forOp.getInductionVar());
884 if (failed(emitter.emitOperand(forOp.getLowerBound())))
887 os << emitter.getOrCreateName(forOp.getInductionVar());
889 Value upperBound = forOp.getUpperBound();
890 bool upperBoundRequiresParentheses = requiresParentheses(upperBound);
891 if (upperBoundRequiresParentheses)
893 if (failed(emitter.emitOperand(upperBound)))
895 if (upperBoundRequiresParentheses)
898 os << emitter.getOrCreateName(forOp.getInductionVar());
900 if (failed(emitter.emitOperand(forOp.getStep())))
905 Region &forRegion = forOp.getRegion();
906 auto regionOps = forRegion.
getOps();
909 for (
auto it = regionOps.begin(); std::next(it) != regionOps.end(); ++it) {
910 if (failed(emitter.emitOperation(*it,
true)))
924 auto emitAllExceptLast = [&emitter](
Region ®ion) {
926 for (; std::next(it) != end; ++it) {
927 if (failed(emitter.emitOperation(*it,
true)))
930 assert(isa<emitc::YieldOp>(*it) &&
931 "Expected last operation in the region to be emitc::yield");
936 if (failed(emitter.emitOperand(ifOp.getCondition())))
940 if (failed(emitAllExceptLast(ifOp.getThenRegion())))
944 Region &elseRegion = ifOp.getElseRegion();
945 if (!elseRegion.
empty()) {
948 if (failed(emitAllExceptLast(elseRegion)))
957 func::ReturnOp returnOp) {
958 raw_ostream &os = emitter.ostream();
960 switch (returnOp.getNumOperands()) {
965 if (failed(emitter.emitOperand(returnOp.getOperand(0))))
969 os <<
" std::make_tuple(";
970 if (failed(emitter.emitOperandsAndAttributes(*returnOp.getOperation())))
978 emitc::ReturnOp returnOp) {
979 raw_ostream &os = emitter.ostream();
981 if (returnOp.getNumOperands() == 0)
985 if (failed(emitter.emitOperand(returnOp.getOperand())))
991 CppEmitter::Scope scope(emitter);
994 if (failed(emitter.emitOperation(op,
false)))
1001 if (!emitter.shouldEmitFile(file))
1004 CppEmitter::Scope scope(emitter);
1007 if (failed(emitter.emitOperation(op,
false)))
1020 return emitter.emitType(functionOp->
getLoc(), arg);
1031 return emitter.emitVariableDeclaration(
1032 functionOp->
getLoc(), arg.
getType(), emitter.getOrCreateName(arg));
1042 if (emitter.shouldDeclareVariablesAtTop()) {
1047 if (isa<emitc::ExpressionOp>(op->
getParentOp()) ||
1048 (isa<emitc::ExpressionOp>(op) &&
1052 if (failed(emitter.emitVariableDeclaration(
1055 op->
emitError(
"unable to declare result variable for op"));
1065 for (
Block &block : blocks) {
1066 emitter.getOrCreateName(block);
1070 for (
Block &block : llvm::drop_begin(blocks)) {
1072 if (emitter.hasValueInScope(arg))
1073 return functionOp->
emitOpError(
" block argument #")
1074 << arg.getArgNumber() <<
" is out of scope";
1075 if (isa<ArrayType, LValueType>(arg.getType()))
1076 return functionOp->
emitOpError(
"cannot emit block argument #")
1077 << arg.getArgNumber() <<
" with type " << arg.getType();
1079 emitter.emitType(block.getParentOp()->getLoc(), arg.getType()))) {
1082 os <<
" " << emitter.getOrCreateName(arg) <<
";\n";
1086 for (
Block &block : blocks) {
1088 if (!block.hasNoPredecessors()) {
1089 if (failed(emitter.emitLabel(block)))
1092 for (
Operation &op : block.getOperations()) {
1093 if (failed(emitter.emitOperation(op,
true)))
1104 func::FuncOp functionOp) {
1106 if (!emitter.shouldDeclareVariablesAtTop() &&
1107 functionOp.getBlocks().size() > 1) {
1108 return functionOp.emitOpError(
1109 "with multiple blocks needs variables declared at top");
1112 if (llvm::any_of(functionOp.getArgumentTypes(), llvm::IsaPred<LValueType>)) {
1113 return functionOp.emitOpError()
1114 <<
"cannot emit lvalue type as argument type";
1117 if (llvm::any_of(functionOp.getResultTypes(), llvm::IsaPred<ArrayType>)) {
1118 return functionOp.emitOpError() <<
"cannot emit array type as result type";
1121 CppEmitter::Scope scope(emitter);
1123 if (failed(emitter.emitTypes(functionOp.getLoc(),
1124 functionOp.getFunctionType().getResults())))
1126 os <<
" " << functionOp.getName();
1129 Operation *operation = functionOp.getOperation();
1141 emitc::FuncOp functionOp) {
1143 if (!emitter.shouldDeclareVariablesAtTop() &&
1144 functionOp.getBlocks().size() > 1) {
1145 return functionOp.emitOpError(
1146 "with multiple blocks needs variables declared at top");
1149 CppEmitter::Scope scope(emitter);
1151 if (functionOp.getSpecifiers()) {
1152 for (
Attribute specifier : functionOp.getSpecifiersAttr()) {
1153 os << cast<StringAttr>(specifier).str() <<
" ";
1157 if (failed(emitter.emitTypes(functionOp.getLoc(),
1158 functionOp.getFunctionType().getResults())))
1160 os <<
" " << functionOp.getName();
1163 Operation *operation = functionOp.getOperation();
1164 if (functionOp.isExternal()) {
1166 functionOp.getArgumentTypes())))
1182 DeclareFuncOp declareFuncOp) {
1183 CppEmitter::Scope scope(emitter);
1186 auto functionOp = SymbolTable::lookupNearestSymbolFrom<emitc::FuncOp>(
1187 declareFuncOp, declareFuncOp.getSymNameAttr());
1192 if (functionOp.getSpecifiers()) {
1193 for (
Attribute specifier : functionOp.getSpecifiersAttr()) {
1194 os << cast<StringAttr>(specifier).str() <<
" ";
1198 if (failed(emitter.emitTypes(functionOp.getLoc(),
1199 functionOp.getFunctionType().getResults())))
1201 os <<
" " << functionOp.getName();
1204 Operation *operation = functionOp.getOperation();
1212 CppEmitter::CppEmitter(raw_ostream &os,
bool declareVariablesAtTop,
1214 : os(os), declareVariablesAtTop(declareVariablesAtTop),
1215 fileId(fileId.str()) {
1216 valueInScopeCount.push(0);
1217 labelInScopeCount.push(0);
1220 std::string CppEmitter::getSubscriptName(emitc::SubscriptOp op) {
1222 llvm::raw_string_ostream ss(out);
1223 ss << getOrCreateName(op.getValue());
1224 for (
auto index : op.getIndices()) {
1225 ss <<
"[" << getOrCreateName(index) <<
"]";
1230 std::string CppEmitter::createMemberAccess(emitc::MemberOp op) {
1232 llvm::raw_string_ostream ss(out);
1233 ss << getOrCreateName(op.getOperand());
1234 ss <<
"." << op.getMember();
1238 std::string CppEmitter::createMemberAccess(emitc::MemberOfPtrOp op) {
1240 llvm::raw_string_ostream ss(out);
1241 ss << getOrCreateName(op.getOperand());
1242 ss <<
"->" << op.getMember();
1246 void CppEmitter::cacheDeferredOpResult(
Value value, StringRef str) {
1247 if (!valueMapper.count(value))
1248 valueMapper.insert(value, str.str());
1252 StringRef CppEmitter::getOrCreateName(
Value val) {
1253 if (!valueMapper.count(val)) {
1255 "cacheDeferredOpResult should have been called on this value, "
1256 "update the emitOperation function.");
1257 valueMapper.insert(val, formatv(
"v{0}", ++valueInScopeCount.top()));
1259 return *valueMapper.begin(val);
1263 StringRef CppEmitter::getOrCreateName(
Block &block) {
1264 if (!blockMapper.count(&block))
1265 blockMapper.insert(&block, formatv(
"label{0}", ++labelInScopeCount.top()));
1266 return *blockMapper.begin(&block);
1269 bool CppEmitter::shouldMapToUnsigned(IntegerType::SignednessSemantics val) {
1271 case IntegerType::Signless:
1275 case IntegerType::Unsigned:
1278 llvm_unreachable(
"Unexpected IntegerType::SignednessSemantics");
1281 bool CppEmitter::hasValueInScope(
Value val) {
return valueMapper.count(val); }
1283 bool CppEmitter::hasBlockLabel(
Block &block) {
1284 return blockMapper.count(&block);
1288 auto printInt = [&](
const APInt &val,
bool isUnsigned) {
1289 if (val.getBitWidth() == 1) {
1290 if (val.getBoolValue())
1296 val.toString(strValue, 10, !isUnsigned,
false);
1301 auto printFloat = [&](
const APFloat &val) {
1302 if (val.isFinite()) {
1305 val.toString(strValue, 0, 0,
false);
1307 switch (llvm::APFloatBase::SemanticsToEnum(val.getSemantics())) {
1308 case llvm::APFloatBase::S_IEEEhalf:
1311 case llvm::APFloatBase::S_BFloat:
1314 case llvm::APFloatBase::S_IEEEsingle:
1317 case llvm::APFloatBase::S_IEEEdouble:
1320 llvm_unreachable(
"unsupported floating point type");
1322 }
else if (val.isNaN()) {
1324 }
else if (val.isInfinity()) {
1325 if (val.isNegative())
1332 if (
auto fAttr = dyn_cast<FloatAttr>(attr)) {
1333 if (!isa<Float16Type, BFloat16Type, Float32Type, Float64Type>(
1336 loc,
"expected floating point attribute to be f16, bf16, f32 or f64");
1338 printFloat(fAttr.getValue());
1341 if (
auto dense = dyn_cast<DenseFPElementsAttr>(attr)) {
1342 if (!isa<Float16Type, BFloat16Type, Float32Type, Float64Type>(
1343 dense.getElementType())) {
1345 loc,
"expected floating point attribute to be f16, bf16, f32 or f64");
1348 interleaveComma(dense, os, [&](
const APFloat &val) { printFloat(val); });
1354 if (
auto iAttr = dyn_cast<IntegerAttr>(attr)) {
1355 if (
auto iType = dyn_cast<IntegerType>(iAttr.getType())) {
1356 printInt(iAttr.getValue(), shouldMapToUnsigned(iType.getSignedness()));
1359 if (
auto iType = dyn_cast<IndexType>(iAttr.getType())) {
1360 printInt(iAttr.getValue(),
false);
1364 if (
auto dense = dyn_cast<DenseIntElementsAttr>(attr)) {
1365 if (
auto iType = dyn_cast<IntegerType>(
1366 cast<TensorType>(dense.getType()).getElementType())) {
1368 interleaveComma(dense, os, [&](
const APInt &val) {
1369 printInt(val, shouldMapToUnsigned(iType.getSignedness()));
1374 if (
auto iType = dyn_cast<IndexType>(
1375 cast<TensorType>(dense.getType()).getElementType())) {
1377 interleaveComma(dense, os,
1378 [&](
const APInt &val) { printInt(val,
false); });
1385 if (
auto oAttr = dyn_cast<emitc::OpaqueAttr>(attr)) {
1386 os << oAttr.getValue();
1391 if (
auto sAttr = dyn_cast<SymbolRefAttr>(attr)) {
1392 if (sAttr.getNestedReferences().size() > 1)
1393 return emitError(loc,
"attribute has more than 1 nested reference");
1394 os << sAttr.getRootReference().getValue();
1399 if (
auto type = dyn_cast<TypeAttr>(attr))
1400 return emitType(loc, type.getValue());
1402 return emitError(loc,
"cannot emit attribute: ") << attr;
1405 LogicalResult CppEmitter::emitExpression(ExpressionOp expressionOp) {
1406 assert(emittedExpressionPrecedence.empty() &&
1407 "Expected precedence stack to be empty");
1408 Operation *rootOp = expressionOp.getRootOp();
1410 emittedExpression = expressionOp;
1412 if (failed(precedence))
1414 pushExpressionPrecedence(precedence.value());
1416 if (failed(emitOperation(*rootOp,
false)))
1419 popExpressionPrecedence();
1420 assert(emittedExpressionPrecedence.empty() &&
1421 "Expected precedence stack to be empty");
1422 emittedExpression =
nullptr;
1427 LogicalResult CppEmitter::emitOperand(
Value value) {
1428 if (isPartOfCurrentExpression(value)) {
1430 assert(def &&
"Expected operand to be defined by an operation");
1432 if (failed(precedence))
1438 bool encloseInParenthesis = precedence.value() <= getExpressionPrecedence();
1439 if (encloseInParenthesis)
1441 pushExpressionPrecedence(precedence.value());
1443 if (failed(emitOperation(*def,
false)))
1446 if (encloseInParenthesis)
1449 popExpressionPrecedence();
1453 auto expressionOp = dyn_cast_if_present<ExpressionOp>(value.
getDefiningOp());
1455 return emitExpression(expressionOp);
1457 os << getOrCreateName(value);
1461 LogicalResult CppEmitter::emitOperands(
Operation &op) {
1465 if (getEmittedExpression())
1466 pushExpressionPrecedence(lowestPrecedence());
1467 if (failed(emitOperand(operand)))
1469 if (getEmittedExpression())
1470 popExpressionPrecedence();
1476 CppEmitter::emitOperandsAndAttributes(
Operation &op,
1478 if (failed(emitOperands(op)))
1483 if (!llvm::is_contained(exclude, attr.getName().strref())) {
1490 auto emitNamedAttribute = [&](
NamedAttribute attr) -> LogicalResult {
1491 if (llvm::is_contained(exclude, attr.getName().strref()))
1493 os <<
"/* " << attr.getName().getValue() <<
" */";
1494 if (failed(emitAttribute(op.
getLoc(), attr.getValue())))
1501 LogicalResult CppEmitter::emitVariableAssignment(
OpResult result) {
1502 if (!hasValueInScope(result)) {
1504 "result variable for the operation has not been declared");
1506 os << getOrCreateName(result) <<
" = ";
1510 LogicalResult CppEmitter::emitVariableDeclaration(
OpResult result,
1511 bool trailingSemicolon) {
1514 if (hasValueInScope(result)) {
1516 "result variable for the operation already declared");
1520 getOrCreateName(result))))
1522 if (trailingSemicolon)
1527 LogicalResult CppEmitter::emitGlobalVariable(GlobalOp op) {
1528 if (op.getExternSpecifier())
1530 else if (op.getStaticSpecifier())
1532 if (op.getConstSpecifier())
1535 if (failed(emitVariableDeclaration(op->getLoc(), op.getType(),
1536 op.getSymName()))) {
1540 std::optional<Attribute> initialValue = op.getInitialValue();
1543 if (failed(emitAttribute(op->getLoc(), *initialValue)))
1551 LogicalResult CppEmitter::emitAssignPrefix(
Operation &op) {
1553 if (getEmittedExpression())
1561 if (shouldDeclareVariablesAtTop()) {
1562 if (failed(emitVariableAssignment(result)))
1565 if (failed(emitVariableDeclaration(result,
false)))
1572 if (!shouldDeclareVariablesAtTop()) {
1574 if (failed(emitVariableDeclaration(result,
true)))
1580 [&](
Value result) { os << getOrCreateName(result); });
1586 LogicalResult CppEmitter::emitLabel(
Block &block) {
1587 if (!hasBlockLabel(block))
1591 os.getOStream() << getOrCreateName(block) <<
":\n";
1595 LogicalResult CppEmitter::emitOperation(
Operation &op,
bool trailingSemicolon) {
1596 LogicalResult status =
1599 .Case<ModuleOp>([&](
auto op) {
return printOperation(*
this, op); })
1601 .Case<cf::BranchOp, cf::CondBranchOp>(
1604 .Case<emitc::AddOp, emitc::ApplyOp, emitc::AssignOp,
1605 emitc::BitwiseAndOp, emitc::BitwiseLeftShiftOp,
1606 emitc::BitwiseNotOp, emitc::BitwiseOrOp,
1607 emitc::BitwiseRightShiftOp, emitc::BitwiseXorOp, emitc::CallOp,
1608 emitc::CallOpaqueOp, emitc::CastOp, emitc::CmpOp,
1609 emitc::ConditionalOp, emitc::ConstantOp, emitc::DeclareFuncOp,
1610 emitc::DivOp, emitc::ExpressionOp, emitc::FileOp, emitc::ForOp,
1611 emitc::FuncOp, emitc::GlobalOp, emitc::IfOp, emitc::IncludeOp,
1612 emitc::LoadOp, emitc::LogicalAndOp, emitc::LogicalNotOp,
1613 emitc::LogicalOrOp, emitc::MulOp, emitc::RemOp, emitc::ReturnOp,
1614 emitc::SubOp, emitc::SwitchOp, emitc::UnaryMinusOp,
1615 emitc::UnaryPlusOp, emitc::VariableOp, emitc::VerbatimOp>(
1619 .Case<func::CallOp, func::FuncOp, func::ReturnOp>(
1621 .Case<emitc::GetGlobalOp>([&](
auto op) {
1625 .Case<emitc::LiteralOp>([&](
auto op) {
1626 cacheDeferredOpResult(op.
getResult(), op.getValue());
1629 .Case<emitc::MemberOp>([&](
auto op) {
1630 cacheDeferredOpResult(op.
getResult(), createMemberAccess(op));
1633 .Case<emitc::MemberOfPtrOp>([&](
auto op) {
1634 cacheDeferredOpResult(op.
getResult(), createMemberAccess(op));
1637 .Case<emitc::SubscriptOp>([&](
auto op) {
1638 cacheDeferredOpResult(op.
getResult(), getSubscriptName(op));
1642 return op.
emitOpError(
"unable to find printer for op");
1651 if (getEmittedExpression() ||
1652 (isa<emitc::ExpressionOp>(op) &&
1658 trailingSemicolon &=
1659 !isa<cf::CondBranchOp, emitc::DeclareFuncOp, emitc::FileOp, emitc::ForOp,
1660 emitc::IfOp, emitc::IncludeOp, emitc::SwitchOp, emitc::VerbatimOp>(
1663 os << (trailingSemicolon ?
";\n" :
"\n");
1668 LogicalResult CppEmitter::emitVariableDeclaration(
Location loc,
Type type,
1670 if (
auto arrType = dyn_cast<emitc::ArrayType>(type)) {
1671 if (failed(emitType(loc, arrType.getElementType())))
1674 for (
auto dim : arrType.getShape()) {
1675 os <<
"[" << dim <<
"]";
1679 if (failed(emitType(loc, type)))
1685 LogicalResult CppEmitter::emitType(
Location loc,
Type type) {
1686 if (
auto iType = dyn_cast<IntegerType>(type)) {
1687 switch (iType.getWidth()) {
1689 return (os <<
"bool"), success();
1694 if (shouldMapToUnsigned(iType.getSignedness()))
1695 return (os <<
"uint" << iType.getWidth() <<
"_t"), success();
1697 return (os <<
"int" << iType.getWidth() <<
"_t"), success();
1699 return emitError(loc,
"cannot emit integer type ") << type;
1702 if (
auto fType = dyn_cast<FloatType>(type)) {
1703 switch (fType.getWidth()) {
1705 if (llvm::isa<Float16Type>(type))
1706 return (os <<
"_Float16"), success();
1707 else if (llvm::isa<BFloat16Type>(type))
1708 return (os <<
"__bf16"), success();
1710 return emitError(loc,
"cannot emit float type ") << type;
1713 return (os <<
"float"), success();
1715 return (os <<
"double"), success();
1717 return emitError(loc,
"cannot emit float type ") << type;
1720 if (
auto iType = dyn_cast<IndexType>(type))
1721 return (os <<
"size_t"), success();
1722 if (
auto sType = dyn_cast<emitc::SizeTType>(type))
1723 return (os <<
"size_t"), success();
1724 if (
auto sType = dyn_cast<emitc::SignedSizeTType>(type))
1725 return (os <<
"ssize_t"), success();
1726 if (
auto pType = dyn_cast<emitc::PtrDiffTType>(type))
1727 return (os <<
"ptrdiff_t"), success();
1728 if (
auto tType = dyn_cast<TensorType>(type)) {
1729 if (!tType.hasRank())
1730 return emitError(loc,
"cannot emit unranked tensor type");
1731 if (!tType.hasStaticShape())
1732 return emitError(loc,
"cannot emit tensor type with non static shape");
1734 if (isa<ArrayType>(tType.getElementType()))
1735 return emitError(loc,
"cannot emit tensor of array type ") << type;
1736 if (failed(emitType(loc, tType.getElementType())))
1738 auto shape = tType.getShape();
1739 for (
auto dimSize : shape) {
1746 if (
auto tType = dyn_cast<TupleType>(type))
1747 return emitTupleType(loc, tType.getTypes());
1748 if (
auto oType = dyn_cast<emitc::OpaqueType>(type)) {
1749 os << oType.getValue();
1752 if (
auto aType = dyn_cast<emitc::ArrayType>(type)) {
1753 if (failed(emitType(loc, aType.getElementType())))
1755 for (
auto dim : aType.getShape())
1756 os <<
"[" << dim <<
"]";
1759 if (
auto lType = dyn_cast<emitc::LValueType>(type))
1760 return emitType(loc, lType.getValueType());
1761 if (
auto pType = dyn_cast<emitc::PointerType>(type)) {
1762 if (isa<ArrayType>(pType.getPointee()))
1763 return emitError(loc,
"cannot emit pointer to array type ") << type;
1764 if (failed(emitType(loc, pType.getPointee())))
1769 return emitError(loc,
"cannot emit type ") << type;
1773 switch (types.size()) {
1778 return emitType(loc, types.front());
1780 return emitTupleType(loc, types);
1785 if (llvm::any_of(types, llvm::IsaPred<ArrayType>)) {
1786 return emitError(loc,
"cannot emit tuple of array type");
1788 os <<
"std::tuple<";
1790 types, os, [&](
Type type) {
return emitType(loc, type); })))
1797 bool declareVariablesAtTop,
1799 CppEmitter emitter(os, declareVariablesAtTop, fileId);
1800 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.