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