brintos

brintos / llvm-project-archived public Read only

0
0
Text · 64.7 KiB · bcce0a5 Raw
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 &region) {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 &region) {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