[Mlir-commits] [mlir] Moving CppEmitter to header file. (PR #106201)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Tue Aug 27 02:03:09 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-mlir
Author: None (ddubov100)
<details>
<summary>Changes</summary>
In projects lowering MLIR, there might be project-specific translators that support the translation of custom dialects. For projects targeting code from the C family, it would be advantageous to use as many EmitC dialect operations as possible. If they want to benefit from EmitC dialect translation, they have two options:
1. Reimplement TranslateToCpp in their translators.
2. Lower everything to the EmitC dialect, though it might take a lot of time for the EmitC dialect to support all required features.
It might be beneficial for such projects to use CppEmitter as a standalone class with a clear API to translate EmitC dialect operations, allowing for custom translation of unsupported operations.
This PR moves CppEmitter to a header file to enable this functionality.
---
Full diff: https://github.com/llvm/llvm-project/pull/106201.diff
2 Files Affected:
- (modified) mlir/include/mlir/Target/Cpp/CppEmitter.h (+155)
- (modified) mlir/lib/Target/Cpp/TranslateToCpp.cpp (+38-171)
``````````diff
diff --git a/mlir/include/mlir/Target/Cpp/CppEmitter.h b/mlir/include/mlir/Target/Cpp/CppEmitter.h
index 99d8696cc8e077..72fba26a0f054a 100644
--- a/mlir/include/mlir/Target/Cpp/CppEmitter.h
+++ b/mlir/include/mlir/Target/Cpp/CppEmitter.h
@@ -13,12 +13,167 @@
#ifndef MLIR_TARGET_CPP_CPPEMITTER_H
#define MLIR_TARGET_CPP_CPPEMITTER_H
+#include "mlir/Dialect/EmitC/IR/EmitC.h"
+#include "mlir/IR/Location.h"
+#include "mlir/IR/Operation.h"
+#include "mlir/Support/IndentedOstream.h"
+
#include "mlir/Support/LLVM.h"
+#include "llvm/ADT/ScopedHashTable.h"
+#include "llvm/Support/raw_ostream.h"
+#include <stack>
namespace mlir {
class Operation;
namespace emitc {
+/// 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 global variable declaration or definition.
+ LogicalResult emitGlobalVariable(GlobalOp 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);
+
+ /// Insert the expression representing the operation into the value cache.
+ void cacheDeferredOpResult(Value value, StringRef str);
+
+ /// Return the existing or a new name for a Value.
+ StringRef getOrCreateName(Value val);
+
+ // Returns the textual representation of a subscript operation.
+ std::string getSubscriptName(emitc::SubscriptOp op);
+
+ // Returns the textual representation of a member (of object) operation.
+ std::string createMemberAccess(emitc::MemberOp op);
+
+ // Returns the textual representation of a member of pointer operation.
+ std::string createMemberAccess(emitc::MemberOfPtrOp op);
+
+ /// 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);
+ ~Scope();
+
+ 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);
+
+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);
+ void popExpressionPrecedence();
+ static int lowestPrecedence();
+ int getExpressionPrecedence();
+};
+
/// 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
/// 'declareVariablesAtTop' enforces that all variables for op results and block
diff --git a/mlir/lib/Target/Cpp/TranslateToCpp.cpp b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
index 1dadb9dd691e70..e9045548730c96 100644
--- a/mlir/lib/Target/Cpp/TranslateToCpp.cpp
+++ b/mlir/lib/Target/Cpp/TranslateToCpp.cpp
@@ -33,6 +33,44 @@ using namespace mlir;
using namespace mlir::emitc;
using llvm::formatv;
+CppEmitter::Scope::Scope(CppEmitter &emitter)
+ : valueMapperScope(emitter.valueMapper),
+ blockMapperScope(emitter.blockMapper), emitter(emitter) {
+ emitter.valueInScopeCount.push(emitter.valueInScopeCount.top());
+ emitter.labelInScopeCount.push(emitter.labelInScopeCount.top());
+}
+
+CppEmitter::Scope::~Scope() {
+ emitter.valueInScopeCount.pop();
+ emitter.labelInScopeCount.pop();
+}
+
+bool CppEmitter::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;
+}
+
+void CppEmitter::pushExpressionPrecedence(int precedence) {
+ emittedExpressionPrecedence.push_back(precedence);
+}
+
+void CppEmitter::popExpressionPrecedence() {
+ emittedExpressionPrecedence.pop_back();
+}
+
+int CppEmitter::lowestPrecedence() { return 0; }
+
+int CppEmitter::getExpressionPrecedence() {
+ if (emittedExpressionPrecedence.empty())
+ return lowestPrecedence();
+ return emittedExpressionPrecedence.back();
+}
+
/// 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.
@@ -111,177 +149,6 @@ 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 global variable declaration or definition.
- LogicalResult emitGlobalVariable(GlobalOp 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);
-
- /// Insert the expression representing the operation into the value cache.
- void cacheDeferredOpResult(Value value, StringRef str);
-
- /// Return the existing or a new name for a Value.
- StringRef getOrCreateName(Value val);
-
- // Returns the textual representation of a subscript operation.
- std::string getSubscriptName(emitc::SubscriptOp op);
-
- // Returns the textual representation of a member (of object) operation.
- std::string createMemberAccess(emitc::MemberOp op);
-
- // Returns the textual representation of a member of pointer operation.
- std::string createMemberAccess(emitc::MemberOfPtrOp op);
-
- /// 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 op should be emitted in a deferred way.
static bool hasDeferredEmission(Operation *op) {
return isa_and_nonnull<emitc::GetGlobalOp, emitc::LiteralOp, emitc::MemberOp,
``````````
</details>
https://github.com/llvm/llvm-project/pull/106201
More information about the Mlir-commits
mailing list