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