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