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

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

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