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