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