diff --git a/mlir/include/mlir/Target/Cpp/CppEmitter.h b/mlir/include/mlir/Target/Cpp/CppEmitter.h index 99d8696cc8e07..72fba26a0f054 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 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 types); + + /// Emits array of types as a std::tuple of the emitted types independently of + /// the array size. + LogicalResult emitTupleType(Location loc, ArrayRef 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 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 valueMapperScope; + llvm::ScopedHashTableScope 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; + using BlockMapper = llvm::ScopedHashTable; + + /// 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 valueInScopeCount; + std::stack labelInScopeCount; + + /// State of the current expression being emitted. + ExpressionOp emittedExpression; + SmallVector 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 1dadb9dd691e7..e9045548730c9 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(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 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 types); - - /// Emits array of types as a std::tuple of the emitted types independently of - /// the array size. - LogicalResult emitTupleType(Location loc, ArrayRef 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 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 valueMapperScope; - llvm::ScopedHashTableScope 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(def->getParentOp()); - return operandExpression == emittedExpression; - }; - -private: - using ValueMapper = llvm::ScopedHashTable; - using BlockMapper = llvm::ScopedHashTable; - - /// 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 valueInScopeCount; - std::stack labelInScopeCount; - - /// State of the current expression being emitted. - ExpressionOp emittedExpression; - SmallVector 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