This commit renames the memory attribute on operations to "memory_effects" to be in line with the naming LLVM IR is using.
452 lines
18 KiB
C++
452 lines
18 KiB
C++
//===- LLVMToLLVMIRTranslation.cpp - Translate LLVM dialect to LLVM IR ----===//
|
|
//
|
|
// 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 implements a translation between the MLIR LLVM dialect and LLVM IR.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h"
|
|
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
|
|
#include "mlir/IR/Operation.h"
|
|
#include "mlir/Support/LLVM.h"
|
|
#include "mlir/Target/LLVMIR/ModuleTranslation.h"
|
|
|
|
#include "llvm/IR/IRBuilder.h"
|
|
#include "llvm/IR/InlineAsm.h"
|
|
#include "llvm/IR/MDBuilder.h"
|
|
#include "llvm/IR/MatrixBuilder.h"
|
|
#include "llvm/IR/Operator.h"
|
|
|
|
using namespace mlir;
|
|
using namespace mlir::LLVM;
|
|
using mlir::LLVM::detail::getLLVMConstant;
|
|
|
|
#include "mlir/Dialect/LLVMIR/LLVMConversionEnumsToLLVM.inc"
|
|
|
|
static llvm::FastMathFlags getFastmathFlags(FastmathFlagsInterface &op) {
|
|
using llvmFMF = llvm::FastMathFlags;
|
|
using FuncT = void (llvmFMF::*)(bool);
|
|
const std::pair<FastmathFlags, FuncT> handlers[] = {
|
|
// clang-format off
|
|
{FastmathFlags::nnan, &llvmFMF::setNoNaNs},
|
|
{FastmathFlags::ninf, &llvmFMF::setNoInfs},
|
|
{FastmathFlags::nsz, &llvmFMF::setNoSignedZeros},
|
|
{FastmathFlags::arcp, &llvmFMF::setAllowReciprocal},
|
|
{FastmathFlags::contract, &llvmFMF::setAllowContract},
|
|
{FastmathFlags::afn, &llvmFMF::setApproxFunc},
|
|
{FastmathFlags::reassoc, &llvmFMF::setAllowReassoc},
|
|
// clang-format on
|
|
};
|
|
llvm::FastMathFlags ret;
|
|
::mlir::LLVM::FastmathFlags fmfMlir = op.getFastmathAttr().getValue();
|
|
for (auto it : handlers)
|
|
if (bitEnumContainsAll(fmfMlir, it.first))
|
|
(ret.*(it.second))(true);
|
|
return ret;
|
|
}
|
|
|
|
/// Convert the value of a DenseI64ArrayAttr to a vector of unsigned indices.
|
|
static SmallVector<unsigned> extractPosition(ArrayRef<int64_t> indices) {
|
|
SmallVector<unsigned> position;
|
|
llvm::append_range(position, indices);
|
|
return position;
|
|
}
|
|
|
|
/// Convert an LLVM type to a string for printing in diagnostics.
|
|
static std::string diagStr(const llvm::Type *type) {
|
|
std::string str;
|
|
llvm::raw_string_ostream os(str);
|
|
type->print(os);
|
|
return os.str();
|
|
}
|
|
|
|
/// Get the declaration of an overloaded llvm intrinsic. First we get the
|
|
/// overloaded argument types and/or result type from the CallIntrinsicOp, and
|
|
/// then use those to get the correct declaration of the overloaded intrinsic.
|
|
static FailureOr<llvm::Function *>
|
|
getOverloadedDeclaration(CallIntrinsicOp op, llvm::Intrinsic::ID id,
|
|
llvm::Module *module,
|
|
LLVM::ModuleTranslation &moduleTranslation) {
|
|
SmallVector<llvm::Type *, 8> allArgTys;
|
|
for (Type type : op->getOperandTypes())
|
|
allArgTys.push_back(moduleTranslation.convertType(type));
|
|
|
|
llvm::Type *resTy;
|
|
if (op.getNumResults() == 0)
|
|
resTy = llvm::Type::getVoidTy(module->getContext());
|
|
else
|
|
resTy = moduleTranslation.convertType(op.getResult(0).getType());
|
|
|
|
// ATM we do not support variadic intrinsics.
|
|
llvm::FunctionType *ft = llvm::FunctionType::get(resTy, allArgTys, false);
|
|
|
|
SmallVector<llvm::Intrinsic::IITDescriptor, 8> table;
|
|
getIntrinsicInfoTableEntries(id, table);
|
|
ArrayRef<llvm::Intrinsic::IITDescriptor> tableRef = table;
|
|
|
|
SmallVector<llvm::Type *, 8> overloadedArgTys;
|
|
if (llvm::Intrinsic::matchIntrinsicSignature(ft, tableRef,
|
|
overloadedArgTys) !=
|
|
llvm::Intrinsic::MatchIntrinsicTypesResult::MatchIntrinsicTypes_Match) {
|
|
return mlir::emitError(op.getLoc(), "call intrinsic signature ")
|
|
<< diagStr(ft) << " to overloaded intrinsic " << op.getIntrinAttr()
|
|
<< " does not match any of the overloads";
|
|
}
|
|
|
|
ArrayRef<llvm::Type *> overloadedArgTysRef = overloadedArgTys;
|
|
return llvm::Intrinsic::getDeclaration(module, id, overloadedArgTysRef);
|
|
}
|
|
|
|
/// Builder for LLVM_CallIntrinsicOp
|
|
static LogicalResult
|
|
convertCallLLVMIntrinsicOp(CallIntrinsicOp op, llvm::IRBuilderBase &builder,
|
|
LLVM::ModuleTranslation &moduleTranslation) {
|
|
llvm::Module *module = builder.GetInsertBlock()->getModule();
|
|
llvm::Intrinsic::ID id =
|
|
llvm::Function::lookupIntrinsicID(op.getIntrinAttr());
|
|
if (!id)
|
|
return mlir::emitError(op.getLoc(), "could not find LLVM intrinsic: ")
|
|
<< op.getIntrinAttr();
|
|
|
|
llvm::Function *fn = nullptr;
|
|
if (llvm::Intrinsic::isOverloaded(id)) {
|
|
auto fnOrFailure =
|
|
getOverloadedDeclaration(op, id, module, moduleTranslation);
|
|
if (failed(fnOrFailure))
|
|
return failure();
|
|
fn = *fnOrFailure;
|
|
} else {
|
|
fn = llvm::Intrinsic::getDeclaration(module, id, {});
|
|
}
|
|
|
|
// Check the result type of the call.
|
|
const llvm::Type *intrinType =
|
|
op.getNumResults() == 0
|
|
? llvm::Type::getVoidTy(module->getContext())
|
|
: moduleTranslation.convertType(op.getResultTypes().front());
|
|
if (intrinType != fn->getReturnType()) {
|
|
return mlir::emitError(op.getLoc(), "intrinsic call returns ")
|
|
<< diagStr(intrinType) << " but " << op.getIntrinAttr()
|
|
<< " actually returns " << diagStr(fn->getReturnType());
|
|
}
|
|
|
|
// Check the argument types of the call. If the function is variadic, check
|
|
// the subrange of required arguments.
|
|
if (!fn->getFunctionType()->isVarArg() &&
|
|
op.getNumOperands() != fn->arg_size()) {
|
|
return mlir::emitError(op.getLoc(), "intrinsic call has ")
|
|
<< op.getNumOperands() << " operands but " << op.getIntrinAttr()
|
|
<< " expects " << fn->arg_size();
|
|
}
|
|
if (fn->getFunctionType()->isVarArg() &&
|
|
op.getNumOperands() < fn->arg_size()) {
|
|
return mlir::emitError(op.getLoc(), "intrinsic call has ")
|
|
<< op.getNumOperands() << " operands but variadic "
|
|
<< op.getIntrinAttr() << " expects at least " << fn->arg_size();
|
|
}
|
|
// Check the arguments up to the number the function requires.
|
|
for (unsigned i = 0, e = fn->arg_size(); i != e; ++i) {
|
|
const llvm::Type *expected = fn->getArg(i)->getType();
|
|
const llvm::Type *actual =
|
|
moduleTranslation.convertType(op.getOperandTypes()[i]);
|
|
if (actual != expected) {
|
|
return mlir::emitError(op.getLoc(), "intrinsic call operand #")
|
|
<< i << " has type " << diagStr(actual) << " but "
|
|
<< op.getIntrinAttr() << " expects " << diagStr(expected);
|
|
}
|
|
}
|
|
|
|
FastmathFlagsInterface itf = op;
|
|
builder.setFastMathFlags(getFastmathFlags(itf));
|
|
|
|
auto *inst =
|
|
builder.CreateCall(fn, moduleTranslation.lookupValues(op.getOperands()));
|
|
if (op.getNumResults() == 1)
|
|
moduleTranslation.mapValue(op->getResults().front()) = inst;
|
|
return success();
|
|
}
|
|
|
|
static void convertLinkerOptionsOp(ArrayAttr options,
|
|
llvm::IRBuilderBase &builder,
|
|
LLVM::ModuleTranslation &moduleTranslation) {
|
|
llvm::Module *llvmModule = moduleTranslation.getLLVMModule();
|
|
llvm::LLVMContext &context = llvmModule->getContext();
|
|
llvm::NamedMDNode *linkerMDNode =
|
|
llvmModule->getOrInsertNamedMetadata("llvm.linker.options");
|
|
SmallVector<llvm::Metadata *> MDNodes;
|
|
MDNodes.reserve(options.size());
|
|
for (auto s : options.getAsRange<StringAttr>()) {
|
|
auto *MDNode = llvm::MDString::get(context, s.getValue());
|
|
MDNodes.push_back(MDNode);
|
|
}
|
|
|
|
auto *listMDNode = llvm::MDTuple::get(context, MDNodes);
|
|
linkerMDNode->addOperand(listMDNode);
|
|
}
|
|
|
|
static LogicalResult
|
|
convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
|
|
LLVM::ModuleTranslation &moduleTranslation) {
|
|
|
|
llvm::IRBuilder<>::FastMathFlagGuard fmfGuard(builder);
|
|
if (auto fmf = dyn_cast<FastmathFlagsInterface>(opInst))
|
|
builder.setFastMathFlags(getFastmathFlags(fmf));
|
|
|
|
#include "mlir/Dialect/LLVMIR/LLVMConversions.inc"
|
|
#include "mlir/Dialect/LLVMIR/LLVMIntrinsicConversions.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.
|
|
if (auto callOp = dyn_cast<LLVM::CallOp>(opInst)) {
|
|
auto operands = moduleTranslation.lookupValues(callOp.getOperands());
|
|
ArrayRef<llvm::Value *> operandsRef(operands);
|
|
llvm::CallInst *call;
|
|
if (auto attr = callOp.getCalleeAttr()) {
|
|
call = builder.CreateCall(
|
|
moduleTranslation.lookupFunction(attr.getValue()), operandsRef);
|
|
} else {
|
|
llvm::FunctionType *calleeType = llvm::cast<llvm::FunctionType>(
|
|
moduleTranslation.convertType(callOp.getCalleeFunctionType()));
|
|
call = builder.CreateCall(calleeType, operandsRef.front(),
|
|
operandsRef.drop_front());
|
|
}
|
|
call->setCallingConv(convertCConvToLLVM(callOp.getCConv()));
|
|
call->setTailCallKind(convertTailCallKindToLLVM(callOp.getTailCallKind()));
|
|
if (callOp.getConvergentAttr())
|
|
call->addFnAttr(llvm::Attribute::Convergent);
|
|
if (callOp.getNoUnwindAttr())
|
|
call->addFnAttr(llvm::Attribute::NoUnwind);
|
|
if (callOp.getWillReturnAttr())
|
|
call->addFnAttr(llvm::Attribute::WillReturn);
|
|
|
|
if (MemoryEffectsAttr memAttr = callOp.getMemoryEffectsAttr()) {
|
|
llvm::MemoryEffects memEffects =
|
|
llvm::MemoryEffects(llvm::MemoryEffects::Location::ArgMem,
|
|
convertModRefInfoToLLVM(memAttr.getArgMem())) |
|
|
llvm::MemoryEffects(
|
|
llvm::MemoryEffects::Location::InaccessibleMem,
|
|
convertModRefInfoToLLVM(memAttr.getInaccessibleMem())) |
|
|
llvm::MemoryEffects(llvm::MemoryEffects::Location::Other,
|
|
convertModRefInfoToLLVM(memAttr.getOther()));
|
|
call->setMemoryEffects(memEffects);
|
|
}
|
|
|
|
moduleTranslation.setAccessGroupsMetadata(callOp, call);
|
|
moduleTranslation.setAliasScopeMetadata(callOp, call);
|
|
moduleTranslation.setTBAAMetadata(callOp, call);
|
|
// If the called function has a result, remap the corresponding value. Note
|
|
// that LLVM IR dialect CallOp has either 0 or 1 result.
|
|
if (opInst.getNumResults() != 0)
|
|
moduleTranslation.mapValue(opInst.getResult(0), call);
|
|
// Check that LLVM call returns void for 0-result functions.
|
|
else if (!call->getType()->isVoidTy())
|
|
return failure();
|
|
moduleTranslation.mapCall(callOp, call);
|
|
return success();
|
|
}
|
|
|
|
if (auto inlineAsmOp = dyn_cast<LLVM::InlineAsmOp>(opInst)) {
|
|
// TODO: refactor function type creation which usually occurs in std-LLVM
|
|
// conversion.
|
|
SmallVector<Type, 8> operandTypes;
|
|
llvm::append_range(operandTypes, inlineAsmOp.getOperands().getTypes());
|
|
|
|
Type resultType;
|
|
if (inlineAsmOp.getNumResults() == 0) {
|
|
resultType = LLVM::LLVMVoidType::get(&moduleTranslation.getContext());
|
|
} else {
|
|
assert(inlineAsmOp.getNumResults() == 1);
|
|
resultType = inlineAsmOp.getResultTypes()[0];
|
|
}
|
|
auto ft = LLVM::LLVMFunctionType::get(resultType, operandTypes);
|
|
llvm::InlineAsm *inlineAsmInst =
|
|
inlineAsmOp.getAsmDialect()
|
|
? llvm::InlineAsm::get(
|
|
static_cast<llvm::FunctionType *>(
|
|
moduleTranslation.convertType(ft)),
|
|
inlineAsmOp.getAsmString(), inlineAsmOp.getConstraints(),
|
|
inlineAsmOp.getHasSideEffects(),
|
|
inlineAsmOp.getIsAlignStack(),
|
|
convertAsmDialectToLLVM(*inlineAsmOp.getAsmDialect()))
|
|
: llvm::InlineAsm::get(static_cast<llvm::FunctionType *>(
|
|
moduleTranslation.convertType(ft)),
|
|
inlineAsmOp.getAsmString(),
|
|
inlineAsmOp.getConstraints(),
|
|
inlineAsmOp.getHasSideEffects(),
|
|
inlineAsmOp.getIsAlignStack());
|
|
llvm::CallInst *inst = builder.CreateCall(
|
|
inlineAsmInst,
|
|
moduleTranslation.lookupValues(inlineAsmOp.getOperands()));
|
|
if (auto maybeOperandAttrs = inlineAsmOp.getOperandAttrs()) {
|
|
llvm::AttributeList attrList;
|
|
for (const auto &it : llvm::enumerate(*maybeOperandAttrs)) {
|
|
Attribute attr = it.value();
|
|
if (!attr)
|
|
continue;
|
|
DictionaryAttr dAttr = cast<DictionaryAttr>(attr);
|
|
TypeAttr tAttr =
|
|
cast<TypeAttr>(dAttr.get(InlineAsmOp::getElementTypeAttrName()));
|
|
llvm::AttrBuilder b(moduleTranslation.getLLVMContext());
|
|
llvm::Type *ty = moduleTranslation.convertType(tAttr.getValue());
|
|
b.addTypeAttr(llvm::Attribute::ElementType, ty);
|
|
// shift to account for the returned value (this is always 1 aggregate
|
|
// value in LLVM).
|
|
int shift = (opInst.getNumResults() > 0) ? 1 : 0;
|
|
attrList = attrList.addAttributesAtIndex(
|
|
moduleTranslation.getLLVMContext(), it.index() + shift, b);
|
|
}
|
|
inst->setAttributes(attrList);
|
|
}
|
|
|
|
if (opInst.getNumResults() != 0)
|
|
moduleTranslation.mapValue(opInst.getResult(0), inst);
|
|
return success();
|
|
}
|
|
|
|
if (auto invOp = dyn_cast<LLVM::InvokeOp>(opInst)) {
|
|
auto operands = moduleTranslation.lookupValues(invOp.getCalleeOperands());
|
|
ArrayRef<llvm::Value *> operandsRef(operands);
|
|
llvm::InvokeInst *result;
|
|
if (auto attr = opInst.getAttrOfType<FlatSymbolRefAttr>("callee")) {
|
|
result = builder.CreateInvoke(
|
|
moduleTranslation.lookupFunction(attr.getValue()),
|
|
moduleTranslation.lookupBlock(invOp.getSuccessor(0)),
|
|
moduleTranslation.lookupBlock(invOp.getSuccessor(1)), operandsRef);
|
|
} else {
|
|
llvm::FunctionType *calleeType = llvm::cast<llvm::FunctionType>(
|
|
moduleTranslation.convertType(invOp.getCalleeFunctionType()));
|
|
result = builder.CreateInvoke(
|
|
calleeType, operandsRef.front(),
|
|
moduleTranslation.lookupBlock(invOp.getSuccessor(0)),
|
|
moduleTranslation.lookupBlock(invOp.getSuccessor(1)),
|
|
operandsRef.drop_front());
|
|
}
|
|
result->setCallingConv(convertCConvToLLVM(invOp.getCConv()));
|
|
moduleTranslation.mapBranch(invOp, result);
|
|
// InvokeOp can only have 0 or 1 result
|
|
if (invOp->getNumResults() != 0) {
|
|
moduleTranslation.mapValue(opInst.getResult(0), result);
|
|
return success();
|
|
}
|
|
return success(result->getType()->isVoidTy());
|
|
}
|
|
|
|
if (auto lpOp = dyn_cast<LLVM::LandingpadOp>(opInst)) {
|
|
llvm::Type *ty = moduleTranslation.convertType(lpOp.getType());
|
|
llvm::LandingPadInst *lpi =
|
|
builder.CreateLandingPad(ty, lpOp.getNumOperands());
|
|
lpi->setCleanup(lpOp.getCleanup());
|
|
|
|
// Add clauses
|
|
for (llvm::Value *operand :
|
|
moduleTranslation.lookupValues(lpOp.getOperands())) {
|
|
// All operands should be constant - checked by verifier
|
|
if (auto *constOperand = dyn_cast<llvm::Constant>(operand))
|
|
lpi->addClause(constOperand);
|
|
}
|
|
moduleTranslation.mapValue(lpOp.getResult(), lpi);
|
|
return success();
|
|
}
|
|
|
|
// Emit branches. We need to look up the remapped blocks and ignore the
|
|
// block arguments that were transformed into PHI nodes.
|
|
if (auto brOp = dyn_cast<LLVM::BrOp>(opInst)) {
|
|
llvm::BranchInst *branch =
|
|
builder.CreateBr(moduleTranslation.lookupBlock(brOp.getSuccessor()));
|
|
moduleTranslation.mapBranch(&opInst, branch);
|
|
moduleTranslation.setLoopMetadata(&opInst, branch);
|
|
return success();
|
|
}
|
|
if (auto condbrOp = dyn_cast<LLVM::CondBrOp>(opInst)) {
|
|
llvm::BranchInst *branch = builder.CreateCondBr(
|
|
moduleTranslation.lookupValue(condbrOp.getOperand(0)),
|
|
moduleTranslation.lookupBlock(condbrOp.getSuccessor(0)),
|
|
moduleTranslation.lookupBlock(condbrOp.getSuccessor(1)));
|
|
moduleTranslation.mapBranch(&opInst, branch);
|
|
moduleTranslation.setLoopMetadata(&opInst, branch);
|
|
return success();
|
|
}
|
|
if (auto switchOp = dyn_cast<LLVM::SwitchOp>(opInst)) {
|
|
llvm::SwitchInst *switchInst = builder.CreateSwitch(
|
|
moduleTranslation.lookupValue(switchOp.getValue()),
|
|
moduleTranslation.lookupBlock(switchOp.getDefaultDestination()),
|
|
switchOp.getCaseDestinations().size());
|
|
|
|
// Handle switch with zero cases.
|
|
if (!switchOp.getCaseValues())
|
|
return success();
|
|
|
|
auto *ty = llvm::cast<llvm::IntegerType>(
|
|
moduleTranslation.convertType(switchOp.getValue().getType()));
|
|
for (auto i :
|
|
llvm::zip(llvm::cast<DenseIntElementsAttr>(*switchOp.getCaseValues()),
|
|
switchOp.getCaseDestinations()))
|
|
switchInst->addCase(
|
|
llvm::ConstantInt::get(ty, std::get<0>(i).getLimitedValue()),
|
|
moduleTranslation.lookupBlock(std::get<1>(i)));
|
|
|
|
moduleTranslation.mapBranch(&opInst, switchInst);
|
|
return success();
|
|
}
|
|
|
|
// Emit addressof. We need to look up the global value referenced by the
|
|
// operation and store it in the MLIR-to-LLVM value mapping. This does not
|
|
// emit any LLVM instruction.
|
|
if (auto addressOfOp = dyn_cast<LLVM::AddressOfOp>(opInst)) {
|
|
LLVM::GlobalOp global =
|
|
addressOfOp.getGlobal(moduleTranslation.symbolTable());
|
|
LLVM::LLVMFuncOp function =
|
|
addressOfOp.getFunction(moduleTranslation.symbolTable());
|
|
|
|
// The verifier should not have allowed this.
|
|
assert((global || function) &&
|
|
"referencing an undefined global or function");
|
|
|
|
moduleTranslation.mapValue(
|
|
addressOfOp.getResult(),
|
|
global ? moduleTranslation.lookupGlobal(global)
|
|
: moduleTranslation.lookupFunction(function.getName()));
|
|
return success();
|
|
}
|
|
|
|
return failure();
|
|
}
|
|
|
|
namespace {
|
|
/// Implementation of the dialect interface that converts operations belonging
|
|
/// to the LLVM dialect to LLVM IR.
|
|
class LLVMDialectLLVMIRTranslationInterface
|
|
: public LLVMTranslationDialectInterface {
|
|
public:
|
|
using LLVMTranslationDialectInterface::LLVMTranslationDialectInterface;
|
|
|
|
/// Translates the given operation to LLVM IR using the provided IR builder
|
|
/// and saving the state in `moduleTranslation`.
|
|
LogicalResult
|
|
convertOperation(Operation *op, llvm::IRBuilderBase &builder,
|
|
LLVM::ModuleTranslation &moduleTranslation) const final {
|
|
return convertOperationImpl(*op, builder, moduleTranslation);
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
void mlir::registerLLVMDialectTranslation(DialectRegistry ®istry) {
|
|
registry.insert<LLVM::LLVMDialect>();
|
|
registry.addExtension(+[](MLIRContext *ctx, LLVM::LLVMDialect *dialect) {
|
|
dialect->addInterfaces<LLVMDialectLLVMIRTranslationInterface>();
|
|
});
|
|
}
|
|
|
|
void mlir::registerLLVMDialectTranslation(MLIRContext &context) {
|
|
DialectRegistry registry;
|
|
registerLLVMDialectTranslation(registry);
|
|
context.appendDialectRegistry(registry);
|
|
}
|