forked from OSchip/llvm-project
TableGen most of the LLVM IR Dialect to LLVM IR conversions
The LLVM IR Dialect strives to be close to the original LLVM IR instructions. The conversion from the LLVM IR Dialect to LLVM IR proper is mostly mechanical and can be automated. Implement TableGen support for generating conversions from a concise pattern form in the TableGen definition of the LLVM IR Dialect operations. It is used for all operations except calls and branches. These operations need access to function and block remapping tables and would require significantly more code to generate the conversions from TableGen definitions than the current manually written conversions. This implementation is accompanied by various necessary changes to the TableGen operation definition infrastructure. In particular, operation definitions now contain named accessors to results as well as named accessors to the variadic operand (returning a vector of operands). The base operation support TableGen file now contains a FunctionAttr definition. The TableGen now allows to query the names of the operation results. PiperOrigin-RevId: 237203077
This commit is contained in:
parent
056fc2fd09
commit
dbaab04a80
|
@ -84,6 +84,9 @@ public:
|
|||
SparseElements,
|
||||
FIRST_ELEMENTS_ATTR = SplatElements,
|
||||
LAST_ELEMENTS_ATTR = SparseElements,
|
||||
|
||||
FIRST_KIND = Bool,
|
||||
LAST_KIND = SparseElements,
|
||||
};
|
||||
|
||||
using ImplType = detail::AttributeStorage;
|
||||
|
@ -110,6 +113,13 @@ public:
|
|||
template <typename U> U dyn_cast_or_null() const;
|
||||
template <typename U> U cast() const;
|
||||
|
||||
// Support dyn_cast'ing Attribute to itself.
|
||||
static bool kindof(Kind kind) {
|
||||
assert(kind >= Kind::FIRST_KIND && kind <= Kind::LAST_KIND &&
|
||||
"incorrect Attribute kind");
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Return the classification for this attribute.
|
||||
Kind getKind() const;
|
||||
|
||||
|
|
|
@ -279,6 +279,14 @@ class Attr<Pred condition, string descr = ""> :
|
|||
bit isOptional = 0b0;
|
||||
}
|
||||
|
||||
// Any attribute.
|
||||
def AnyAttr : Attr<CPred<"true">, "any"> {
|
||||
let storageType = "Attribute";
|
||||
let returnType = "Attribute";
|
||||
let convertFromStorage = "{0}";
|
||||
let constBuilderCall = "{1}";
|
||||
}
|
||||
|
||||
// Decorates an attribute to have an (unvalidated) default value if not present.
|
||||
class DefaultValuedAttr<Attr attr, string val> :
|
||||
Attr<attr.predicate, attr.description> {
|
||||
|
@ -357,6 +365,15 @@ def I32Attr : IntegerAttrBase<I32, "32-bit integer"> {
|
|||
}
|
||||
def StrAttr : StringBasedAttr<"string">;
|
||||
|
||||
// Attributes containing functions.
|
||||
def FunctionAttr
|
||||
: Attr<CPred<"{0}.isa<FunctionAttr>()">, "function"> {
|
||||
let storageType = [{ FunctionAttr }];
|
||||
let returnType = [{ Function * }];
|
||||
let convertFromStorage = [{ {0}.getValue() }];
|
||||
let constBuilderCall = [{ {0}.getFunctionAttr({1}) }];
|
||||
}
|
||||
|
||||
// DerivedAttr are attributes whose value is computed from properties
|
||||
// of the operation. They do not require additional storage and are
|
||||
// materialized as needed.
|
||||
|
|
|
@ -37,7 +37,24 @@ def LLVM_Type : Type<CPred<"{0}.isa<::mlir::LLVM::LLVMType>()">,
|
|||
// this class is specialized below for both cases and should not be used
|
||||
// directly.
|
||||
class LLVM_Op<string mnemonic, list<OpTrait> traits = []> :
|
||||
Op<!strconcat("llvm.", mnemonic), traits>;
|
||||
Op<!strconcat("llvm.", mnemonic), traits> {
|
||||
// A pattern for constructing the LLVM IR Instruction (or other Value) that
|
||||
// corresponds to this op. This pattern can use `builder` to refer to an
|
||||
// `llvm::IRBuilder<>` instance, $-names of arguments and results and the
|
||||
// following special variable names:
|
||||
// - $_resultType - substituted with the LLVM IR type of the result;
|
||||
// - $_numOperands - substituted with the number of operands (including
|
||||
// the variadic ones);
|
||||
// - $_hasResult - substituted with a check that a variadic-result op does
|
||||
// have a result (LLVM ops can have 0 or 1 result);
|
||||
// - $_location - mlir::Location object of the instruction.
|
||||
// Additionally, `$$` can be used to produce the dollar character.
|
||||
string llvmBuilder = "";
|
||||
}
|
||||
|
||||
class LLVM_Builder<string builder> {
|
||||
string llvmBuilder = builder;
|
||||
}
|
||||
|
||||
def LLVM_OneResultOpBuilder {
|
||||
code builder = [{
|
||||
|
@ -72,7 +89,7 @@ class LLVM_TwoBuilders<code b1, code b2> {
|
|||
|
||||
// Base class for LLVM operations with one result.
|
||||
class LLVM_OneResultOp<string mnemonic, list<OpTrait> traits = []> :
|
||||
LLVM_Op<mnemonic, traits>, Results<(outs LLVM_Type)> {
|
||||
LLVM_Op<mnemonic, traits>, Results<(outs LLVM_Type:$res)> {
|
||||
let builder = LLVM_OneResultOpBuilder.builder;
|
||||
}
|
||||
|
||||
|
@ -100,7 +117,7 @@ class LLVM_ZeroResultOp<string mnemonic, list<OpTrait> traits = []> :
|
|||
// zero results and an optional list of successors.
|
||||
class LLVM_TerminatorOp<string mnemonic, list<OpTrait> traits = []> :
|
||||
LLVM_Op<mnemonic, !listconcat(traits, [Terminator])>,
|
||||
Arguments<(ins Variadic<LLVM_Type>)>, Results<(outs)> {
|
||||
Arguments<(ins Variadic<LLVM_Type>:$args)>, Results<(outs)> {
|
||||
let builder = [{
|
||||
static void build(Builder *builder, OperationState *result,
|
||||
ArrayRef<Value *> properOperands,
|
||||
|
@ -120,49 +137,64 @@ class LLVM_TerminatorOp<string mnemonic, list<OpTrait> traits = []> :
|
|||
}
|
||||
|
||||
// Class for arithmetic binary instructions.
|
||||
class LLVM_ArithmeticOp<string mnemonic, list<OpTrait> traits = []> :
|
||||
class LLVM_ArithmeticOp<string mnemonic, string builderFunc,
|
||||
list<OpTrait> traits = []> :
|
||||
LLVM_OneResultOp<mnemonic,
|
||||
!listconcat([NoSideEffect, SameValueType], traits)>,
|
||||
Arguments<(ins LLVM_Type:$lhs, LLVM_Type:$rhs)>;
|
||||
|
||||
// Class for variadic instructions.
|
||||
class LLVM_VariadicOneResultOp<string mnemonic, list<OpTrait> traits = []> :
|
||||
LLVM_OneResultOp<mnemonic, traits>, Arguments<(ins Variadic<LLVM_Type>)>;
|
||||
Arguments<(ins LLVM_Type:$lhs, LLVM_Type:$rhs)>,
|
||||
LLVM_Builder<"$res = builder." # builderFunc # "($lhs, $rhs);">;
|
||||
|
||||
// Integer binary instructions.
|
||||
def LLVM_AddOp : LLVM_ArithmeticOp<"add", [Commutative]>;
|
||||
def LLVM_SubOp : LLVM_ArithmeticOp<"sub">;
|
||||
def LLVM_MulOp : LLVM_ArithmeticOp<"mul", [Commutative]>;
|
||||
def LLVM_UDivOp : LLVM_ArithmeticOp<"udiv">;
|
||||
def LLVM_SDivOp : LLVM_ArithmeticOp<"sdiv">;
|
||||
def LLVM_URemOp : LLVM_ArithmeticOp<"urem">;
|
||||
def LLVM_SRemOp : LLVM_ArithmeticOp<"srem">;
|
||||
def LLVM_AddOp : LLVM_ArithmeticOp<"add", "CreateAdd", [Commutative]>;
|
||||
def LLVM_SubOp : LLVM_ArithmeticOp<"sub", "CreateSub">;
|
||||
def LLVM_MulOp : LLVM_ArithmeticOp<"mul", "CreateMul", [Commutative]>;
|
||||
def LLVM_UDivOp : LLVM_ArithmeticOp<"udiv", "CreateUDiv">;
|
||||
def LLVM_SDivOp : LLVM_ArithmeticOp<"sdiv", "CreateSDiv">;
|
||||
def LLVM_URemOp : LLVM_ArithmeticOp<"urem", "CreateURem">;
|
||||
def LLVM_SRemOp : LLVM_ArithmeticOp<"srem", "CreateSRem">;
|
||||
|
||||
// Other integer instructions.
|
||||
def LLVM_ICmpOp : LLVM_OneResultOp<"icmp", [NoSideEffect]>,
|
||||
Arguments<(ins LLVM_Type:$lhs, LLVM_Type:$rhs)>;
|
||||
Arguments<(ins I32Attr:$predicate, LLVM_Type:$lhs,
|
||||
LLVM_Type:$rhs)> {
|
||||
let llvmBuilder = [{ $res = builder.CreateICmp(
|
||||
getLLVMCmpPredicate(static_cast<CmpIPredicate>($predicate)), $lhs, $rhs);
|
||||
}];
|
||||
}
|
||||
|
||||
// Floating point binary instructions.
|
||||
def LLVM_FAddOp : LLVM_ArithmeticOp<"fadd">;
|
||||
def LLVM_FSubOp : LLVM_ArithmeticOp<"fsub">;
|
||||
def LLVM_FMulOp : LLVM_ArithmeticOp<"fmul">;
|
||||
def LLVM_FDivOp : LLVM_ArithmeticOp<"fdiv">;
|
||||
def LLVM_FRemOp : LLVM_ArithmeticOp<"frem">;
|
||||
def LLVM_FAddOp : LLVM_ArithmeticOp<"fadd", "CreateFAdd">;
|
||||
def LLVM_FSubOp : LLVM_ArithmeticOp<"fsub", "CreateFSub">;
|
||||
def LLVM_FMulOp : LLVM_ArithmeticOp<"fmul", "CreateFMul">;
|
||||
def LLVM_FDivOp : LLVM_ArithmeticOp<"fdiv", "CreateFDiv">;
|
||||
def LLVM_FRemOp : LLVM_ArithmeticOp<"frem", "CreateFRem">;
|
||||
|
||||
// Memory-related instructions.
|
||||
def LLVM_AllocaOp : LLVM_OneResultOp<"alloca">,
|
||||
Arguments<(ins LLVM_Type:$arraySize)>;
|
||||
def LLVM_GEPOp : LLVM_VariadicOneResultOp<"getelementptr", [NoSideEffect]>;
|
||||
def LLVM_LoadOp : LLVM_OneResultOp<"load">, Arguments<(ins LLVM_Type:$addr)>;
|
||||
Arguments<(ins LLVM_Type:$arraySize)> {
|
||||
string llvmBuilder = [{
|
||||
$res = builder.CreateAlloca($_resultType, $arraySize);
|
||||
}];
|
||||
}
|
||||
def LLVM_GEPOp : LLVM_OneResultOp<"getelementptr", [NoSideEffect]>,
|
||||
Arguments<(ins LLVM_Type:$base, Variadic<LLVM_Type>:$indices)>,
|
||||
LLVM_Builder<"$res = builder.CreateGEP($base, $indices);">;
|
||||
def LLVM_LoadOp : LLVM_OneResultOp<"load">, Arguments<(ins LLVM_Type:$addr)>,
|
||||
LLVM_Builder<"$res = builder.CreateLoad($addr);">;
|
||||
def LLVM_StoreOp : LLVM_ZeroResultOp<"store">,
|
||||
Arguments<(ins LLVM_Type:$value, LLVM_Type:$addr)>;
|
||||
def LLVM_BitcastOp : LLVM_OneResultOp<"bitcast", [NoSideEffect]>,
|
||||
Arguments<(ins LLVM_Type)>;
|
||||
Arguments<(ins LLVM_Type:$value, LLVM_Type:$addr)>,
|
||||
LLVM_Builder<"builder.CreateStore($value, $addr);">;
|
||||
def LLVM_BitcastOp
|
||||
: LLVM_OneResultOp<"bitcast", [NoSideEffect]>,
|
||||
Arguments<(ins LLVM_Type:$arg)>,
|
||||
LLVM_Builder<"$res = builder.CreateBitCast($arg, $_resultType);">;
|
||||
|
||||
|
||||
// Call-related instructions.
|
||||
def LLVM_CallOp : LLVM_Op<"call">, Arguments<(ins Variadic<LLVM_Type>)>,
|
||||
Results<(outs Variadic<LLVM_Type>)> {
|
||||
def LLVM_CallOp : LLVM_Op<"call">,
|
||||
Arguments<(ins OptionalAttr<FunctionAttr>:$callee,
|
||||
Variadic<LLVM_Type>)>,
|
||||
Results<(outs Variadic<LLVM_Type>)> {
|
||||
let builder = LLVM_TwoBuilders<
|
||||
LLVM_OneResultOpBuilder.builder,
|
||||
LLVM_ZeroResultOpBuilder.builder
|
||||
|
@ -175,13 +207,28 @@ def LLVM_CallOp : LLVM_Op<"call">, Arguments<(ins Variadic<LLVM_Type>)>,
|
|||
}];
|
||||
}
|
||||
def LLVM_ExtractValueOp : LLVM_OneResultOp<"extractvalue", [NoSideEffect]>,
|
||||
Arguments<(ins LLVM_Type)>;
|
||||
Arguments<(ins LLVM_Type:$container,
|
||||
ArrayAttr:$position)> {
|
||||
string llvmBuilder = [{
|
||||
$res = builder.CreateExtractValue($container, extractPosition($position));
|
||||
}];
|
||||
}
|
||||
def LLVM_InsertValueOp : LLVM_OneResultOp<"insertvalue", [NoSideEffect]>,
|
||||
Arguments<(ins LLVM_Type, LLVM_Type)>;
|
||||
Arguments<(ins LLVM_Type:$container, LLVM_Type:$value,
|
||||
ArrayAttr:$position)> {
|
||||
string llvmBuilder = [{
|
||||
$res = builder.CreateInsertValue($container, $value,
|
||||
extractPosition($position));
|
||||
}];
|
||||
}
|
||||
|
||||
// Misc instructions.
|
||||
def LLVM_SelectOp : LLVM_OneResultOp<"select", [NoSideEffect]>,
|
||||
Arguments<(ins LLVM_Type, LLVM_Type, LLVM_Type)>;
|
||||
def LLVM_SelectOp
|
||||
: LLVM_OneResultOp<"select", [NoSideEffect]>,
|
||||
Arguments<(ins LLVM_Type:$condition, LLVM_Type:$trueValue,
|
||||
LLVM_Type:$falseValue)>,
|
||||
LLVM_Builder<
|
||||
"$res = builder.CreateSelect($condition, $trueValue, $falseValue);">;
|
||||
|
||||
// Terminators.
|
||||
def LLVM_BrOp : LLVM_TerminatorOp<"br", [NoSideEffect]>;
|
||||
|
@ -196,12 +243,28 @@ def LLVM_CondBrOp : LLVM_TerminatorOp<"cond_br", [NoSideEffect]> {
|
|||
return false;
|
||||
}];
|
||||
}
|
||||
def LLVM_ReturnOp : LLVM_TerminatorOp<"return", [NoSideEffect]>;
|
||||
def LLVM_ReturnOp : LLVM_TerminatorOp<"return", [NoSideEffect]> {
|
||||
string llvmBuilder = [{
|
||||
if ($_numOperands != 0)
|
||||
builder.CreateRet($args[0]);
|
||||
else
|
||||
builder.CreateRetVoid();
|
||||
}];
|
||||
|
||||
let verifier = [{
|
||||
if (getNumOperands() > 1)
|
||||
return emitOpError("expects at most 1 operand");
|
||||
return false;
|
||||
}];
|
||||
}
|
||||
|
||||
// Pseudo-operations (do not appear in LLVM IR but necessary for the dialect to
|
||||
// work correctly).
|
||||
def LLVM_UndefOp : LLVM_OneResultOp<"undef", [NoSideEffect]>;
|
||||
def LLVM_ConstantOp : LLVM_OneResultOp<"constant", [NoSideEffect]>,
|
||||
Arguments<(ins)>;
|
||||
def LLVM_UndefOp : LLVM_OneResultOp<"undef", [NoSideEffect]>,
|
||||
LLVM_Builder<"$res = llvm::UndefValue::get($_resultType);">;
|
||||
def LLVM_ConstantOp
|
||||
: LLVM_OneResultOp<"constant", [NoSideEffect]>,
|
||||
Arguments<(ins AnyAttr:$value)>,
|
||||
LLVM_Builder<"$res = getLLVMConstant($_resultType, $value, $_location);">;
|
||||
|
||||
#endif // LLVMIR_OPS
|
||||
|
|
|
@ -61,6 +61,9 @@ private:
|
|||
bool convertBlock(const Block &bb, bool ignoreArguments);
|
||||
bool convertInstruction(const Instruction &inst, llvm::IRBuilder<> &builder);
|
||||
|
||||
template <typename Range>
|
||||
SmallVector<llvm::Value *, 8> lookupValues(Range &&values);
|
||||
|
||||
llvm::Constant *getLLVMConstant(llvm::Type *llvmType, Attribute attr,
|
||||
Location loc);
|
||||
|
||||
|
@ -177,80 +180,39 @@ static llvm::CmpInst::Predicate getLLVMCmpPredicate(CmpIPredicate p) {
|
|||
}
|
||||
}
|
||||
|
||||
// A helper to look up remapped operands in the value remapping table.
|
||||
template <typename Range>
|
||||
SmallVector<llvm::Value *, 8> ModuleTranslation::lookupValues(Range &&values) {
|
||||
SmallVector<llvm::Value *, 8> remapped;
|
||||
remapped.reserve(llvm::size(values));
|
||||
for (const Value *v : values) {
|
||||
remapped.push_back(valueMapping.lookup(v));
|
||||
}
|
||||
return remapped;
|
||||
}
|
||||
|
||||
// Given a single MLIR instruction, create the corresponding LLVM IR instruction
|
||||
// using the `builder`. LLVM IR Builder does not have a generic interface so
|
||||
// this has to be a long chain of `if`s calling different functions with a
|
||||
// different number of arguments.
|
||||
// TODO(zinenko): the conversion is largely mechanical and should be tablegen'ed
|
||||
bool ModuleTranslation::convertInstruction(const Instruction &inst,
|
||||
llvm::IRBuilder<> &builder) {
|
||||
#define CONV_BINARY_OP(CLASS, FUNC) \
|
||||
if (auto op = inst.dyn_cast<CLASS>()) { \
|
||||
valueMapping[op->getResult()] = builder.FUNC( \
|
||||
valueMapping.lookup(op->lhs()), valueMapping.lookup(op->rhs())); \
|
||||
return false; \
|
||||
}
|
||||
auto extractPosition = [](ArrayAttr attr) {
|
||||
SmallVector<unsigned, 4> position;
|
||||
position.reserve(attr.size());
|
||||
for (Attribute v : attr)
|
||||
position.push_back(v.cast<IntegerAttr>().getValue().getZExtValue());
|
||||
return position;
|
||||
};
|
||||
|
||||
CONV_BINARY_OP(LLVM::AddOp, CreateAdd);
|
||||
CONV_BINARY_OP(LLVM::SubOp, CreateSub);
|
||||
CONV_BINARY_OP(LLVM::MulOp, CreateMul);
|
||||
CONV_BINARY_OP(LLVM::SDivOp, CreateSDiv);
|
||||
CONV_BINARY_OP(LLVM::UDivOp, CreateUDiv);
|
||||
CONV_BINARY_OP(LLVM::SRemOp, CreateSRem);
|
||||
CONV_BINARY_OP(LLVM::URemOp, CreateURem);
|
||||
CONV_BINARY_OP(LLVM::FAddOp, CreateFAdd);
|
||||
CONV_BINARY_OP(LLVM::FSubOp, CreateFSub);
|
||||
CONV_BINARY_OP(LLVM::FMulOp, CreateFMul);
|
||||
CONV_BINARY_OP(LLVM::FDivOp, CreateFDiv);
|
||||
CONV_BINARY_OP(LLVM::FRemOp, CreateFRem);
|
||||
|
||||
#undef CONV_BINARY_OP
|
||||
|
||||
if (auto op = inst.dyn_cast<LLVM::ICmpOp>()) {
|
||||
auto attr = op->getAttrOfType<IntegerAttr>("predicate");
|
||||
auto predicate = static_cast<CmpIPredicate>(attr.getValue().getSExtValue());
|
||||
|
||||
valueMapping[op->getResult()] = builder.CreateICmp(
|
||||
getLLVMCmpPredicate(predicate), valueMapping.lookup(op->lhs()),
|
||||
valueMapping.lookup(op->rhs()));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pseudo-ops. These do not exist as LLVM operations but produce (constant)
|
||||
// values.
|
||||
if (auto op = inst.dyn_cast<LLVM::UndefOp>()) {
|
||||
auto wrappedType = op->getResult()->getType().dyn_cast<LLVM::LLVMType>();
|
||||
valueMapping[op->getResult()] =
|
||||
llvm::UndefValue::get(wrappedType.getUnderlyingType());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto op = inst.dyn_cast<LLVM::ConstantOp>()) {
|
||||
Attribute attr = op->getAttr("value");
|
||||
auto type = op->getResult()->getType().cast<LLVM::LLVMType>();
|
||||
valueMapping[op->getResult()] =
|
||||
getLLVMConstant(type.getUnderlyingType(), attr, inst.getLoc());
|
||||
return false;
|
||||
}
|
||||
|
||||
// A helper to look up remapped operands in the value remapping table.
|
||||
auto lookupValues =
|
||||
[this](const llvm::iterator_range<Instruction::const_operand_iterator>
|
||||
&values) {
|
||||
SmallVector<llvm::Value *, 8> remapped;
|
||||
remapped.reserve(llvm::size(values));
|
||||
for (const Value *v : values) {
|
||||
remapped.push_back(valueMapping.lookup(v));
|
||||
}
|
||||
return remapped;
|
||||
};
|
||||
#include "mlir/LLVMIR/LLVMConversions.inc"
|
||||
|
||||
// Emit function calls. If the "callee" attribute is present, this is a
|
||||
// direct function call and we also need to look up the remapped function
|
||||
// itself. Otherwise, this is an indirect call and the callee is the first
|
||||
// operand, look it up as a normal value. Return the llvm::Value representing
|
||||
// the function result, which may be of llvm::VoidTy type.
|
||||
auto convertCall = [this, lookupValues,
|
||||
auto convertCall = [this,
|
||||
&builder](const Instruction &inst) -> llvm::Value * {
|
||||
auto operands = lookupValues(inst.getOperands());
|
||||
ArrayRef<llvm::Value *> operandsRef(operands);
|
||||
|
@ -287,67 +249,6 @@ bool ModuleTranslation::convertInstruction(const Instruction &inst,
|
|||
return false;
|
||||
}
|
||||
|
||||
if (auto op = inst.dyn_cast<LLVM::ReturnOp>()) {
|
||||
if (op->getNumOperands() == 0)
|
||||
builder.CreateRetVoid();
|
||||
else
|
||||
builder.CreateRet(valueMapping.lookup(op->getOperand(0)));
|
||||
return false;
|
||||
}
|
||||
|
||||
auto extractPosition = [](ArrayAttr attr) {
|
||||
SmallVector<unsigned, 4> position;
|
||||
position.reserve(attr.size());
|
||||
for (Attribute v : attr)
|
||||
position.push_back(v.cast<IntegerAttr>().getValue().getZExtValue());
|
||||
return position;
|
||||
};
|
||||
|
||||
if (auto op = inst.dyn_cast<LLVM::ExtractValueOp>()) {
|
||||
auto attr = op->getAttrOfType<ArrayAttr>("position");
|
||||
valueMapping[op->getResult()] = builder.CreateExtractValue(
|
||||
valueMapping.lookup(op->getOperand()), extractPosition(attr));
|
||||
return false;
|
||||
}
|
||||
if (auto op = inst.dyn_cast<LLVM::InsertValueOp>()) {
|
||||
auto attr = op->getAttrOfType<ArrayAttr>("position");
|
||||
valueMapping[op->getResult()] = builder.CreateInsertValue(
|
||||
valueMapping.lookup(op->getOperand(0)),
|
||||
valueMapping.lookup(op->getOperand(1)), extractPosition(attr));
|
||||
return false;
|
||||
}
|
||||
if (auto op = inst.dyn_cast<LLVM::BitcastOp>()) {
|
||||
valueMapping[op->getResult()] = builder.CreateBitCast(
|
||||
valueMapping.lookup(op->getOperand()),
|
||||
op->getType().cast<LLVM::LLVMType>().getUnderlyingType());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto op = inst.dyn_cast<LLVM::GEPOp>()) {
|
||||
auto mappedOperands = lookupValues(op->getOperands());
|
||||
valueMapping[op->getResult()] =
|
||||
builder.CreateGEP(mappedOperands.front(),
|
||||
llvm::makeArrayRef(mappedOperands).drop_front());
|
||||
return false;
|
||||
}
|
||||
if (auto op = inst.dyn_cast<LLVM::LoadOp>()) {
|
||||
valueMapping[op->getResult()] =
|
||||
builder.CreateLoad(valueMapping.lookup(op->getOperand()));
|
||||
return false;
|
||||
}
|
||||
if (auto op = inst.dyn_cast<LLVM::StoreOp>()) {
|
||||
builder.CreateStore(valueMapping.lookup(op->getOperand(0)),
|
||||
valueMapping.lookup(op->getOperand(1)));
|
||||
return false;
|
||||
}
|
||||
if (auto op = inst.dyn_cast<LLVM::SelectOp>()) {
|
||||
valueMapping[op->getResult()] =
|
||||
builder.CreateSelect(valueMapping.lookup(op->getOperand(0)),
|
||||
valueMapping.lookup(op->getOperand(1)),
|
||||
valueMapping.lookup(op->getOperand(2)));
|
||||
return false;
|
||||
}
|
||||
|
||||
inst.emitError("unsupported or non-LLVM operation: " +
|
||||
inst.getName().getStringRef());
|
||||
return true;
|
||||
|
|
|
@ -0,0 +1,181 @@
|
|||
//===- LLVMIRConversionGen.cpp - MLIR LLVM IR builder generator -----------===//
|
||||
//
|
||||
// Copyright 2019 The MLIR Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
//
|
||||
// This file uses tablegen definitions of the LLVM IR Dialect operations to
|
||||
// generate the code building the LLVM IR from it.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "mlir/TableGen/GenInfo.h"
|
||||
#include "mlir/TableGen/Operator.h"
|
||||
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/Support/FormatVariadic.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/TableGen/Record.h"
|
||||
#include "llvm/TableGen/TableGenBackend.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace mlir;
|
||||
|
||||
static bool emitError(const Twine &message) {
|
||||
llvm::errs() << message << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Helper structure to return a position of the substring in a string.
|
||||
struct StringLoc {
|
||||
size_t pos;
|
||||
size_t length;
|
||||
|
||||
// Take a substring identified by this location in the given string.
|
||||
StringRef in(StringRef str) const { return str.substr(pos, length); }
|
||||
|
||||
// A location is invalid if its position is outside the string.
|
||||
explicit operator bool() { return pos != std::string::npos; }
|
||||
};
|
||||
} // namespace
|
||||
|
||||
// Find the next TableGen variable in the given pattern. These variables start
|
||||
// with a `$` character and can contain alphannumeric characters or underscores.
|
||||
// Return the position of the variable in the pattern and its length, including
|
||||
// the `$` character. The escape syntax `$$` is also detected and returned.
|
||||
static StringLoc findNextVariable(StringRef str) {
|
||||
size_t startPos = str.find('$');
|
||||
if (startPos == std::string::npos)
|
||||
return {startPos, 0};
|
||||
|
||||
// If we see "$$", return immediately.
|
||||
if (startPos != str.size() - 1 && str[startPos + 1] == '$')
|
||||
return {startPos, 2};
|
||||
|
||||
// Otherwise, the symbol spans until the first character that is not
|
||||
// alphanumeric or '_'.
|
||||
size_t endPos = str.find_if_not([](char c) { return isAlnum(c) || c == '_'; },
|
||||
startPos + 1);
|
||||
if (endPos == std::string::npos)
|
||||
endPos = str.size();
|
||||
|
||||
return {startPos, endPos - startPos};
|
||||
}
|
||||
|
||||
// Check if `name` is the name of the variadic argument of `op`. The variadic
|
||||
// argument can only appear at the last position in the list of arguments.
|
||||
static bool isVariadicArgumentName(const tblgen::Operator &op, StringRef name) {
|
||||
return op.hasVariadicOperand() && op.getArgName(op.getNumArgs() - 1) == name;
|
||||
}
|
||||
|
||||
// Check if `result` is a known name of a result of `op`.
|
||||
static bool isResultName(const tblgen::Operator &op, StringRef name) {
|
||||
for (int i = 0, e = op.getNumResults(); i < e; ++i)
|
||||
if (op.getResultName(i) == name)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if `name` is a known name of an attribute of `op`.
|
||||
static bool isAttributeName(const tblgen::Operator &op, StringRef name) {
|
||||
return llvm::any_of(
|
||||
op.getAttributes(),
|
||||
[name](const tblgen::NamedAttribute &attr) { return attr.name == name; });
|
||||
}
|
||||
|
||||
// Check if `name` is a known name of an operand of `op`.
|
||||
static bool isOperandName(const tblgen::Operator &op, StringRef name) {
|
||||
for (int i = 0, e = op.getNumOperands(); i < e; ++i)
|
||||
if (op.getOperand(i).name == name)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Emit to `os` the operator-name driven check and the call to LLVM IRBuilder
|
||||
// for one definition of a LLVM IR Dialect operation. Return true on success.
|
||||
static bool emitOneBuilder(const Record &record, raw_ostream &os) {
|
||||
auto op = tblgen::Operator(record);
|
||||
|
||||
if (!record.getValue("llvmBuilder"))
|
||||
return emitError("no 'llvmBuilder' field for op " + op.getOperationName());
|
||||
|
||||
// Return early if there is no builder specified.
|
||||
auto builderStrRef = record.getValueAsString("llvmBuilder");
|
||||
if (builderStrRef.empty())
|
||||
return true;
|
||||
|
||||
// Progressively create the builder string by replacing $-variables with
|
||||
// value lookups. Keep only the not-yet-traversed part of the builder pattern
|
||||
// to avoid re-traversing the string multiple times.
|
||||
std::string builder;
|
||||
llvm::raw_string_ostream bs(builder);
|
||||
while (auto loc = findNextVariable(builderStrRef)) {
|
||||
auto name = loc.in(builderStrRef).drop_front();
|
||||
// First, insert the non-matched part as is.
|
||||
bs << builderStrRef.substr(0, loc.pos);
|
||||
// Then, rewrite the name based on its kind.
|
||||
bool isVariadicArg = isVariadicArgumentName(op, name);
|
||||
if (isOperandName(op, name)) {
|
||||
auto result = isVariadicArg
|
||||
? formatv("lookupValues(op->{0}())", name)
|
||||
: formatv("valueMapping.lookup(op->{0}())", name);
|
||||
bs << result;
|
||||
} else if (isAttributeName(op, name)) {
|
||||
bs << formatv("op->{0}()", name);
|
||||
} else if (isResultName(op, name)) {
|
||||
bs << formatv("valueMapping[op->{0}()]", name);
|
||||
} else if (name == "_resultType") {
|
||||
bs << "op->getResult()->getType().cast<LLVM::LLVMType>()."
|
||||
"getUnderlyingType()";
|
||||
} else if (name == "_hasResult") {
|
||||
bs << "inst.getNumResults() == 1";
|
||||
} else if (name == "_location") {
|
||||
bs << "inst.getLoc()";
|
||||
} else if (name == "_numOperands") {
|
||||
bs << "inst.getNumOperands()";
|
||||
} else if (name == "$") {
|
||||
bs << '$';
|
||||
} else {
|
||||
return emitError(name + " is neither an argument nor a result of " +
|
||||
op.getOperationName());
|
||||
}
|
||||
// Finally, only keep the untraversed part of the string.
|
||||
builderStrRef = builderStrRef.substr(loc.pos + loc.length);
|
||||
}
|
||||
|
||||
// Output the check and the rewritten builder string.
|
||||
os << "if (auto op = inst.dyn_cast<" << op.getQualCppClassName()
|
||||
<< ">()) {\n";
|
||||
os << bs.str() << builderStrRef << "\n";
|
||||
os << " return false;\n";
|
||||
os << "}\n";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Emit all builders. Returns false on success because of the generator
|
||||
// registration requirements.
|
||||
static bool emitBuilders(const RecordKeeper &recordKeeper, raw_ostream &os) {
|
||||
for (const auto *def : recordKeeper.getAllDerivedDefinitions("LLVM_Op")) {
|
||||
if (!emitOneBuilder(*def, os))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static mlir::GenRegistration
|
||||
genLLVMIRConversions("gen-llvmir-conversions",
|
||||
"Generate LLVM IR conversions", emitBuilders);
|
|
@ -99,6 +99,9 @@ public:
|
|||
// Emit query methods for the named operands.
|
||||
void emitNamedOperands();
|
||||
|
||||
// Emit query methods for the named results.
|
||||
void emitNamedResults();
|
||||
|
||||
// Emit builder method for the operation.
|
||||
void emitBuilder();
|
||||
|
||||
|
@ -165,6 +168,7 @@ void OpEmitter::emit(const Record &def, raw_ostream &os) {
|
|||
<< emitter.op.getOperationName() << "\"; };\n";
|
||||
|
||||
emitter.emitNamedOperands();
|
||||
emitter.emitNamedResults();
|
||||
emitter.emitBuilder();
|
||||
emitter.emitParser();
|
||||
emitter.emitPrinter();
|
||||
|
@ -233,10 +237,48 @@ void OpEmitter::emitNamedOperands() {
|
|||
return this->getInstruction()->getOperand({1});
|
||||
}
|
||||
)";
|
||||
|
||||
const auto variadicOperandMethods = R"( SmallVector<Value *, 4> {0}() {
|
||||
assert(getInstruction()->getNumOperands() >= {1});
|
||||
SmallVector<Value *, 4> operands(
|
||||
std::next(getInstruction()->operand_begin(), {1}),
|
||||
getInstruction()->operand_end());
|
||||
return operands;
|
||||
}
|
||||
SmallVector<const Value *, 4> {0}() const {
|
||||
assert(getInstruction()->getNumOperands() >= {1});
|
||||
SmallVector<const Value *, 4> operands(
|
||||
std::next(getInstruction()->operand_begin(), {1}),
|
||||
getInstruction()->operand_end());
|
||||
return operands;
|
||||
}
|
||||
)";
|
||||
|
||||
for (int i = 0, e = op.getNumOperands(); i != e; ++i) {
|
||||
const auto &operand = op.getOperand(i);
|
||||
if (!operand.type.isVariadic() && !operand.name.empty())
|
||||
os << formatv(operandMethods, operand.name, i);
|
||||
if (!operand.name.empty()) {
|
||||
if (operand.type.isVariadic()) {
|
||||
assert(i == e - 1 && "only the last operand can be variadic");
|
||||
os << formatv(variadicOperandMethods, operand.name, i);
|
||||
} else {
|
||||
os << formatv(operandMethods, operand.name, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpEmitter::emitNamedResults() {
|
||||
const auto resultMethods = R"( Value *{0}() {
|
||||
return this->getInstruction()->getResult({1});
|
||||
}
|
||||
const Value *{0}() const {
|
||||
return this->getInstruction()->getResult({1});
|
||||
}
|
||||
)";
|
||||
for (int i = 0, e = op.getNumResults(); i != e; ++i) {
|
||||
const auto &result = op.getResult(i);
|
||||
if (!result.type.isVariadic() && !result.name.empty())
|
||||
os << formatv(resultMethods, result.name, i);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -505,10 +547,10 @@ void OpEmitter::emitVerifier() {
|
|||
auto attrPred = attr.getPredicate();
|
||||
if (!attrPred.isNull()) {
|
||||
OUT(6) << formatv("if (!({0})) return emitOpError(\"attribute '{1}' "
|
||||
"failed to satisfy constraint of {2}\");\n",
|
||||
"failed to satisfy {2} attribute constraints\");\n",
|
||||
formatv(attrPred.getCondition(),
|
||||
formatv("this->getAttr(\"{0}\")", name)),
|
||||
name, attr.getTableGenDefName());
|
||||
name, attr.getDescription());
|
||||
}
|
||||
|
||||
if (allowMissingAttr)
|
||||
|
|
Loading…
Reference in New Issue