Import and switch to fork of EmitC's C/C++ emitter
Imports the source files `CppEmitter.h` and `TranslateToCpp.cpp` from
iml130/mlir-emitc@f9968f65, adds a build configuration and adjusts the
IREE build config to make use of the new build target.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d03c45b..fd87436 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -339,10 +339,6 @@
# been added and within the scope of its settings (i.e. build type override,
# etc).
function(add_bundled_mlir_dependent_projects)
- if(${IREE_ENABLE_EMITC})
- add_subdirectory(third_party/mlir-emitc EXCLUDE_FROM_ALL)
- endif()
-
if(${IREE_BUILD_COMPILER})
add_subdirectory(third_party/mlir-hlo EXCLUDE_FROM_ALL)
endif()
@@ -459,6 +455,10 @@
add_subdirectory(build_tools/third_party/mlir-hlo EXCLUDE_FROM_ALL)
endif()
+if(${IREE_ENABLE_EMITC})
+ add_subdirectory(build_tools/third_party/mlir-emitc EXCLUDE_FROM_ALL)
+endif()
+
if(${IREE_BUILD_TESTS})
enable_testing(iree)
endif()
diff --git a/build_tools/cmake/iree_copts.cmake b/build_tools/cmake/iree_copts.cmake
index 246af40..a3a4d42 100644
--- a/build_tools/cmake/iree_copts.cmake
+++ b/build_tools/cmake/iree_copts.cmake
@@ -432,10 +432,6 @@
#-------------------------------------------------------------------------------
if(IREE_ENABLE_EMITC)
- set(EMITC_BUILD_EMBEDDED ON)
- set(EMITC_ENABLE_HLO OFF)
- set(EMITC_INCLUDE_TESTS OFF)
-
add_definitions(-DIREE_HAVE_EMITC_DIALECT)
endif()
diff --git a/build_tools/third_party/mlir-emitc/CMakeLists.txt b/build_tools/third_party/mlir-emitc/CMakeLists.txt
new file mode 100644
index 0000000..8725ba1
--- /dev/null
+++ b/build_tools/third_party/mlir-emitc/CMakeLists.txt
@@ -0,0 +1,30 @@
+# Copyright 2021 The IREE Authors
+#
+# Licensed under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+set(MLIR_EMITC_SOURCE_DIR
+ "${IREE_SOURCE_DIR}/third_party/mlir-emitc/"
+)
+
+external_cc_library(
+ PACKAGE
+ emitc
+ NAME
+ TranslateToCpp
+ ROOT
+ ${MLIR_EMITC_SOURCE_DIR}
+ HDRS
+ "include/emitc/Target/Cpp/CppEmitter.h"
+ SRCS
+ "lib/Target/Cpp/TranslateToCpp.cpp"
+ DEPS
+ MLIREmitC
+ MLIRIR
+ MLIRSCF
+ MLIRStandard
+ INCLUDES
+ "${MLIR_EMITC_SOURCE_DIR}/include/"
+ PUBLIC
+)
diff --git a/iree/compiler/Dialect/VM/Target/C/CMakeLists.txt b/iree/compiler/Dialect/VM/Target/C/CMakeLists.txt
index 21005a4..2678ecc 100644
--- a/iree/compiler/Dialect/VM/Target/C/CMakeLists.txt
+++ b/iree/compiler/Dialect/VM/Target/C/CMakeLists.txt
@@ -20,17 +20,15 @@
"TranslationRegistration.cpp"
DEPS
LLVMSupport
- MLIRTargetCpp
MLIRIR
MLIRPass
MLIRSupport
+ emitc::TranslateToCpp
iree::compiler::Dialect::VM::Analysis
iree::compiler::Dialect::VM::IR
iree::compiler::Dialect::VM::Conversion::VMToEmitC
iree::compiler::Dialect::VM::Utils::CallingConvention
iree::compiler::Dialect::VM::Utils::ConstantEncoding
- INCLUDES
- "${PROJECT_SOURCE_DIR}/third_party/mlir-emitc/include"
PUBLIC
)
endif()
diff --git a/iree/tools/CMakeLists.txt b/iree/tools/CMakeLists.txt
index f6235b0..1217d8e 100644
--- a/iree/tools/CMakeLists.txt
+++ b/iree/tools/CMakeLists.txt
@@ -49,10 +49,7 @@
)
set(IREE_TRANSLATE_CONDITIONAL_DEPS
MLIREmitC
- MLIRTargetCpp
- )
- set(IREE_EMITC_INCLUDES
- "${PROJECT_SOURCE_DIR}/third_party/mlir-emitc/include"
+ emitc::TranslateToCpp
)
endif()
@@ -271,8 +268,6 @@
::init_xla_dialects
iree::compiler::Codegen::Codegen
iree::compiler::Dialect::HAL::Conversion::Passes
- INCLUDES
- "${IREE_EMITC_INCLUDES}"
PUBLIC
)
@@ -360,8 +355,6 @@
iree::compiler::Dialect::VM::Target::init_targets
iree::compiler::Translation::IREEVM
${IREE_TRANSLATE_CONDITIONAL_DEPS}
- INCLUDES
- "${IREE_EMITC_INCLUDES}"
PUBLIC
)
diff --git a/third_party/mlir-emitc/LICENSE b/third_party/mlir-emitc/LICENSE
new file mode 100644
index 0000000..537293d
--- /dev/null
+++ b/third_party/mlir-emitc/LICENSE
@@ -0,0 +1,218 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+---- LLVM Exceptions to the Apache 2.0 License ----
+
+As an exception, if, as a result of your compiling your source code, portions
+of this Software are embedded into an Object form of such source code, you
+may redistribute such embedded portions in such Object form without complying
+with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
+
+In addition, if you combine or link compiled forms of this Software with
+software that is licensed under the GPLv2 ("Combined Software") and if a
+court of competent jurisdiction determines that the patent provision (Section
+3), the indemnity provision (Section 9) or other Section of the License
+conflicts with the conditions of the GPLv2, you may retroactively and
+prospectively choose to deem waived or otherwise exclude such Section(s) of
+the License, but only in their entirety and only with respect to the Combined
+Software.
diff --git a/third_party/mlir-emitc/README.md b/third_party/mlir-emitc/README.md
new file mode 100644
index 0000000..e448432
--- /dev/null
+++ b/third_party/mlir-emitc/README.md
@@ -0,0 +1,7 @@
+# MLIR EmitC
+
+This contains a participial copy of MLIR EmitC, forked from https://github.com/iml130/mlir-emitc.
+
+The initial import contains the C/C++ emitter (namely the files `CppEmitter.h` and `TranslateToCpp.cpp`)
+and reflects the state of iml130/mlir-emitc@f9968f65 for those files.<br>
+It is intended to switch to the C/C++ emitter in the MLIR core repository as soon as possible.
\ No newline at end of file
diff --git a/third_party/mlir-emitc/include/emitc/Target/Cpp/CppEmitter.h b/third_party/mlir-emitc/include/emitc/Target/Cpp/CppEmitter.h
new file mode 100644
index 0000000..84d85f8
--- /dev/null
+++ b/third_party/mlir-emitc/include/emitc/Target/Cpp/CppEmitter.h
@@ -0,0 +1,181 @@
+//===- CppEmitter.h - Helpers to create C++ emitter -------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines helpers to emit C++ code using the EmitC dialect.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef EMITC_TARGET_CPP_CPPEMITTER_H
+#define EMITC_TARGET_CPP_CPPEMITTER_H
+
+#include "mlir/IR/BuiltinTypes.h"
+#include "mlir/IR/Value.h"
+#include "llvm/ADT/ScopedHashTable.h"
+#include "llvm/Support/raw_ostream.h"
+#include <stack>
+
+namespace mlir {
+namespace emitc {
+
+/// Convenience functions to produce interleaved output with functions returning
+/// a LogicalResult. This is different than those in STL as functions used on
+/// each element doesn't return a string.
+template <typename ForwardIterator, typename UnaryFunctor,
+ typename NullaryFunctor>
+inline LogicalResult
+interleaveWithError(ForwardIterator begin, ForwardIterator end,
+ UnaryFunctor eachFn, NullaryFunctor betweenFn) {
+ if (begin == end)
+ return success();
+ if (failed(eachFn(*begin)))
+ return failure();
+ ++begin;
+ for (; begin != end; ++begin) {
+ betweenFn();
+ if (failed(eachFn(*begin)))
+ return failure();
+ }
+ return success();
+}
+
+template <typename Container, typename UnaryFunctor, typename NullaryFunctor>
+inline LogicalResult interleaveWithError(const Container &c,
+ UnaryFunctor eachFn,
+ NullaryFunctor betweenFn) {
+ return interleaveWithError(c.begin(), c.end(), eachFn, betweenFn);
+}
+
+template <typename Container, typename UnaryFunctor>
+inline LogicalResult interleaveCommaWithError(const Container &c,
+ raw_ostream &os,
+ UnaryFunctor eachFn) {
+ return interleaveWithError(c.begin(), c.end(), eachFn, [&]() { os << ", "; });
+}
+
+/// Emitter that uses dialect specific emitters to emit C++ code.
+struct CppEmitter {
+ explicit CppEmitter(raw_ostream &os, bool declareVariablesAtTop);
+
+ /// Emits attribute or returns failure.
+ LogicalResult emitAttribute(Operation &op, Attribute attr);
+
+ /// Emits operation 'op' with/without training semicolon or returns failure.
+ LogicalResult emitOperation(Operation &op, bool trailingSemicolon);
+
+ /// Emits type 'type' or returns failure.
+ LogicalResult emitType(Operation &op, Type type);
+
+ /// Emits array of types as a std::tuple of the emitted types.
+ /// - emits void for an empty array;
+ /// - emits the type of the only element for arrays of size one;
+ /// - emits a std::tuple otherwise;
+ LogicalResult emitTypes(Operation &op, ArrayRef<Type> types);
+
+ /// Emits array of types as a std::tuple of the emitted types independently of
+ /// the array size.
+ LogicalResult emitTupleType(Operation &op, ArrayRef<Type> types);
+
+ /// Emits an assignment for a variable which has been declared previously.
+ LogicalResult emitVariableAssignment(OpResult result);
+
+ /// Emits a variable declaration for a result of an operation.
+ LogicalResult emitVariableDeclaration(OpResult result,
+ bool trailingSemicolon);
+
+ /// Emits the variable declaration and assignment prefix for 'op'.
+ /// - emits separate variable followed by std::tie for multi-valued operation;
+ /// - emits single type followed by variable for single result;
+ /// - emits nothing if no value produced by op;
+ /// Emits final '=' operator where a type is produced. Returns failure if
+ /// any result type could not be converted.
+ LogicalResult emitAssignPrefix(Operation &op);
+
+ /// Emits a label for the block.
+ LogicalResult emitLabel(Block &block);
+
+ /// Emits the operands and atttributes of the operation. All operands are
+ /// emitted first and then all attributes in alphabetical order.
+ LogicalResult emitOperandsAndAttributes(Operation &op,
+ ArrayRef<StringRef> exclude = {});
+
+ /// Emits the operands of the operation. All operands are emitted in order.
+ LogicalResult emitOperands(Operation &op);
+
+ /// Return the existing or a new name for a Value.
+ StringRef getOrCreateName(Value val);
+
+ /// Return the existing or a new label of a Block.
+ StringRef getOrCreateName(Block &block);
+
+ /// Whether to map an mlir integer to a signed integer in C++.
+ bool shouldMapToSigned(IntegerType::SignednessSemantics val);
+
+ /// RAII helper function to manage entering/exiting C++ scopes.
+ struct Scope {
+ Scope(CppEmitter &emitter)
+ : valueMapperScope(emitter.valueMapper),
+ blockMapperScope(emitter.blockMapper), emitter(emitter) {
+ emitter.valueInScopeCount.push(emitter.valueInScopeCount.top());
+ emitter.labelInScopeCount.push(emitter.labelInScopeCount.top());
+ }
+ ~Scope() {
+ emitter.valueInScopeCount.pop();
+ emitter.labelInScopeCount.pop();
+ }
+
+ private:
+ llvm::ScopedHashTableScope<Value, std::string> valueMapperScope;
+ llvm::ScopedHashTableScope<Block *, std::string> blockMapperScope;
+ CppEmitter &emitter;
+ };
+
+ /// Returns wether the Value is assigned to a C++ variable in the scope.
+ bool hasValueInScope(Value val);
+
+ // Returns whether a label is assigned to the block.
+ bool hasBlockLabel(Block &block);
+
+ /// Returns the output stream.
+ raw_ostream &ostream() { return os; };
+
+ /// Returns if all variables for op results and basic block arguments need to
+ /// be declared at the beginning of a function.
+ bool shouldDeclareVariablesAtTop() { return declareVariablesAtTop; };
+
+private:
+ using ValueMapper = llvm::ScopedHashTable<Value, std::string>;
+ using BlockMapper = llvm::ScopedHashTable<Block *, std::string>;
+
+ /// Output stream to emit to.
+ raw_ostream &os;
+
+ /// Boolean to enforce that all variables for op results and block
+ /// arguments are declared at the beginning of the function. This also
+ /// includes results from ops located in nested regions.
+ bool declareVariablesAtTop;
+
+ /// Map from value to name of C++ variable that contain the name.
+ ValueMapper valueMapper;
+
+ /// Map from block to name of C++ label.
+ BlockMapper blockMapper;
+
+ /// The number of values in the current scope. This is used to declare the
+ /// names of values in a scope.
+ std::stack<int64_t> valueInScopeCount;
+ std::stack<int64_t> labelInScopeCount;
+};
+
+/// Translates the given operation to C++ code. The operation or operations in
+/// the region of 'op' need almost all be in EmitC dialect.
+LogicalResult translateToCpp(Operation &op, raw_ostream &os,
+ bool declareVariablesAtTop = false);
+} // namespace emitc
+} // namespace mlir
+
+#endif // EMITC_TARGET_CPP_CPPEMITTER_H
diff --git a/third_party/mlir-emitc/lib/Target/Cpp/TranslateToCpp.cpp b/third_party/mlir-emitc/lib/Target/Cpp/TranslateToCpp.cpp
new file mode 100644
index 0000000..d100171
--- /dev/null
+++ b/third_party/mlir-emitc/lib/Target/Cpp/TranslateToCpp.cpp
@@ -0,0 +1,826 @@
+//===- TranslateToCpp.cpp - Translating to C++ calls ----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "emitc/Target/Cpp/CppEmitter.h"
+#include "mlir/Dialect/EmitC/IR/EmitC.h"
+#include "mlir/Dialect/SCF/SCF.h"
+#include "mlir/Dialect/StandardOps/IR/Ops.h"
+#include "mlir/IR/BuiltinOps.h"
+#include "mlir/IR/BuiltinTypes.h"
+#include "mlir/IR/Dialect.h"
+#include "mlir/IR/Operation.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/TypeSwitch.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/FormatVariadic.h"
+
+#define DEBUG_TYPE "translate-to-cpp"
+
+using namespace mlir;
+using namespace mlir::emitc;
+using llvm::formatv;
+
+static LogicalResult printConstantOp(CppEmitter &emitter, Operation *operation,
+ Attribute value) {
+ OpResult result = operation->getResult(0);
+
+ // Only emit an assignment as the variable was already declared when printing
+ // the FuncOp.
+ if (emitter.shouldDeclareVariablesAtTop()) {
+ // Skip the assignment if the emitc.const has no value.
+ if (auto oAttr = value.dyn_cast<emitc::OpaqueAttr>()) {
+ if (oAttr.getValue().empty())
+ return success();
+ }
+
+ if (failed(emitter.emitVariableAssignment(result)))
+ return failure();
+ return emitter.emitAttribute(*operation, value);
+ }
+
+ // Emit a variable declaration for an emitc.const op without value.
+ if (auto oAttr = value.dyn_cast<emitc::OpaqueAttr>()) {
+ if (oAttr.getValue().empty())
+ // The semicolon gets printed by the emitOperation function.
+ return emitter.emitVariableDeclaration(result,
+ /*trailingSemicolon=*/false);
+ }
+
+ // Emit a variable declaration.
+ if (failed(emitter.emitAssignPrefix(*operation)))
+ return failure();
+ return emitter.emitAttribute(*operation, value);
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+ emitc::ConstantOp constantOp) {
+ Operation *operation = constantOp.getOperation();
+ Attribute value = constantOp.value();
+
+ return printConstantOp(emitter, operation, value);
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+ mlir::ConstantOp constantOp) {
+ Operation *operation = constantOp.getOperation();
+ Attribute value = constantOp.value();
+
+ return printConstantOp(emitter, operation, value);
+}
+
+static LogicalResult printOperation(CppEmitter &emitter, BranchOp branchOp) {
+ raw_ostream &os = emitter.ostream();
+ Block &successor = *branchOp.getSuccessor();
+
+ for (auto pair :
+ llvm::zip(branchOp.getOperands(), successor.getArguments())) {
+ Value &operand = std::get<0>(pair);
+ BlockArgument &argument = std::get<1>(pair);
+ os << emitter.getOrCreateName(argument) << " = "
+ << emitter.getOrCreateName(operand) << ";\n";
+ }
+
+ os << "goto ";
+ if (!(emitter.hasBlockLabel(successor)))
+ return branchOp.emitOpError() << "Unable to find label for successor block";
+ os << emitter.getOrCreateName(successor);
+ return success();
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+ CondBranchOp condBranchOp) {
+ raw_ostream &os = emitter.ostream();
+ Block &trueSuccessor = *condBranchOp.getTrueDest();
+ Block &falseSuccessor = *condBranchOp.getFalseDest();
+
+ os << "if (" << emitter.getOrCreateName(condBranchOp.getCondition())
+ << ") {\n";
+
+ // If condition is true.
+ for (auto pair : llvm::zip(condBranchOp.getTrueOperands(),
+ trueSuccessor.getArguments())) {
+ Value &operand = std::get<0>(pair);
+ BlockArgument &argument = std::get<1>(pair);
+ os << emitter.getOrCreateName(argument) << " = "
+ << emitter.getOrCreateName(operand) << ";\n";
+ }
+
+ os << "goto ";
+ if (!(emitter.hasBlockLabel(trueSuccessor))) {
+ return condBranchOp.emitOpError()
+ << "Unable to find label for successor block";
+ }
+ os << emitter.getOrCreateName(trueSuccessor) << ";\n";
+ os << "} else {\n";
+ // If condition is false.
+ for (auto pair : llvm::zip(condBranchOp.getFalseOperands(),
+ falseSuccessor.getArguments())) {
+ Value &operand = std::get<0>(pair);
+ BlockArgument &argument = std::get<1>(pair);
+ os << emitter.getOrCreateName(argument) << " = "
+ << emitter.getOrCreateName(operand) << ";\n";
+ }
+
+ os << "goto ";
+ if (!(emitter.hasBlockLabel(falseSuccessor))) {
+ return condBranchOp.emitOpError()
+ << "Unable to find label for successor block";
+ }
+ os << emitter.getOrCreateName(falseSuccessor) << ";\n";
+ os << "}";
+ return success();
+}
+
+static LogicalResult printOperation(CppEmitter &emitter, mlir::CallOp callOp) {
+ if (failed(emitter.emitAssignPrefix(*callOp.getOperation())))
+ return failure();
+
+ raw_ostream &os = emitter.ostream();
+ os << callOp.getCallee() << "(";
+ if (failed(emitter.emitOperands(*callOp.getOperation())))
+ return failure();
+ os << ")";
+ return success();
+}
+
+static LogicalResult printOperation(CppEmitter &emitter, emitc::CallOp callOp) {
+ raw_ostream &os = emitter.ostream();
+ Operation &op = *callOp.getOperation();
+
+ if (failed(emitter.emitAssignPrefix(op)))
+ return failure();
+ os << callOp.callee();
+
+ auto emitArgs = [&](Attribute attr) -> LogicalResult {
+ if (auto t = attr.dyn_cast<IntegerAttr>()) {
+ // Index attributes are treated specially as operand index.
+ if (t.getType().isIndex()) {
+ int64_t idx = t.getInt();
+ if ((idx < 0) || (idx >= op.getNumOperands()))
+ return op.emitOpError() << "invalid operand index";
+ if (!emitter.hasValueInScope(op.getOperand(idx)))
+ return op.emitOpError()
+ << "operand " << idx << "'s value not defined in scope";
+ os << emitter.getOrCreateName(op.getOperand(idx));
+ return success();
+ }
+ }
+ if (failed(emitter.emitAttribute(op, attr)))
+ return failure();
+
+ return success();
+ };
+
+ if (callOp.template_args()) {
+ os << "<";
+ if (failed(interleaveCommaWithError(*callOp.template_args(), os, emitArgs)))
+ return failure();
+ os << ">";
+ }
+
+ os << "(";
+
+ LogicalResult emittedArgs =
+ callOp.args() ? interleaveCommaWithError(*callOp.args(), os, emitArgs)
+ : emitter.emitOperands(op);
+ if (failed(emittedArgs))
+ return failure();
+ os << ")";
+ return success();
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+ emitc::ApplyOp applyOp) {
+ raw_ostream &os = emitter.ostream();
+ Operation &op = *applyOp.getOperation();
+
+ if (failed(emitter.emitAssignPrefix(op)))
+ return failure();
+ os << applyOp.applicableOperator();
+ os << emitter.getOrCreateName(applyOp.getOperand());
+
+ return success();
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+ emitc::IncludeOp includeOp) {
+ raw_ostream &os = emitter.ostream();
+
+ os << "#include ";
+ if (includeOp.is_standard_include())
+ os << "<" << includeOp.include() << ">";
+ else
+ os << "\"" << includeOp.include() << "\"";
+
+ return success();
+}
+
+static LogicalResult printOperation(CppEmitter &emitter, scf::ForOp forOp) {
+
+ raw_ostream &os = emitter.ostream();
+
+ OperandRange operands = forOp.getIterOperands();
+ Block::BlockArgListType iterArgs = forOp.getRegionIterArgs();
+ Operation::result_range results = forOp.getResults();
+
+ if (!emitter.shouldDeclareVariablesAtTop()) {
+ for (OpResult result : results) {
+ if (failed(emitter.emitVariableDeclaration(result,
+ /*trailingSemicolon=*/true)))
+ return failure();
+ }
+ }
+
+ for (auto pair : llvm::zip(iterArgs, operands)) {
+ if (failed(emitter.emitType(*forOp.getOperation(),
+ std::get<0>(pair).getType())))
+ return failure();
+ os << " " << emitter.getOrCreateName(std::get<0>(pair)) << " = ";
+ os << emitter.getOrCreateName(std::get<1>(pair)) << ";";
+ os << "\n";
+ }
+
+ os << "for (";
+ if (failed(emitter.emitType(*forOp.getOperation(),
+ forOp.getInductionVar().getType())))
+ return failure();
+ os << " ";
+ os << emitter.getOrCreateName(forOp.getInductionVar());
+ os << " = ";
+ os << emitter.getOrCreateName(forOp.lowerBound());
+ os << "; ";
+ os << emitter.getOrCreateName(forOp.getInductionVar());
+ os << " < ";
+ os << emitter.getOrCreateName(forOp.upperBound());
+ os << "; ";
+ os << emitter.getOrCreateName(forOp.getInductionVar());
+ os << " += ";
+ os << emitter.getOrCreateName(forOp.step());
+ os << ") {\n";
+
+ Region &forRegion = forOp.region();
+ auto regionOps = forRegion.getOps();
+
+ // We skip the trailing yield op because this updates the result variables
+ // of the for op in the generated code. Instead we update the iterArgs at
+ // the end of a loop iteration and set the result variables after the for
+ // loop.
+ for (auto it = regionOps.begin(); std::next(it) != regionOps.end(); ++it) {
+ if (failed(emitter.emitOperation(*it, /*trailingSemicolon=*/true)))
+ return failure();
+ }
+
+ Operation *yieldOp = forRegion.getBlocks().front().getTerminator();
+ // Copy yield operands into iterArgs at the end of a loop iteration.
+ for (auto pair : llvm::zip(iterArgs, yieldOp->getOperands())) {
+ BlockArgument iterArg = std::get<0>(pair);
+ Value operand = std::get<1>(pair);
+ os << emitter.getOrCreateName(iterArg) << " = "
+ << emitter.getOrCreateName(operand) << ";\n";
+ }
+
+ os << "}\n";
+
+ // Copy iterArgs into results after the for loop.
+ for (auto pair : llvm::zip(results, iterArgs)) {
+ OpResult result = std::get<0>(pair);
+ BlockArgument iterArg = std::get<1>(pair);
+ os << emitter.getOrCreateName(result) << " = "
+ << emitter.getOrCreateName(iterArg) << ";\n";
+ }
+
+ return success();
+}
+
+static LogicalResult printOperation(CppEmitter &emitter, scf::IfOp ifOp) {
+ raw_ostream &os = emitter.ostream();
+
+ if (!emitter.shouldDeclareVariablesAtTop()) {
+ for (OpResult result : ifOp.getResults()) {
+ if (failed(emitter.emitVariableDeclaration(result,
+ /*trailingSemicolon=*/true)))
+ return failure();
+ }
+ }
+
+ os << "if (";
+ if (failed(emitter.emitOperands(*ifOp.getOperation())))
+ return failure();
+ os << ") {\n";
+
+ Region &thenRegion = ifOp.thenRegion();
+ for (Operation &op : thenRegion.getOps()) {
+ // Note: This prints a superfluous semicolon if the terminating yield op has
+ // zero results.
+ if (failed(emitter.emitOperation(op, /*trailingSemicolon=*/true)))
+ return failure();
+ }
+
+ os << "}\n";
+
+ Region &elseRegion = ifOp.elseRegion();
+ if (!elseRegion.empty()) {
+ os << "else {\n";
+
+ for (Operation &op : elseRegion.getOps()) {
+ // Note: This prints a superfluous semicolon if the terminating yield op
+ // has zero results.
+ if (failed(emitter.emitOperation(op, /*trailingSemicolon=*/true)))
+ return failure();
+ }
+
+ os << "}\n";
+ }
+
+ return success();
+}
+
+static LogicalResult printOperation(CppEmitter &emitter, scf::YieldOp yieldOp) {
+ raw_ostream &os = emitter.ostream();
+ Operation &parentOp = *yieldOp.getOperation()->getParentOp();
+
+ if (yieldOp.getNumOperands() != parentOp.getNumResults()) {
+ return yieldOp.emitError("number of operands does not to match the number "
+ "of the parent op's results");
+ }
+
+ if (failed(interleaveWithError(
+ llvm::zip(parentOp.getResults(), yieldOp.getOperands()),
+ [&](auto pair) -> LogicalResult {
+ auto result = std::get<0>(pair);
+ auto operand = std::get<1>(pair);
+ os << emitter.getOrCreateName(result) << " = ";
+
+ if (!emitter.hasValueInScope(operand))
+ return yieldOp.emitError() << "operand value not in scope";
+ os << emitter.getOrCreateName(operand);
+ return success();
+ },
+ [&]() { os << ";\n"; })))
+ return failure();
+
+ return success();
+}
+
+static LogicalResult printOperation(CppEmitter &emitter, ReturnOp returnOp) {
+ raw_ostream &os = emitter.ostream();
+ os << "return";
+ switch (returnOp.getNumOperands()) {
+ case 0:
+ return success();
+ case 1:
+ os << " " << emitter.getOrCreateName(returnOp.getOperand(0));
+ return success(emitter.hasValueInScope(returnOp.getOperand(0)));
+ default:
+ os << " std::make_tuple(";
+ if (failed(emitter.emitOperandsAndAttributes(*returnOp.getOperation())))
+ return failure();
+ os << ")";
+ return success();
+ }
+}
+
+static LogicalResult printOperation(CppEmitter &emitter, ModuleOp moduleOp) {
+ CppEmitter::Scope scope(emitter);
+
+ for (Operation &op : moduleOp) {
+ if (failed(emitter.emitOperation(op, /*trailingSemicolon=*/false)))
+ return failure();
+ }
+ return success();
+}
+
+static LogicalResult printOperation(CppEmitter &emitter, FuncOp functionOp) {
+ // We need to declare variables at top if the function has multiple blocks.
+ if (!emitter.shouldDeclareVariablesAtTop() &&
+ functionOp.getBlocks().size() > 1) {
+ return functionOp.emitOpError()
+ << "with multiple blocks needs variables declared at top";
+ }
+
+ CppEmitter::Scope scope(emitter);
+ raw_ostream &os = emitter.ostream();
+ if (failed(emitter.emitTypes(*functionOp.getOperation(),
+ functionOp.getType().getResults())))
+ return failure();
+ os << " " << functionOp.getName();
+
+ os << "(";
+ if (failed(interleaveCommaWithError(
+ functionOp.getArguments(), os,
+ [&](BlockArgument arg) -> LogicalResult {
+ if (failed(emitter.emitType(*functionOp.getOperation(),
+ arg.getType())))
+ return failure();
+ os << " " << emitter.getOrCreateName(arg);
+ return success();
+ })))
+ return failure();
+ os << ") {\n";
+
+ if (emitter.shouldDeclareVariablesAtTop()) {
+ // Declare all variables that hold op results including those from nested
+ // regions.
+ WalkResult result =
+ functionOp.walk<WalkOrder::PreOrder>([&](Operation *op) -> WalkResult {
+ for (OpResult result : op->getResults()) {
+ if (failed(emitter.emitVariableDeclaration(
+ result, /*trailingSemicolon=*/true))) {
+ return WalkResult(
+ op->emitError("Unable to declare result variable for op"));
+ }
+ }
+ return WalkResult::advance();
+ });
+ if (result.wasInterrupted())
+ return failure();
+ }
+
+ Region::BlockListType &blocks = functionOp.getBlocks();
+ // Create label names for basic blocks.
+ for (Block &block : blocks) {
+ emitter.getOrCreateName(block);
+ }
+
+ // Declare variables for basic block arguments.
+ for (auto it = std::next(blocks.begin()); it != blocks.end(); ++it) {
+ Block &block = *it;
+ for (BlockArgument &arg : block.getArguments()) {
+ if (emitter.hasValueInScope(arg))
+ return functionOp.emitOpError(" block argument #")
+ << arg.getArgNumber() << " is out of scope";
+ if (failed(emitter.emitType(*block.getParentOp(), arg.getType()))) {
+ return failure();
+ }
+ os << " " << emitter.getOrCreateName(arg) << ";\n";
+ }
+ }
+
+ for (Block &block : blocks) {
+ // Only print a label if there is more than one block.
+ if (blocks.size() > 1) {
+ if (failed(emitter.emitLabel(block)))
+ return failure();
+ }
+ for (Operation &op : block.getOperations()) {
+ // Don't print additional semicolons after these operations.
+ bool trailingSemicolon = !isa<scf::IfOp, scf::ForOp, CondBranchOp>(op);
+
+ if (failed(emitter.emitOperation(
+ op, /*trailingSemicolon=*/trailingSemicolon)))
+ return failure();
+ }
+ }
+ os << "}\n";
+ return success();
+}
+
+CppEmitter::CppEmitter(raw_ostream &os, bool declareVariablesAtTop)
+ : os(os), declareVariablesAtTop(declareVariablesAtTop) {
+ valueInScopeCount.push(0);
+ labelInScopeCount.push(0);
+}
+
+/// Return the existing or a new name for a Value.
+StringRef CppEmitter::getOrCreateName(Value val) {
+ if (!valueMapper.count(val))
+ valueMapper.insert(val, formatv("v{0}", ++valueInScopeCount.top()));
+ return *valueMapper.begin(val);
+}
+
+/// Return the existing or a new label for a Block.
+StringRef CppEmitter::getOrCreateName(Block &block) {
+ if (!blockMapper.count(&block))
+ blockMapper.insert(&block, formatv("label{0}", ++labelInScopeCount.top()));
+ return *blockMapper.begin(&block);
+}
+
+bool CppEmitter::shouldMapToSigned(IntegerType::SignednessSemantics val) {
+ switch (val) {
+ case IntegerType::Signless:
+ return true;
+ case IntegerType::Signed:
+ return true;
+ case IntegerType::Unsigned:
+ return false;
+ }
+}
+
+bool CppEmitter::hasValueInScope(Value val) { return valueMapper.count(val); }
+
+bool CppEmitter::hasBlockLabel(Block &block) {
+ return blockMapper.count(&block);
+}
+
+LogicalResult CppEmitter::emitAttribute(Operation &op, Attribute attr) {
+ auto printInt = [&](APInt val, bool isSigned) {
+ if (val.getBitWidth() == 1) {
+ if (val.getBoolValue())
+ os << "true";
+ else
+ os << "false";
+ } else {
+ val.print(os, isSigned);
+ }
+ };
+
+ auto printFloat = [&](APFloat val) {
+ if (val.isFinite()) {
+ SmallString<128> strValue;
+ // Use default values of toString except don't truncate zeros.
+ val.toString(strValue, 0, 0, false);
+ switch (llvm::APFloatBase::SemanticsToEnum(val.getSemantics())) {
+ case llvm::APFloatBase::S_IEEEsingle:
+ os << "(float)";
+ break;
+ case llvm::APFloatBase::S_IEEEdouble:
+ os << "(double)";
+ break;
+ default:
+ break;
+ };
+ os << strValue;
+ } else if (val.isNaN()) {
+ os << "NAN";
+ } else if (val.isInfinity()) {
+ if (val.isNegative())
+ os << "-";
+ os << "INFINITY";
+ }
+ };
+
+ // Print floating point attributes.
+ if (auto fAttr = attr.dyn_cast<FloatAttr>()) {
+ printFloat(fAttr.getValue());
+ return success();
+ }
+ if (auto dense = attr.dyn_cast<DenseFPElementsAttr>()) {
+ os << '{';
+ interleaveComma(dense, os, [&](APFloat val) { printFloat(val); });
+ os << '}';
+ return success();
+ }
+
+ // Print integer attributes.
+ if (auto iAttr = attr.dyn_cast<IntegerAttr>()) {
+ if (auto iType = iAttr.getType().dyn_cast<IntegerType>()) {
+ printInt(iAttr.getValue(), shouldMapToSigned(iType.getSignedness()));
+ return success();
+ }
+ if (auto iType = iAttr.getType().dyn_cast<IndexType>()) {
+ printInt(iAttr.getValue(), false);
+ return success();
+ }
+ }
+ if (auto dense = attr.dyn_cast<DenseIntElementsAttr>()) {
+ if (auto iType = dense.getType()
+ .cast<TensorType>()
+ .getElementType()
+ .dyn_cast<IntegerType>()) {
+ os << '{';
+ interleaveComma(dense, os, [&](APInt val) {
+ printInt(val, shouldMapToSigned(iType.getSignedness()));
+ });
+ os << '}';
+ return success();
+ }
+ if (auto iType = dense.getType()
+ .cast<TensorType>()
+ .getElementType()
+ .dyn_cast<IndexType>()) {
+ os << '{';
+ interleaveComma(dense, os, [&](APInt val) { printInt(val, false); });
+ os << '}';
+ return success();
+ }
+ }
+
+ // Print opaque attributes.
+ if (auto oAttr = attr.dyn_cast<emitc::OpaqueAttr>()) {
+ os << oAttr.getValue();
+ return success();
+ }
+
+ // Print symbolic reference attributes.
+ if (auto sAttr = attr.dyn_cast<SymbolRefAttr>()) {
+ if (sAttr.getNestedReferences().size() > 1)
+ return op.emitError(" attribute has more than 1 nested reference");
+ os << sAttr.getRootReference();
+ return success();
+ }
+
+ // Print type attributes.
+ if (auto type = attr.dyn_cast<TypeAttr>())
+ return emitType(op, type.getValue());
+
+ return op.emitError("cannot emit attribute of type ") << attr.getType();
+}
+
+LogicalResult CppEmitter::emitOperands(Operation &op) {
+ auto emitOperandName = [&](Value result) -> LogicalResult {
+ if (!hasValueInScope(result))
+ return op.emitOpError() << "operand value not in scope";
+ os << getOrCreateName(result);
+ return success();
+ };
+ return interleaveCommaWithError(op.getOperands(), os, emitOperandName);
+}
+
+LogicalResult
+CppEmitter::emitOperandsAndAttributes(Operation &op,
+ ArrayRef<StringRef> exclude) {
+ if (failed(emitOperands(op)))
+ return failure();
+ // Insert comma in between operands and non-filtered attributes if needed.
+ if (op.getNumOperands() > 0) {
+ for (NamedAttribute attr : op.getAttrs()) {
+ if (!llvm::is_contained(exclude, attr.first.strref())) {
+ os << ", ";
+ break;
+ }
+ }
+ }
+ // Emit attributes.
+ auto emitNamedAttribute = [&](NamedAttribute attr) -> LogicalResult {
+ if (llvm::is_contained(exclude, attr.first.strref()))
+ return success();
+ os << "/* " << attr.first << " */";
+ if (failed(emitAttribute(op, attr.second)))
+ return failure();
+ return success();
+ };
+ return interleaveCommaWithError(op.getAttrs(), os, emitNamedAttribute);
+}
+
+LogicalResult CppEmitter::emitVariableAssignment(OpResult result) {
+ if (!hasValueInScope(result)) {
+ return result.getDefiningOp()->emitOpError(
+ "result variable for the operation has not been declared.");
+ }
+ os << getOrCreateName(result) << " = ";
+ return success();
+}
+
+LogicalResult CppEmitter::emitVariableDeclaration(OpResult result,
+ bool trailingSemicolon) {
+ if (hasValueInScope(result)) {
+ return result.getDefiningOp()->emitError(
+ "result variable for the operation already declared.");
+ }
+ if (failed(emitType(*result.getOwner(), result.getType())))
+ return failure();
+ os << " " << getOrCreateName(result);
+ if (trailingSemicolon)
+ os << ";\n";
+ return success();
+}
+
+LogicalResult CppEmitter::emitAssignPrefix(Operation &op) {
+ switch (op.getNumResults()) {
+ case 0:
+ break;
+ case 1: {
+ OpResult result = op.getResult(0);
+ if (shouldDeclareVariablesAtTop()) {
+ if (failed(emitVariableAssignment(result)))
+ return failure();
+ } else {
+ if (failed(emitVariableDeclaration(result, /*trailingSemicolon=*/false)))
+ return failure();
+ os << " = ";
+ }
+ break;
+ }
+ default:
+ if (!shouldDeclareVariablesAtTop()) {
+ for (OpResult result : op.getResults()) {
+ if (failed(emitVariableDeclaration(result, /*trailingSemicolon=*/true)))
+ return failure();
+ }
+ }
+ os << "std::tie(";
+ interleaveComma(op.getResults(), os,
+ [&](Value result) { os << getOrCreateName(result); });
+ os << ") = ";
+ }
+ return success();
+}
+
+LogicalResult CppEmitter::emitLabel(Block &block) {
+ if (!hasBlockLabel(block))
+ return block.getParentOp()->emitError("Label for block not found.");
+ os << getOrCreateName(block) << ":\n";
+ return success();
+}
+
+LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) {
+ LogicalResult status =
+ llvm::TypeSwitch<Operation *, LogicalResult>(&op)
+ // EmitC ops.
+ .Case<emitc::ApplyOp, emitc::CallOp, emitc::ConstantOp,
+ emitc::IncludeOp>(
+ [&](auto op) { return printOperation(*this, op); })
+ // SCF ops.
+ .Case<scf::ForOp, scf::IfOp, scf::YieldOp>(
+ [&](auto op) { return printOperation(*this, op); })
+ // Standard ops.
+ .Case<BranchOp, mlir::CallOp, CondBranchOp, mlir::ConstantOp, FuncOp,
+ ModuleOp, ReturnOp>(
+ [&](auto op) { return printOperation(*this, op); })
+ .Default([&](Operation *) {
+ return op.emitOpError() << "unable to find printer for op";
+ });
+
+ if (failed(status))
+ return failure();
+ os << (trailingSemicolon ? ";\n" : "\n");
+ return success();
+}
+
+LogicalResult CppEmitter::emitType(Operation &op, Type type) {
+ if (auto iType = type.dyn_cast<IntegerType>()) {
+ switch (iType.getWidth()) {
+ case 1:
+ return (os << "bool"), success();
+ case 8:
+ case 16:
+ case 32:
+ case 64:
+ if (shouldMapToSigned(iType.getSignedness()))
+ return (os << "int" << iType.getWidth() << "_t"), success();
+ else
+ return (os << "uint" << iType.getWidth() << "_t"), success();
+ default:
+ return op.emitError("cannot emit integer type ") << type;
+ }
+ }
+ if (auto fType = type.dyn_cast<FloatType>()) {
+ switch (fType.getWidth()) {
+ case 32:
+ return (os << "float"), success();
+ case 64:
+ return (os << "double"), success();
+ default:
+ return op.emitError("cannot emit float type ") << type;
+ }
+ }
+ if (auto iType = type.dyn_cast<IndexType>())
+ return (os << "size_t"), success();
+ if (auto tType = type.dyn_cast<TensorType>()) {
+ if (!tType.hasRank())
+ return op.emitError("cannot emit unranked tensor type");
+ if (!tType.hasStaticShape())
+ return op.emitError("cannot emit tensor type with non static shape");
+ os << "Tensor<";
+ if (failed(emitType(op, tType.getElementType())))
+ return failure();
+ auto shape = tType.getShape();
+ for (auto dimSize : shape) {
+ os << ", ";
+ os << dimSize;
+ }
+ os << ">";
+ return success();
+ }
+ if (auto tType = type.dyn_cast<TupleType>())
+ return emitTupleType(op, tType.getTypes());
+ if (auto oType = type.dyn_cast<emitc::OpaqueType>()) {
+ os << oType.getValue();
+ return success();
+ }
+ return op.emitError("cannot emit type ") << type;
+}
+
+LogicalResult CppEmitter::emitTypes(Operation &op, ArrayRef<Type> types) {
+ switch (types.size()) {
+ case 0:
+ os << "void";
+ return success();
+ case 1:
+ return emitType(op, types.front());
+ default:
+ return emitTupleType(op, types);
+ }
+}
+
+LogicalResult CppEmitter::emitTupleType(Operation &op, ArrayRef<Type> types) {
+ os << "std::tuple<";
+ if (failed(interleaveCommaWithError(
+ types, os, [&](Type type) { return emitType(op, type); })))
+ return failure();
+ os << ">";
+ return success();
+}
+
+LogicalResult emitc::translateToCpp(Operation &op, raw_ostream &os,
+ bool declareVariablesAtTop) {
+ CppEmitter emitter(os, declareVariablesAtTop);
+ return emitter.emitOperation(op, /*trailingSemicolon=*/false);
+}