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