Skip to content

Moving CppEmitter to header file. #106201

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 155 additions & 0 deletions mlir/include/mlir/Target/Cpp/CppEmitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
209 changes: 38 additions & 171 deletions mlir/lib/Target/Cpp/TranslateToCpp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down
Loading