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