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