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

source code of mlir/lib/Target/Cpp/TranslateToCpp.cpp