1959 lines · cpp
1//===- TranslateToCpp.cpp - Translating to C++ calls ----------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"10#include "mlir/Dialect/EmitC/IR/EmitC.h"11#include "mlir/Dialect/Func/IR/FuncOps.h"12#include "mlir/IR/BuiltinOps.h"13#include "mlir/IR/BuiltinTypes.h"14#include "mlir/IR/Dialect.h"15#include "mlir/IR/Operation.h"16#include "mlir/IR/SymbolTable.h"17#include "mlir/IR/Value.h"18#include "mlir/Support/IndentedOstream.h"19#include "mlir/Support/LLVM.h"20#include "mlir/Target/Cpp/CppEmitter.h"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"26#include <stack>27 28#define DEBUG_TYPE "translate-to-cpp"29 30using namespace mlir;31using namespace mlir::emitc;32using llvm::formatv;33 34/// Convenience functions to produce interleaved output with functions returning35/// a LogicalResult. This is different than those in STLExtras as functions used36/// on each element doesn't return a string.37template <typename ForwardIterator, typename UnaryFunctor,38 typename NullaryFunctor>39static inline LogicalResult40interleaveWithError(ForwardIterator begin, ForwardIterator end,41 UnaryFunctor eachFn, NullaryFunctor betweenFn) {42 if (begin == end)43 return success();44 if (failed(eachFn(*begin)))45 return failure();46 ++begin;47 for (; begin != end; ++begin) {48 betweenFn();49 if (failed(eachFn(*begin)))50 return failure();51 }52 return success();53}54 55template <typename Container, typename UnaryFunctor, typename NullaryFunctor>56static inline LogicalResult interleaveWithError(const Container &c,57 UnaryFunctor eachFn,58 NullaryFunctor betweenFn) {59 return interleaveWithError(c.begin(), c.end(), eachFn, betweenFn);60}61 62template <typename Container, typename UnaryFunctor>63static inline LogicalResult interleaveCommaWithError(const Container &c,64 raw_ostream &os,65 UnaryFunctor eachFn) {66 return interleaveWithError(c.begin(), c.end(), eachFn, [&]() { os << ", "; });67}68 69/// Return the precedence of a operator as an integer, higher values70/// imply higher precedence.71static FailureOr<int> getOperatorPrecedence(Operation *operation) {72 return llvm::TypeSwitch<Operation *, FailureOr<int>>(operation)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:88 return 8;89 case emitc::CmpPredicate::lt:90 case emitc::CmpPredicate::le:91 case emitc::CmpPredicate::gt:92 case emitc::CmpPredicate::ge:93 return 9;94 case emitc::CmpPredicate::three_way:95 return 10;96 }97 return op->emitError("unsupported cmp predicate");98 })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"); });112}113 114namespace {115/// Emitter that uses dialect specific emitters to emit C++ code.116struct CppEmitter {117 explicit CppEmitter(raw_ostream &os, bool declareVariablesAtTop,118 StringRef fileId);119 120 /// Emits attribute or returns failure.121 LogicalResult emitAttribute(Location loc, Attribute attr);122 123 /// Emits operation 'op' with/without training semicolon or returns failure.124 ///125 /// For operations that should never be followed by a semicolon, like ForOp,126 /// the `trailingSemicolon` argument is ignored and a semicolon is not127 /// emitted.128 LogicalResult emitOperation(Operation &op, bool trailingSemicolon);129 130 /// Emits type 'type' or returns failure.131 LogicalResult emitType(Location loc, Type type);132 133 /// Emits array of types as a std::tuple of the emitted types.134 /// - emits void for an empty array;135 /// - emits the type of the only element for arrays of size one;136 /// - emits a std::tuple otherwise;137 LogicalResult emitTypes(Location loc, ArrayRef<Type> types);138 139 /// Emits array of types as a std::tuple of the emitted types independently of140 /// the array size.141 LogicalResult emitTupleType(Location loc, ArrayRef<Type> types);142 143 /// Emits an assignment for a variable which has been declared previously.144 LogicalResult emitVariableAssignment(OpResult result);145 146 /// Emits a variable declaration for a result of an operation.147 LogicalResult emitVariableDeclaration(OpResult result,148 bool trailingSemicolon);149 150 /// Emits a declaration of a variable with the given type and name.151 LogicalResult emitVariableDeclaration(Location loc, Type type,152 StringRef name);153 154 /// Emits the variable declaration and assignment prefix for 'op'.155 /// - emits separate variable followed by std::tie for multi-valued operation;156 /// - emits single type followed by variable for single result;157 /// - emits nothing if no value produced by op;158 /// Emits final '=' operator where a type is produced. Returns failure if159 /// any result type could not be converted.160 LogicalResult emitAssignPrefix(Operation &op);161 162 /// Emits a global variable declaration or definition.163 LogicalResult emitGlobalVariable(GlobalOp op);164 165 /// Emits a label for the block.166 LogicalResult emitLabel(Block &block);167 168 /// Emits the operands and atttributes of the operation. All operands are169 /// emitted first and then all attributes in alphabetical order.170 LogicalResult emitOperandsAndAttributes(Operation &op,171 ArrayRef<StringRef> exclude = {});172 173 /// Emits the operands of the operation. All operands are emitted in order.174 LogicalResult emitOperands(Operation &op);175 176 /// Emits value as an operand of some operation. Unless \p isInBrackets is177 /// true, operands emitted as sub-expressions will be parenthesized if needed178 /// in order to enforce correct evaluation based on precedence and179 /// associativity.180 LogicalResult emitOperand(Value value, bool isInBrackets = false);181 182 /// Emit an expression as a C expression.183 LogicalResult emitExpression(ExpressionOp expressionOp);184 185 /// Insert the expression representing the operation into the value cache.186 void cacheDeferredOpResult(Value value, StringRef str);187 188 /// Return the existing or a new name for a Value.189 StringRef getOrCreateName(Value val);190 191 /// Return the existing or a new name for a loop induction variable of an192 /// emitc::ForOp.193 StringRef getOrCreateInductionVarName(Value val);194 195 /// Return the existing or a new label of a Block.196 StringRef getOrCreateName(Block &block);197 198 LogicalResult emitInlinedExpression(Value value);199 200 /// Whether to map an mlir integer to a unsigned integer in C++.201 bool shouldMapToUnsigned(IntegerType::SignednessSemantics val);202 203 /// Abstract RAII helper function to manage entering/exiting C++ scopes.204 struct Scope {205 ~Scope() { emitter.labelInScopeCount.pop(); }206 207 private:208 llvm::ScopedHashTableScope<Value, std::string> valueMapperScope;209 llvm::ScopedHashTableScope<Block *, std::string> blockMapperScope;210 211 protected:212 Scope(CppEmitter &emitter)213 : valueMapperScope(emitter.valueMapper),214 blockMapperScope(emitter.blockMapper), emitter(emitter) {215 emitter.labelInScopeCount.push(emitter.labelInScopeCount.top());216 }217 CppEmitter &emitter;218 };219 220 /// RAII helper function to manage entering/exiting functions, while re-using221 /// value names.222 struct FunctionScope : Scope {223 FunctionScope(CppEmitter &emitter) : Scope(emitter) {224 // Re-use value names.225 emitter.resetValueCounter();226 }227 };228 229 /// RAII helper function to manage entering/exiting emitc::forOp loops and230 /// handle induction variable naming.231 struct LoopScope : Scope {232 LoopScope(CppEmitter &emitter) : Scope(emitter) {233 emitter.increaseLoopNestingLevel();234 }235 ~LoopScope() { emitter.decreaseLoopNestingLevel(); }236 };237 238 /// Returns wether the Value is assigned to a C++ variable in the scope.239 bool hasValueInScope(Value val);240 241 // Returns whether a label is assigned to the block.242 bool hasBlockLabel(Block &block);243 244 /// Returns the output stream.245 raw_indented_ostream &ostream() { return os; };246 247 /// Returns if all variables for op results and basic block arguments need to248 /// be declared at the beginning of a function.249 bool shouldDeclareVariablesAtTop() { return declareVariablesAtTop; };250 251 /// Returns whether this file op should be emitted252 bool shouldEmitFile(FileOp file) {253 return !fileId.empty() && file.getId() == fileId;254 }255 256 /// Is expression currently being emitted.257 bool isEmittingExpression() { return emittedExpression; }258 259 /// Determine whether given value is part of the expression potentially being260 /// emitted.261 bool isPartOfCurrentExpression(Value value) {262 if (!emittedExpression)263 return false;264 Operation *def = value.getDefiningOp();265 if (!def)266 return false;267 return isPartOfCurrentExpression(def);268 }269 270 /// Determine whether given operation is part of the expression potentially271 /// being emitted.272 bool isPartOfCurrentExpression(Operation *def) {273 auto operandExpression = dyn_cast<ExpressionOp>(def->getParentOp());274 return operandExpression && operandExpression == emittedExpression;275 };276 277 // Resets the value counter to 0.278 void resetValueCounter();279 280 // Increases the loop nesting level by 1.281 void increaseLoopNestingLevel();282 283 // Decreases the loop nesting level by 1.284 void decreaseLoopNestingLevel();285 286private:287 using ValueMapper = llvm::ScopedHashTable<Value, std::string>;288 using BlockMapper = llvm::ScopedHashTable<Block *, std::string>;289 290 /// Output stream to emit to.291 raw_indented_ostream os;292 293 /// Boolean to enforce that all variables for op results and block294 /// arguments are declared at the beginning of the function. This also295 /// includes results from ops located in nested regions.296 bool declareVariablesAtTop;297 298 /// Only emit file ops whos id matches this value.299 std::string fileId;300 301 /// Map from value to name of C++ variable that contain the name.302 ValueMapper valueMapper;303 304 /// Map from block to name of C++ label.305 BlockMapper blockMapper;306 307 /// Default values representing outermost scope.308 llvm::ScopedHashTableScope<Value, std::string> defaultValueMapperScope;309 llvm::ScopedHashTableScope<Block *, std::string> defaultBlockMapperScope;310 311 std::stack<int64_t> labelInScopeCount;312 313 /// Keeps track of the amount of nested loops the emitter currently operates314 /// in.315 uint64_t loopNestingLevel{0};316 317 /// Emitter-level count of created values to enable unique identifiers.318 unsigned int valueCount{0};319 320 /// State of the current expression being emitted.321 ExpressionOp emittedExpression;322 SmallVector<int> emittedExpressionPrecedence;323 324 void pushExpressionPrecedence(int precedence) {325 emittedExpressionPrecedence.push_back(precedence);326 }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();333 }334};335} // namespace336 337/// Determine whether expression \p op should be emitted in a deferred way.338static bool hasDeferredEmission(Operation *op) {339 return isa_and_nonnull<emitc::GetGlobalOp, emitc::LiteralOp, emitc::MemberOp,340 emitc::MemberOfPtrOp, emitc::SubscriptOp,341 emitc::GetFieldOp>(op);342}343 344/// Determine whether expression \p expressionOp should be emitted inline, i.e.345/// as part of its user. This function recommends inlining of any expressions346/// that can be inlined unless it is used by another expression, under the347/// assumption that any expression fusion/re-materialization was taken care of348/// by transformations run by the backend.349static bool shouldBeInlined(ExpressionOp expressionOp) {350 // Do not inline if expression is marked as such.351 if (expressionOp.getDoNotInline())352 return false;353 354 // Do not inline expressions with multiple uses.355 Value result = expressionOp.getResult();356 if (!result.hasOneUse())357 return false;358 359 Operation *user = *result.getUsers().begin();360 361 // Do not inline expressions used by operations with deferred emission, since362 // their translation requires the materialization of variables.363 if (hasDeferredEmission(user))364 return false;365 366 // Do not inline expressions used by other expressions or by ops with the367 // CExpressionInterface. If this was intended, the user could have been merged368 // into the expression op.369 if (isa<emitc::ExpressionOp, emitc::CExpressionInterface>(*user))370 return false;371 372 // Expressions with no side-effects can safely be inlined.373 if (!expressionOp.hasSideEffects())374 return true;375 376 // Expressions with side-effects can be only inlined if side-effect ordering377 // in the program is provably retained.378 379 // Require the user to immediately follow the expression.380 if (++Block::iterator(expressionOp) != Block::iterator(user))381 return false;382 383 // These single-operand ops are safe.384 if (isa<emitc::IfOp, emitc::SwitchOp, emitc::ReturnOp>(user))385 return true;386 387 // For assignment look for specific cases to inline as evaluation order of388 // its lvalue and rvalue is undefined in C.389 if (auto assignOp = dyn_cast<emitc::AssignOp>(user)) {390 // Inline if this assignment is of the form `<var> = <expression>`.391 if (expressionOp.getResult() == assignOp.getValue() &&392 isa_and_present<VariableOp>(assignOp.getVar().getDefiningOp()))393 return true;394 }395 396 return false;397}398 399static LogicalResult printOperation(CppEmitter &emitter,400 emitc::GetFieldOp getFieldOp) {401 emitter.cacheDeferredOpResult(getFieldOp.getResult(),402 getFieldOp.getFieldName());403 return success();404}405 406static LogicalResult printOperation(CppEmitter &emitter,407 emitc::GetGlobalOp getGlobalOp) {408 emitter.cacheDeferredOpResult(getGlobalOp.getResult(), getGlobalOp.getName());409 return success();410}411 412static LogicalResult printOperation(CppEmitter &emitter,413 emitc::LiteralOp literalOp) {414 emitter.cacheDeferredOpResult(literalOp.getResult(), literalOp.getValue());415 return success();416}417 418static LogicalResult printOperation(CppEmitter &emitter,419 emitc::MemberOp memberOp) {420 std::string out;421 llvm::raw_string_ostream ss(out);422 ss << emitter.getOrCreateName(memberOp.getOperand());423 ss << "." << memberOp.getMember();424 emitter.cacheDeferredOpResult(memberOp.getResult(), out);425 return success();426}427 428static LogicalResult printOperation(CppEmitter &emitter,429 emitc::MemberOfPtrOp memberOfPtrOp) {430 std::string out;431 llvm::raw_string_ostream ss(out);432 ss << emitter.getOrCreateName(memberOfPtrOp.getOperand());433 ss << "->" << memberOfPtrOp.getMember();434 emitter.cacheDeferredOpResult(memberOfPtrOp.getResult(), out);435 return success();436}437 438static LogicalResult printOperation(CppEmitter &emitter,439 emitc::SubscriptOp subscriptOp) {440 std::string out;441 llvm::raw_string_ostream ss(out);442 ss << emitter.getOrCreateName(subscriptOp.getValue());443 for (auto index : subscriptOp.getIndices()) {444 ss << "[" << emitter.getOrCreateName(index) << "]";445 }446 emitter.cacheDeferredOpResult(subscriptOp.getResult(), out);447 return success();448}449 450static LogicalResult printConstantOp(CppEmitter &emitter, Operation *operation,451 Attribute value) {452 OpResult result = operation->getResult(0);453 454 // Only emit an assignment as the variable was already declared when printing455 // the FuncOp.456 if (emitter.shouldDeclareVariablesAtTop()) {457 // Skip the assignment if the emitc.constant has no value.458 if (auto oAttr = dyn_cast<emitc::OpaqueAttr>(value)) {459 if (oAttr.getValue().empty())460 return success();461 }462 463 if (failed(emitter.emitVariableAssignment(result)))464 return failure();465 return emitter.emitAttribute(operation->getLoc(), value);466 }467 468 // Emit a variable declaration for an emitc.constant op without value.469 if (auto oAttr = dyn_cast<emitc::OpaqueAttr>(value)) {470 if (oAttr.getValue().empty())471 // The semicolon gets printed by the emitOperation function.472 return emitter.emitVariableDeclaration(result,473 /*trailingSemicolon=*/false);474 }475 476 // Emit a variable declaration.477 if (failed(emitter.emitAssignPrefix(*operation)))478 return failure();479 return emitter.emitAttribute(operation->getLoc(), value);480}481 482static LogicalResult printOperation(CppEmitter &emitter,483 emitc::ConstantOp constantOp) {484 Operation *operation = constantOp.getOperation();485 Attribute value = constantOp.getValue();486 487 if (emitter.isPartOfCurrentExpression(operation))488 return emitter.emitAttribute(operation->getLoc(), value);489 490 return printConstantOp(emitter, operation, value);491}492 493static LogicalResult printOperation(CppEmitter &emitter,494 emitc::VariableOp variableOp) {495 Operation *operation = variableOp.getOperation();496 Attribute value = variableOp.getValue();497 498 return printConstantOp(emitter, operation, value);499}500 501static LogicalResult printOperation(CppEmitter &emitter,502 emitc::GlobalOp globalOp) {503 504 return emitter.emitGlobalVariable(globalOp);505}506 507static LogicalResult printOperation(CppEmitter &emitter,508 emitc::AssignOp assignOp) {509 OpResult result = assignOp.getVar().getDefiningOp()->getResult(0);510 511 if (failed(emitter.emitVariableAssignment(result)))512 return failure();513 514 return emitter.emitOperand(assignOp.getValue());515}516 517static LogicalResult printOperation(CppEmitter &emitter, emitc::LoadOp loadOp) {518 if (failed(emitter.emitAssignPrefix(*loadOp)))519 return failure();520 521 return emitter.emitOperand(loadOp.getOperand());522}523 524static LogicalResult printBinaryOperation(CppEmitter &emitter,525 Operation *operation,526 StringRef binaryOperator) {527 raw_ostream &os = emitter.ostream();528 529 if (failed(emitter.emitAssignPrefix(*operation)))530 return failure();531 532 if (failed(emitter.emitOperand(operation->getOperand(0))))533 return failure();534 535 os << " " << binaryOperator << " ";536 537 if (failed(emitter.emitOperand(operation->getOperand(1))))538 return failure();539 540 return success();541}542 543static LogicalResult printUnaryOperation(CppEmitter &emitter,544 Operation *operation,545 StringRef unaryOperator) {546 raw_ostream &os = emitter.ostream();547 548 if (failed(emitter.emitAssignPrefix(*operation)))549 return failure();550 551 os << unaryOperator;552 553 if (failed(emitter.emitOperand(operation->getOperand(0))))554 return failure();555 556 return success();557}558 559static LogicalResult printOperation(CppEmitter &emitter, emitc::AddOp addOp) {560 Operation *operation = addOp.getOperation();561 562 return printBinaryOperation(emitter, operation, "+");563}564 565static LogicalResult printOperation(CppEmitter &emitter, emitc::DivOp divOp) {566 Operation *operation = divOp.getOperation();567 568 return printBinaryOperation(emitter, operation, "/");569}570 571static LogicalResult printOperation(CppEmitter &emitter, emitc::MulOp mulOp) {572 Operation *operation = mulOp.getOperation();573 574 return printBinaryOperation(emitter, operation, "*");575}576 577static LogicalResult printOperation(CppEmitter &emitter, emitc::RemOp remOp) {578 Operation *operation = remOp.getOperation();579 580 return printBinaryOperation(emitter, operation, "%");581}582 583static LogicalResult printOperation(CppEmitter &emitter, emitc::SubOp subOp) {584 Operation *operation = subOp.getOperation();585 586 return printBinaryOperation(emitter, operation, "-");587}588 589static LogicalResult emitSwitchCase(CppEmitter &emitter,590 raw_indented_ostream &os, Region ®ion) {591 for (Region::OpIterator iteratorOp = region.op_begin(), end = region.op_end();592 std::next(iteratorOp) != end; ++iteratorOp) {593 if (failed(emitter.emitOperation(*iteratorOp, /*trailingSemicolon=*/true)))594 return failure();595 }596 os << "break;\n";597 return success();598}599 600static LogicalResult printOperation(CppEmitter &emitter,601 emitc::SwitchOp switchOp) {602 raw_indented_ostream &os = emitter.ostream();603 604 os << "switch (";605 if (failed(emitter.emitOperand(switchOp.getArg())))606 return failure();607 os << ") {";608 609 for (auto pair : llvm::zip(switchOp.getCases(), switchOp.getCaseRegions())) {610 os << "\ncase " << std::get<0>(pair) << ": {\n";611 os.indent();612 613 if (failed(emitSwitchCase(emitter, os, std::get<1>(pair))))614 return failure();615 616 os.unindent() << "}";617 }618 619 os << "\ndefault: {\n";620 os.indent();621 622 if (failed(emitSwitchCase(emitter, os, switchOp.getDefaultRegion())))623 return failure();624 625 os.unindent() << "}\n}";626 return success();627}628 629static LogicalResult printOperation(CppEmitter &emitter, emitc::DoOp doOp) {630 raw_indented_ostream &os = emitter.ostream();631 632 os << "do {\n";633 os.indent();634 635 Block &bodyBlock = doOp.getBodyRegion().front();636 for (Operation &op : bodyBlock) {637 if (failed(emitter.emitOperation(op, /*trailingSemicolon=*/true)))638 return failure();639 }640 641 os.unindent() << "} while (";642 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()))))647 return failure();648 649 os << ");";650 return success();651}652 653static LogicalResult printOperation(CppEmitter &emitter, emitc::CmpOp cmpOp) {654 Operation *operation = cmpOp.getOperation();655 656 StringRef binaryOperator;657 658 switch (cmpOp.getPredicate()) {659 case emitc::CmpPredicate::eq:660 binaryOperator = "==";661 break;662 case emitc::CmpPredicate::ne:663 binaryOperator = "!=";664 break;665 case emitc::CmpPredicate::lt:666 binaryOperator = "<";667 break;668 case emitc::CmpPredicate::le:669 binaryOperator = "<=";670 break;671 case emitc::CmpPredicate::gt:672 binaryOperator = ">";673 break;674 case emitc::CmpPredicate::ge:675 binaryOperator = ">=";676 break;677 case emitc::CmpPredicate::three_way:678 binaryOperator = "<=>";679 break;680 }681 682 return printBinaryOperation(emitter, operation, binaryOperator);683}684 685static LogicalResult printOperation(CppEmitter &emitter,686 emitc::ConditionalOp conditionalOp) {687 raw_ostream &os = emitter.ostream();688 689 if (failed(emitter.emitAssignPrefix(*conditionalOp)))690 return failure();691 692 if (failed(emitter.emitOperand(conditionalOp.getCondition())))693 return failure();694 695 os << " ? ";696 697 if (failed(emitter.emitOperand(conditionalOp.getTrueValue())))698 return failure();699 700 os << " : ";701 702 if (failed(emitter.emitOperand(conditionalOp.getFalseValue())))703 return failure();704 705 return success();706}707 708static LogicalResult printOperation(CppEmitter &emitter,709 emitc::VerbatimOp verbatimOp) {710 raw_ostream &os = emitter.ostream();711 712 FailureOr<SmallVector<ReplacementItem>> items =713 verbatimOp.parseFormatString();714 if (failed(items))715 return failure();716 717 auto fmtArg = verbatimOp.getFmtArgs().begin();718 719 for (ReplacementItem &item : *items) {720 if (auto *str = std::get_if<StringRef>(&item)) {721 os << *str;722 } else {723 if (failed(emitter.emitOperand(*fmtArg++)))724 return failure();725 }726 }727 728 return success();729}730 731static LogicalResult printOperation(CppEmitter &emitter,732 cf::BranchOp branchOp) {733 raw_ostream &os = emitter.ostream();734 Block &successor = *branchOp.getSuccessor();735 736 for (auto pair :737 llvm::zip(branchOp.getOperands(), successor.getArguments())) {738 Value &operand = std::get<0>(pair);739 BlockArgument &argument = std::get<1>(pair);740 os << emitter.getOrCreateName(argument) << " = "741 << emitter.getOrCreateName(operand) << ";\n";742 }743 744 os << "goto ";745 if (!(emitter.hasBlockLabel(successor)))746 return branchOp.emitOpError("unable to find label for successor block");747 os << emitter.getOrCreateName(successor);748 return success();749}750 751static LogicalResult printOperation(CppEmitter &emitter,752 cf::CondBranchOp condBranchOp) {753 raw_indented_ostream &os = emitter.ostream();754 Block &trueSuccessor = *condBranchOp.getTrueDest();755 Block &falseSuccessor = *condBranchOp.getFalseDest();756 757 os << "if (";758 if (failed(emitter.emitOperand(condBranchOp.getCondition())))759 return failure();760 os << ") {\n";761 762 os.indent();763 764 // If condition is true.765 for (auto pair : llvm::zip(condBranchOp.getTrueOperands(),766 trueSuccessor.getArguments())) {767 Value &operand = std::get<0>(pair);768 BlockArgument &argument = std::get<1>(pair);769 os << emitter.getOrCreateName(argument) << " = "770 << emitter.getOrCreateName(operand) << ";\n";771 }772 773 os << "goto ";774 if (!(emitter.hasBlockLabel(trueSuccessor))) {775 return condBranchOp.emitOpError("unable to find label for successor block");776 }777 os << emitter.getOrCreateName(trueSuccessor) << ";\n";778 os.unindent() << "} else {\n";779 os.indent();780 // If condition is false.781 for (auto pair : llvm::zip(condBranchOp.getFalseOperands(),782 falseSuccessor.getArguments())) {783 Value &operand = std::get<0>(pair);784 BlockArgument &argument = std::get<1>(pair);785 os << emitter.getOrCreateName(argument) << " = "786 << emitter.getOrCreateName(operand) << ";\n";787 }788 789 os << "goto ";790 if (!(emitter.hasBlockLabel(falseSuccessor))) {791 return condBranchOp.emitOpError()792 << "unable to find label for successor block";793 }794 os << emitter.getOrCreateName(falseSuccessor) << ";\n";795 os.unindent() << "}";796 return success();797}798 799static LogicalResult printCallOperation(CppEmitter &emitter, Operation *callOp,800 StringRef callee) {801 if (failed(emitter.emitAssignPrefix(*callOp)))802 return failure();803 804 raw_ostream &os = emitter.ostream();805 os << callee << "(";806 if (failed(emitter.emitOperands(*callOp)))807 return failure();808 os << ")";809 return success();810}811 812static LogicalResult printOperation(CppEmitter &emitter, func::CallOp callOp) {813 Operation *operation = callOp.getOperation();814 StringRef callee = callOp.getCallee();815 816 return printCallOperation(emitter, operation, callee);817}818 819static LogicalResult printOperation(CppEmitter &emitter, emitc::CallOp callOp) {820 Operation *operation = callOp.getOperation();821 StringRef callee = callOp.getCallee();822 823 return printCallOperation(emitter, operation, callee);824}825 826static LogicalResult printOperation(CppEmitter &emitter,827 emitc::CallOpaqueOp callOpaqueOp) {828 raw_ostream &os = emitter.ostream();829 Operation &op = *callOpaqueOp.getOperation();830 831 if (failed(emitter.emitAssignPrefix(op)))832 return failure();833 os << callOpaqueOp.getCallee();834 835 // Template arguments can't refer to SSA values and as such the template836 // arguments which are supplied in form of attributes can be emitted as is. We837 // don't need to handle integer attributes specially like we do for arguments838 // - see below.839 auto emitTemplateArgs = [&](Attribute attr) -> LogicalResult {840 return emitter.emitAttribute(op.getLoc(), attr);841 };842 843 if (callOpaqueOp.getTemplateArgs()) {844 os << "<";845 if (failed(interleaveCommaWithError(*callOpaqueOp.getTemplateArgs(), os,846 emitTemplateArgs)))847 return failure();848 os << ">";849 }850 851 auto emitArgs = [&](Attribute attr) -> LogicalResult {852 if (auto t = dyn_cast<IntegerAttr>(attr)) {853 // Index attributes are treated specially as operand index.854 if (t.getType().isIndex()) {855 int64_t idx = t.getInt();856 Value operand = op.getOperand(idx);857 return emitter.emitOperand(operand);858 }859 }860 if (failed(emitter.emitAttribute(op.getLoc(), attr)))861 return failure();862 863 return success();864 };865 866 os << "(";867 868 LogicalResult emittedArgs =869 callOpaqueOp.getArgs()870 ? interleaveCommaWithError(*callOpaqueOp.getArgs(), os, emitArgs)871 : emitter.emitOperands(op);872 if (failed(emittedArgs))873 return failure();874 os << ")";875 return success();876}877 878static LogicalResult printOperation(CppEmitter &emitter,879 emitc::ApplyOp applyOp) {880 raw_ostream &os = emitter.ostream();881 Operation &op = *applyOp.getOperation();882 883 if (failed(emitter.emitAssignPrefix(op)))884 return failure();885 os << applyOp.getApplicableOperator();886 return emitter.emitOperand(applyOp.getOperand());887}888 889static LogicalResult printOperation(CppEmitter &emitter,890 emitc::BitwiseAndOp bitwiseAndOp) {891 Operation *operation = bitwiseAndOp.getOperation();892 return printBinaryOperation(emitter, operation, "&");893}894 895static LogicalResult896printOperation(CppEmitter &emitter,897 emitc::BitwiseLeftShiftOp bitwiseLeftShiftOp) {898 Operation *operation = bitwiseLeftShiftOp.getOperation();899 return printBinaryOperation(emitter, operation, "<<");900}901 902static LogicalResult printOperation(CppEmitter &emitter,903 emitc::BitwiseNotOp bitwiseNotOp) {904 Operation *operation = bitwiseNotOp.getOperation();905 return printUnaryOperation(emitter, operation, "~");906}907 908static LogicalResult printOperation(CppEmitter &emitter,909 emitc::BitwiseOrOp bitwiseOrOp) {910 Operation *operation = bitwiseOrOp.getOperation();911 return printBinaryOperation(emitter, operation, "|");912}913 914static LogicalResult915printOperation(CppEmitter &emitter,916 emitc::BitwiseRightShiftOp bitwiseRightShiftOp) {917 Operation *operation = bitwiseRightShiftOp.getOperation();918 return printBinaryOperation(emitter, operation, ">>");919}920 921static LogicalResult printOperation(CppEmitter &emitter,922 emitc::BitwiseXorOp bitwiseXorOp) {923 Operation *operation = bitwiseXorOp.getOperation();924 return printBinaryOperation(emitter, operation, "^");925}926 927static LogicalResult printOperation(CppEmitter &emitter,928 emitc::UnaryPlusOp unaryPlusOp) {929 Operation *operation = unaryPlusOp.getOperation();930 return printUnaryOperation(emitter, operation, "+");931}932 933static LogicalResult printOperation(CppEmitter &emitter,934 emitc::UnaryMinusOp unaryMinusOp) {935 Operation *operation = unaryMinusOp.getOperation();936 return printUnaryOperation(emitter, operation, "-");937}938 939static LogicalResult printOperation(CppEmitter &emitter, emitc::CastOp castOp) {940 raw_ostream &os = emitter.ostream();941 Operation &op = *castOp.getOperation();942 943 if (failed(emitter.emitAssignPrefix(op)))944 return failure();945 os << "(";946 if (failed(emitter.emitType(op.getLoc(), op.getResult(0).getType())))947 return failure();948 os << ") ";949 return emitter.emitOperand(castOp.getOperand());950}951 952static LogicalResult printOperation(CppEmitter &emitter,953 emitc::ExpressionOp expressionOp) {954 if (shouldBeInlined(expressionOp))955 return success();956 957 Operation &op = *expressionOp.getOperation();958 959 if (failed(emitter.emitAssignPrefix(op)))960 return failure();961 962 return emitter.emitExpression(expressionOp);963}964 965static LogicalResult printOperation(CppEmitter &emitter,966 emitc::IncludeOp includeOp) {967 raw_ostream &os = emitter.ostream();968 969 os << "#include ";970 if (includeOp.getIsStandardInclude())971 os << "<" << includeOp.getInclude() << ">";972 else973 os << "\"" << includeOp.getInclude() << "\"";974 975 return success();976}977 978static LogicalResult printOperation(CppEmitter &emitter,979 emitc::LogicalAndOp logicalAndOp) {980 Operation *operation = logicalAndOp.getOperation();981 return printBinaryOperation(emitter, operation, "&&");982}983 984static LogicalResult printOperation(CppEmitter &emitter,985 emitc::LogicalNotOp logicalNotOp) {986 Operation *operation = logicalNotOp.getOperation();987 return printUnaryOperation(emitter, operation, "!");988}989 990static LogicalResult printOperation(CppEmitter &emitter,991 emitc::LogicalOrOp logicalOrOp) {992 Operation *operation = logicalOrOp.getOperation();993 return printBinaryOperation(emitter, operation, "||");994}995 996static LogicalResult printOperation(CppEmitter &emitter, emitc::ForOp forOp) {997 raw_indented_ostream &os = emitter.ostream();998 999 // Utility function to determine whether a value is an expression that will be1000 // inlined, and as such should be wrapped in parentheses in order to guarantee1001 // its precedence and associativity.1002 auto requiresParentheses = [&](Value value) {1003 auto expressionOp = value.getDefiningOp<ExpressionOp>();1004 if (!expressionOp)1005 return false;1006 return shouldBeInlined(expressionOp);1007 };1008 1009 os << "for (";1010 if (failed(1011 emitter.emitType(forOp.getLoc(), forOp.getInductionVar().getType())))1012 return failure();1013 os << " ";1014 os << emitter.getOrCreateInductionVarName(forOp.getInductionVar());1015 os << " = ";1016 if (failed(emitter.emitOperand(forOp.getLowerBound())))1017 return failure();1018 os << "; ";1019 os << emitter.getOrCreateInductionVarName(forOp.getInductionVar());1020 os << " < ";1021 Value upperBound = forOp.getUpperBound();1022 bool upperBoundRequiresParentheses = requiresParentheses(upperBound);1023 if (upperBoundRequiresParentheses)1024 os << "(";1025 if (failed(emitter.emitOperand(upperBound)))1026 return failure();1027 if (upperBoundRequiresParentheses)1028 os << ")";1029 os << "; ";1030 os << emitter.getOrCreateInductionVarName(forOp.getInductionVar());1031 os << " += ";1032 if (failed(emitter.emitOperand(forOp.getStep())))1033 return failure();1034 os << ") {\n";1035 os.indent();1036 1037 CppEmitter::LoopScope lScope(emitter);1038 1039 Region &forRegion = forOp.getRegion();1040 auto regionOps = forRegion.getOps();1041 1042 // We skip the trailing yield op.1043 for (auto it = regionOps.begin(); std::next(it) != regionOps.end(); ++it) {1044 if (failed(emitter.emitOperation(*it, /*trailingSemicolon=*/true)))1045 return failure();1046 }1047 1048 os.unindent() << "}";1049 1050 return success();1051}1052 1053static LogicalResult printOperation(CppEmitter &emitter, emitc::IfOp ifOp) {1054 raw_indented_ostream &os = emitter.ostream();1055 1056 // Helper function to emit all ops except the last one, expected to be1057 // emitc::yield.1058 auto emitAllExceptLast = [&emitter](Region ®ion) {1059 Region::OpIterator it = region.op_begin(), end = region.op_end();1060 for (; std::next(it) != end; ++it) {1061 if (failed(emitter.emitOperation(*it, /*trailingSemicolon=*/true)))1062 return failure();1063 }1064 assert(isa<emitc::YieldOp>(*it) &&1065 "Expected last operation in the region to be emitc::yield");1066 return success();1067 };1068 1069 os << "if (";1070 if (failed(emitter.emitOperand(ifOp.getCondition())))1071 return failure();1072 os << ") {\n";1073 os.indent();1074 if (failed(emitAllExceptLast(ifOp.getThenRegion())))1075 return failure();1076 os.unindent() << "}";1077 1078 Region &elseRegion = ifOp.getElseRegion();1079 if (!elseRegion.empty()) {1080 os << " else {\n";1081 os.indent();1082 if (failed(emitAllExceptLast(elseRegion)))1083 return failure();1084 os.unindent() << "}";1085 }1086 1087 return success();1088}1089 1090static LogicalResult printOperation(CppEmitter &emitter,1091 func::ReturnOp returnOp) {1092 raw_ostream &os = emitter.ostream();1093 os << "return";1094 switch (returnOp.getNumOperands()) {1095 case 0:1096 return success();1097 case 1:1098 os << " ";1099 if (failed(emitter.emitOperand(returnOp.getOperand(0))))1100 return failure();1101 return success();1102 default:1103 os << " std::make_tuple(";1104 if (failed(emitter.emitOperandsAndAttributes(*returnOp.getOperation())))1105 return failure();1106 os << ")";1107 return success();1108 }1109}1110 1111static LogicalResult printOperation(CppEmitter &emitter,1112 emitc::ReturnOp returnOp) {1113 raw_ostream &os = emitter.ostream();1114 os << "return";1115 if (returnOp.getNumOperands() == 0)1116 return success();1117 1118 os << " ";1119 if (failed(emitter.emitOperand(returnOp.getOperand())))1120 return failure();1121 return success();1122}1123 1124static LogicalResult printOperation(CppEmitter &emitter, ModuleOp moduleOp) {1125 for (Operation &op : moduleOp) {1126 if (failed(emitter.emitOperation(op, /*trailingSemicolon=*/false)))1127 return failure();1128 }1129 return success();1130}1131 1132static LogicalResult printOperation(CppEmitter &emitter, ClassOp classOp) {1133 raw_indented_ostream &os = emitter.ostream();1134 os << "class " << classOp.getSymName();1135 if (classOp.getFinalSpecifier())1136 os << " final";1137 os << " {\n public:\n";1138 os.indent();1139 1140 for (Operation &op : classOp) {1141 if (failed(emitter.emitOperation(op, /*trailingSemicolon=*/false)))1142 return failure();1143 }1144 1145 os.unindent();1146 os << "};";1147 return success();1148}1149 1150static LogicalResult printOperation(CppEmitter &emitter, FieldOp fieldOp) {1151 raw_ostream &os = emitter.ostream();1152 if (failed(emitter.emitVariableDeclaration(1153 fieldOp->getLoc(), fieldOp.getType(), fieldOp.getSymName())))1154 return failure();1155 std::optional<Attribute> initialValue = fieldOp.getInitialValue();1156 if (initialValue) {1157 os << " = ";1158 if (failed(emitter.emitAttribute(fieldOp->getLoc(), *initialValue)))1159 return failure();1160 }1161 1162 os << ";";1163 return success();1164}1165 1166static LogicalResult printOperation(CppEmitter &emitter, FileOp file) {1167 if (!emitter.shouldEmitFile(file))1168 return success();1169 1170 for (Operation &op : file) {1171 if (failed(emitter.emitOperation(op, /*trailingSemicolon=*/false)))1172 return failure();1173 }1174 return success();1175}1176 1177static LogicalResult printFunctionArgs(CppEmitter &emitter,1178 Operation *functionOp,1179 ArrayRef<Type> arguments) {1180 raw_indented_ostream &os = emitter.ostream();1181 1182 return (1183 interleaveCommaWithError(arguments, os, [&](Type arg) -> LogicalResult {1184 return emitter.emitType(functionOp->getLoc(), arg);1185 }));1186}1187 1188static LogicalResult printFunctionArgs(CppEmitter &emitter,1189 Operation *functionOp,1190 Region::BlockArgListType arguments) {1191 raw_indented_ostream &os = emitter.ostream();1192 1193 return (interleaveCommaWithError(1194 arguments, os, [&](BlockArgument arg) -> LogicalResult {1195 return emitter.emitVariableDeclaration(1196 functionOp->getLoc(), arg.getType(), emitter.getOrCreateName(arg));1197 }));1198}1199 1200static LogicalResult printFunctionBody(CppEmitter &emitter,1201 Operation *functionOp,1202 Region::BlockListType &blocks) {1203 raw_indented_ostream &os = emitter.ostream();1204 os.indent();1205 1206 if (emitter.shouldDeclareVariablesAtTop()) {1207 // Declare all variables that hold op results including those from nested1208 // regions.1209 WalkResult result =1210 functionOp->walk<WalkOrder::PreOrder>([&](Operation *op) -> WalkResult {1211 if (isa<emitc::ExpressionOp>(op->getParentOp()) ||1212 (isa<emitc::ExpressionOp>(op) &&1213 shouldBeInlined(cast<emitc::ExpressionOp>(op))))1214 return WalkResult::skip();1215 for (OpResult result : op->getResults()) {1216 if (failed(emitter.emitVariableDeclaration(1217 result, /*trailingSemicolon=*/true))) {1218 return WalkResult(1219 op->emitError("unable to declare result variable for op"));1220 }1221 }1222 return WalkResult::advance();1223 });1224 if (result.wasInterrupted())1225 return failure();1226 }1227 1228 // Create label names for basic blocks.1229 for (Block &block : blocks) {1230 emitter.getOrCreateName(block);1231 }1232 1233 // Declare variables for basic block arguments.1234 for (Block &block : llvm::drop_begin(blocks)) {1235 for (BlockArgument &arg : block.getArguments()) {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();1242 if (failed(1243 emitter.emitType(block.getParentOp()->getLoc(), arg.getType()))) {1244 return failure();1245 }1246 os << " " << emitter.getOrCreateName(arg) << ";\n";1247 }1248 }1249 1250 for (Block &block : blocks) {1251 // Only print a label if the block has predecessors.1252 if (!block.hasNoPredecessors()) {1253 if (failed(emitter.emitLabel(block)))1254 return failure();1255 }1256 for (Operation &op : block.getOperations()) {1257 if (failed(emitter.emitOperation(op, /*trailingSemicolon=*/true)))1258 return failure();1259 }1260 }1261 1262 os.unindent();1263 1264 return success();1265}1266 1267static LogicalResult printOperation(CppEmitter &emitter,1268 func::FuncOp functionOp) {1269 // We need to declare variables at top if the function has multiple blocks.1270 if (!emitter.shouldDeclareVariablesAtTop() &&1271 functionOp.getBlocks().size() > 1) {1272 return functionOp.emitOpError(1273 "with multiple blocks needs variables declared at top");1274 }1275 1276 if (llvm::any_of(functionOp.getArgumentTypes(), llvm::IsaPred<LValueType>)) {1277 return functionOp.emitOpError()1278 << "cannot emit lvalue type as argument type";1279 }1280 1281 if (llvm::any_of(functionOp.getResultTypes(), llvm::IsaPred<ArrayType>)) {1282 return functionOp.emitOpError() << "cannot emit array type as result type";1283 }1284 1285 CppEmitter::FunctionScope scope(emitter);1286 raw_indented_ostream &os = emitter.ostream();1287 if (failed(emitter.emitTypes(functionOp.getLoc(),1288 functionOp.getFunctionType().getResults())))1289 return failure();1290 os << " " << functionOp.getName();1291 1292 os << "(";1293 Operation *operation = functionOp.getOperation();1294 if (failed(printFunctionArgs(emitter, operation, functionOp.getArguments())))1295 return failure();1296 os << ") {\n";1297 if (failed(printFunctionBody(emitter, operation, functionOp.getBlocks())))1298 return failure();1299 os << "}";1300 1301 return success();1302}1303 1304static LogicalResult printOperation(CppEmitter &emitter,1305 emitc::FuncOp functionOp) {1306 // We need to declare variables at top if the function has multiple blocks.1307 if (!emitter.shouldDeclareVariablesAtTop() &&1308 functionOp.getBlocks().size() > 1) {1309 return functionOp.emitOpError(1310 "with multiple blocks needs variables declared at top");1311 }1312 1313 CppEmitter::FunctionScope scope(emitter);1314 raw_indented_ostream &os = emitter.ostream();1315 if (functionOp.getSpecifiers()) {1316 for (Attribute specifier : functionOp.getSpecifiersAttr()) {1317 os << cast<StringAttr>(specifier).str() << " ";1318 }1319 }1320 1321 if (failed(emitter.emitTypes(functionOp.getLoc(),1322 functionOp.getFunctionType().getResults())))1323 return failure();1324 os << " " << functionOp.getName();1325 1326 os << "(";1327 Operation *operation = functionOp.getOperation();1328 if (functionOp.isExternal()) {1329 if (failed(printFunctionArgs(emitter, operation,1330 functionOp.getArgumentTypes())))1331 return failure();1332 os << ");";1333 return success();1334 }1335 if (failed(printFunctionArgs(emitter, operation, functionOp.getArguments())))1336 return failure();1337 os << ") {\n";1338 if (failed(printFunctionBody(emitter, operation, functionOp.getBlocks())))1339 return failure();1340 os << "}";1341 1342 return success();1343}1344 1345static LogicalResult printOperation(CppEmitter &emitter,1346 DeclareFuncOp declareFuncOp) {1347 raw_indented_ostream &os = emitter.ostream();1348 1349 CppEmitter::FunctionScope scope(emitter);1350 auto functionOp = SymbolTable::lookupNearestSymbolFrom<emitc::FuncOp>(1351 declareFuncOp, declareFuncOp.getSymNameAttr());1352 1353 if (!functionOp)1354 return failure();1355 1356 if (functionOp.getSpecifiers()) {1357 for (Attribute specifier : functionOp.getSpecifiersAttr()) {1358 os << cast<StringAttr>(specifier).str() << " ";1359 }1360 }1361 1362 if (failed(emitter.emitTypes(functionOp.getLoc(),1363 functionOp.getFunctionType().getResults())))1364 return failure();1365 os << " " << functionOp.getName();1366 1367 os << "(";1368 Operation *operation = functionOp.getOperation();1369 if (failed(printFunctionArgs(emitter, operation, functionOp.getArguments())))1370 return failure();1371 os << ");";1372 1373 return success();1374}1375 1376CppEmitter::CppEmitter(raw_ostream &os, bool declareVariablesAtTop,1377 StringRef fileId)1378 : os(os), declareVariablesAtTop(declareVariablesAtTop),1379 fileId(fileId.str()), defaultValueMapperScope(valueMapper),1380 defaultBlockMapperScope(blockMapper) {1381 labelInScopeCount.push(0);1382}1383 1384void CppEmitter::cacheDeferredOpResult(Value value, StringRef str) {1385 if (!valueMapper.count(value))1386 valueMapper.insert(value, str.str());1387}1388 1389/// Return the existing or a new name for a Value.1390StringRef CppEmitter::getOrCreateName(Value val) {1391 if (!valueMapper.count(val)) {1392 assert(!hasDeferredEmission(val.getDefiningOp()) &&1393 "cacheDeferredOpResult should have been called on this value, "1394 "update the emitOperation function.");1395 1396 valueMapper.insert(val, formatv("v{0}", ++valueCount));1397 }1398 return *valueMapper.begin(val);1399}1400 1401/// Return the existing or a new name for a loop induction variable Value.1402/// Loop induction variables follow natural naming: i, j, k, ..., t, uX.1403StringRef CppEmitter::getOrCreateInductionVarName(Value val) {1404 if (!valueMapper.count(val)) {1405 1406 int64_t identifier = 'i' + loopNestingLevel;1407 1408 if (identifier >= 'i' && identifier <= 't') {1409 valueMapper.insert(val,1410 formatv("{0}{1}", (char)identifier, ++valueCount));1411 } else {1412 // If running out of letters, continue with uX.1413 valueMapper.insert(val, formatv("u{0}", ++valueCount));1414 }1415 }1416 return *valueMapper.begin(val);1417}1418 1419/// Return the existing or a new label for a Block.1420StringRef CppEmitter::getOrCreateName(Block &block) {1421 if (!blockMapper.count(&block))1422 blockMapper.insert(&block, formatv("label{0}", ++labelInScopeCount.top()));1423 return *blockMapper.begin(&block);1424}1425 1426bool CppEmitter::shouldMapToUnsigned(IntegerType::SignednessSemantics val) {1427 switch (val) {1428 case IntegerType::Signless:1429 return false;1430 case IntegerType::Signed:1431 return false;1432 case IntegerType::Unsigned:1433 return true;1434 }1435 llvm_unreachable("Unexpected IntegerType::SignednessSemantics");1436}1437 1438bool CppEmitter::hasValueInScope(Value val) { return valueMapper.count(val); }1439 1440bool CppEmitter::hasBlockLabel(Block &block) {1441 return blockMapper.count(&block);1442}1443 1444LogicalResult CppEmitter::emitAttribute(Location loc, Attribute attr) {1445 auto printInt = [&](const APInt &val, bool isUnsigned) {1446 if (val.getBitWidth() == 1) {1447 if (val.getBoolValue())1448 os << "true";1449 else1450 os << "false";1451 } else {1452 SmallString<128> strValue;1453 val.toString(strValue, 10, !isUnsigned, false);1454 os << strValue;1455 }1456 };1457 1458 auto printFloat = [&](const APFloat &val) {1459 if (val.isFinite()) {1460 SmallString<128> strValue;1461 // Use default values of toString except don't truncate zeros.1462 val.toString(strValue, 0, 0, false);1463 os << strValue;1464 switch (llvm::APFloatBase::SemanticsToEnum(val.getSemantics())) {1465 case llvm::APFloatBase::S_IEEEhalf:1466 os << "f16";1467 break;1468 case llvm::APFloatBase::S_BFloat:1469 os << "bf16";1470 break;1471 case llvm::APFloatBase::S_IEEEsingle:1472 os << "f";1473 break;1474 case llvm::APFloatBase::S_IEEEdouble:1475 break;1476 default:1477 llvm_unreachable("unsupported floating point type");1478 };1479 } else if (val.isNaN()) {1480 os << "NAN";1481 } else if (val.isInfinity()) {1482 if (val.isNegative())1483 os << "-";1484 os << "INFINITY";1485 }1486 };1487 1488 // Print floating point attributes.1489 if (auto fAttr = dyn_cast<FloatAttr>(attr)) {1490 if (!isa<Float16Type, BFloat16Type, Float32Type, Float64Type>(1491 fAttr.getType())) {1492 return emitError(1493 loc, "expected floating point attribute to be f16, bf16, f32 or f64");1494 }1495 printFloat(fAttr.getValue());1496 return success();1497 }1498 if (auto dense = dyn_cast<DenseFPElementsAttr>(attr)) {1499 if (!isa<Float16Type, BFloat16Type, Float32Type, Float64Type>(1500 dense.getElementType())) {1501 return emitError(1502 loc, "expected floating point attribute to be f16, bf16, f32 or f64");1503 }1504 os << '{';1505 interleaveComma(dense, os, [&](const APFloat &val) { printFloat(val); });1506 os << '}';1507 return success();1508 }1509 1510 // Print integer attributes.1511 if (auto iAttr = dyn_cast<IntegerAttr>(attr)) {1512 if (auto iType = dyn_cast<IntegerType>(iAttr.getType())) {1513 printInt(iAttr.getValue(), shouldMapToUnsigned(iType.getSignedness()));1514 return success();1515 }1516 if (auto iType = dyn_cast<IndexType>(iAttr.getType())) {1517 printInt(iAttr.getValue(), false);1518 return success();1519 }1520 }1521 if (auto dense = dyn_cast<DenseIntElementsAttr>(attr)) {1522 if (auto iType = dyn_cast<IntegerType>(1523 cast<ShapedType>(dense.getType()).getElementType())) {1524 os << '{';1525 interleaveComma(dense, os, [&](const APInt &val) {1526 printInt(val, shouldMapToUnsigned(iType.getSignedness()));1527 });1528 os << '}';1529 return success();1530 }1531 if (auto iType = dyn_cast<IndexType>(1532 cast<ShapedType>(dense.getType()).getElementType())) {1533 os << '{';1534 interleaveComma(dense, os,1535 [&](const APInt &val) { printInt(val, false); });1536 os << '}';1537 return success();1538 }1539 }1540 1541 // Print opaque attributes.1542 if (auto oAttr = dyn_cast<emitc::OpaqueAttr>(attr)) {1543 os << oAttr.getValue();1544 return success();1545 }1546 1547 // Print symbolic reference attributes.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();1552 return success();1553 }1554 1555 // Print type attributes.1556 if (auto type = dyn_cast<TypeAttr>(attr))1557 return emitType(loc, type.getValue());1558 1559 return emitError(loc, "cannot emit attribute: ") << attr;1560}1561 1562LogicalResult CppEmitter::emitExpression(ExpressionOp expressionOp) {1563 assert(emittedExpressionPrecedence.empty() &&1564 "Expected precedence stack to be empty");1565 Operation *rootOp = expressionOp.getRootOp();1566 1567 emittedExpression = expressionOp;1568 FailureOr<int> precedence = getOperatorPrecedence(rootOp);1569 if (failed(precedence))1570 return failure();1571 pushExpressionPrecedence(precedence.value());1572 1573 if (failed(emitOperation(*rootOp, /*trailingSemicolon=*/false)))1574 return failure();1575 1576 popExpressionPrecedence();1577 assert(emittedExpressionPrecedence.empty() &&1578 "Expected precedence stack to be empty");1579 emittedExpression = nullptr;1580 1581 return success();1582}1583 1584LogicalResult CppEmitter::emitOperand(Value value, bool isInBrackets) {1585 if (isPartOfCurrentExpression(value)) {1586 Operation *def = value.getDefiningOp();1587 assert(def && "Expected operand to be defined by an operation");1588 FailureOr<int> precedence = getOperatorPrecedence(def);1589 if (failed(precedence))1590 return failure();1591 1592 // Unless already in brackets, sub-expressions with equal or lower1593 // precedence need to be parenthesized as they might be evaluated in the1594 // wrong order depending on the shape of the expression tree.1595 bool encloseInParenthesis =1596 !isInBrackets && precedence.value() <= getExpressionPrecedence();1597 1598 if (encloseInParenthesis)1599 os << "(";1600 pushExpressionPrecedence(precedence.value());1601 1602 if (failed(emitOperation(*def, /*trailingSemicolon=*/false)))1603 return failure();1604 1605 if (encloseInParenthesis)1606 os << ")";1607 1608 popExpressionPrecedence();1609 return success();1610 }1611 1612 auto expressionOp = value.getDefiningOp<ExpressionOp>();1613 if (expressionOp && shouldBeInlined(expressionOp))1614 return emitExpression(expressionOp);1615 1616 if (BlockArgument arg = dyn_cast<BlockArgument>(value)) {1617 // If this operand is a block argument of an expression, emit instead the1618 // matching expression parameter.1619 Operation *argOp = arg.getParentBlock()->getParentOp();1620 if (auto expressionOp = dyn_cast<ExpressionOp>(argOp)) {1621 // This scenario is only expected when one of the operations within the1622 // expression being emitted references one of the expression's block1623 // arguments.1624 assert(expressionOp == emittedExpression &&1625 "Expected expression being emitted");1626 value = expressionOp->getOperand(arg.getArgNumber());1627 }1628 }1629 1630 os << getOrCreateName(value);1631 return success();1632}1633 1634LogicalResult CppEmitter::emitOperands(Operation &op) {1635 return interleaveCommaWithError(op.getOperands(), os, [&](Value operand) {1636 // Emit operand under guarantee that if it's part of an expression then it1637 // is being emitted within brackets.1638 return emitOperand(operand, /*isInBrackets=*/true);1639 });1640}1641 1642LogicalResult1643CppEmitter::emitOperandsAndAttributes(Operation &op,1644 ArrayRef<StringRef> exclude) {1645 if (failed(emitOperands(op)))1646 return failure();1647 // Insert comma in between operands and non-filtered attributes if needed.1648 if (op.getNumOperands() > 0) {1649 for (NamedAttribute attr : op.getAttrs()) {1650 if (!llvm::is_contained(exclude, attr.getName().strref())) {1651 os << ", ";1652 break;1653 }1654 }1655 }1656 // Emit attributes.1657 auto emitNamedAttribute = [&](NamedAttribute attr) -> LogicalResult {1658 if (llvm::is_contained(exclude, attr.getName().strref()))1659 return success();1660 os << "/* " << attr.getName().getValue() << " */";1661 if (failed(emitAttribute(op.getLoc(), attr.getValue())))1662 return failure();1663 return success();1664 };1665 return interleaveCommaWithError(op.getAttrs(), os, emitNamedAttribute);1666}1667 1668LogicalResult CppEmitter::emitVariableAssignment(OpResult result) {1669 if (!hasValueInScope(result)) {1670 return result.getDefiningOp()->emitOpError(1671 "result variable for the operation has not been declared");1672 }1673 os << getOrCreateName(result) << " = ";1674 return success();1675}1676 1677LogicalResult CppEmitter::emitVariableDeclaration(OpResult result,1678 bool trailingSemicolon) {1679 if (hasDeferredEmission(result.getDefiningOp()))1680 return success();1681 if (hasValueInScope(result)) {1682 return result.getDefiningOp()->emitError(1683 "result variable for the operation already declared");1684 }1685 if (failed(emitVariableDeclaration(result.getOwner()->getLoc(),1686 result.getType(),1687 getOrCreateName(result))))1688 return failure();1689 if (trailingSemicolon)1690 os << ";\n";1691 return success();1692}1693 1694LogicalResult CppEmitter::emitGlobalVariable(GlobalOp op) {1695 if (op.getExternSpecifier())1696 os << "extern ";1697 else if (op.getStaticSpecifier())1698 os << "static ";1699 if (op.getConstSpecifier())1700 os << "const ";1701 1702 if (failed(emitVariableDeclaration(op->getLoc(), op.getType(),1703 op.getSymName()))) {1704 return failure();1705 }1706 1707 std::optional<Attribute> initialValue = op.getInitialValue();1708 if (initialValue) {1709 os << " = ";1710 if (failed(emitAttribute(op->getLoc(), *initialValue)))1711 return failure();1712 }1713 1714 os << ";";1715 return success();1716}1717 1718LogicalResult CppEmitter::emitAssignPrefix(Operation &op) {1719 // If op is being emitted as part of an expression, bail out.1720 if (isEmittingExpression())1721 return success();1722 1723 switch (op.getNumResults()) {1724 case 0:1725 break;1726 case 1: {1727 OpResult result = op.getResult(0);1728 if (shouldDeclareVariablesAtTop()) {1729 if (failed(emitVariableAssignment(result)))1730 return failure();1731 } else {1732 if (failed(emitVariableDeclaration(result, /*trailingSemicolon=*/false)))1733 return failure();1734 os << " = ";1735 }1736 break;1737 }1738 default:1739 if (!shouldDeclareVariablesAtTop()) {1740 for (OpResult result : op.getResults()) {1741 if (failed(emitVariableDeclaration(result, /*trailingSemicolon=*/true)))1742 return failure();1743 }1744 }1745 os << "std::tie(";1746 interleaveComma(op.getResults(), os,1747 [&](Value result) { os << getOrCreateName(result); });1748 os << ") = ";1749 }1750 return success();1751}1752 1753LogicalResult CppEmitter::emitLabel(Block &block) {1754 if (!hasBlockLabel(block))1755 return block.getParentOp()->emitError("label for block not found");1756 // FIXME: Add feature in `raw_indented_ostream` to ignore indent for block1757 // label instead of using `getOStream`.1758 os.getOStream() << getOrCreateName(block) << ":\n";1759 return success();1760}1761 1762LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) {1763 LogicalResult status =1764 llvm::TypeSwitch<Operation *, LogicalResult>(&op)1765 // Builtin ops.1766 .Case<ModuleOp>([&](auto op) { return printOperation(*this, op); })1767 // CF ops.1768 .Case<cf::BranchOp, cf::CondBranchOp>(1769 [&](auto op) { return printOperation(*this, op); })1770 // EmitC ops.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>(1787 1788 [&](auto op) { return printOperation(*this, op); })1789 // Func ops.1790 .Case<func::CallOp, func::FuncOp, func::ReturnOp>(1791 [&](auto op) { return printOperation(*this, op); })1792 .Default([&](Operation *) {1793 return op.emitOpError("unable to find printer for op");1794 });1795 1796 if (failed(status))1797 return failure();1798 1799 if (hasDeferredEmission(&op))1800 return success();1801 1802 if (isEmittingExpression() ||1803 (isa<emitc::ExpressionOp>(op) &&1804 shouldBeInlined(cast<emitc::ExpressionOp>(op))))1805 return success();1806 1807 // Never emit a semicolon for some operations, especially if endening with1808 // `}`.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);1813 1814 os << (trailingSemicolon ? ";\n" : "\n");1815 1816 return success();1817}1818 1819LogicalResult CppEmitter::emitVariableDeclaration(Location loc, Type type,1820 StringRef name) {1821 if (auto arrType = dyn_cast<emitc::ArrayType>(type)) {1822 if (failed(emitType(loc, arrType.getElementType())))1823 return failure();1824 os << " " << name;1825 for (auto dim : arrType.getShape()) {1826 os << "[" << dim << "]";1827 }1828 return success();1829 }1830 if (failed(emitType(loc, type)))1831 return failure();1832 os << " " << name;1833 return success();1834}1835 1836LogicalResult CppEmitter::emitType(Location loc, Type type) {1837 if (auto iType = dyn_cast<IntegerType>(type)) {1838 switch (iType.getWidth()) {1839 case 1:1840 return (os << "bool"), success();1841 case 8:1842 case 16:1843 case 32:1844 case 64:1845 if (shouldMapToUnsigned(iType.getSignedness()))1846 return (os << "uint" << iType.getWidth() << "_t"), success();1847 else1848 return (os << "int" << iType.getWidth() << "_t"), success();1849 default:1850 return emitError(loc, "cannot emit integer type ") << type;1851 }1852 }1853 if (auto fType = dyn_cast<FloatType>(type)) {1854 switch (fType.getWidth()) {1855 case 16: {1856 if (llvm::isa<Float16Type>(type))1857 return (os << "_Float16"), success();1858 if (llvm::isa<BFloat16Type>(type))1859 return (os << "__bf16"), success();1860 else1861 return emitError(loc, "cannot emit float type ") << type;1862 }1863 case 32:1864 return (os << "float"), success();1865 case 64:1866 return (os << "double"), success();1867 default:1868 return emitError(loc, "cannot emit float type ") << type;1869 }1870 }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");1884 os << "Tensor<";1885 if (isa<ArrayType>(tType.getElementType()))1886 return emitError(loc, "cannot emit tensor of array type ") << type;1887 if (failed(emitType(loc, tType.getElementType())))1888 return failure();1889 auto shape = tType.getShape();1890 for (auto dimSize : shape) {1891 os << ", ";1892 os << dimSize;1893 }1894 os << ">";1895 return success();1896 }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();1901 return success();1902 }1903 if (auto aType = dyn_cast<emitc::ArrayType>(type)) {1904 if (failed(emitType(loc, aType.getElementType())))1905 return failure();1906 for (auto dim : aType.getShape())1907 os << "[" << dim << "]";1908 return success();1909 }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())))1916 return failure();1917 os << "*";1918 return success();1919 }1920 return emitError(loc, "cannot emit type ") << type;1921}1922 1923LogicalResult CppEmitter::emitTypes(Location loc, ArrayRef<Type> types) {1924 switch (types.size()) {1925 case 0:1926 os << "void";1927 return success();1928 case 1:1929 return emitType(loc, types.front());1930 default:1931 return emitTupleType(loc, types);1932 }1933}1934 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");1938 }1939 os << "std::tuple<";1940 if (failed(interleaveCommaWithError(1941 types, os, [&](Type type) { return emitType(loc, type); })))1942 return failure();1943 os << ">";1944 return success();1945}1946 1947void CppEmitter::resetValueCounter() { valueCount = 0; }1948 1949void CppEmitter::increaseLoopNestingLevel() { loopNestingLevel++; }1950 1951void CppEmitter::decreaseLoopNestingLevel() { loopNestingLevel--; }1952 1953LogicalResult emitc::translateToCpp(Operation *op, raw_ostream &os,1954 bool declareVariablesAtTop,1955 StringRef fileId) {1956 CppEmitter emitter(os, declareVariablesAtTop, fileId);1957 return emitter.emitOperation(*op, /*trailingSemicolon=*/false);1958}1959