blob: 579b5e3106f279da6553a9ef007bf6c5e93c1ee9 [file] [log] [blame]
//===- CppEmitter.h - Helpers to create C++ emitter -------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines helpers to emit C++ code using the EmitC dialect.
//
//===----------------------------------------------------------------------===//
#ifndef EMITC_TARGET_CPP_CPPEMITTER_H
#define EMITC_TARGET_CPP_CPPEMITTER_H
#include "mlir/IR/BuiltinTypes.h"
#include "mlir/IR/Value.h"
#include "mlir/Support/IndentedOstream.h"
#include "llvm/ADT/ScopedHashTable.h"
#include "llvm/Support/raw_ostream.h"
#include <stack>
namespace mlir {
namespace emitc {
/// Convenience functions to produce interleaved output with functions returning
/// a LogicalResult. This is different than those in STL as functions used on
/// each element doesn't return a string.
template <typename ForwardIterator, typename UnaryFunctor,
typename NullaryFunctor>
inline LogicalResult
interleaveWithError(ForwardIterator begin, ForwardIterator end,
UnaryFunctor eachFn, NullaryFunctor betweenFn) {
if (begin == end)
return success();
if (failed(eachFn(*begin)))
return failure();
++begin;
for (; begin != end; ++begin) {
betweenFn();
if (failed(eachFn(*begin)))
return failure();
}
return success();
}
template <typename Container, typename UnaryFunctor, typename NullaryFunctor>
inline LogicalResult interleaveWithError(const Container &c,
UnaryFunctor eachFn,
NullaryFunctor betweenFn) {
return interleaveWithError(c.begin(), c.end(), eachFn, betweenFn);
}
template <typename Container, typename UnaryFunctor>
inline LogicalResult interleaveCommaWithError(const Container &c,
raw_ostream &os,
UnaryFunctor eachFn) {
return interleaveWithError(c.begin(), c.end(), eachFn, [&]() { os << ", "; });
}
/// Emitter that uses dialect specific emitters to emit C++ code.
struct CppEmitter {
explicit CppEmitter(raw_ostream &os, bool declareVariablesAtTop);
/// Emits attribute or returns failure.
LogicalResult emitAttribute(Operation &op, Attribute attr);
/// Emits operation 'op' with/without training semicolon or returns failure.
LogicalResult emitOperation(Operation &op, bool trailingSemicolon);
/// Emits type 'type' or returns failure.
LogicalResult emitType(Operation &op, Type type);
/// Emits array of types as a std::tuple of the emitted types.
/// - emits void for an empty array;
/// - emits the type of the only element for arrays of size one;
/// - emits a std::tuple otherwise;
LogicalResult emitTypes(Operation &op, ArrayRef<Type> types);
/// Emits array of types as a std::tuple of the emitted types independently of
/// the array size.
LogicalResult emitTupleType(Operation &op, ArrayRef<Type> types);
/// Emits an assignment for a variable which has been declared previously.
LogicalResult emitVariableAssignment(OpResult result);
/// Emits a variable declaration for a result of an operation.
LogicalResult emitVariableDeclaration(OpResult result,
bool trailingSemicolon);
/// Emits the variable declaration and assignment prefix for 'op'.
/// - emits separate variable followed by std::tie for multi-valued operation;
/// - emits single type followed by variable for single result;
/// - emits nothing if no value produced by op;
/// Emits final '=' operator where a type is produced. Returns failure if
/// any result type could not be converted.
LogicalResult emitAssignPrefix(Operation &op);
/// Emits a label for the block.
LogicalResult emitLabel(Block &block);
/// Emits the operands and atttributes of the operation. All operands are
/// emitted first and then all attributes in alphabetical order.
LogicalResult emitOperandsAndAttributes(Operation &op,
ArrayRef<StringRef> exclude = {});
/// Emits the operands of the operation. All operands are emitted in order.
LogicalResult emitOperands(Operation &op);
/// Return the existing or a new name for a Value.
StringRef getOrCreateName(Value val);
/// Return the existing or a new label of a Block.
StringRef getOrCreateName(Block &block);
/// Whether to map an mlir integer to a signed integer in C++.
bool shouldMapToSigned(IntegerType::SignednessSemantics val);
/// RAII helper function to manage entering/exiting C++ scopes.
struct Scope {
Scope(CppEmitter &emitter)
: valueMapperScope(emitter.valueMapper),
blockMapperScope(emitter.blockMapper), emitter(emitter) {
emitter.valueInScopeCount.push(emitter.valueInScopeCount.top());
emitter.labelInScopeCount.push(emitter.labelInScopeCount.top());
}
~Scope() {
emitter.valueInScopeCount.pop();
emitter.labelInScopeCount.pop();
}
private:
llvm::ScopedHashTableScope<Value, std::string> valueMapperScope;
llvm::ScopedHashTableScope<Block *, std::string> blockMapperScope;
CppEmitter &emitter;
};
/// Returns wether the Value is assigned to a C++ variable in the scope.
bool hasValueInScope(Value val);
// Returns whether a label is assigned to the block.
bool hasBlockLabel(Block &block);
/// Returns the output stream.
raw_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; };
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;
};
/// 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
/// arguments are declared at the beginning of the function.
LogicalResult translateToCpp(Operation *op, raw_ostream &os,
bool declareVariablesAtTop = false);
} // namespace emitc
} // namespace mlir
#endif // EMITC_TARGET_CPP_CPPEMITTER_H