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"
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
30using namespace mlir;
31using namespace mlir::emitc;
32using 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.
37template <typename ForwardIterator, typename UnaryFunctor,
38 typename NullaryFunctor>
39static 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
55template <typename Container, typename UnaryFunctor, typename NullaryFunctor>
56static inline LogicalResult interleaveWithError(const Container &c,
57 UnaryFunctor eachFn,
58 NullaryFunctor betweenFn) {
59 return interleaveWithError(c.begin(), c.end(), eachFn, betweenFn);
60}
61
62template <typename Container, typename UnaryFunctor>
63static inline LogicalResult interleaveCommaWithError(const Container &c,
64 raw_ostream &os,
65 UnaryFunctor eachFn) {
66 return interleaveWithError(c.begin(), c.end(), eachFn, [&]() { os << ", "; });
67}
68
69/// Return the precedence of a operator as an integer, higher values
70/// imply higher precedence.
71static 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
114namespace {
115/// Emitter that uses dialect specific emitters to emit C++ code.
116struct CppEmitter {
117 explicit CppEmitter(raw_ostream &os, bool declareVariablesAtTop,
118 StringRef fileId);
119
120 /// Emits attribute or returns failure.
121 LogicalResult emitAttribute(Location loc, Attribute attr);
122
123 /// Emits operation 'op' with/without training semicolon or returns failure.
124 ///
125 /// For operations that should never be followed by a semicolon, like ForOp,
126 /// the `trailingSemicolon` argument is ignored and a semicolon is 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
292private:
293 using ValueMapper = llvm::ScopedHashTable<Value, std::string>;
294 using BlockMapper = llvm::ScopedHashTable<Block *, std::string>;
295
296 /// Output stream to emit to.
297 raw_indented_ostream os;
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.
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.
355static 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
405static 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
437static 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
448static 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
456static LogicalResult printOperation(CppEmitter &emitter,
457 emitc::GlobalOp globalOp) {
458
459 return emitter.emitGlobalVariable(globalOp);
460}
461
462static 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
472static 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
479static 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
498static 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
514static LogicalResult printOperation(CppEmitter &emitter, emitc::AddOp addOp) {
515 Operation *operation = addOp.getOperation();
516
517 return printBinaryOperation(emitter, operation, "+");
518}
519
520static LogicalResult printOperation(CppEmitter &emitter, emitc::DivOp divOp) {
521 Operation *operation = divOp.getOperation();
522
523 return printBinaryOperation(emitter, operation, "/");
524}
525
526static LogicalResult printOperation(CppEmitter &emitter, emitc::MulOp mulOp) {
527 Operation *operation = mulOp.getOperation();
528
529 return printBinaryOperation(emitter, operation, "*");
530}
531
532static LogicalResult printOperation(CppEmitter &emitter, emitc::RemOp remOp) {
533 Operation *operation = remOp.getOperation();
534
535 return printBinaryOperation(emitter, operation, "%");
536}
537
538static LogicalResult printOperation(CppEmitter &emitter, emitc::SubOp subOp) {
539 Operation *operation = subOp.getOperation();
540
541 return printBinaryOperation(emitter, operation, "-");
542}
543
544static 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
555static 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
584static 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
608static 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
640static 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
663static 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
686static 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
706static 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
754static 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
767static 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
774static 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
781static 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
833static 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
844static LogicalResult printOperation(CppEmitter &emitter,
845 emitc::BitwiseAndOp bitwiseAndOp) {
846 Operation *operation = bitwiseAndOp.getOperation();
847 return printBinaryOperation(emitter, operation, "&");
848}
849
850static LogicalResult
851printOperation(CppEmitter &emitter,
852 emitc::BitwiseLeftShiftOp bitwiseLeftShiftOp) {
853 Operation *operation = bitwiseLeftShiftOp.getOperation();
854 return printBinaryOperation(emitter, operation, "<<");
855}
856
857static LogicalResult printOperation(CppEmitter &emitter,
858 emitc::BitwiseNotOp bitwiseNotOp) {
859 Operation *operation = bitwiseNotOp.getOperation();
860 return printUnaryOperation(emitter, operation, "~");
861}
862
863static LogicalResult printOperation(CppEmitter &emitter,
864 emitc::BitwiseOrOp bitwiseOrOp) {
865 Operation *operation = bitwiseOrOp.getOperation();
866 return printBinaryOperation(emitter, operation, "|");
867}
868
869static LogicalResult
870printOperation(CppEmitter &emitter,
871 emitc::BitwiseRightShiftOp bitwiseRightShiftOp) {
872 Operation *operation = bitwiseRightShiftOp.getOperation();
873 return printBinaryOperation(emitter, operation, ">>");
874}
875
876static LogicalResult printOperation(CppEmitter &emitter,
877 emitc::BitwiseXorOp bitwiseXorOp) {
878 Operation *operation = bitwiseXorOp.getOperation();
879 return printBinaryOperation(emitter, operation, "^");
880}
881
882static LogicalResult printOperation(CppEmitter &emitter,
883 emitc::UnaryPlusOp unaryPlusOp) {
884 Operation *operation = unaryPlusOp.getOperation();
885 return printUnaryOperation(emitter, operation, "+");
886}
887
888static LogicalResult printOperation(CppEmitter &emitter,
889 emitc::UnaryMinusOp unaryMinusOp) {
890 Operation *operation = unaryMinusOp.getOperation();
891 return printUnaryOperation(emitter, operation, "-");
892}
893
894static 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
907static 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
920static 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
933static LogicalResult printOperation(CppEmitter &emitter,
934 emitc::LogicalAndOp logicalAndOp) {
935 Operation *operation = logicalAndOp.getOperation();
936 return printBinaryOperation(emitter, operation, "&&");
937}
938
939static LogicalResult printOperation(CppEmitter &emitter,
940 emitc::LogicalNotOp logicalNotOp) {
941 Operation *operation = logicalNotOp.getOperation();
942 return printUnaryOperation(emitter, operation, "!");
943}
944
945static LogicalResult printOperation(CppEmitter &emitter,
946 emitc::LogicalOrOp logicalOrOp) {
947 Operation *operation = logicalOrOp.getOperation();
948 return printBinaryOperation(emitter, operation, "||");
949}
950
951static 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
1008static 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
1045static 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
1066static 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
1079static 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
1087static 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
1105static 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
1121static 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
1132static 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
1143static LogicalResult printFunctionArgs(CppEmitter &emitter,
1144 Operation *functionOp,
1145 Region::BlockArgListType arguments) {
1146 raw_indented_ostream &os = emitter.ostream();
1147
1149 arguments, os, [&](BlockArgument arg) -> LogicalResult {
1150 return emitter.emitVariableDeclaration(
1151 functionOp->getLoc(), arg.getType(), emitter.getOrCreateName(arg));
1152 }));
1153}
1154
1155static 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.
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
1222static 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
1259static 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
1300static LogicalResult printOperation(CppEmitter &emitter,
1301 DeclareFuncOp declareFuncOp) {
1302 raw_indented_ostream &os = emitter.ostream();
1303
1304 CppEmitter::FunctionScope scope(emitter);
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
1331CppEmitter::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
1339std::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
1349std::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
1357std::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
1365void 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.
1371StringRef 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.
1384StringRef 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.
1401StringRef 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
1407bool 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
1419bool CppEmitter::hasValueInScope(Value val) { return valueMapper.count(val); }
1420
1421bool CppEmitter::hasBlockLabel(Block &block) {
1422 return blockMapper.count(&block);
1423}
1424
1425LogicalResult 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
1543LogicalResult 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
1565LogicalResult 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
1613LogicalResult 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
1627LogicalResult
1628CppEmitter::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
1653LogicalResult 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
1662LogicalResult 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
1679LogicalResult 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
1703LogicalResult 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
1738LogicalResult 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
1747LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) {
1748 LogicalResult status =
1749 llvm::TypeSwitch<Operation *, LogicalResult>(&op)
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
1826LogicalResult 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
1843LogicalResult 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
1930LogicalResult 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
1942LogicalResult 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
1954void CppEmitter::resetValueCounter() { valueCount = 0; }
1955
1956void CppEmitter::increaseLoopNestingLevel() { loopNestingLevel++; }
1957
1958void CppEmitter::decreaseLoopNestingLevel() { loopNestingLevel--; }
1959
1961 bool declareVariablesAtTop,
1962 StringRef fileId) {
1963 CppEmitter emitter(os, declareVariablesAtTop, fileId);
1964 return emitter.emitOperation(*op, /*trailingSemicolon=*/false);
1965}
return success()
static LogicalResult printCallOperation(CppEmitter &emitter, Operation *callOp, StringRef callee)
static FailureOr< int > getOperatorPrecedence(Operation *operation)
Return the precedence of a operator as an integer, higher values imply higher precedence.
static bool shouldBeInlined(ExpressionOp expressionOp)
Determine whether expression expressionOp should be emitted inline, i.e.
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 & front()
Definition Block.h:153
Operation & back()
Definition Block.h:152
BlockArgListType getArguments()
Definition Block.h:87
Block * getSuccessor(unsigned i)
Definition Block.cpp:269
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
Definition Block.cpp:31
This is a value defined by a result of an operation.
Definition Value.h:457
Operation is the basic unit of execution within MLIR.
Definition Operation.h:88
Value getOperand(unsigned idx)
Definition Operation.h:350
ArrayRef< NamedAttribute > getAttrs()
Return all of the attributes on this operation.
Definition Operation.h:512
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition Operation.h:407
Location getLoc()
The source location the operation was defined or derived from.
Definition Operation.h:223
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition Operation.h:234
unsigned getNumOperands()
Definition Operation.h:346
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
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
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
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.
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
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
iterator_range< OpIterator > getOps()
Definition Region.h:172
bool empty()
Definition Region.h:60
MutableArrayRef< BlockArgument > BlockArgListType
Definition Region.h:80
OpIterator op_end()
Definition Region.h:171
static Operation * lookupNearestSymbolFrom(Operation *from, StringAttr symbol)
Returns the operation registered with the given symbol name within the closest parent operation of,...
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
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
raw_ostream subclass that simplifies indention a sequence of code.
raw_indented_ostream & indent()
Increases the indent and returning this raw_indented_ostream.
raw_indented_ostream & unindent()
Decreases 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