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); +}