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>
39static inline LogicalResult
41 UnaryFunctor eachFn, NullaryFunctor betweenFn) {
44 if (failed(eachFn(*begin)))
47 for (; begin != end; ++begin) {
49 if (failed(eachFn(*begin)))
55template <
typename Container,
typename UnaryFunctor,
typename NullaryFunctor>
58 NullaryFunctor betweenFn) {
62template <
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,
121 LogicalResult emitAttribute(Location loc, Attribute attr);
128 LogicalResult emitOperation(Operation &op,
bool trailingSemicolon);
131 LogicalResult emitType(Location loc, Type type);
137 LogicalResult emitTypes(Location loc, ArrayRef<Type> types);
141 LogicalResult emitTupleType(Location loc, ArrayRef<Type> types);
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,
171 ArrayRef<StringRef> exclude = {});
174 LogicalResult emitOperands(Operation &op);
180 LogicalResult emitOperand(Value value,
bool isInBrackets =
false);
183 LogicalResult emitExpression(ExpressionOp expressionOp);
186 void cacheDeferredOpResult(Value value, StringRef str);
189 StringRef getOrCreateName(Value val);
193 StringRef getOrCreateInductionVarName(Value val);
196 StringRef getOrCreateName(
Block &block);
198 LogicalResult emitInlinedExpression(Value value);
201 bool shouldMapToUnsigned(IntegerType::SignednessSemantics val);
205 ~Scope() { emitter.labelInScopeCount.pop(); }
208 llvm::ScopedHashTableScope<Value, std::string> valueMapperScope;
209 llvm::ScopedHashTableScope<Block *, std::string> blockMapperScope;
212 Scope(CppEmitter &emitter)
213 : valueMapperScope(emitter.valueMapper),
214 blockMapperScope(emitter.blockMapper), emitter(emitter) {
215 emitter.labelInScopeCount.push(emitter.labelInScopeCount.top());
222 struct FunctionScope : Scope {
223 FunctionScope(CppEmitter &emitter) : Scope(emitter) {
225 emitter.resetValueCounter();
231 struct LoopScope : Scope {
232 LoopScope(CppEmitter &emitter) : Scope(emitter) {
233 emitter.increaseLoopNestingLevel();
235 ~LoopScope() { emitter.decreaseLoopNestingLevel(); }
239 bool hasValueInScope(Value val);
242 bool hasBlockLabel(
Block &block);
245 raw_indented_ostream &ostream() {
return os; };
249 bool shouldDeclareVariablesAtTop() {
return declareVariablesAtTop; };
252 bool shouldEmitFile(FileOp file) {
253 return !fileId.empty() && file.getId() == fileId;
257 bool isEmittingExpression() {
return emittedExpression; }
261 bool isPartOfCurrentExpression(Value value) {
262 if (!emittedExpression)
267 return isPartOfCurrentExpression(def);
272 bool isPartOfCurrentExpression(Operation *def) {
273 auto operandExpression = dyn_cast<ExpressionOp>(def->
getParentOp());
274 return operandExpression && operandExpression == emittedExpression;
278 void resetValueCounter();
281 void increaseLoopNestingLevel();
284 void decreaseLoopNestingLevel();
287 using ValueMapper = llvm::ScopedHashTable<Value, std::string>;
288 using BlockMapper = llvm::ScopedHashTable<Block *, std::string>;
291 raw_indented_ostream os;
296 bool declareVariablesAtTop;
302 ValueMapper valueMapper;
305 BlockMapper blockMapper;
308 llvm::ScopedHashTableScope<Value, std::string> defaultValueMapperScope;
309 llvm::ScopedHashTableScope<Block *, std::string> defaultBlockMapperScope;
311 std::stack<int64_t> labelInScopeCount;
315 uint64_t loopNestingLevel{0};
318 unsigned int valueCount{0};
321 ExpressionOp emittedExpression;
322 SmallVector<int> emittedExpressionPrecedence;
324 void pushExpressionPrecedence(
int precedence) {
325 emittedExpressionPrecedence.push_back(precedence);
327 void popExpressionPrecedence() { emittedExpressionPrecedence.pop_back(); }
328 static int lowestPrecedence() {
return 0; }
329 int getExpressionPrecedence() {
330 if (emittedExpressionPrecedence.empty())
331 return lowestPrecedence();
332 return emittedExpressionPrecedence.back();
339 return isa_and_nonnull<emitc::GetGlobalOp, emitc::LiteralOp, emitc::MemberOp,
340 emitc::MemberOfPtrOp, emitc::SubscriptOp,
341 emitc::GetFieldOp>(op);
351 if (expressionOp.getDoNotInline())
369 if (isa<emitc::ExpressionOp, emitc::CExpressionInterface>(*user))
373 if (!expressionOp.hasSideEffects())
384 if (isa<emitc::IfOp, emitc::SwitchOp, emitc::ReturnOp>(user))
389 if (
auto assignOp = dyn_cast<emitc::AssignOp>(user)) {
391 if (expressionOp.getResult() == assignOp.getValue() &&
392 isa_and_present<VariableOp>(assignOp.getVar().getDefiningOp()))
400 emitc::GetFieldOp getFieldOp) {
401 emitter.cacheDeferredOpResult(getFieldOp.getResult(),
402 getFieldOp.getFieldName());
407 emitc::GetGlobalOp getGlobalOp) {
408 emitter.cacheDeferredOpResult(getGlobalOp.getResult(), getGlobalOp.getName());
413 emitc::LiteralOp literalOp) {
414 emitter.cacheDeferredOpResult(literalOp.getResult(), literalOp.getValue());
419 emitc::MemberOp memberOp) {
421 llvm::raw_string_ostream ss(out);
422 ss << emitter.getOrCreateName(memberOp.getOperand());
423 ss <<
"." << memberOp.getMember();
424 emitter.cacheDeferredOpResult(memberOp.getResult(), out);
429 emitc::MemberOfPtrOp memberOfPtrOp) {
431 llvm::raw_string_ostream ss(out);
432 ss << emitter.getOrCreateName(memberOfPtrOp.getOperand());
433 ss <<
"->" << memberOfPtrOp.getMember();
434 emitter.cacheDeferredOpResult(memberOfPtrOp.getResult(), out);
439 emitc::SubscriptOp subscriptOp) {
441 llvm::raw_string_ostream ss(out);
442 ss << emitter.getOrCreateName(subscriptOp.getValue());
443 for (
auto index : subscriptOp.getIndices()) {
444 ss <<
"[" << emitter.getOrCreateName(
index) <<
"]";
446 emitter.cacheDeferredOpResult(subscriptOp.getResult(), out);
456 if (emitter.shouldDeclareVariablesAtTop()) {
458 if (
auto oAttr = dyn_cast<emitc::OpaqueAttr>(value)) {
459 if (oAttr.getValue().empty())
463 if (failed(emitter.emitVariableAssignment(
result)))
465 return emitter.emitAttribute(operation->
getLoc(), value);
469 if (
auto oAttr = dyn_cast<emitc::OpaqueAttr>(value)) {
470 if (oAttr.getValue().empty())
472 return emitter.emitVariableDeclaration(
result,
477 if (failed(emitter.emitAssignPrefix(*operation)))
479 return emitter.emitAttribute(operation->
getLoc(), value);
483 emitc::ConstantOp constantOp) {
484 Operation *operation = constantOp.getOperation();
487 if (emitter.isPartOfCurrentExpression(operation))
488 return emitter.emitAttribute(operation->
getLoc(), value);
494 emitc::VariableOp variableOp) {
495 Operation *operation = variableOp.getOperation();
502 emitc::GlobalOp globalOp) {
504 return emitter.emitGlobalVariable(globalOp);
508 emitc::AssignOp assignOp) {
509 OpResult result = assignOp.getVar().getDefiningOp()->getResult(0);
511 if (failed(emitter.emitVariableAssignment(
result)))
514 return emitter.emitOperand(assignOp.getValue());
518 if (failed(emitter.emitAssignPrefix(*loadOp)))
521 return emitter.emitOperand(loadOp.getOperand());
526 StringRef binaryOperator) {
529 if (failed(emitter.emitAssignPrefix(*operation)))
532 if (failed(emitter.emitOperand(operation->
getOperand(0))))
535 os <<
" " << binaryOperator <<
" ";
537 if (failed(emitter.emitOperand(operation->
getOperand(1))))
545 StringRef unaryOperator) {
548 if (failed(emitter.emitAssignPrefix(*operation)))
553 if (failed(emitter.emitOperand(operation->
getOperand(0))))
560 Operation *operation = addOp.getOperation();
566 Operation *operation = divOp.getOperation();
572 Operation *operation = mulOp.getOperation();
578 Operation *operation = remOp.getOperation();
584 Operation *operation = subOp.getOperation();
592 std::next(iteratorOp) != end; ++iteratorOp) {
593 if (failed(emitter.emitOperation(*iteratorOp,
true)))
601 emitc::SwitchOp switchOp) {
605 if (failed(emitter.emitOperand(switchOp.getArg())))
609 for (
auto pair : llvm::zip(switchOp.getCases(), switchOp.getCaseRegions())) {
610 os <<
"\ncase " << std::get<0>(pair) <<
": {\n";
619 os <<
"\ndefault: {\n";
622 if (failed(
emitSwitchCase(emitter, os, switchOp.getDefaultRegion())))
635 Block &bodyBlock = doOp.getBodyRegion().
front();
637 if (failed(emitter.emitOperation(op,
true)))
643 Block &condBlock = doOp.getConditionRegion().
front();
644 auto condYield = cast<emitc::YieldOp>(condBlock.
back());
645 if (failed(emitter.emitExpression(
646 cast<emitc::ExpressionOp>(condYield.getOperand(0).getDefiningOp()))))
654 Operation *operation = cmpOp.getOperation();
656 StringRef binaryOperator;
658 switch (cmpOp.getPredicate()) {
659 case emitc::CmpPredicate::eq:
660 binaryOperator =
"==";
662 case emitc::CmpPredicate::ne:
663 binaryOperator =
"!=";
665 case emitc::CmpPredicate::lt:
666 binaryOperator =
"<";
668 case emitc::CmpPredicate::le:
669 binaryOperator =
"<=";
671 case emitc::CmpPredicate::gt:
672 binaryOperator =
">";
674 case emitc::CmpPredicate::ge:
675 binaryOperator =
">=";
677 case emitc::CmpPredicate::three_way:
678 binaryOperator =
"<=>";
686 emitc::ConditionalOp conditionalOp) {
689 if (failed(emitter.emitAssignPrefix(*conditionalOp)))
692 if (failed(emitter.emitOperand(conditionalOp.getCondition())))
697 if (failed(emitter.emitOperand(conditionalOp.getTrueValue())))
702 if (failed(emitter.emitOperand(conditionalOp.getFalseValue())))
709 emitc::VerbatimOp verbatimOp) {
712 FailureOr<SmallVector<ReplacementItem>> items =
713 verbatimOp.parseFormatString();
717 auto fmtArg = verbatimOp.getFmtArgs().begin();
720 if (
auto *str = std::get_if<StringRef>(&item)) {
723 if (failed(emitter.emitOperand(*fmtArg++)))
732 cf::BranchOp branchOp) {
737 llvm::zip(branchOp.getOperands(), successor.
getArguments())) {
738 Value &operand = std::get<0>(pair);
740 os << emitter.getOrCreateName(argument) <<
" = "
741 << emitter.getOrCreateName(operand) <<
";\n";
745 if (!(emitter.hasBlockLabel(successor)))
746 return branchOp.emitOpError(
"unable to find label for successor block");
747 os << emitter.getOrCreateName(successor);
752 cf::CondBranchOp condBranchOp) {
754 Block &trueSuccessor = *condBranchOp.getTrueDest();
755 Block &falseSuccessor = *condBranchOp.getFalseDest();
758 if (failed(emitter.emitOperand(condBranchOp.getCondition())))
765 for (
auto pair : llvm::zip(condBranchOp.getTrueOperands(),
767 Value &operand = std::get<0>(pair);
769 os << emitter.getOrCreateName(argument) <<
" = "
770 << emitter.getOrCreateName(operand) <<
";\n";
774 if (!(emitter.hasBlockLabel(trueSuccessor))) {
775 return condBranchOp.emitOpError(
"unable to find label for successor block");
777 os << emitter.getOrCreateName(trueSuccessor) <<
";\n";
781 for (
auto pair : llvm::zip(condBranchOp.getFalseOperands(),
783 Value &operand = std::get<0>(pair);
785 os << emitter.getOrCreateName(argument) <<
" = "
786 << emitter.getOrCreateName(operand) <<
";\n";
790 if (!(emitter.hasBlockLabel(falseSuccessor))) {
791 return condBranchOp.emitOpError()
792 <<
"unable to find label for successor block";
794 os << emitter.getOrCreateName(falseSuccessor) <<
";\n";
801 if (failed(emitter.emitAssignPrefix(*callOp)))
806 if (failed(emitter.emitOperands(*callOp)))
813 Operation *operation = callOp.getOperation();
814 StringRef callee = callOp.getCallee();
820 Operation *operation = callOp.getOperation();
821 StringRef callee = callOp.getCallee();
827 emitc::CallOpaqueOp callOpaqueOp) {
829 Operation &op = *callOpaqueOp.getOperation();
831 if (failed(emitter.emitAssignPrefix(op)))
833 os << callOpaqueOp.getCallee();
839 auto emitTemplateArgs = [&](
Attribute attr) -> LogicalResult {
840 return emitter.emitAttribute(op.
getLoc(), attr);
843 if (callOpaqueOp.getTemplateArgs()) {
851 auto emitArgs = [&](
Attribute attr) -> LogicalResult {
852 if (
auto t = dyn_cast<IntegerAttr>(attr)) {
854 if (t.getType().isIndex()) {
857 return emitter.emitOperand(operand);
860 if (failed(emitter.emitAttribute(op.
getLoc(), attr)))
868 LogicalResult emittedArgs =
869 callOpaqueOp.getArgs()
871 : emitter.emitOperands(op);
872 if (failed(emittedArgs))
879 emitc::ApplyOp applyOp) {
883 if (failed(emitter.emitAssignPrefix(op)))
885 os << applyOp.getApplicableOperator();
886 return emitter.emitOperand(applyOp.getOperand());
890 emitc::BitwiseAndOp bitwiseAndOp) {
891 Operation *operation = bitwiseAndOp.getOperation();
897 emitc::BitwiseLeftShiftOp bitwiseLeftShiftOp) {
898 Operation *operation = bitwiseLeftShiftOp.getOperation();
903 emitc::BitwiseNotOp bitwiseNotOp) {
904 Operation *operation = bitwiseNotOp.getOperation();
909 emitc::BitwiseOrOp bitwiseOrOp) {
910 Operation *operation = bitwiseOrOp.getOperation();
916 emitc::BitwiseRightShiftOp bitwiseRightShiftOp) {
917 Operation *operation = bitwiseRightShiftOp.getOperation();
922 emitc::BitwiseXorOp bitwiseXorOp) {
923 Operation *operation = bitwiseXorOp.getOperation();
928 emitc::UnaryPlusOp unaryPlusOp) {
929 Operation *operation = unaryPlusOp.getOperation();
934 emitc::UnaryMinusOp unaryMinusOp) {
935 Operation *operation = unaryMinusOp.getOperation();
943 if (failed(emitter.emitAssignPrefix(op)))
949 return emitter.emitOperand(castOp.getOperand());
953 emitc::ExpressionOp expressionOp) {
957 Operation &op = *expressionOp.getOperation();
959 if (failed(emitter.emitAssignPrefix(op)))
962 return emitter.emitExpression(expressionOp);
966 emitc::IncludeOp includeOp) {
970 if (includeOp.getIsStandardInclude())
971 os <<
"<" << includeOp.getInclude() <<
">";
973 os <<
"\"" << includeOp.getInclude() <<
"\"";
979 emitc::LogicalAndOp logicalAndOp) {
980 Operation *operation = logicalAndOp.getOperation();
985 emitc::LogicalNotOp logicalNotOp) {
986 Operation *operation = logicalNotOp.getOperation();
991 emitc::LogicalOrOp logicalOrOp) {
992 Operation *operation = logicalOrOp.getOperation();
1002 auto requiresParentheses = [&](
Value value) {
1011 emitter.emitType(forOp.getLoc(), forOp.getInductionVar().getType())))
1014 os << emitter.getOrCreateInductionVarName(forOp.getInductionVar());
1016 if (failed(emitter.emitOperand(forOp.getLowerBound())))
1019 os << emitter.getOrCreateInductionVarName(forOp.getInductionVar());
1021 Value upperBound = forOp.getUpperBound();
1022 bool upperBoundRequiresParentheses = requiresParentheses(upperBound);
1023 if (upperBoundRequiresParentheses)
1025 if (failed(emitter.emitOperand(upperBound)))
1027 if (upperBoundRequiresParentheses)
1030 os << emitter.getOrCreateInductionVarName(forOp.getInductionVar());
1032 if (failed(emitter.emitOperand(forOp.getStep())))
1037 CppEmitter::LoopScope lScope(emitter);
1039 Region &forRegion = forOp.getRegion();
1040 auto regionOps = forRegion.
getOps();
1043 for (
auto it = regionOps.begin(); std::next(it) != regionOps.end(); ++it) {
1044 if (failed(emitter.emitOperation(*it,
true)))
1058 auto emitAllExceptLast = [&emitter](
Region ®ion) {
1060 for (; std::next(it) != end; ++it) {
1061 if (failed(emitter.emitOperation(*it,
true)))
1064 assert(isa<emitc::YieldOp>(*it) &&
1065 "Expected last operation in the region to be emitc::yield");
1070 if (failed(emitter.emitOperand(ifOp.getCondition())))
1074 if (failed(emitAllExceptLast(ifOp.getThenRegion())))
1078 Region &elseRegion = ifOp.getElseRegion();
1079 if (!elseRegion.
empty()) {
1082 if (failed(emitAllExceptLast(elseRegion)))
1091 func::ReturnOp returnOp) {
1094 switch (returnOp.getNumOperands()) {
1099 if (failed(emitter.emitOperand(returnOp.getOperand(0))))
1103 os <<
" std::make_tuple(";
1104 if (failed(emitter.emitOperandsAndAttributes(*returnOp.getOperation())))
1112 emitc::ReturnOp returnOp) {
1115 if (returnOp.getNumOperands() == 0)
1119 if (failed(emitter.emitOperand(returnOp.getOperand())))
1126 if (failed(emitter.emitOperation(op,
false)))
1134 os <<
"class " << classOp.getSymName();
1135 if (classOp.getFinalSpecifier())
1137 os <<
" {\n public:\n";
1141 if (failed(emitter.emitOperation(op,
false)))
1152 if (failed(emitter.emitVariableDeclaration(
1153 fieldOp->getLoc(), fieldOp.getType(), fieldOp.getSymName())))
1155 std::optional<Attribute> initialValue = fieldOp.getInitialValue();
1158 if (failed(emitter.emitAttribute(fieldOp->getLoc(), *initialValue)))
1167 if (!emitter.shouldEmitFile(file))
1171 if (failed(emitter.emitOperation(op,
false)))
1184 return emitter.emitType(functionOp->
getLoc(), arg);
1195 return emitter.emitVariableDeclaration(
1196 functionOp->
getLoc(), arg.
getType(), emitter.getOrCreateName(arg));
1206 if (emitter.shouldDeclareVariablesAtTop()) {
1211 if (isa<emitc::ExpressionOp>(op->
getParentOp()) ||
1212 (isa<emitc::ExpressionOp>(op) &&
1216 if (failed(emitter.emitVariableDeclaration(
1219 op->
emitError(
"unable to declare result variable for op"));
1224 if (
result.wasInterrupted())
1229 for (
Block &block : blocks) {
1230 emitter.getOrCreateName(block);
1234 for (
Block &block : llvm::drop_begin(blocks)) {
1236 if (emitter.hasValueInScope(arg))
1237 return functionOp->
emitOpError(
" block argument #")
1238 << arg.getArgNumber() <<
" is out of scope";
1239 if (isa<ArrayType, LValueType>(arg.getType()))
1240 return functionOp->
emitOpError(
"cannot emit block argument #")
1241 << arg.getArgNumber() <<
" with type " << arg.getType();
1243 emitter.emitType(block.getParentOp()->getLoc(), arg.getType()))) {
1246 os <<
" " << emitter.getOrCreateName(arg) <<
";\n";
1250 for (
Block &block : blocks) {
1252 if (!block.hasNoPredecessors()) {
1253 if (failed(emitter.emitLabel(block)))
1256 for (
Operation &op : block.getOperations()) {
1257 if (failed(emitter.emitOperation(op,
true)))
1268 func::FuncOp functionOp) {
1270 if (!emitter.shouldDeclareVariablesAtTop() &&
1271 functionOp.getBlocks().size() > 1) {
1272 return functionOp.emitOpError(
1273 "with multiple blocks needs variables declared at top");
1276 if (llvm::any_of(functionOp.getArgumentTypes(), llvm::IsaPred<LValueType>)) {
1277 return functionOp.emitOpError()
1278 <<
"cannot emit lvalue type as argument type";
1281 if (llvm::any_of(functionOp.getResultTypes(), llvm::IsaPred<ArrayType>)) {
1282 return functionOp.emitOpError() <<
"cannot emit array type as result type";
1285 CppEmitter::FunctionScope scope(emitter);
1287 if (failed(emitter.emitTypes(functionOp.getLoc(),
1288 functionOp.getFunctionType().getResults())))
1290 os <<
" " << functionOp.getName();
1293 Operation *operation = functionOp.getOperation();
1305 emitc::FuncOp functionOp) {
1307 if (!emitter.shouldDeclareVariablesAtTop() &&
1308 functionOp.getBlocks().size() > 1) {
1309 return functionOp.emitOpError(
1310 "with multiple blocks needs variables declared at top");
1313 CppEmitter::FunctionScope scope(emitter);
1315 if (functionOp.getSpecifiers()) {
1316 for (
Attribute specifier : functionOp.getSpecifiersAttr()) {
1317 os << cast<StringAttr>(specifier).str() <<
" ";
1321 if (failed(emitter.emitTypes(functionOp.getLoc(),
1322 functionOp.getFunctionType().getResults())))
1324 os <<
" " << functionOp.getName();
1327 Operation *operation = functionOp.getOperation();
1328 if (functionOp.isExternal()) {
1330 functionOp.getArgumentTypes())))
1346 DeclareFuncOp declareFuncOp) {
1349 CppEmitter::FunctionScope scope(emitter);
1351 declareFuncOp, declareFuncOp.getSymNameAttr());
1356 if (functionOp.getSpecifiers()) {
1357 for (
Attribute specifier : functionOp.getSpecifiersAttr()) {
1358 os << cast<StringAttr>(specifier).str() <<
" ";
1362 if (failed(emitter.emitTypes(functionOp.getLoc(),
1363 functionOp.getFunctionType().getResults())))
1365 os <<
" " << functionOp.getName();
1368 Operation *operation = functionOp.getOperation();
1376CppEmitter::CppEmitter(
raw_ostream &os,
bool declareVariablesAtTop,
1378 : os(os), declareVariablesAtTop(declareVariablesAtTop),
1379 fileId(fileId.str()), defaultValueMapperScope(valueMapper),
1380 defaultBlockMapperScope(blockMapper) {
1381 labelInScopeCount.push(0);
1384void CppEmitter::cacheDeferredOpResult(
Value value, StringRef str) {
1385 if (!valueMapper.count(value))
1386 valueMapper.insert(value, str.str());
1390StringRef CppEmitter::getOrCreateName(Value val) {
1391 if (!valueMapper.count(val)) {
1393 "cacheDeferredOpResult should have been called on this value, "
1394 "update the emitOperation function.");
1396 valueMapper.insert(val, formatv(
"v{0}", ++valueCount));
1398 return *valueMapper.begin(val);
1403StringRef CppEmitter::getOrCreateInductionVarName(Value val) {
1404 if (!valueMapper.count(val)) {
1406 int64_t identifier =
'i' + loopNestingLevel;
1408 if (identifier >=
'i' && identifier <=
't') {
1409 valueMapper.insert(val,
1410 formatv(
"{0}{1}", (
char)identifier, ++valueCount));
1413 valueMapper.insert(val, formatv(
"u{0}", ++valueCount));
1416 return *valueMapper.begin(val);
1420StringRef CppEmitter::getOrCreateName(
Block &block) {
1421 if (!blockMapper.count(&block))
1422 blockMapper.insert(&block, formatv(
"label{0}", ++labelInScopeCount.top()));
1423 return *blockMapper.begin(&block);
1426bool CppEmitter::shouldMapToUnsigned(IntegerType::SignednessSemantics val) {
1428 case IntegerType::Signless:
1430 case IntegerType::Signed:
1432 case IntegerType::Unsigned:
1435 llvm_unreachable(
"Unexpected IntegerType::SignednessSemantics");
1438bool CppEmitter::hasValueInScope(Value val) {
return valueMapper.count(val); }
1440bool CppEmitter::hasBlockLabel(
Block &block) {
1441 return blockMapper.count(&block);
1444LogicalResult CppEmitter::emitAttribute(Location loc, Attribute attr) {
1445 auto printInt = [&](
const APInt &val,
bool isUnsigned) {
1446 if (val.getBitWidth() == 1) {
1447 if (val.getBoolValue())
1452 SmallString<128> strValue;
1453 val.toString(strValue, 10, !isUnsigned,
false);
1458 auto printFloat = [&](
const APFloat &val) {
1459 if (val.isFinite()) {
1460 SmallString<128> strValue;
1462 val.toString(strValue, 0, 0,
false);
1464 switch (llvm::APFloatBase::SemanticsToEnum(val.getSemantics())) {
1465 case llvm::APFloatBase::S_IEEEhalf:
1468 case llvm::APFloatBase::S_BFloat:
1471 case llvm::APFloatBase::S_IEEEsingle:
1474 case llvm::APFloatBase::S_IEEEdouble:
1477 llvm_unreachable(
"unsupported floating point type");
1479 }
else if (val.isNaN()) {
1481 }
else if (val.isInfinity()) {
1482 if (val.isNegative())
1489 if (
auto fAttr = dyn_cast<FloatAttr>(attr)) {
1490 if (!isa<Float16Type, BFloat16Type, Float32Type, Float64Type>(
1493 loc,
"expected floating point attribute to be f16, bf16, f32 or f64");
1495 printFloat(fAttr.getValue());
1498 if (
auto dense = dyn_cast<DenseFPElementsAttr>(attr)) {
1499 if (!isa<Float16Type, BFloat16Type, Float32Type, Float64Type>(
1500 dense.getElementType())) {
1502 loc,
"expected floating point attribute to be f16, bf16, f32 or f64");
1505 interleaveComma(dense, os, [&](
const APFloat &val) { printFloat(val); });
1511 if (
auto iAttr = dyn_cast<IntegerAttr>(attr)) {
1512 if (
auto iType = dyn_cast<IntegerType>(iAttr.getType())) {
1513 printInt(iAttr.getValue(), shouldMapToUnsigned(iType.getSignedness()));
1516 if (
auto iType = dyn_cast<IndexType>(iAttr.getType())) {
1517 printInt(iAttr.getValue(),
false);
1521 if (
auto dense = dyn_cast<DenseIntElementsAttr>(attr)) {
1522 if (
auto iType = dyn_cast<IntegerType>(
1523 cast<ShapedType>(dense.getType()).getElementType())) {
1525 interleaveComma(dense, os, [&](
const APInt &val) {
1526 printInt(val, shouldMapToUnsigned(iType.getSignedness()));
1531 if (
auto iType = dyn_cast<IndexType>(
1532 cast<ShapedType>(dense.getType()).getElementType())) {
1534 interleaveComma(dense, os,
1535 [&](
const APInt &val) { printInt(val,
false); });
1542 if (
auto oAttr = dyn_cast<emitc::OpaqueAttr>(attr)) {
1543 os << oAttr.getValue();
1548 if (
auto sAttr = dyn_cast<SymbolRefAttr>(attr)) {
1549 if (sAttr.getNestedReferences().size() > 1)
1550 return emitError(loc,
"attribute has more than 1 nested reference");
1551 os << sAttr.getRootReference().getValue();
1556 if (
auto type = dyn_cast<TypeAttr>(attr))
1557 return emitType(loc, type.getValue());
1559 return emitError(loc,
"cannot emit attribute: ") << attr;
1562LogicalResult CppEmitter::emitExpression(ExpressionOp expressionOp) {
1563 assert(emittedExpressionPrecedence.empty() &&
1564 "Expected precedence stack to be empty");
1565 Operation *rootOp = expressionOp.getRootOp();
1567 emittedExpression = expressionOp;
1571 pushExpressionPrecedence(precedence.value());
1573 if (
failed(emitOperation(*rootOp,
false)))
1576 popExpressionPrecedence();
1577 assert(emittedExpressionPrecedence.empty() &&
1578 "Expected precedence stack to be empty");
1579 emittedExpression =
nullptr;
1584LogicalResult CppEmitter::emitOperand(Value value,
bool isInBrackets) {
1585 if (isPartOfCurrentExpression(value)) {
1587 assert(def &&
"Expected operand to be defined by an operation");
1595 bool encloseInParenthesis =
1596 !isInBrackets && precedence.value() <= getExpressionPrecedence();
1598 if (encloseInParenthesis)
1600 pushExpressionPrecedence(precedence.value());
1602 if (
failed(emitOperation(*def,
false)))
1605 if (encloseInParenthesis)
1608 popExpressionPrecedence();
1614 return emitExpression(expressionOp);
1616 if (BlockArgument arg = dyn_cast<BlockArgument>(value)) {
1619 Operation *argOp = arg.getParentBlock()->getParentOp();
1620 if (
auto expressionOp = dyn_cast<ExpressionOp>(argOp)) {
1624 assert(expressionOp == emittedExpression &&
1625 "Expected expression being emitted");
1626 value = expressionOp->getOperand(arg.getArgNumber());
1630 os << getOrCreateName(value);
1634LogicalResult CppEmitter::emitOperands(Operation &op) {
1638 return emitOperand(operand, true);
1643CppEmitter::emitOperandsAndAttributes(Operation &op,
1644 ArrayRef<StringRef> exclude) {
1645 if (
failed(emitOperands(op)))
1649 for (NamedAttribute attr : op.
getAttrs()) {
1650 if (!llvm::is_contained(exclude, attr.getName().strref())) {
1657 auto emitNamedAttribute = [&](NamedAttribute attr) -> LogicalResult {
1658 if (llvm::is_contained(exclude, attr.getName().strref()))
1660 os <<
"/* " << attr.getName().getValue() <<
" */";
1661 if (
failed(emitAttribute(op.
getLoc(), attr.getValue())))
1668LogicalResult CppEmitter::emitVariableAssignment(OpResult
result) {
1669 if (!hasValueInScope(
result)) {
1670 return result.getDefiningOp()->emitOpError(
1671 "result variable for the operation has not been declared");
1673 os << getOrCreateName(
result) <<
" = ";
1677LogicalResult CppEmitter::emitVariableDeclaration(OpResult
result,
1678 bool trailingSemicolon) {
1681 if (hasValueInScope(
result)) {
1682 return result.getDefiningOp()->emitError(
1683 "result variable for the operation already declared");
1685 if (
failed(emitVariableDeclaration(
result.getOwner()->getLoc(),
1687 getOrCreateName(
result))))
1689 if (trailingSemicolon)
1694LogicalResult CppEmitter::emitGlobalVariable(GlobalOp op) {
1695 if (op.getExternSpecifier())
1697 else if (op.getStaticSpecifier())
1699 if (op.getConstSpecifier())
1702 if (
failed(emitVariableDeclaration(op->getLoc(), op.getType(),
1703 op.getSymName()))) {
1707 std::optional<Attribute> initialValue = op.getInitialValue();
1710 if (
failed(emitAttribute(op->getLoc(), *initialValue)))
1718LogicalResult CppEmitter::emitAssignPrefix(Operation &op) {
1720 if (isEmittingExpression())
1728 if (shouldDeclareVariablesAtTop()) {
1739 if (!shouldDeclareVariablesAtTop()) {
1747 [&](Value
result) { os << getOrCreateName(result); });
1753LogicalResult CppEmitter::emitLabel(
Block &block) {
1754 if (!hasBlockLabel(block))
1758 os.getOStream() << getOrCreateName(block) <<
":\n";
1762LogicalResult CppEmitter::emitOperation(Operation &op,
bool trailingSemicolon) {
1763 LogicalResult status =
1764 llvm::TypeSwitch<Operation *, LogicalResult>(&op)
1766 .Case<ModuleOp>([&](
auto op) {
return printOperation(*
this, op); })
1768 .Case<cf::BranchOp, cf::CondBranchOp>(
1771 .Case<emitc::AddOp, emitc::ApplyOp, emitc::AssignOp,
1772 emitc::BitwiseAndOp, emitc::BitwiseLeftShiftOp,
1773 emitc::BitwiseNotOp, emitc::BitwiseOrOp,
1774 emitc::BitwiseRightShiftOp, emitc::BitwiseXorOp, emitc::CallOp,
1775 emitc::CallOpaqueOp, emitc::CastOp, emitc::ClassOp,
1776 emitc::CmpOp, emitc::ConditionalOp, emitc::ConstantOp,
1777 emitc::DeclareFuncOp, emitc::DivOp, emitc::DoOp,
1778 emitc::ExpressionOp, emitc::FieldOp, emitc::FileOp,
1779 emitc::ForOp, emitc::FuncOp, emitc::GetFieldOp,
1780 emitc::GetGlobalOp, emitc::GlobalOp, emitc::IfOp,
1781 emitc::IncludeOp, emitc::LiteralOp, emitc::LoadOp,
1782 emitc::LogicalAndOp, emitc::LogicalNotOp, emitc::LogicalOrOp,
1783 emitc::MemberOfPtrOp, emitc::MemberOp, emitc::MulOp,
1784 emitc::RemOp, emitc::ReturnOp, emitc::SubscriptOp, emitc::SubOp,
1785 emitc::SwitchOp, emitc::UnaryMinusOp, emitc::UnaryPlusOp,
1786 emitc::VariableOp, emitc::VerbatimOp>(
1790 .Case<func::CallOp, func::FuncOp, func::ReturnOp>(
1792 .Default([&](Operation *) {
1793 return op.
emitOpError(
"unable to find printer for op");
1802 if (isEmittingExpression() ||
1803 (isa<emitc::ExpressionOp>(op) &&
1809 trailingSemicolon &=
1810 !isa<cf::CondBranchOp, emitc::DeclareFuncOp, emitc::DoOp, emitc::FileOp,
1811 emitc::ForOp, emitc::IfOp, emitc::IncludeOp, emitc::SwitchOp,
1812 emitc::VerbatimOp>(op);
1814 os << (trailingSemicolon ?
";\n" :
"\n");
1819LogicalResult CppEmitter::emitVariableDeclaration(Location loc, Type type,
1821 if (
auto arrType = dyn_cast<emitc::ArrayType>(type)) {
1822 if (
failed(emitType(loc, arrType.getElementType())))
1825 for (
auto dim : arrType.getShape()) {
1826 os <<
"[" << dim <<
"]";
1830 if (
failed(emitType(loc, type)))
1836LogicalResult CppEmitter::emitType(Location loc, Type type) {
1837 if (
auto iType = dyn_cast<IntegerType>(type)) {
1838 switch (iType.getWidth()) {
1840 return (os <<
"bool"),
success();
1845 if (shouldMapToUnsigned(iType.getSignedness()))
1846 return (os <<
"uint" << iType.getWidth() <<
"_t"),
success();
1848 return (os <<
"int" << iType.getWidth() <<
"_t"),
success();
1850 return emitError(loc,
"cannot emit integer type ") << type;
1853 if (
auto fType = dyn_cast<FloatType>(type)) {
1854 switch (fType.getWidth()) {
1856 if (llvm::isa<Float16Type>(type))
1857 return (os <<
"_Float16"),
success();
1858 if (llvm::isa<BFloat16Type>(type))
1859 return (os <<
"__bf16"),
success();
1861 return emitError(loc,
"cannot emit float type ") << type;
1864 return (os <<
"float"),
success();
1866 return (os <<
"double"),
success();
1868 return emitError(loc,
"cannot emit float type ") << type;
1871 if (
auto iType = dyn_cast<IndexType>(type))
1872 return (os <<
"size_t"),
success();
1873 if (
auto sType = dyn_cast<emitc::SizeTType>(type))
1874 return (os <<
"size_t"),
success();
1875 if (
auto sType = dyn_cast<emitc::SignedSizeTType>(type))
1876 return (os <<
"ssize_t"),
success();
1877 if (
auto pType = dyn_cast<emitc::PtrDiffTType>(type))
1878 return (os <<
"ptrdiff_t"),
success();
1879 if (
auto tType = dyn_cast<TensorType>(type)) {
1880 if (!tType.hasRank())
1881 return emitError(loc,
"cannot emit unranked tensor type");
1882 if (!tType.hasStaticShape())
1883 return emitError(loc,
"cannot emit tensor type with non static shape");
1885 if (isa<ArrayType>(tType.getElementType()))
1886 return emitError(loc,
"cannot emit tensor of array type ") << type;
1887 if (
failed(emitType(loc, tType.getElementType())))
1889 auto shape = tType.getShape();
1890 for (
auto dimSize : shape) {
1897 if (
auto tType = dyn_cast<TupleType>(type))
1898 return emitTupleType(loc, tType.getTypes());
1899 if (
auto oType = dyn_cast<emitc::OpaqueType>(type)) {
1900 os << oType.getValue();
1903 if (
auto aType = dyn_cast<emitc::ArrayType>(type)) {
1904 if (
failed(emitType(loc, aType.getElementType())))
1906 for (
auto dim : aType.getShape())
1907 os <<
"[" << dim <<
"]";
1910 if (
auto lType = dyn_cast<emitc::LValueType>(type))
1911 return emitType(loc, lType.getValueType());
1912 if (
auto pType = dyn_cast<emitc::PointerType>(type)) {
1913 if (isa<ArrayType>(pType.getPointee()))
1914 return emitError(loc,
"cannot emit pointer to array type ") << type;
1915 if (
failed(emitType(loc, pType.getPointee())))
1920 return emitError(loc,
"cannot emit type ") << type;
1923LogicalResult CppEmitter::emitTypes(Location loc, ArrayRef<Type> types) {
1924 switch (types.size()) {
1929 return emitType(loc, types.front());
1931 return emitTupleType(loc, types);
1935LogicalResult CppEmitter::emitTupleType(Location loc, ArrayRef<Type> types) {
1936 if (llvm::any_of(types, llvm::IsaPred<ArrayType>)) {
1937 return emitError(loc,
"cannot emit tuple of array type");
1939 os <<
"std::tuple<";
1941 types, os, [&](Type type) {
return emitType(loc, type); })))
1947void CppEmitter::resetValueCounter() { valueCount = 0; }
1949void CppEmitter::increaseLoopNestingLevel() { loopNestingLevel++; }
1951void CppEmitter::decreaseLoopNestingLevel() { loopNestingLevel--; }
1954 bool declareVariablesAtTop,
1956 CppEmitter emitter(os, declareVariablesAtTop, fileId);
1957 return emitter.emitOperation(*op,
false);
static LogicalResult printOperation(CppEmitter &emitter, emitc::GetFieldOp getFieldOp)
static LogicalResult printCallOperation(CppEmitter &emitter, Operation *callOp, StringRef callee)
static FailureOr< int > getOperatorPrecedence(Operation *operation)
Return the precedence of a operator as an integer, higher values imply higher precedence.
static bool shouldBeInlined(ExpressionOp expressionOp)
Determine whether expression expressionOp should be emitted inline, i.e.
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 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 is a value defined by a result of an operation.
Operation is the basic unit of execution within MLIR.
Value getOperand(unsigned idx)
ArrayRef< NamedAttribute > getAttrs()
Return all of the attributes on this operation.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Location getLoc()
The source location the operation was defined or derived from.
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
unsigned getNumOperands()
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
operand_range getOperands()
Returns an iterator on the underlying Value's.
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
result_range getResults()
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
unsigned getNumResults()
Return the number of results held by this operation.
This class provides iteration over the held operations of blocks directly within a region.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
llvm::iplist< Block > BlockListType
OpIterator op_begin()
Return iterators that walk the operations nested directly within this region.
iterator_range< OpIterator > getOps()
MutableArrayRef< BlockArgument > BlockArgListType
static Operation * lookupNearestSymbolFrom(Operation *from, StringAttr symbol)
Returns the operation registered with the given symbol name within the closest parent operation of,...
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
A utility result that is used to signal how to proceed with an ongoing walk:
static WalkResult advance()
raw_ostream subclass that simplifies indention a sequence of code.
raw_indented_ostream & indent()
Increases the indent and returning this raw_indented_ostream.
raw_indented_ostream & unindent()
Decreases the indent and returning this raw_indented_ostream.
LogicalResult translateToCpp(Operation *op, raw_ostream &os, bool declareVariablesAtTop=false, StringRef fileId={})
Translates the given operation to C++ code.
std::variant< StringRef, Placeholder > ReplacementItem
Include the generated interface declarations.
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
This iterator enumerates the elements in "forward" order.