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