[Mlir-commits] [mlir] [mlir][EmitC] Refactor MLIR Translate To Cpp (PR #84973)

Weizhe Chen llvmlistbot at llvm.org
Tue Mar 12 14:32:31 PDT 2024


https://github.com/weizchen updated https://github.com/llvm/llvm-project/pull/84973

>From 3437b30327dc3f93bd906a9d270e64d64655e2a2 Mon Sep 17 00:00:00 2001
From: Weizhe Chen <jack.chen3 at huawei.com>
Date: Tue, 12 Mar 2024 12:57:37 -0400
Subject: [PATCH] [mlir][EmitC] Refactor MLIR Translate To Cpp

This refactors the MLIR TranslateToCpp part by introducing CppTranslationInterface
that breaks down the overall translation into different dialects. This majorly helps
the readability of the code and extensibility for future c++ translation addition.
---
 mlir/include/mlir/Target/Cpp/CppEmitter.h     |    6 -
 .../mlir/Target/Cpp/CppTranslationInterface.h |   55 +
 .../mlir/Target/Cpp/CppTranslationUtils.h     |   85 ++
 mlir/include/mlir/Target/Cpp/Dialect/All.h    |   35 +
 .../Dialect/Builtin/BuiltinToCppTranslation.h |   31 +
 .../ControlFlow/ControlFlowToCppTranslation.h |   31 +
 .../Cpp/Dialect/EmitC/EmitCToCppTranslation.h |   31 +
 .../Cpp/Dialect/Func/FuncToCppTranslation.h   |   31 +
 mlir/include/mlir/Target/Cpp/TranslateToCpp.h |  194 +++
 mlir/lib/Target/Cpp/CMakeLists.txt            |   28 +-
 mlir/lib/Target/Cpp/CppTranslationUtils.cpp   |  219 ++++
 .../Builtin/BuiltinToCppTranslation.cpp       |   74 ++
 .../Target/Cpp/Dialect/Builtin/CMakeLists.txt |   11 +
 mlir/lib/Target/Cpp/Dialect/CMakeLists.txt    |    4 +
 .../Cpp/Dialect/ControlFlow/CMakeLists.txt    |   12 +
 .../ControlFlowToCppTranslation.cpp           |  128 ++
 .../Target/Cpp/Dialect/EmitC/CMakeLists.txt   |   12 +
 .../Dialect/EmitC/EmitCToCppTranslation.cpp   |  561 +++++++++
 .../Target/Cpp/Dialect/Func/CMakeLists.txt    |   12 +
 .../Cpp/Dialect/Func/FuncToCppTranslation.cpp |  126 ++
 mlir/lib/Target/Cpp/TranslateRegistration.cpp |   15 +-
 mlir/lib/Target/Cpp/TranslateToCpp.cpp        | 1077 +----------------
 mlir/test/Target/Cpp/invalid.mlir             |    2 +-
 23 files changed, 1697 insertions(+), 1083 deletions(-)
 create mode 100644 mlir/include/mlir/Target/Cpp/CppTranslationInterface.h
 create mode 100644 mlir/include/mlir/Target/Cpp/CppTranslationUtils.h
 create mode 100644 mlir/include/mlir/Target/Cpp/Dialect/All.h
 create mode 100644 mlir/include/mlir/Target/Cpp/Dialect/Builtin/BuiltinToCppTranslation.h
 create mode 100644 mlir/include/mlir/Target/Cpp/Dialect/ControlFlow/ControlFlowToCppTranslation.h
 create mode 100644 mlir/include/mlir/Target/Cpp/Dialect/EmitC/EmitCToCppTranslation.h
 create mode 100644 mlir/include/mlir/Target/Cpp/Dialect/Func/FuncToCppTranslation.h
 create mode 100644 mlir/include/mlir/Target/Cpp/TranslateToCpp.h
 create mode 100644 mlir/lib/Target/Cpp/CppTranslationUtils.cpp
 create mode 100644 mlir/lib/Target/Cpp/Dialect/Builtin/BuiltinToCppTranslation.cpp
 create mode 100644 mlir/lib/Target/Cpp/Dialect/Builtin/CMakeLists.txt
 create mode 100644 mlir/lib/Target/Cpp/Dialect/CMakeLists.txt
 create mode 100644 mlir/lib/Target/Cpp/Dialect/ControlFlow/CMakeLists.txt
 create mode 100644 mlir/lib/Target/Cpp/Dialect/ControlFlow/ControlFlowToCppTranslation.cpp
 create mode 100644 mlir/lib/Target/Cpp/Dialect/EmitC/CMakeLists.txt
 create mode 100644 mlir/lib/Target/Cpp/Dialect/EmitC/EmitCToCppTranslation.cpp
 create mode 100644 mlir/lib/Target/Cpp/Dialect/Func/CMakeLists.txt
 create mode 100644 mlir/lib/Target/Cpp/Dialect/Func/FuncToCppTranslation.cpp

diff --git a/mlir/include/mlir/Target/Cpp/CppEmitter.h b/mlir/include/mlir/Target/Cpp/CppEmitter.h
index 30d3fff9fca88b..4aa551fa205123 100644
--- a/mlir/include/mlir/Target/Cpp/CppEmitter.h
+++ b/mlir/include/mlir/Target/Cpp/CppEmitter.h
@@ -14,13 +14,8 @@
 #define MLIR_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 {
 
 /// Translates the given operation to C++ code. The operation or operations in
 /// the region of 'op' need almost all be in EmitC dialect. The parameter
@@ -28,7 +23,6 @@ namespace emitc {
 /// arguments are declared at the beginning of the function.
 LogicalResult translateToCpp(Operation *op, raw_ostream &os,
                              bool declareVariablesAtTop = false);
-} // namespace emitc
 } // namespace mlir
 
 #endif // MLIR_TARGET_CPP_CPPEMITTER_H
diff --git a/mlir/include/mlir/Target/Cpp/CppTranslationInterface.h b/mlir/include/mlir/Target/Cpp/CppTranslationInterface.h
new file mode 100644
index 00000000000000..d0a964a82ec2ec
--- /dev/null
+++ b/mlir/include/mlir/Target/Cpp/CppTranslationInterface.h
@@ -0,0 +1,55 @@
+//===--- CppTranslationInterface.h - Translation to Cpp iface ---*- 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 header file defines dialect interfaces for translation to Cpp.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_TARGET_CPP_CPPTRANSLATIONINTERFACE_H
+#define MLIR_TARGET_CPP_CPPTRANSLATIONINTERFACE_H
+
+#include "mlir/IR/DialectInterface.h"
+#include "mlir/Support/LogicalResult.h"
+
+namespace mlir {
+struct CppEmitter;
+
+/// Base class for dialect interfaces providing translation to Cpp. Dialects
+/// should implement this interface with supported operation translations to
+/// be registered and used with translate-to-cpp.
+class CppTranslationDialectInterface
+    : public DialectInterface::Base<CppTranslationDialectInterface> {
+public:
+  CppTranslationDialectInterface(Dialect *dialect) : Base(dialect) {}
+
+  /// Hook for derived dialect interface to provide op translation to Cpp.
+  virtual LogicalResult emitOperation(Operation *op, CppEmitter &cppEmitter,
+                                      bool trailingSemicolon) const {
+    return failure();
+  }
+};
+
+/// Interface collection for translation to Cpp, dispatches to a concrete
+/// interface implementation based on the dialect to which the given op belongs.
+class CppTranslationInterface
+    : public DialectInterfaceCollection<CppTranslationDialectInterface> {
+public:
+  using Base::Base;
+
+  /// Translates the given operation to Cpp using the derived dialect interface.
+  virtual LogicalResult emitOperation(Operation *op, CppEmitter &cppEmitter,
+                                      bool trailingSemicolon) const {
+    if (const CppTranslationDialectInterface *iface = getInterfaceFor(op))
+      return iface->emitOperation(op, cppEmitter, trailingSemicolon);
+    return failure();
+  }
+};
+
+} // namespace mlir
+
+#endif // MLIR_TARGET_CPP_CPPTRANSLATIONINTERFACE_H
diff --git a/mlir/include/mlir/Target/Cpp/CppTranslationUtils.h b/mlir/include/mlir/Target/Cpp/CppTranslationUtils.h
new file mode 100644
index 00000000000000..be41a4e6515315
--- /dev/null
+++ b/mlir/include/mlir/Target/Cpp/CppTranslationUtils.h
@@ -0,0 +1,85 @@
+//===- CppTranslationUtils.h - Helpers to used in 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 common helper functions used across different dialects
+// during the Cpp translation.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_TARGET_CPP_CPPTRANSLATIONUTILS_H
+#define MLIR_TARGET_CPP_CPPTRANSLATIONUTILS_H
+
+#include "mlir/Dialect/EmitC/IR/EmitC.h"
+#include "mlir/Target/Cpp/TranslateToCpp.h"
+
+using namespace mlir;
+
+/// Convenience functions to produce interleaved output with functions returning
+/// a LogicalResult. This is different than those in STLExtras 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 << ", "; });
+}
+
+/// Determine whether expression \p expressionOp should be emitted inline, i.e.
+/// as part of its user. This function recommends inlining of any expressions
+/// that can be inlined unless it is used by another expression, under the
+/// assumption that  any expression fusion/re-materialization was taken care of
+/// by transformations run by the backend.
+bool shouldBeInlined(emitc::ExpressionOp expressionOp);
+
+LogicalResult printConstantOp(CppEmitter &emitter, Operation *operation,
+                              Attribute value);
+
+LogicalResult printBinaryOperation(CppEmitter &emitter, Operation *operation,
+                                   StringRef binaryOperator);
+
+LogicalResult printUnaryOperation(CppEmitter &emitter, Operation *operation,
+                                  StringRef unaryOperator);
+
+LogicalResult printCallOperation(CppEmitter &emitter, Operation *callOp,
+                                 StringRef callee);
+
+LogicalResult printFunctionArgs(CppEmitter &emitter, Operation *functionOp,
+                                ArrayRef<Type> arguments);
+
+LogicalResult printFunctionArgs(CppEmitter &emitter, Operation *functionOp,
+                                Region::BlockArgListType arguments);
+
+LogicalResult printFunctionBody(CppEmitter &emitter, Operation *functionOp,
+                                Region::BlockListType &blocks);
+
+#endif // MLIR_TARGET_CPP_CPPTRANSLATIONUTILS_H
diff --git a/mlir/include/mlir/Target/Cpp/Dialect/All.h b/mlir/include/mlir/Target/Cpp/Dialect/All.h
new file mode 100644
index 00000000000000..556588efb0118c
--- /dev/null
+++ b/mlir/include/mlir/Target/Cpp/Dialect/All.h
@@ -0,0 +1,35 @@
+//===----- All.h - MLIR To Cpp Translation Registration ---------*- 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 a helper to register the all dialect translations to Cpp.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_TARGET_CPP_DIALECT_ALL_H
+#define MLIR_TARGET_CPP_DIALECT_ALL_H
+
+#include "mlir/Target/Cpp/Dialect/Builtin/BuiltinToCppTranslation.h"
+#include "mlir/Target/Cpp/Dialect/ControlFlow/ControlFlowToCppTranslation.h"
+#include "mlir/Target/Cpp/Dialect/EmitC/EmitCToCppTranslation.h"
+#include "mlir/Target/Cpp/Dialect/Func/FuncToCppTranslation.h"
+
+namespace mlir {
+class DialectRegistry;
+
+/// Registers all dialects that can be translated to Cpp and the
+/// corresponding translation interfaces.
+static inline void registerAllToCppTranslations(DialectRegistry &registry) {
+  registerBuiltinDialectCppTranslation(registry);
+  registerControlFlowDialectCppTranslation(registry);
+  registerEmitCDialectCppTranslation(registry);
+  registerFuncDialectCppTranslation(registry);
+}
+
+} // namespace mlir
+
+#endif // MLIR_TARGET_CPP_DIALECT_ALL_H
diff --git a/mlir/include/mlir/Target/Cpp/Dialect/Builtin/BuiltinToCppTranslation.h b/mlir/include/mlir/Target/Cpp/Dialect/Builtin/BuiltinToCppTranslation.h
new file mode 100644
index 00000000000000..25f19022f49043
--- /dev/null
+++ b/mlir/include/mlir/Target/Cpp/Dialect/Builtin/BuiltinToCppTranslation.h
@@ -0,0 +1,31 @@
+//===---- BuiltinToCppTranslation.h - Builtin Dialect to Cpp ----*- 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 provides registration calls for Builtin dialect to Cpp translation.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_TARGET_CPP_DIALECT_BUILTIN_BUILTINTOCPPTRANSLATION_H
+#define MLIR_TARGET_CPP_DIALECT_BUILTIN_BUILTINTOCPPTRANSLATION_H
+
+namespace mlir {
+
+class DialectRegistry;
+class MLIRContext;
+
+/// Register the Builtin dialect and the translation from it to the Cpp in the
+/// given registry;
+void registerBuiltinDialectCppTranslation(DialectRegistry &registry);
+
+/// Register the Builtin dialect and the translation from it in the registry
+/// associated with the given context.
+void registerBuiltinDialectCppTranslation(MLIRContext &context);
+
+} // namespace mlir
+
+#endif // MLIR_TARGET_CPP_DIALECT_BUILTIN_BUILTINTOCPPTRANSLATION_H
diff --git a/mlir/include/mlir/Target/Cpp/Dialect/ControlFlow/ControlFlowToCppTranslation.h b/mlir/include/mlir/Target/Cpp/Dialect/ControlFlow/ControlFlowToCppTranslation.h
new file mode 100644
index 00000000000000..fea57fd1c02e28
--- /dev/null
+++ b/mlir/include/mlir/Target/Cpp/Dialect/ControlFlow/ControlFlowToCppTranslation.h
@@ -0,0 +1,31 @@
+//===- ControlFlowToCppTranslation.h - ControlFlow Dialect to Cpp -*-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 provides registration calls for ControlFlow dialect to Cpp translation.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_TARGET_CPP_DIALECT_CONTROLFLOW_CONTROLFLOWTOCPPTRANSLATION_H
+#define MLIR_TARGET_CPP_DIALECT_CONTROLFLOW_CONTROLFLOWTOCPPTRANSLATION_H
+
+namespace mlir {
+
+class DialectRegistry;
+class MLIRContext;
+
+/// Register the ControlFlow dialect and the translation from it to the Cpp in
+/// the given registry;
+void registerControlFlowDialectCppTranslation(DialectRegistry &registry);
+
+/// Register the ControlFlow dialect and the translation from it in the registry
+/// associated with the given context.
+void registerControlFlowDialectCppTranslation(MLIRContext &context);
+
+} // namespace mlir
+
+#endif // MLIR_TARGET_CPP_DIALECT_CONTROLFLOW_CONTROLFLOWTOCPPTRANSLATION_H
diff --git a/mlir/include/mlir/Target/Cpp/Dialect/EmitC/EmitCToCppTranslation.h b/mlir/include/mlir/Target/Cpp/Dialect/EmitC/EmitCToCppTranslation.h
new file mode 100644
index 00000000000000..af47ffad1ab62e
--- /dev/null
+++ b/mlir/include/mlir/Target/Cpp/Dialect/EmitC/EmitCToCppTranslation.h
@@ -0,0 +1,31 @@
+//===------ EmitCToCppTranslation.h - EmitC Dialect to Cpp-------*- 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 provides registration calls for EmitC dialect to Cpp translation.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_TARGET_CPP_DIALECT_EMITC_EMITCTOCPPTRANSLATION_H
+#define MLIR_TARGET_CPP_DIALECT_EMITC_EMITCTOCPPTRANSLATION_H
+
+namespace mlir {
+
+class DialectRegistry;
+class MLIRContext;
+
+/// Register the EmitC dialect and the translation from it to the Cpp in the
+/// given registry;
+void registerEmitCDialectCppTranslation(DialectRegistry &registry);
+
+/// Register the EmitC dialect and the translation from it in the registry
+/// associated with the given context.
+void registerEmitCDialectCppTranslation(MLIRContext &context);
+
+} // namespace mlir
+
+#endif // MLIR_TARGET_CPP_DIALECT_EMITC_EMITCTOCPPTRANSLATION_H
diff --git a/mlir/include/mlir/Target/Cpp/Dialect/Func/FuncToCppTranslation.h b/mlir/include/mlir/Target/Cpp/Dialect/Func/FuncToCppTranslation.h
new file mode 100644
index 00000000000000..b167093dc334bb
--- /dev/null
+++ b/mlir/include/mlir/Target/Cpp/Dialect/Func/FuncToCppTranslation.h
@@ -0,0 +1,31 @@
+//===------- FuncToCppTranslation.h - Func Dialect to Cpp -------*- 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 provides registration calls for Func dialect to Cpp translation.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_TARGET_CPP_DIALECT_FUNC_FUNCTOCPPTRANSLATION_H
+#define MLIR_TARGET_CPP_DIALECT_FUNC_FUNCTOCPPTRANSLATION_H
+
+namespace mlir {
+
+class DialectRegistry;
+class MLIRContext;
+
+/// Register the Func dialect and the translation from it to the Cpp in the
+/// given registry;
+void registerFuncDialectCppTranslation(DialectRegistry &registry);
+
+/// Register the Func dialect and the translation from it in the registry
+/// associated with the given context.
+void registerFuncDialectCppTranslation(MLIRContext &context);
+
+} // namespace mlir
+
+#endif // MLIR_TARGET_CPP_DIALECT_FUNC_FUNCTOCPPTRANSLATION_H
diff --git a/mlir/include/mlir/Target/Cpp/TranslateToCpp.h b/mlir/include/mlir/Target/Cpp/TranslateToCpp.h
new file mode 100644
index 00000000000000..9deb44a9fe0066
--- /dev/null
+++ b/mlir/include/mlir/Target/Cpp/TranslateToCpp.h
@@ -0,0 +1,194 @@
+//===-- TranslateToCpp.h - Define CppEmitter struct -------------*- 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 the CppEmitter meta struct used for Cpp translation.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_TARGET_CPP_TRANSLATETOCPP_H
+#define MLIR_TARGET_CPP_TRANSLATETOCPP_H
+
+#include "mlir/Dialect/EmitC/IR/EmitC.h"
+#include "mlir/IR/BuiltinTypes.h"
+#include "mlir/Support/IndentedOstream.h"
+#include "mlir/Target/Cpp/CppEmitter.h"
+#include "mlir/Target/Cpp/CppTranslationInterface.h"
+#include "llvm/ADT/ScopedHashTable.h"
+#include "llvm/Support/raw_ostream.h"
+#include <stack>
+
+namespace mlir {
+
+/// Emitter that uses dialect specific emitters to emit C++ code.
+struct CppEmitter {
+  friend LogicalResult translateToCpp(Operation *op, raw_ostream &os,
+                                      bool declareVariablesAtTop);
+
+  explicit CppEmitter(raw_ostream &os, Operation *module,
+                      bool declareVariablesAtTop);
+
+  /// Emits attribute or returns failure.
+  LogicalResult emitAttribute(Location loc, 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(Location loc, 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(Location loc, ArrayRef<Type> types);
+
+  /// Emits array of types as a std::tuple of the emitted types independently of
+  /// the array size.
+  LogicalResult emitTupleType(Location loc, 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 a declaration of a variable with the given type and name.
+  LogicalResult emitVariableDeclaration(Location loc, Type type,
+                                        StringRef name);
+
+  /// 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);
+
+  /// Emits value as an operands of an operation
+  LogicalResult emitOperand(Value value);
+
+  /// Emit an expression as a C expression.
+  LogicalResult emitExpression(emitc::ExpressionOp expressionOp);
+
+  /// 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 unsigned integer in C++.
+  bool shouldMapToUnsigned(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_indented_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; };
+
+  /// Get expression currently being emitted.
+  emitc::ExpressionOp getEmittedExpression() { return emittedExpression; }
+
+  /// Determine whether given value is part of the expression potentially being
+  /// emitted.
+  bool isPartOfCurrentExpression(Value value) {
+    if (!emittedExpression)
+      return false;
+    Operation *def = value.getDefiningOp();
+    if (!def)
+      return false;
+    auto operandExpression = dyn_cast<emitc::ExpressionOp>(def->getParentOp());
+    return operandExpression == emittedExpression;
+  };
+
+protected:
+  using ValueMapper = llvm::ScopedHashTable<Value, std::string>;
+  using BlockMapper = llvm::ScopedHashTable<Block *, std::string>;
+
+  /// Output stream to emit to.
+  raw_indented_ostream os;
+
+  /// Module op to get current context
+  Operation *module;
+
+  /// 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;
+
+  /// A dialect interface collection used for dispatching the translation to
+  /// specific dialects.
+  CppTranslationInterface iface;
+
+  /// 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;
+
+  /// State of the current expression being emitted.
+  emitc::ExpressionOp emittedExpression;
+  SmallVector<int> emittedExpressionPrecedence;
+
+  void pushExpressionPrecedence(int precedence) {
+    emittedExpressionPrecedence.push_back(precedence);
+  }
+  void popExpressionPrecedence() { emittedExpressionPrecedence.pop_back(); }
+  static int lowestPrecedence() { return 0; }
+  int getExpressionPrecedence() {
+    if (emittedExpressionPrecedence.empty())
+      return lowestPrecedence();
+    return emittedExpressionPrecedence.back();
+  }
+};
+
+} // namespace mlir
+
+#endif // MLIR_TARGET_CPP_TRANSLATETOCPP_H
diff --git a/mlir/lib/Target/Cpp/CMakeLists.txt b/mlir/lib/Target/Cpp/CMakeLists.txt
index d8f372cf162453..f3c35c99061216 100644
--- a/mlir/lib/Target/Cpp/CMakeLists.txt
+++ b/mlir/lib/Target/Cpp/CMakeLists.txt
@@ -1,17 +1,33 @@
-add_mlir_translation_library(MLIRTargetCpp
-  TranslateRegistration.cpp
+add_subdirectory(Dialect)
+
+  set(LLVM_OPTIONAL_SOURCES
+    CppTranslationUtils.cpp
+    TranslateRegistration.cpp
+    TranslateToCpp.cpp
+  )
+
+  add_mlir_translation_library(MLIRTargetCpp
   TranslateToCpp.cpp
+  CppTranslationUtils.cpp
 
   ADDITIONAL_HEADER_DIRS
   ${EMITC_MAIN_INCLUDE_DIR}/emitc/Target/Cpp
 
   LINK_LIBS PUBLIC
-  MLIRControlFlowDialect
   MLIREmitCDialect
-  MLIRFuncDialect
   MLIRIR
-  MLIRMathDialect
-  MLIRSCFDialect
   MLIRSupport
   MLIRTranslateLib
   )
+
+  add_mlir_translation_library(MLIRToCppTranslationRegistration
+  TranslateRegistration.cpp
+
+  LINK_LIBS PUBLIC
+  MLIRBuiltinToCppTranslation
+  MLIRControlFlowToCppTranslation
+  MLIREmitCToCppTranslation
+  MLIRFuncToCppTranslation
+  MLIRMathDialect
+  MLIRSCFDialect
+  )
diff --git a/mlir/lib/Target/Cpp/CppTranslationUtils.cpp b/mlir/lib/Target/Cpp/CppTranslationUtils.cpp
new file mode 100644
index 00000000000000..25a4f236e50575
--- /dev/null
+++ b/mlir/lib/Target/Cpp/CppTranslationUtils.cpp
@@ -0,0 +1,219 @@
+//===- CppTranslationUtils.cpp - Helpers used in 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 common helper functions used across the Cpp translation.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_TARGET_CPP_CPPTRANSLATIONUTILS_CPP
+#define MLIR_TARGET_CPP_CPPTRANSLATIONUTILS_CPP
+
+#include "mlir/Target/Cpp/CppTranslationUtils.h"
+#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
+
+using namespace mlir;
+
+bool shouldBeInlined(emitc::ExpressionOp expressionOp) {
+  // Do not inline if expression is marked as such.
+  if (expressionOp.getDoNotInline())
+    return false;
+
+  // Do not inline expressions with side effects to prevent side-effect
+  // reordering.
+  if (expressionOp.hasSideEffects())
+    return false;
+
+  // Do not inline expressions with multiple uses.
+  Value result = expressionOp.getResult();
+  if (!result.hasOneUse())
+    return false;
+
+  // Do not inline expressions used by other expressions, as any desired
+  // expression folding was taken care of by transformations.
+  Operation *user = *result.getUsers().begin();
+  return !user->getParentOfType<emitc::ExpressionOp>();
+}
+
+// ! constant, binary and unart op are currently only used by emitc translation
+
+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.constant has no value.
+    if (auto oAttr = dyn_cast<emitc::OpaqueAttr>(value)) {
+      if (oAttr.getValue().empty())
+        return success();
+    }
+
+    if (failed(emitter.emitVariableAssignment(result)))
+      return failure();
+    return emitter.emitAttribute(operation->getLoc(), value);
+  }
+
+  // Emit a variable declaration for an emitc.constant op without value.
+  if (auto oAttr = dyn_cast<emitc::OpaqueAttr>(value)) {
+    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->getLoc(), value);
+}
+
+LogicalResult printBinaryOperation(CppEmitter &emitter, Operation *operation,
+                                   StringRef binaryOperator) {
+  raw_ostream &os = emitter.ostream();
+
+  if (failed(emitter.emitAssignPrefix(*operation)))
+    return failure();
+
+  if (failed(emitter.emitOperand(operation->getOperand(0))))
+    return failure();
+
+  os << " " << binaryOperator << " ";
+
+  if (failed(emitter.emitOperand(operation->getOperand(1))))
+    return failure();
+
+  return success();
+}
+
+LogicalResult printUnaryOperation(CppEmitter &emitter, Operation *operation,
+                                  StringRef unaryOperator) {
+  raw_ostream &os = emitter.ostream();
+
+  if (failed(emitter.emitAssignPrefix(*operation)))
+    return failure();
+
+  os << unaryOperator;
+
+  if (failed(emitter.emitOperand(operation->getOperand(0))))
+    return failure();
+
+  return success();
+}
+
+LogicalResult printCallOperation(CppEmitter &emitter, Operation *callOp,
+                                 StringRef callee) {
+  if (failed(emitter.emitAssignPrefix(*callOp)))
+    return failure();
+
+  raw_ostream &os = emitter.ostream();
+  os << callee << "(";
+  if (failed(emitter.emitOperands(*callOp)))
+    return failure();
+  os << ")";
+  return success();
+}
+
+LogicalResult printFunctionArgs(CppEmitter &emitter, Operation *functionOp,
+                                ArrayRef<Type> arguments) {
+  raw_indented_ostream &os = emitter.ostream();
+
+  return (
+      interleaveCommaWithError(arguments, os, [&](Type arg) -> LogicalResult {
+        return emitter.emitType(functionOp->getLoc(), arg);
+      }));
+}
+
+LogicalResult printFunctionArgs(CppEmitter &emitter, Operation *functionOp,
+                                Region::BlockArgListType arguments) {
+  raw_indented_ostream &os = emitter.ostream();
+
+  return (interleaveCommaWithError(
+      arguments, os, [&](BlockArgument arg) -> LogicalResult {
+        return emitter.emitVariableDeclaration(
+            functionOp->getLoc(), arg.getType(), emitter.getOrCreateName(arg));
+      }));
+}
+
+LogicalResult printFunctionBody(CppEmitter &emitter, Operation *functionOp,
+                                Region::BlockListType &blocks) {
+  raw_indented_ostream &os = emitter.ostream();
+  os.indent();
+
+  if (emitter.shouldDeclareVariablesAtTop()) {
+    // Declare all variables that hold op results including those from nested
+    // regions.
+    WalkResult result =
+        functionOp->walk<WalkOrder::PreOrder>([&](Operation *op) -> WalkResult {
+          if (isa<emitc::LiteralOp>(op) ||
+              isa<emitc::ExpressionOp>(op->getParentOp()) ||
+              (isa<emitc::ExpressionOp>(op) &&
+               shouldBeInlined(cast<emitc::ExpressionOp>(op))))
+            return WalkResult::skip();
+          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();
+  }
+
+  // Create label names for basic blocks.
+  for (Block &block : blocks) {
+    emitter.getOrCreateName(block);
+  }
+
+  // Declare variables for basic block arguments.
+  for (Block &block : llvm::drop_begin(blocks)) {
+    for (BlockArgument &arg : block.getArguments()) {
+      if (emitter.hasValueInScope(arg))
+        return functionOp->emitOpError(" block argument #")
+               << arg.getArgNumber() << " is out of scope";
+      if (isa<emitc::ArrayType>(arg.getType()))
+        return functionOp->emitOpError("cannot emit block argument #")
+               << arg.getArgNumber() << " with array type";
+      if (failed(
+              emitter.emitType(block.getParentOp()->getLoc(), arg.getType()))) {
+        return failure();
+      }
+      os << " " << emitter.getOrCreateName(arg) << ";\n";
+    }
+  }
+
+  for (Block &block : blocks) {
+    // Only print a label if the block has predecessors.
+    if (!block.hasNoPredecessors()) {
+      if (failed(emitter.emitLabel(block)))
+        return failure();
+    }
+    for (Operation &op : block.getOperations()) {
+      // When generating code for an emitc.if or cf.cond_br op no semicolon
+      // needs to be printed after the closing brace.
+      // When generating code for an emitc.for and emitc.verbatim op, printing a
+      // trailing semicolon is handled within the printOperation function.
+      bool trailingSemicolon =
+          !isa<cf::CondBranchOp, emitc::DeclareFuncOp, emitc::ForOp,
+               emitc::IfOp, emitc::LiteralOp, emitc::VerbatimOp>(op);
+
+      if (failed(emitter.emitOperation(
+              op, /*trailingSemicolon=*/trailingSemicolon)))
+        return failure();
+    }
+  }
+
+  os.unindent();
+
+  return success();
+}
+
+#endif // MLIR_TARGET_CPP_CPPTRANSLATIONUTILS_CPP
diff --git a/mlir/lib/Target/Cpp/Dialect/Builtin/BuiltinToCppTranslation.cpp b/mlir/lib/Target/Cpp/Dialect/Builtin/BuiltinToCppTranslation.cpp
new file mode 100644
index 00000000000000..f46d4220fb0223
--- /dev/null
+++ b/mlir/lib/Target/Cpp/Dialect/Builtin/BuiltinToCppTranslation.cpp
@@ -0,0 +1,74 @@
+//===-- BuiltinToCppTranslation.cpp - Translate Builtin dialect to Cpp ----===//
+//
+// 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 implements a translation between the MLIR Builtin dialect and Cpp.
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/Target/Cpp/Dialect/Builtin/BuiltinToCppTranslation.h"
+
+#include "mlir/IR/BuiltinDialect.h"
+#include "mlir/IR/BuiltinOps.h"
+#include "mlir/Target/Cpp/CppTranslationInterface.h"
+#include "mlir/Target/Cpp/CppTranslationUtils.h"
+#include "llvm/ADT/TypeSwitch.h"
+
+using namespace mlir;
+
+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();
+}
+
+namespace {
+/// Implementation of the dialect interface that converts Builtin ops to Cpp.
+class BuiltinDialectCppTranslationInterface
+    : public CppTranslationDialectInterface {
+public:
+  using CppTranslationDialectInterface::CppTranslationDialectInterface;
+
+  LogicalResult emitOperation(Operation *op, CppEmitter &cppEmitter,
+                              bool trailingSemicolon) const final {
+    LogicalResult status =
+        llvm::TypeSwitch<Operation *, LogicalResult>(op)
+            // Builtin ops.
+            .Case<ModuleOp>(
+                [&](auto op) { return printOperation(cppEmitter, op); })
+            .Default([&](Operation *) {
+              return op->emitOpError("unable to find printer for op: ")
+                     << op->getName();
+            });
+
+    if (failed(status))
+      return failure();
+
+    cppEmitter.ostream() << (trailingSemicolon ? ";\n" : "\n");
+
+    return success();
+  }
+};
+
+} // namespace
+
+void mlir::registerBuiltinDialectCppTranslation(DialectRegistry &registry) {
+  registry.insert<BuiltinDialect>();
+  registry.addExtension(+[](MLIRContext *ctx, BuiltinDialect *dialect) {
+    dialect->addInterfaces<BuiltinDialectCppTranslationInterface>();
+  });
+}
+
+void mlir::registerBuiltinDialectCppTranslation(MLIRContext &context) {
+  DialectRegistry registry;
+  registerBuiltinDialectCppTranslation(registry);
+  context.appendDialectRegistry(registry);
+}
diff --git a/mlir/lib/Target/Cpp/Dialect/Builtin/CMakeLists.txt b/mlir/lib/Target/Cpp/Dialect/Builtin/CMakeLists.txt
new file mode 100644
index 00000000000000..79ff5b85842c92
--- /dev/null
+++ b/mlir/lib/Target/Cpp/Dialect/Builtin/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_mlir_translation_library(MLIRBuiltinToCppTranslation
+  BuiltinToCppTranslation.cpp
+
+  LINK_COMPONENTS
+  Core
+
+  LINK_LIBS PUBLIC
+  MLIRIR
+  MLIRSupport
+  MLIRTargetCpp
+  )
diff --git a/mlir/lib/Target/Cpp/Dialect/CMakeLists.txt b/mlir/lib/Target/Cpp/Dialect/CMakeLists.txt
new file mode 100644
index 00000000000000..d64ee424e6622d
--- /dev/null
+++ b/mlir/lib/Target/Cpp/Dialect/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_subdirectory(Builtin)
+add_subdirectory(ControlFlow)
+add_subdirectory(EmitC)
+add_subdirectory(Func)
diff --git a/mlir/lib/Target/Cpp/Dialect/ControlFlow/CMakeLists.txt b/mlir/lib/Target/Cpp/Dialect/ControlFlow/CMakeLists.txt
new file mode 100644
index 00000000000000..f21a58678951ba
--- /dev/null
+++ b/mlir/lib/Target/Cpp/Dialect/ControlFlow/CMakeLists.txt
@@ -0,0 +1,12 @@
+add_mlir_translation_library(MLIRControlFlowToCppTranslation
+  ControlFlowToCppTranslation.cpp
+
+  LINK_COMPONENTS
+  Core
+
+  LINK_LIBS PUBLIC
+  MLIRIR
+  MLIRControlFlowDialect
+  MLIRSupport
+  MLIRTargetCpp
+  )
diff --git a/mlir/lib/Target/Cpp/Dialect/ControlFlow/ControlFlowToCppTranslation.cpp b/mlir/lib/Target/Cpp/Dialect/ControlFlow/ControlFlowToCppTranslation.cpp
new file mode 100644
index 00000000000000..b8de90a1ae7c31
--- /dev/null
+++ b/mlir/lib/Target/Cpp/Dialect/ControlFlow/ControlFlowToCppTranslation.cpp
@@ -0,0 +1,128 @@
+//===--- ControlFlowToCppTranslation.cpp - Translate CF dialect to Cpp ----===//
+//
+// 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 implements translation between the ControlFlow dialect and Cpp.
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/Target/Cpp/Dialect/ControlFlow/ControlFlowToCppTranslation.h"
+
+#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
+#include "mlir/Target/Cpp/CppTranslationInterface.h"
+#include "mlir/Target/Cpp/CppTranslationUtils.h"
+#include "llvm/ADT/TypeSwitch.h"
+
+using namespace mlir;
+
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    cf::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,
+                                    cf::CondBranchOp condBranchOp) {
+  raw_indented_ostream &os = emitter.ostream();
+  Block &trueSuccessor = *condBranchOp.getTrueDest();
+  Block &falseSuccessor = *condBranchOp.getFalseDest();
+
+  os << "if (" << emitter.getOrCreateName(condBranchOp.getCondition())
+     << ") {\n";
+
+  os.indent();
+
+  // 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.unindent() << "} else {\n";
+  os.indent();
+  // 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.unindent() << "}";
+  return success();
+}
+
+namespace {
+/// Implementation of the dialect interface that converts ControlFlow op to Cpp.
+class ControlFlowDialectCppTranslationInterface
+    : public CppTranslationDialectInterface {
+public:
+  using CppTranslationDialectInterface::CppTranslationDialectInterface;
+
+  LogicalResult emitOperation(Operation *op, CppEmitter &cppEmitter,
+                              bool trailingSemicolon) const final {
+    LogicalResult status =
+        llvm::TypeSwitch<Operation *, LogicalResult>(op)
+            // CF ops.
+            .Case<cf::BranchOp, cf::CondBranchOp>(
+                [&](auto op) { return printOperation(cppEmitter, op); })
+            .Default([&](Operation *) {
+              return op->emitOpError("unable to find printer for op")
+                     << op->getName();
+            });
+
+    if (failed(status))
+      return failure();
+
+    cppEmitter.ostream() << (trailingSemicolon ? ";\n" : "\n");
+
+    return success();
+  }
+};
+} // namespace
+
+void mlir::registerControlFlowDialectCppTranslation(DialectRegistry &registry) {
+  registry.insert<cf::ControlFlowDialect>();
+  registry.addExtension(+[](MLIRContext *ctx, cf::ControlFlowDialect *dialect) {
+    dialect->addInterfaces<ControlFlowDialectCppTranslationInterface>();
+  });
+}
+
+void mlir::registerControlFlowDialectCppTranslation(MLIRContext &context) {
+  DialectRegistry registry;
+  registerControlFlowDialectCppTranslation(registry);
+  context.appendDialectRegistry(registry);
+}
diff --git a/mlir/lib/Target/Cpp/Dialect/EmitC/CMakeLists.txt b/mlir/lib/Target/Cpp/Dialect/EmitC/CMakeLists.txt
new file mode 100644
index 00000000000000..9ee2df0435e5b4
--- /dev/null
+++ b/mlir/lib/Target/Cpp/Dialect/EmitC/CMakeLists.txt
@@ -0,0 +1,12 @@
+add_mlir_translation_library(MLIREmitCToCppTranslation
+  EmitCToCppTranslation.cpp
+
+  LINK_COMPONENTS
+  Core
+
+  LINK_LIBS PUBLIC
+  MLIRIR
+  MLIREmitCDialect
+  MLIRSupport
+  MLIRTargetCpp
+  )
diff --git a/mlir/lib/Target/Cpp/Dialect/EmitC/EmitCToCppTranslation.cpp b/mlir/lib/Target/Cpp/Dialect/EmitC/EmitCToCppTranslation.cpp
new file mode 100644
index 00000000000000..e39cc6c5436e31
--- /dev/null
+++ b/mlir/lib/Target/Cpp/Dialect/EmitC/EmitCToCppTranslation.cpp
@@ -0,0 +1,561 @@
+//===---- EmitCToCppTranslation.cpp - Translate EmitC dialect to Cpp ------===//
+//
+// 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 implements translation between the EmitC dialect and Cpp.
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/Target/Cpp/Dialect/EmitC/EmitCToCppTranslation.h"
+
+#include "mlir/Dialect/EmitC/IR/EmitC.h"
+#include "mlir/Support/IndentedOstream.h"
+#include "mlir/Target/Cpp/CppTranslationInterface.h"
+#include "mlir/Target/Cpp/CppTranslationUtils.h"
+#include "llvm/ADT/TypeSwitch.h"
+
+using namespace mlir;
+using namespace mlir::emitc;
+
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    emitc::ConstantOp constantOp) {
+  Operation *operation = constantOp.getOperation();
+  Attribute value = constantOp.getValue();
+
+  return printConstantOp(emitter, operation, value);
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    emitc::VariableOp variableOp) {
+  Operation *operation = variableOp.getOperation();
+  Attribute value = variableOp.getValue();
+
+  return printConstantOp(emitter, operation, value);
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    emitc::AssignOp assignOp) {
+  auto variableOp = cast<emitc::VariableOp>(assignOp.getVar().getDefiningOp());
+  OpResult result = variableOp->getResult(0);
+
+  if (failed(emitter.emitVariableAssignment(result)))
+    return failure();
+
+  return emitter.emitOperand(assignOp.getValue());
+}
+
+static LogicalResult printOperation(CppEmitter &emitter, emitc::AddOp addOp) {
+  Operation *operation = addOp.getOperation();
+
+  return printBinaryOperation(emitter, operation, "+");
+}
+
+static LogicalResult printOperation(CppEmitter &emitter, emitc::DivOp divOp) {
+  Operation *operation = divOp.getOperation();
+
+  return printBinaryOperation(emitter, operation, "/");
+}
+
+static LogicalResult printOperation(CppEmitter &emitter, emitc::MulOp mulOp) {
+  Operation *operation = mulOp.getOperation();
+
+  return printBinaryOperation(emitter, operation, "*");
+}
+
+static LogicalResult printOperation(CppEmitter &emitter, emitc::RemOp remOp) {
+  Operation *operation = remOp.getOperation();
+
+  return printBinaryOperation(emitter, operation, "%");
+}
+
+static LogicalResult printOperation(CppEmitter &emitter, emitc::SubOp subOp) {
+  Operation *operation = subOp.getOperation();
+
+  return printBinaryOperation(emitter, operation, "-");
+}
+
+static LogicalResult printOperation(CppEmitter &emitter, emitc::CmpOp cmpOp) {
+  Operation *operation = cmpOp.getOperation();
+
+  StringRef binaryOperator;
+
+  switch (cmpOp.getPredicate()) {
+  case emitc::CmpPredicate::eq:
+    binaryOperator = "==";
+    break;
+  case emitc::CmpPredicate::ne:
+    binaryOperator = "!=";
+    break;
+  case emitc::CmpPredicate::lt:
+    binaryOperator = "<";
+    break;
+  case emitc::CmpPredicate::le:
+    binaryOperator = "<=";
+    break;
+  case emitc::CmpPredicate::gt:
+    binaryOperator = ">";
+    break;
+  case emitc::CmpPredicate::ge:
+    binaryOperator = ">=";
+    break;
+  case emitc::CmpPredicate::three_way:
+    binaryOperator = "<=>";
+    break;
+  }
+
+  return printBinaryOperation(emitter, operation, binaryOperator);
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    emitc::ConditionalOp conditionalOp) {
+  raw_ostream &os = emitter.ostream();
+
+  if (failed(emitter.emitAssignPrefix(*conditionalOp)))
+    return failure();
+
+  if (failed(emitter.emitOperand(conditionalOp.getCondition())))
+    return failure();
+
+  os << " ? ";
+
+  if (failed(emitter.emitOperand(conditionalOp.getTrueValue())))
+    return failure();
+
+  os << " : ";
+
+  if (failed(emitter.emitOperand(conditionalOp.getFalseValue())))
+    return failure();
+
+  return success();
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    emitc::VerbatimOp verbatimOp) {
+  raw_ostream &os = emitter.ostream();
+
+  os << verbatimOp.getValue();
+
+  return success();
+}
+
+static LogicalResult printOperation(CppEmitter &emitter, emitc::CallOp callOp) {
+  Operation *operation = callOp.getOperation();
+  StringRef callee = callOp.getCallee();
+
+  return printCallOperation(emitter, operation, callee);
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    emitc::CallOpaqueOp callOpaqueOp) {
+  raw_ostream &os = emitter.ostream();
+  Operation &op = *callOpaqueOp.getOperation();
+
+  if (failed(emitter.emitAssignPrefix(op)))
+    return failure();
+  os << callOpaqueOp.getCallee();
+
+  auto emitArgs = [&](Attribute attr) -> LogicalResult {
+    if (auto t = dyn_cast<IntegerAttr>(attr)) {
+      // Index attributes are treated specially as operand index.
+      if (t.getType().isIndex()) {
+        int64_t idx = t.getInt();
+        Value operand = op.getOperand(idx);
+        auto literalDef =
+            dyn_cast_if_present<emitc::LiteralOp>(operand.getDefiningOp());
+        if (!literalDef && !emitter.hasValueInScope(operand))
+          return op.emitOpError("operand ")
+                 << idx << "'s value not defined in scope";
+        os << emitter.getOrCreateName(operand);
+        return success();
+      }
+    }
+    if (failed(emitter.emitAttribute(op.getLoc(), attr)))
+      return failure();
+
+    return success();
+  };
+
+  if (callOpaqueOp.getTemplateArgs()) {
+    os << "<";
+    if (failed(interleaveCommaWithError(*callOpaqueOp.getTemplateArgs(), os,
+                                        emitArgs)))
+      return failure();
+    os << ">";
+  }
+
+  os << "(";
+
+  LogicalResult emittedArgs =
+      callOpaqueOp.getArgs()
+          ? interleaveCommaWithError(*callOpaqueOp.getArgs(), 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.getApplicableOperator();
+  os << emitter.getOrCreateName(applyOp.getOperand());
+
+  return success();
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    emitc::BitwiseAndOp bitwiseAndOp) {
+  Operation *operation = bitwiseAndOp.getOperation();
+  return printBinaryOperation(emitter, operation, "&");
+}
+
+static LogicalResult
+printOperation(CppEmitter &emitter,
+               emitc::BitwiseLeftShiftOp bitwiseLeftShiftOp) {
+  Operation *operation = bitwiseLeftShiftOp.getOperation();
+  return printBinaryOperation(emitter, operation, "<<");
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    emitc::BitwiseNotOp bitwiseNotOp) {
+  Operation *operation = bitwiseNotOp.getOperation();
+  return printUnaryOperation(emitter, operation, "~");
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    emitc::BitwiseOrOp bitwiseOrOp) {
+  Operation *operation = bitwiseOrOp.getOperation();
+  return printBinaryOperation(emitter, operation, "|");
+}
+
+static LogicalResult
+printOperation(CppEmitter &emitter,
+               emitc::BitwiseRightShiftOp bitwiseRightShiftOp) {
+  Operation *operation = bitwiseRightShiftOp.getOperation();
+  return printBinaryOperation(emitter, operation, ">>");
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    emitc::BitwiseXorOp bitwiseXorOp) {
+  Operation *operation = bitwiseXorOp.getOperation();
+  return printBinaryOperation(emitter, operation, "^");
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    emitc::UnaryPlusOp unaryPlusOp) {
+  Operation *operation = unaryPlusOp.getOperation();
+  return printUnaryOperation(emitter, operation, "+");
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    emitc::UnaryMinusOp unaryMinusOp) {
+  Operation *operation = unaryMinusOp.getOperation();
+  return printUnaryOperation(emitter, operation, "-");
+}
+
+static LogicalResult printOperation(CppEmitter &emitter, emitc::CastOp castOp) {
+  raw_ostream &os = emitter.ostream();
+  Operation &op = *castOp.getOperation();
+
+  if (failed(emitter.emitAssignPrefix(op)))
+    return failure();
+  os << "(";
+  if (failed(emitter.emitType(op.getLoc(), op.getResult(0).getType())))
+    return failure();
+  os << ") ";
+  return emitter.emitOperand(castOp.getOperand());
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    emitc::ExpressionOp expressionOp) {
+  if (shouldBeInlined(expressionOp))
+    return success();
+
+  Operation &op = *expressionOp.getOperation();
+
+  if (failed(emitter.emitAssignPrefix(op)))
+    return failure();
+
+  return emitter.emitExpression(expressionOp);
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    emitc::IncludeOp includeOp) {
+  raw_ostream &os = emitter.ostream();
+
+  os << "#include ";
+  if (includeOp.getIsStandardInclude())
+    os << "<" << includeOp.getInclude() << ">";
+  else
+    os << "\"" << includeOp.getInclude() << "\"";
+
+  return success();
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    emitc::LogicalAndOp logicalAndOp) {
+  Operation *operation = logicalAndOp.getOperation();
+  return printBinaryOperation(emitter, operation, "&&");
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    emitc::LogicalNotOp logicalNotOp) {
+  Operation *operation = logicalNotOp.getOperation();
+  return printUnaryOperation(emitter, operation, "!");
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    emitc::LogicalOrOp logicalOrOp) {
+  Operation *operation = logicalOrOp.getOperation();
+  return printBinaryOperation(emitter, operation, "||");
+}
+
+static LogicalResult printOperation(CppEmitter &emitter, emitc::ForOp forOp) {
+
+  raw_indented_ostream &os = emitter.ostream();
+
+  // Utility function to determine whether a value is an expression that will be
+  // inlined, and as such should be wrapped in parentheses in order to guarantee
+  // its precedence and associativity.
+  auto requiresParentheses = [&](Value value) {
+    auto expressionOp =
+        dyn_cast_if_present<ExpressionOp>(value.getDefiningOp());
+    if (!expressionOp)
+      return false;
+    return shouldBeInlined(expressionOp);
+  };
+
+  os << "for (";
+  if (failed(
+          emitter.emitType(forOp.getLoc(), forOp.getInductionVar().getType())))
+    return failure();
+  os << " ";
+  os << emitter.getOrCreateName(forOp.getInductionVar());
+  os << " = ";
+  if (failed(emitter.emitOperand(forOp.getLowerBound())))
+    return failure();
+  os << "; ";
+  os << emitter.getOrCreateName(forOp.getInductionVar());
+  os << " < ";
+  Value upperBound = forOp.getUpperBound();
+  bool upperBoundRequiresParentheses = requiresParentheses(upperBound);
+  if (upperBoundRequiresParentheses)
+    os << "(";
+  if (failed(emitter.emitOperand(upperBound)))
+    return failure();
+  if (upperBoundRequiresParentheses)
+    os << ")";
+  os << "; ";
+  os << emitter.getOrCreateName(forOp.getInductionVar());
+  os << " += ";
+  if (failed(emitter.emitOperand(forOp.getStep())))
+    return failure();
+  os << ") {\n";
+  os.indent();
+
+  Region &forRegion = forOp.getRegion();
+  auto regionOps = forRegion.getOps();
+
+  // We skip the trailing yield op.
+  for (auto it = regionOps.begin(); std::next(it) != regionOps.end(); ++it) {
+    if (failed(emitter.emitOperation(*it, /*trailingSemicolon=*/true)))
+      return failure();
+  }
+
+  os.unindent() << "}";
+
+  return success();
+}
+
+static LogicalResult printOperation(CppEmitter &emitter, emitc::IfOp ifOp) {
+  raw_indented_ostream &os = emitter.ostream();
+
+  // Helper function to emit all ops except the last one, expected to be
+  // emitc::yield.
+  auto emitAllExceptLast = [&emitter](Region &region) {
+    Region::OpIterator it = region.op_begin(), end = region.op_end();
+    for (; std::next(it) != end; ++it) {
+      if (failed(emitter.emitOperation(*it, /*trailingSemicolon=*/true)))
+        return failure();
+    }
+    assert(isa<emitc::YieldOp>(*it) &&
+           "Expected last operation in the region to be emitc::yield");
+    return success();
+  };
+
+  os << "if (";
+  if (failed(emitter.emitOperand(ifOp.getCondition())))
+    return failure();
+  os << ") {\n";
+  os.indent();
+  if (failed(emitAllExceptLast(ifOp.getThenRegion())))
+    return failure();
+  os.unindent() << "}";
+
+  Region &elseRegion = ifOp.getElseRegion();
+  if (!elseRegion.empty()) {
+    os << " else {\n";
+    os.indent();
+    if (failed(emitAllExceptLast(elseRegion)))
+      return failure();
+    os.unindent() << "}";
+  }
+
+  return success();
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    emitc::ReturnOp returnOp) {
+  raw_ostream &os = emitter.ostream();
+  os << "return";
+  if (returnOp.getNumOperands() == 0)
+    return success();
+
+  os << " ";
+  if (failed(emitter.emitOperand(returnOp.getOperand())))
+    return failure();
+  return success();
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    emitc::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_indented_ostream &os = emitter.ostream();
+  if (functionOp.getSpecifiers()) {
+    for (Attribute specifier : functionOp.getSpecifiersAttr()) {
+      os << cast<StringAttr>(specifier).str() << " ";
+    }
+  }
+
+  if (failed(emitter.emitTypes(functionOp.getLoc(),
+                               functionOp.getFunctionType().getResults())))
+    return failure();
+  os << " " << functionOp.getName();
+
+  os << "(";
+  Operation *operation = functionOp.getOperation();
+  if (functionOp.isExternal()) {
+    if (failed(printFunctionArgs(emitter, operation,
+                                 functionOp.getArgumentTypes())))
+      return failure();
+    os << ");";
+    return success();
+  }
+  if (failed(printFunctionArgs(emitter, operation, functionOp.getArguments())))
+    return failure();
+  os << ") {\n";
+  if (failed(printFunctionBody(emitter, operation, functionOp.getBlocks())))
+    return failure();
+  os << "}\n";
+
+  return success();
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    emitc::DeclareFuncOp declareFuncOp) {
+  CppEmitter::Scope scope(emitter);
+  raw_indented_ostream &os = emitter.ostream();
+
+  auto functionOp = SymbolTable::lookupNearestSymbolFrom<emitc::FuncOp>(
+      declareFuncOp, declareFuncOp.getSymNameAttr());
+
+  if (!functionOp)
+    return failure();
+
+  if (functionOp.getSpecifiers()) {
+    for (Attribute specifier : functionOp.getSpecifiersAttr()) {
+      os << cast<StringAttr>(specifier).str() << " ";
+    }
+  }
+
+  if (failed(emitter.emitTypes(functionOp.getLoc(),
+                               functionOp.getFunctionType().getResults())))
+    return failure();
+  os << " " << functionOp.getName();
+
+  os << "(";
+  Operation *operation = functionOp.getOperation();
+  if (failed(printFunctionArgs(emitter, operation, functionOp.getArguments())))
+    return failure();
+  os << ");";
+
+  return success();
+}
+
+namespace {
+/// Implementation of the dialect interface that converts EmitC ops to Cpp.
+class EmitCDialectCppTranslationInterface
+    : public CppTranslationDialectInterface {
+public:
+  using CppTranslationDialectInterface::CppTranslationDialectInterface;
+
+  LogicalResult emitOperation(Operation *op, CppEmitter &cppEmitter,
+                              bool trailingSemicolon) const final {
+    LogicalResult status =
+        llvm::TypeSwitch<Operation *, LogicalResult>(op)
+            // EmitC ops.
+            .Case<emitc::AddOp, emitc::ApplyOp, emitc::AssignOp,
+                  emitc::BitwiseAndOp, emitc::BitwiseLeftShiftOp,
+                  emitc::BitwiseNotOp, emitc::BitwiseOrOp,
+                  emitc::BitwiseRightShiftOp, emitc::BitwiseXorOp,
+                  emitc::CallOp, emitc::CallOpaqueOp, emitc::CastOp,
+                  emitc::CmpOp, emitc::ConditionalOp, emitc::ConstantOp,
+                  emitc::DeclareFuncOp, emitc::DivOp, emitc::ExpressionOp,
+                  emitc::ForOp, emitc::FuncOp, emitc::IfOp, emitc::IncludeOp,
+                  emitc::LogicalAndOp, emitc::LogicalNotOp, emitc::LogicalOrOp,
+                  emitc::MulOp, emitc::RemOp, emitc::ReturnOp, emitc::SubOp,
+                  emitc::UnaryMinusOp, emitc::UnaryPlusOp, emitc::VariableOp,
+                  emitc::VerbatimOp>(
+                [&](auto op) { return printOperation(cppEmitter, op); })
+            .Case<emitc::LiteralOp>([&](auto op) { return success(); })
+            .Default([&](Operation *) {
+              return op->emitOpError("unable to find printer for op")
+                     << op->getName();
+            });
+
+    if (failed(status))
+      return failure();
+
+    if (isa<emitc::LiteralOp>(op))
+      return success();
+
+    if (cppEmitter.getEmittedExpression() ||
+        (isa<emitc::ExpressionOp>(op) &&
+         shouldBeInlined(cast<emitc::ExpressionOp>(op))))
+      return success();
+
+    cppEmitter.ostream() << (trailingSemicolon ? ";\n" : "\n");
+
+    return success();
+  }
+};
+} // namespace
+
+void mlir::registerEmitCDialectCppTranslation(DialectRegistry &registry) {
+  registry.insert<emitc::EmitCDialect>();
+  registry.addExtension(+[](MLIRContext *ctx, emitc::EmitCDialect *dialect) {
+    dialect->addInterfaces<EmitCDialectCppTranslationInterface>();
+  });
+}
+
+void mlir::registerEmitCDialectCppTranslation(MLIRContext &context) {
+  DialectRegistry registry;
+  registerEmitCDialectCppTranslation(registry);
+  context.appendDialectRegistry(registry);
+}
diff --git a/mlir/lib/Target/Cpp/Dialect/Func/CMakeLists.txt b/mlir/lib/Target/Cpp/Dialect/Func/CMakeLists.txt
new file mode 100644
index 00000000000000..6f7dbd403b8090
--- /dev/null
+++ b/mlir/lib/Target/Cpp/Dialect/Func/CMakeLists.txt
@@ -0,0 +1,12 @@
+add_mlir_translation_library(MLIRFuncToCppTranslation
+  FuncToCppTranslation.cpp
+
+  LINK_COMPONENTS
+  Core
+
+  LINK_LIBS PUBLIC
+  MLIRIR
+  MLIRFuncDialect
+  MLIRSupport
+  MLIRTargetCpp
+  )
diff --git a/mlir/lib/Target/Cpp/Dialect/Func/FuncToCppTranslation.cpp b/mlir/lib/Target/Cpp/Dialect/Func/FuncToCppTranslation.cpp
new file mode 100644
index 00000000000000..3c289e4b5f0836
--- /dev/null
+++ b/mlir/lib/Target/Cpp/Dialect/Func/FuncToCppTranslation.cpp
@@ -0,0 +1,126 @@
+//===------ FuncToCppTranslation.cpp - Translate Func dialect to Cpp ------===//
+//
+// 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 implements translation between the Func dialect and Cpp.
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/Target/Cpp/Dialect/Func/FuncToCppTranslation.h"
+
+#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
+#include "mlir/Dialect/Func/IR/FuncOps.h"
+#include "mlir/Support/IndentedOstream.h"
+#include "mlir/Target/Cpp/CppTranslationInterface.h"
+#include "mlir/Target/Cpp/CppTranslationUtils.h"
+#include "llvm/ADT/TypeSwitch.h"
+
+using namespace mlir;
+
+static LogicalResult printOperation(CppEmitter &emitter, func::CallOp callOp) {
+  Operation *operation = callOp.getOperation();
+  StringRef callee = callOp.getCallee();
+
+  return printCallOperation(emitter, operation, callee);
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    func::ReturnOp returnOp) {
+  raw_ostream &os = emitter.ostream();
+  os << "return";
+  switch (returnOp.getNumOperands()) {
+  case 0:
+    return success();
+  case 1:
+    os << " ";
+    if (failed(emitter.emitOperand(returnOp.getOperand(0))))
+      return failure();
+    return success();
+  default:
+    os << " std::make_tuple(";
+    if (failed(emitter.emitOperandsAndAttributes(*returnOp.getOperation())))
+      return failure();
+    os << ")";
+    return success();
+  }
+}
+
+static LogicalResult printOperation(CppEmitter &emitter,
+                                    func::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");
+  }
+
+  if (llvm::any_of(functionOp.getResultTypes(),
+                   [](Type type) { return isa<emitc::ArrayType>(type); })) {
+    return functionOp.emitOpError() << "cannot emit array type as result type";
+  }
+
+  CppEmitter::Scope scope(emitter);
+  raw_indented_ostream &os = emitter.ostream();
+  if (failed(emitter.emitTypes(functionOp.getLoc(),
+                               functionOp.getFunctionType().getResults())))
+    return failure();
+  os << " " << functionOp.getName();
+
+  os << "(";
+  Operation *operation = functionOp.getOperation();
+  if (failed(printFunctionArgs(emitter, operation, functionOp.getArguments())))
+    return failure();
+  os << ") {\n";
+  if (failed(printFunctionBody(emitter, operation, functionOp.getBlocks())))
+    return failure();
+  os << "}\n";
+
+  return success();
+}
+
+namespace {
+/// Implementation of the dialect interface that converts Func ops to Cpp.
+class FuncDialectCppTranslationInterface
+    : public CppTranslationDialectInterface {
+public:
+  using CppTranslationDialectInterface::CppTranslationDialectInterface;
+
+  LogicalResult emitOperation(Operation *op, CppEmitter &cppEmitter,
+                              bool trailingSemicolon) const final {
+    LogicalResult status =
+        llvm::TypeSwitch<Operation *, LogicalResult>(op)
+            // Func ops.
+            .Case<func::CallOp, func::FuncOp, func::ReturnOp>(
+                [&](auto op) { return printOperation(cppEmitter, op); })
+            .Default([&](Operation *) {
+              return op->emitOpError("unable to find printer for op")
+                     << op->getName();
+            });
+
+    if (failed(status))
+      return failure();
+
+    cppEmitter.ostream() << (trailingSemicolon ? ";\n" : "\n");
+
+    return success();
+  }
+};
+
+} // namespace
+
+void mlir::registerFuncDialectCppTranslation(DialectRegistry &registry) {
+  registry.insert<func::FuncDialect>();
+  registry.addExtension(+[](MLIRContext *ctx, func::FuncDialect *dialect) {
+    dialect->addInterfaces<FuncDialectCppTranslationInterface>();
+  });
+}
+
+void mlir::registerFuncDialectCppTranslation(MLIRContext &context) {
+  DialectRegistry registry;
+  registerFuncDialectCppTranslation(registry);
+  context.appendDialectRegistry(registry);
+}
diff --git a/mlir/lib/Target/Cpp/TranslateRegistration.cpp b/mlir/lib/Target/Cpp/TranslateRegistration.cpp
index 4104b177d7d9af..e8af37292b0834 100644
--- a/mlir/lib/Target/Cpp/TranslateRegistration.cpp
+++ b/mlir/lib/Target/Cpp/TranslateRegistration.cpp
@@ -6,14 +6,12 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "mlir/Dialect/ControlFlow/IR/ControlFlow.h"
-#include "mlir/Dialect/EmitC/IR/EmitC.h"
-#include "mlir/Dialect/Func/IR/FuncOps.h"
 #include "mlir/Dialect/Math/IR/Math.h"
 #include "mlir/Dialect/SCF/IR/SCF.h"
 #include "mlir/IR/BuiltinOps.h"
 #include "mlir/IR/Dialect.h"
 #include "mlir/Target/Cpp/CppEmitter.h"
+#include "mlir/Target/Cpp/Dialect/All.h"
 #include "mlir/Tools/mlir-translate/Translation.h"
 #include "llvm/Support/CommandLine.h"
 
@@ -34,17 +32,14 @@ void registerToCppTranslation() {
   TranslateFromMLIRRegistration reg(
       "mlir-to-cpp", "translate from mlir to cpp",
       [](Operation *op, raw_ostream &output) {
-        return emitc::translateToCpp(
-            op, output,
-            /*declareVariablesAtTop=*/declareVariablesAtTop);
+        return translateToCpp(op, output,
+                              /*declareVariablesAtTop=*/declareVariablesAtTop);
       },
       [](DialectRegistry &registry) {
         // clang-format off
-        registry.insert<cf::ControlFlowDialect,
-                        emitc::EmitCDialect,
-                        func::FuncDialect,
-                        math::MathDialect,
+        registry.insert<math::MathDialect,
                         scf::SCFDialect>();
+        registerAllToCppTranslations(registry);
         // clang-format on
       });
 }
diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
index 7cbb1e9265e174..e68671f07bc232 100644
--- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp
+++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
@@ -6,17 +6,15 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
+#include "mlir/Target/Cpp/TranslateToCpp.h"
+
 #include "mlir/Dialect/EmitC/IR/EmitC.h"
-#include "mlir/Dialect/Func/IR/FuncOps.h"
-#include "mlir/IR/BuiltinOps.h"
 #include "mlir/IR/BuiltinTypes.h"
 #include "mlir/IR/Dialect.h"
 #include "mlir/IR/Operation.h"
-#include "mlir/IR/SymbolTable.h"
 #include "mlir/Support/IndentedOstream.h"
 #include "mlir/Support/LLVM.h"
-#include "mlir/Target/Cpp/CppEmitter.h"
+#include "mlir/Target/Cpp/CppTranslationUtils.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringMap.h"
@@ -31,41 +29,6 @@ using namespace mlir;
 using namespace mlir::emitc;
 using llvm::formatv;
 
-/// Convenience functions to produce interleaved output with functions returning
-/// a LogicalResult. This is different than those in STLExtras 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 << ", "; });
-}
-
 /// Return the precedence of a operator as an integer, higher values
 /// imply higher precedence.
 static FailureOr<int> getOperatorPrecedence(Operation *operation) {
@@ -109,984 +72,10 @@ static FailureOr<int> getOperatorPrecedence(Operation *operation) {
       .Default([](auto op) { return op->emitError("unsupported operation"); });
 }
 
-namespace {
-/// 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(Location loc, 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(Location loc, 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(Location loc, ArrayRef<Type> types);
-
-  /// Emits array of types as a std::tuple of the emitted types independently of
-  /// the array size.
-  LogicalResult emitTupleType(Location loc, 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 a declaration of a variable with the given type and name.
-  LogicalResult emitVariableDeclaration(Location loc, Type type,
-                                        StringRef name);
-
-  /// 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);
-
-  /// Emits value as an operands of an operation
-  LogicalResult emitOperand(Value value);
-
-  /// Emit an expression as a C expression.
-  LogicalResult emitExpression(ExpressionOp expressionOp);
-
-  /// 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 unsigned integer in C++.
-  bool shouldMapToUnsigned(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_indented_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; };
-
-  /// Get expression currently being emitted.
-  ExpressionOp getEmittedExpression() { return emittedExpression; }
-
-  /// Determine whether given value is part of the expression potentially being
-  /// emitted.
-  bool isPartOfCurrentExpression(Value value) {
-    if (!emittedExpression)
-      return false;
-    Operation *def = value.getDefiningOp();
-    if (!def)
-      return false;
-    auto operandExpression = dyn_cast<ExpressionOp>(def->getParentOp());
-    return operandExpression == emittedExpression;
-  };
-
-private:
-  using ValueMapper = llvm::ScopedHashTable<Value, std::string>;
-  using BlockMapper = llvm::ScopedHashTable<Block *, std::string>;
-
-  /// Output stream to emit to.
-  raw_indented_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;
-
-  /// State of the current expression being emitted.
-  ExpressionOp emittedExpression;
-  SmallVector<int> emittedExpressionPrecedence;
-
-  void pushExpressionPrecedence(int precedence) {
-    emittedExpressionPrecedence.push_back(precedence);
-  }
-  void popExpressionPrecedence() { emittedExpressionPrecedence.pop_back(); }
-  static int lowestPrecedence() { return 0; }
-  int getExpressionPrecedence() {
-    if (emittedExpressionPrecedence.empty())
-      return lowestPrecedence();
-    return emittedExpressionPrecedence.back();
-  }
-};
-} // namespace
-
-/// Determine whether expression \p expressionOp should be emitted inline, i.e.
-/// as part of its user. This function recommends inlining of any expressions
-/// that can be inlined unless it is used by another expression, under the
-/// assumption that  any expression fusion/re-materialization was taken care of
-/// by transformations run by the backend.
-static bool shouldBeInlined(ExpressionOp expressionOp) {
-  // Do not inline if expression is marked as such.
-  if (expressionOp.getDoNotInline())
-    return false;
-
-  // Do not inline expressions with side effects to prevent side-effect
-  // reordering.
-  if (expressionOp.hasSideEffects())
-    return false;
-
-  // Do not inline expressions with multiple uses.
-  Value result = expressionOp.getResult();
-  if (!result.hasOneUse())
-    return false;
-
-  // Do not inline expressions used by other expressions, as any desired
-  // expression folding was taken care of by transformations.
-  Operation *user = *result.getUsers().begin();
-  return !user->getParentOfType<ExpressionOp>();
-}
-
-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.constant has no value.
-    if (auto oAttr = dyn_cast<emitc::OpaqueAttr>(value)) {
-      if (oAttr.getValue().empty())
-        return success();
-    }
-
-    if (failed(emitter.emitVariableAssignment(result)))
-      return failure();
-    return emitter.emitAttribute(operation->getLoc(), value);
-  }
-
-  // Emit a variable declaration for an emitc.constant op without value.
-  if (auto oAttr = dyn_cast<emitc::OpaqueAttr>(value)) {
-    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->getLoc(), value);
-}
-
-static LogicalResult printOperation(CppEmitter &emitter,
-                                    emitc::ConstantOp constantOp) {
-  Operation *operation = constantOp.getOperation();
-  Attribute value = constantOp.getValue();
-
-  return printConstantOp(emitter, operation, value);
-}
-
-static LogicalResult printOperation(CppEmitter &emitter,
-                                    emitc::VariableOp variableOp) {
-  Operation *operation = variableOp.getOperation();
-  Attribute value = variableOp.getValue();
-
-  return printConstantOp(emitter, operation, value);
-}
-
-static LogicalResult printOperation(CppEmitter &emitter,
-                                    emitc::AssignOp assignOp) {
-  auto variableOp = cast<emitc::VariableOp>(assignOp.getVar().getDefiningOp());
-  OpResult result = variableOp->getResult(0);
-
-  if (failed(emitter.emitVariableAssignment(result)))
-    return failure();
-
-  return emitter.emitOperand(assignOp.getValue());
-}
-
-static LogicalResult printBinaryOperation(CppEmitter &emitter,
-                                          Operation *operation,
-                                          StringRef binaryOperator) {
-  raw_ostream &os = emitter.ostream();
-
-  if (failed(emitter.emitAssignPrefix(*operation)))
-    return failure();
-
-  if (failed(emitter.emitOperand(operation->getOperand(0))))
-    return failure();
-
-  os << " " << binaryOperator << " ";
-
-  if (failed(emitter.emitOperand(operation->getOperand(1))))
-    return failure();
-
-  return success();
-}
-
-static LogicalResult printUnaryOperation(CppEmitter &emitter,
-                                         Operation *operation,
-                                         StringRef unaryOperator) {
-  raw_ostream &os = emitter.ostream();
-
-  if (failed(emitter.emitAssignPrefix(*operation)))
-    return failure();
-
-  os << unaryOperator;
-
-  if (failed(emitter.emitOperand(operation->getOperand(0))))
-    return failure();
-
-  return success();
-}
-
-static LogicalResult printOperation(CppEmitter &emitter, emitc::AddOp addOp) {
-  Operation *operation = addOp.getOperation();
-
-  return printBinaryOperation(emitter, operation, "+");
-}
-
-static LogicalResult printOperation(CppEmitter &emitter, emitc::DivOp divOp) {
-  Operation *operation = divOp.getOperation();
-
-  return printBinaryOperation(emitter, operation, "/");
-}
-
-static LogicalResult printOperation(CppEmitter &emitter, emitc::MulOp mulOp) {
-  Operation *operation = mulOp.getOperation();
-
-  return printBinaryOperation(emitter, operation, "*");
-}
-
-static LogicalResult printOperation(CppEmitter &emitter, emitc::RemOp remOp) {
-  Operation *operation = remOp.getOperation();
-
-  return printBinaryOperation(emitter, operation, "%");
-}
-
-static LogicalResult printOperation(CppEmitter &emitter, emitc::SubOp subOp) {
-  Operation *operation = subOp.getOperation();
-
-  return printBinaryOperation(emitter, operation, "-");
-}
-
-static LogicalResult printOperation(CppEmitter &emitter, emitc::CmpOp cmpOp) {
-  Operation *operation = cmpOp.getOperation();
-
-  StringRef binaryOperator;
-
-  switch (cmpOp.getPredicate()) {
-  case emitc::CmpPredicate::eq:
-    binaryOperator = "==";
-    break;
-  case emitc::CmpPredicate::ne:
-    binaryOperator = "!=";
-    break;
-  case emitc::CmpPredicate::lt:
-    binaryOperator = "<";
-    break;
-  case emitc::CmpPredicate::le:
-    binaryOperator = "<=";
-    break;
-  case emitc::CmpPredicate::gt:
-    binaryOperator = ">";
-    break;
-  case emitc::CmpPredicate::ge:
-    binaryOperator = ">=";
-    break;
-  case emitc::CmpPredicate::three_way:
-    binaryOperator = "<=>";
-    break;
-  }
-
-  return printBinaryOperation(emitter, operation, binaryOperator);
-}
-
-static LogicalResult printOperation(CppEmitter &emitter,
-                                    emitc::ConditionalOp conditionalOp) {
-  raw_ostream &os = emitter.ostream();
-
-  if (failed(emitter.emitAssignPrefix(*conditionalOp)))
-    return failure();
-
-  if (failed(emitter.emitOperand(conditionalOp.getCondition())))
-    return failure();
-
-  os << " ? ";
-
-  if (failed(emitter.emitOperand(conditionalOp.getTrueValue())))
-    return failure();
-
-  os << " : ";
-
-  if (failed(emitter.emitOperand(conditionalOp.getFalseValue())))
-    return failure();
-
-  return success();
-}
-
-static LogicalResult printOperation(CppEmitter &emitter,
-                                    emitc::VerbatimOp verbatimOp) {
-  raw_ostream &os = emitter.ostream();
-
-  os << verbatimOp.getValue();
-
-  return success();
-}
-
-static LogicalResult printOperation(CppEmitter &emitter,
-                                    cf::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,
-                                    cf::CondBranchOp condBranchOp) {
-  raw_indented_ostream &os = emitter.ostream();
-  Block &trueSuccessor = *condBranchOp.getTrueDest();
-  Block &falseSuccessor = *condBranchOp.getFalseDest();
-
-  os << "if (" << emitter.getOrCreateName(condBranchOp.getCondition())
-     << ") {\n";
-
-  os.indent();
-
-  // 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.unindent() << "} else {\n";
-  os.indent();
-  // 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.unindent() << "}";
-  return success();
-}
-
-static LogicalResult printCallOperation(CppEmitter &emitter, Operation *callOp,
-                                        StringRef callee) {
-  if (failed(emitter.emitAssignPrefix(*callOp)))
-    return failure();
-
-  raw_ostream &os = emitter.ostream();
-  os << callee << "(";
-  if (failed(emitter.emitOperands(*callOp)))
-    return failure();
-  os << ")";
-  return success();
-}
-
-static LogicalResult printOperation(CppEmitter &emitter, func::CallOp callOp) {
-  Operation *operation = callOp.getOperation();
-  StringRef callee = callOp.getCallee();
-
-  return printCallOperation(emitter, operation, callee);
-}
-
-static LogicalResult printOperation(CppEmitter &emitter, emitc::CallOp callOp) {
-  Operation *operation = callOp.getOperation();
-  StringRef callee = callOp.getCallee();
-
-  return printCallOperation(emitter, operation, callee);
-}
-
-static LogicalResult printOperation(CppEmitter &emitter,
-                                    emitc::CallOpaqueOp callOpaqueOp) {
-  raw_ostream &os = emitter.ostream();
-  Operation &op = *callOpaqueOp.getOperation();
-
-  if (failed(emitter.emitAssignPrefix(op)))
-    return failure();
-  os << callOpaqueOp.getCallee();
-
-  auto emitArgs = [&](Attribute attr) -> LogicalResult {
-    if (auto t = dyn_cast<IntegerAttr>(attr)) {
-      // Index attributes are treated specially as operand index.
-      if (t.getType().isIndex()) {
-        int64_t idx = t.getInt();
-        Value operand = op.getOperand(idx);
-        auto literalDef =
-            dyn_cast_if_present<LiteralOp>(operand.getDefiningOp());
-        if (!literalDef && !emitter.hasValueInScope(operand))
-          return op.emitOpError("operand ")
-                 << idx << "'s value not defined in scope";
-        os << emitter.getOrCreateName(operand);
-        return success();
-      }
-    }
-    if (failed(emitter.emitAttribute(op.getLoc(), attr)))
-      return failure();
-
-    return success();
-  };
-
-  if (callOpaqueOp.getTemplateArgs()) {
-    os << "<";
-    if (failed(interleaveCommaWithError(*callOpaqueOp.getTemplateArgs(), os,
-                                        emitArgs)))
-      return failure();
-    os << ">";
-  }
-
-  os << "(";
-
-  LogicalResult emittedArgs =
-      callOpaqueOp.getArgs()
-          ? interleaveCommaWithError(*callOpaqueOp.getArgs(), 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.getApplicableOperator();
-  os << emitter.getOrCreateName(applyOp.getOperand());
-
-  return success();
-}
-
-static LogicalResult printOperation(CppEmitter &emitter,
-                                    emitc::BitwiseAndOp bitwiseAndOp) {
-  Operation *operation = bitwiseAndOp.getOperation();
-  return printBinaryOperation(emitter, operation, "&");
-}
-
-static LogicalResult
-printOperation(CppEmitter &emitter,
-               emitc::BitwiseLeftShiftOp bitwiseLeftShiftOp) {
-  Operation *operation = bitwiseLeftShiftOp.getOperation();
-  return printBinaryOperation(emitter, operation, "<<");
-}
-
-static LogicalResult printOperation(CppEmitter &emitter,
-                                    emitc::BitwiseNotOp bitwiseNotOp) {
-  Operation *operation = bitwiseNotOp.getOperation();
-  return printUnaryOperation(emitter, operation, "~");
-}
-
-static LogicalResult printOperation(CppEmitter &emitter,
-                                    emitc::BitwiseOrOp bitwiseOrOp) {
-  Operation *operation = bitwiseOrOp.getOperation();
-  return printBinaryOperation(emitter, operation, "|");
-}
-
-static LogicalResult
-printOperation(CppEmitter &emitter,
-               emitc::BitwiseRightShiftOp bitwiseRightShiftOp) {
-  Operation *operation = bitwiseRightShiftOp.getOperation();
-  return printBinaryOperation(emitter, operation, ">>");
-}
-
-static LogicalResult printOperation(CppEmitter &emitter,
-                                    emitc::BitwiseXorOp bitwiseXorOp) {
-  Operation *operation = bitwiseXorOp.getOperation();
-  return printBinaryOperation(emitter, operation, "^");
-}
-
-static LogicalResult printOperation(CppEmitter &emitter,
-                                    emitc::UnaryPlusOp unaryPlusOp) {
-  Operation *operation = unaryPlusOp.getOperation();
-  return printUnaryOperation(emitter, operation, "+");
-}
-
-static LogicalResult printOperation(CppEmitter &emitter,
-                                    emitc::UnaryMinusOp unaryMinusOp) {
-  Operation *operation = unaryMinusOp.getOperation();
-  return printUnaryOperation(emitter, operation, "-");
-}
-
-static LogicalResult printOperation(CppEmitter &emitter, emitc::CastOp castOp) {
-  raw_ostream &os = emitter.ostream();
-  Operation &op = *castOp.getOperation();
-
-  if (failed(emitter.emitAssignPrefix(op)))
-    return failure();
-  os << "(";
-  if (failed(emitter.emitType(op.getLoc(), op.getResult(0).getType())))
-    return failure();
-  os << ") ";
-  return emitter.emitOperand(castOp.getOperand());
-}
-
-static LogicalResult printOperation(CppEmitter &emitter,
-                                    emitc::ExpressionOp expressionOp) {
-  if (shouldBeInlined(expressionOp))
-    return success();
-
-  Operation &op = *expressionOp.getOperation();
-
-  if (failed(emitter.emitAssignPrefix(op)))
-    return failure();
-
-  return emitter.emitExpression(expressionOp);
-}
-
-static LogicalResult printOperation(CppEmitter &emitter,
-                                    emitc::IncludeOp includeOp) {
-  raw_ostream &os = emitter.ostream();
-
-  os << "#include ";
-  if (includeOp.getIsStandardInclude())
-    os << "<" << includeOp.getInclude() << ">";
-  else
-    os << "\"" << includeOp.getInclude() << "\"";
-
-  return success();
-}
-
-static LogicalResult printOperation(CppEmitter &emitter,
-                                    emitc::LogicalAndOp logicalAndOp) {
-  Operation *operation = logicalAndOp.getOperation();
-  return printBinaryOperation(emitter, operation, "&&");
-}
-
-static LogicalResult printOperation(CppEmitter &emitter,
-                                    emitc::LogicalNotOp logicalNotOp) {
-  Operation *operation = logicalNotOp.getOperation();
-  return printUnaryOperation(emitter, operation, "!");
-}
-
-static LogicalResult printOperation(CppEmitter &emitter,
-                                    emitc::LogicalOrOp logicalOrOp) {
-  Operation *operation = logicalOrOp.getOperation();
-  return printBinaryOperation(emitter, operation, "||");
-}
-
-static LogicalResult printOperation(CppEmitter &emitter, emitc::ForOp forOp) {
-
-  raw_indented_ostream &os = emitter.ostream();
-
-  // Utility function to determine whether a value is an expression that will be
-  // inlined, and as such should be wrapped in parentheses in order to guarantee
-  // its precedence and associativity.
-  auto requiresParentheses = [&](Value value) {
-    auto expressionOp =
-        dyn_cast_if_present<ExpressionOp>(value.getDefiningOp());
-    if (!expressionOp)
-      return false;
-    return shouldBeInlined(expressionOp);
-  };
-
-  os << "for (";
-  if (failed(
-          emitter.emitType(forOp.getLoc(), forOp.getInductionVar().getType())))
-    return failure();
-  os << " ";
-  os << emitter.getOrCreateName(forOp.getInductionVar());
-  os << " = ";
-  if (failed(emitter.emitOperand(forOp.getLowerBound())))
-    return failure();
-  os << "; ";
-  os << emitter.getOrCreateName(forOp.getInductionVar());
-  os << " < ";
-  Value upperBound = forOp.getUpperBound();
-  bool upperBoundRequiresParentheses = requiresParentheses(upperBound);
-  if (upperBoundRequiresParentheses)
-    os << "(";
-  if (failed(emitter.emitOperand(upperBound)))
-    return failure();
-  if (upperBoundRequiresParentheses)
-    os << ")";
-  os << "; ";
-  os << emitter.getOrCreateName(forOp.getInductionVar());
-  os << " += ";
-  if (failed(emitter.emitOperand(forOp.getStep())))
-    return failure();
-  os << ") {\n";
-  os.indent();
-
-  Region &forRegion = forOp.getRegion();
-  auto regionOps = forRegion.getOps();
-
-  // We skip the trailing yield op.
-  for (auto it = regionOps.begin(); std::next(it) != regionOps.end(); ++it) {
-    if (failed(emitter.emitOperation(*it, /*trailingSemicolon=*/true)))
-      return failure();
-  }
-
-  os.unindent() << "}";
-
-  return success();
-}
-
-static LogicalResult printOperation(CppEmitter &emitter, emitc::IfOp ifOp) {
-  raw_indented_ostream &os = emitter.ostream();
-
-  // Helper function to emit all ops except the last one, expected to be
-  // emitc::yield.
-  auto emitAllExceptLast = [&emitter](Region &region) {
-    Region::OpIterator it = region.op_begin(), end = region.op_end();
-    for (; std::next(it) != end; ++it) {
-      if (failed(emitter.emitOperation(*it, /*trailingSemicolon=*/true)))
-        return failure();
-    }
-    assert(isa<emitc::YieldOp>(*it) &&
-           "Expected last operation in the region to be emitc::yield");
-    return success();
-  };
-
-  os << "if (";
-  if (failed(emitter.emitOperand(ifOp.getCondition())))
-    return failure();
-  os << ") {\n";
-  os.indent();
-  if (failed(emitAllExceptLast(ifOp.getThenRegion())))
-    return failure();
-  os.unindent() << "}";
-
-  Region &elseRegion = ifOp.getElseRegion();
-  if (!elseRegion.empty()) {
-    os << " else {\n";
-    os.indent();
-    if (failed(emitAllExceptLast(elseRegion)))
-      return failure();
-    os.unindent() << "}";
-  }
-
-  return success();
-}
-
-static LogicalResult printOperation(CppEmitter &emitter,
-                                    func::ReturnOp returnOp) {
-  raw_ostream &os = emitter.ostream();
-  os << "return";
-  switch (returnOp.getNumOperands()) {
-  case 0:
-    return success();
-  case 1:
-    os << " ";
-    if (failed(emitter.emitOperand(returnOp.getOperand(0))))
-      return failure();
-    return success();
-  default:
-    os << " std::make_tuple(";
-    if (failed(emitter.emitOperandsAndAttributes(*returnOp.getOperation())))
-      return failure();
-    os << ")";
-    return success();
-  }
-}
-
-static LogicalResult printOperation(CppEmitter &emitter,
-                                    emitc::ReturnOp returnOp) {
-  raw_ostream &os = emitter.ostream();
-  os << "return";
-  if (returnOp.getNumOperands() == 0)
-    return success();
-
-  os << " ";
-  if (failed(emitter.emitOperand(returnOp.getOperand())))
-    return failure();
-  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 printFunctionArgs(CppEmitter &emitter,
-                                       Operation *functionOp,
-                                       ArrayRef<Type> arguments) {
-  raw_indented_ostream &os = emitter.ostream();
-
-  return (
-      interleaveCommaWithError(arguments, os, [&](Type arg) -> LogicalResult {
-        return emitter.emitType(functionOp->getLoc(), arg);
-      }));
-}
-
-static LogicalResult printFunctionArgs(CppEmitter &emitter,
-                                       Operation *functionOp,
-                                       Region::BlockArgListType arguments) {
-  raw_indented_ostream &os = emitter.ostream();
-
-  return (interleaveCommaWithError(
-      arguments, os, [&](BlockArgument arg) -> LogicalResult {
-        return emitter.emitVariableDeclaration(
-            functionOp->getLoc(), arg.getType(), emitter.getOrCreateName(arg));
-      }));
-}
-
-static LogicalResult printFunctionBody(CppEmitter &emitter,
-                                       Operation *functionOp,
-                                       Region::BlockListType &blocks) {
-  raw_indented_ostream &os = emitter.ostream();
-  os.indent();
-
-  if (emitter.shouldDeclareVariablesAtTop()) {
-    // Declare all variables that hold op results including those from nested
-    // regions.
-    WalkResult result =
-        functionOp->walk<WalkOrder::PreOrder>([&](Operation *op) -> WalkResult {
-          if (isa<emitc::LiteralOp>(op) ||
-              isa<emitc::ExpressionOp>(op->getParentOp()) ||
-              (isa<emitc::ExpressionOp>(op) &&
-               shouldBeInlined(cast<emitc::ExpressionOp>(op))))
-            return WalkResult::skip();
-          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();
-  }
-
-  // Create label names for basic blocks.
-  for (Block &block : blocks) {
-    emitter.getOrCreateName(block);
-  }
-
-  // Declare variables for basic block arguments.
-  for (Block &block : llvm::drop_begin(blocks)) {
-    for (BlockArgument &arg : block.getArguments()) {
-      if (emitter.hasValueInScope(arg))
-        return functionOp->emitOpError(" block argument #")
-               << arg.getArgNumber() << " is out of scope";
-      if (isa<ArrayType>(arg.getType()))
-        return functionOp->emitOpError("cannot emit block argument #")
-               << arg.getArgNumber() << " with array type";
-      if (failed(
-              emitter.emitType(block.getParentOp()->getLoc(), arg.getType()))) {
-        return failure();
-      }
-      os << " " << emitter.getOrCreateName(arg) << ";\n";
-    }
-  }
-
-  for (Block &block : blocks) {
-    // Only print a label if the block has predecessors.
-    if (!block.hasNoPredecessors()) {
-      if (failed(emitter.emitLabel(block)))
-        return failure();
-    }
-    for (Operation &op : block.getOperations()) {
-      // When generating code for an emitc.if or cf.cond_br op no semicolon
-      // needs to be printed after the closing brace.
-      // When generating code for an emitc.for and emitc.verbatim op, printing a
-      // trailing semicolon is handled within the printOperation function.
-      bool trailingSemicolon =
-          !isa<cf::CondBranchOp, emitc::DeclareFuncOp, emitc::ForOp,
-               emitc::IfOp, emitc::LiteralOp, emitc::VerbatimOp>(op);
-
-      if (failed(emitter.emitOperation(
-              op, /*trailingSemicolon=*/trailingSemicolon)))
-        return failure();
-    }
-  }
-
-  os.unindent();
-
-  return success();
-}
-
-static LogicalResult printOperation(CppEmitter &emitter,
-                                    func::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");
-  }
-
-  if (llvm::any_of(functionOp.getResultTypes(),
-                   [](Type type) { return isa<ArrayType>(type); })) {
-    return functionOp.emitOpError() << "cannot emit array type as result type";
-  }
-
-  CppEmitter::Scope scope(emitter);
-  raw_indented_ostream &os = emitter.ostream();
-  if (failed(emitter.emitTypes(functionOp.getLoc(),
-                               functionOp.getFunctionType().getResults())))
-    return failure();
-  os << " " << functionOp.getName();
-
-  os << "(";
-  Operation *operation = functionOp.getOperation();
-  if (failed(printFunctionArgs(emitter, operation, functionOp.getArguments())))
-    return failure();
-  os << ") {\n";
-  if (failed(printFunctionBody(emitter, operation, functionOp.getBlocks())))
-    return failure();
-  os << "}\n";
-
-  return success();
-}
-
-static LogicalResult printOperation(CppEmitter &emitter,
-                                    emitc::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_indented_ostream &os = emitter.ostream();
-  if (functionOp.getSpecifiers()) {
-    for (Attribute specifier : functionOp.getSpecifiersAttr()) {
-      os << cast<StringAttr>(specifier).str() << " ";
-    }
-  }
-
-  if (failed(emitter.emitTypes(functionOp.getLoc(),
-                               functionOp.getFunctionType().getResults())))
-    return failure();
-  os << " " << functionOp.getName();
-
-  os << "(";
-  Operation *operation = functionOp.getOperation();
-  if (functionOp.isExternal()) {
-    if (failed(printFunctionArgs(emitter, operation,
-                                 functionOp.getArgumentTypes())))
-      return failure();
-    os << ");";
-    return success();
-  }
-  if (failed(printFunctionArgs(emitter, operation, functionOp.getArguments())))
-    return failure();
-  os << ") {\n";
-  if (failed(printFunctionBody(emitter, operation, functionOp.getBlocks())))
-    return failure();
-  os << "}\n";
-
-  return success();
-}
-
-static LogicalResult printOperation(CppEmitter &emitter,
-                                    DeclareFuncOp declareFuncOp) {
-  CppEmitter::Scope scope(emitter);
-  raw_indented_ostream &os = emitter.ostream();
-
-  auto functionOp = SymbolTable::lookupNearestSymbolFrom<emitc::FuncOp>(
-      declareFuncOp, declareFuncOp.getSymNameAttr());
-
-  if (!functionOp)
-    return failure();
-
-  if (functionOp.getSpecifiers()) {
-    for (Attribute specifier : functionOp.getSpecifiersAttr()) {
-      os << cast<StringAttr>(specifier).str() << " ";
-    }
-  }
-
-  if (failed(emitter.emitTypes(functionOp.getLoc(),
-                               functionOp.getFunctionType().getResults())))
-    return failure();
-  os << " " << functionOp.getName();
-
-  os << "(";
-  Operation *operation = functionOp.getOperation();
-  if (failed(printFunctionArgs(emitter, operation, functionOp.getArguments())))
-    return failure();
-  os << ");";
-
-  return success();
-}
-
-CppEmitter::CppEmitter(raw_ostream &os, bool declareVariablesAtTop)
-    : os(os), declareVariablesAtTop(declareVariablesAtTop) {
+CppEmitter::CppEmitter(raw_ostream &os, Operation *module,
+                       bool declareVariablesAtTop)
+    : os(os), module(module), declareVariablesAtTop(declareVariablesAtTop),
+      iface(module->getContext()) {
   valueInScopeCount.push(0);
   labelInScopeCount.push(0);
 }
@@ -1394,48 +383,16 @@ LogicalResult CppEmitter::emitLabel(Block &block) {
 }
 
 LogicalResult CppEmitter::emitOperation(Operation &op, bool trailingSemicolon) {
-  LogicalResult status =
-      llvm::TypeSwitch<Operation *, LogicalResult>(&op)
-          // Builtin ops.
-          .Case<ModuleOp>([&](auto op) { return printOperation(*this, op); })
-          // CF ops.
-          .Case<cf::BranchOp, cf::CondBranchOp>(
-              [&](auto op) { return printOperation(*this, op); })
-          // EmitC ops.
-          .Case<emitc::AddOp, emitc::ApplyOp, emitc::AssignOp,
-                emitc::BitwiseAndOp, emitc::BitwiseLeftShiftOp,
-                emitc::BitwiseNotOp, emitc::BitwiseOrOp,
-                emitc::BitwiseRightShiftOp, emitc::BitwiseXorOp, emitc::CallOp,
-                emitc::CallOpaqueOp, emitc::CastOp, emitc::CmpOp,
-                emitc::ConditionalOp, emitc::ConstantOp, emitc::DeclareFuncOp,
-                emitc::DivOp, emitc::ExpressionOp, emitc::ForOp, emitc::FuncOp,
-                emitc::IfOp, emitc::IncludeOp, emitc::LogicalAndOp,
-                emitc::LogicalNotOp, emitc::LogicalOrOp, emitc::MulOp,
-                emitc::RemOp, emitc::ReturnOp, emitc::SubOp,
-                emitc::UnaryMinusOp, emitc::UnaryPlusOp, emitc::VariableOp,
-                emitc::VerbatimOp>(
-              [&](auto op) { return printOperation(*this, op); })
-          // Func ops.
-          .Case<func::CallOp, func::FuncOp, func::ReturnOp>(
-              [&](auto op) { return printOperation(*this, op); })
-          .Case<emitc::LiteralOp>([&](auto op) { return success(); })
-          .Default([&](Operation *) {
-            return op.emitOpError("unable to find printer for op");
-          });
+  const CppTranslationDialectInterface *opIface = iface.getInterfaceFor(&op);
+  if (!opIface)
+    return op.emitError("cannot be converted to Cpp: missing "
+                        "`CppTranslationDialectInterface` registration for "
+                        "dialect for op: ")
+           << op.getName();
 
-  if (failed(status))
+  if (failed(opIface->emitOperation(&op, *this, trailingSemicolon)))
     return failure();
 
-  if (isa<emitc::LiteralOp>(op))
-    return success();
-
-  if (getEmittedExpression() ||
-      (isa<emitc::ExpressionOp>(op) &&
-       shouldBeInlined(cast<emitc::ExpressionOp>(op))))
-    return success();
-
-  os << (trailingSemicolon ? ";\n" : "\n");
-
   return success();
 }
 
@@ -1551,8 +508,8 @@ LogicalResult CppEmitter::emitTupleType(Location loc, ArrayRef<Type> types) {
   return success();
 }
 
-LogicalResult emitc::translateToCpp(Operation *op, raw_ostream &os,
-                                    bool declareVariablesAtTop) {
-  CppEmitter emitter(os, declareVariablesAtTop);
+LogicalResult mlir::translateToCpp(Operation *op, raw_ostream &os,
+                                   bool declareVariablesAtTop) {
+  CppEmitter emitter(os, op, declareVariablesAtTop);
   return emitter.emitOperation(*op, /*trailingSemicolon=*/false);
 }
diff --git a/mlir/test/Target/Cpp/invalid.mlir b/mlir/test/Target/Cpp/invalid.mlir
index 552c04a9b07f7d..9a8ce3892bb761 100644
--- a/mlir/test/Target/Cpp/invalid.mlir
+++ b/mlir/test/Target/Cpp/invalid.mlir
@@ -11,7 +11,7 @@ func.func @multiple_blocks() {
 // -----
 
 func.func @unsupported_std_op(%arg0: f64) -> f64 {
-  // expected-error at +1 {{'math.absf' op unable to find printer for op}}
+  // expected-error at +1 {{cannot be converted to Cpp: missing `CppTranslationDialectInterface` registration for dialect for op: math.absf}}
   %0 = math.absf %arg0 : f64
   return %0 : f64
 }



More information about the Mlir-commits mailing list