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