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