14 #include "../PassDetail.h" 37 #include "llvm/ADT/TypeSwitch.h" 38 #include "llvm/IR/DerivedTypes.h" 39 #include "llvm/IR/IRBuilder.h" 40 #include "llvm/IR/Type.h" 41 #include "llvm/Support/CommandLine.h" 42 #include "llvm/Support/FormatVariadic.h" 48 #define PASS_NAME "convert-func-to-llvm" 54 bool filterArgAndResAttrs,
56 for (
const auto &attr : attrs) {
59 attr.getName() ==
"func.varargs" ||
60 (filterArgAndResAttrs &&
64 result.push_back(attr);
70 return DictionaryAttr::get(
72 b.
getNamedAttr(LLVM::LLVMDialect::getStructAttrsAttrName(), attrs));
85 size_t numArguments) {
87 numArguments + 1, DictionaryAttr::get(builder.
getContext()));
89 for (
auto *it = attributes.begin(); it != attributes.end();) {
92 assert(arrayAttrs.size() == numArguments &&
93 "Number of arg attrs and args should match");
94 std::copy(arrayAttrs.begin(), arrayAttrs.end(), allAttrs.begin() + 1);
98 assert(!arrayAttrs.empty() &&
"expected array to be non-empty");
99 allAttrs[0] = (arrayAttrs.size() == 1)
102 it = attributes.erase(it);
112 attributes.emplace_back(newArgAttrs);
115 *argAttrs = newArgAttrs;
129 LLVM::LLVMFuncOp newFuncOp) {
130 auto type = funcOp.getFunctionType();
134 Type wrapperFuncType;
136 std::tie(wrapperFuncType, resultIsNowArg) =
140 auto wrapperFuncOp = rewriter.
create<LLVM::LLVMFuncOp>(
141 loc, llvm::formatv(
"_mlir_ciface_{0}", funcOp.getName()).str(),
142 wrapperFuncType, LLVM::Linkage::External,
false,
143 LLVM::CConv::C, attributes);
149 size_t argOffset = resultIsNowArg ? 1 : 0;
151 Value arg = wrapperFuncOp.getArgument(en.index() + argOffset);
152 if (
auto memrefType = en.value().dyn_cast<MemRefType>()) {
153 Value loaded = rewriter.
create<LLVM::LoadOp>(loc, arg);
158 Value loaded = rewriter.
create<LLVM::LoadOp>(loc, arg);
166 auto call = rewriter.
create<LLVM::CallOp>(loc, newFuncOp, args);
168 if (resultIsNowArg) {
169 rewriter.
create<LLVM::StoreOp>(loc, call.getResult(0),
170 wrapperFuncOp.getArgument(0));
173 rewriter.
create<LLVM::ReturnOp>(loc, call.getResults());
189 LLVM::LLVMFuncOp newFuncOp) {
194 std::tie(wrapperType, resultIsNowArg) =
199 assert(wrapperType &&
"unexpected type conversion failure");
208 auto wrapperFunc = builder.
create<LLVM::LLVMFuncOp>(
209 loc, llvm::formatv(
"_mlir_ciface_{0}", funcOp.getName()).str(),
210 wrapperType, LLVM::Linkage::External,
false,
211 LLVM::CConv::C, attributes);
216 FunctionType type = funcOp.getFunctionType();
218 args.reserve(type.getNumInputs());
219 ValueRange wrapperArgsRange(newFuncOp.getArguments());
221 if (resultIsNowArg) {
228 Value result = builder.
create<LLVM::AllocaOp>(loc, resultType, one);
229 args.push_back(result);
237 auto memRefType = en.value().
dyn_cast<MemRefType>();
239 if (memRefType || unrankedMemRefType) {
240 numToDrop = memRefType
246 wrapperArgsRange.take_front(numToDrop))
248 builder, loc, typeConverter, unrankedMemRefType,
249 wrapperArgsRange.take_front(numToDrop));
256 builder.
create<LLVM::AllocaOp>(loc, ptrTy, one, 0);
257 builder.
create<LLVM::StoreOp>(loc, packed, allocated);
260 arg = wrapperArgsRange[0];
264 wrapperArgsRange = wrapperArgsRange.drop_front(numToDrop);
266 assert(wrapperArgsRange.empty() &&
"did not map some of the arguments");
268 auto call = builder.
create<LLVM::CallOp>(loc, wrapperFunc, args);
270 if (resultIsNowArg) {
271 Value result = builder.
create<LLVM::LoadOp>(loc, args.front());
274 builder.
create<LLVM::ReturnOp>(loc, call.getResults());
287 convertFuncOpToLLVMFuncOp(func::FuncOp funcOp,
291 auto varargsAttr = funcOp->getAttrOfType<
BoolAttr>(
"func.varargs");
293 auto llvmType = getTypeConverter()->convertFunctionSignature(
294 funcOp.getFunctionType(), varargsAttr && varargsAttr.getValue(),
304 if (ArrayAttr resAttrDicts = funcOp.getAllResultAttrs()) {
305 assert(!resAttrDicts.empty() &&
"expected array to be non-empty");
306 auto newResAttrDicts =
307 (funcOp.getNumResults() == 1)
310 {wrapAsStructAttrs(rewriter, resAttrDicts)});
314 if (ArrayAttr argAttrDicts = funcOp.getAllArgAttrs()) {
317 for (
unsigned i = 0, e = funcOp.getNumArguments(); i < e; ++i) {
318 auto mapping = result.getInputMapping(i);
319 assert(mapping &&
"unexpected deletion of function argument");
320 for (
size_t j = 0;
j < mapping->size; ++
j)
321 newArgAttrs[mapping->inputNo +
j] = argAttrDicts[i];
323 attributes.push_back(
328 if (pair.value().getName() ==
"llvm.linkage") {
329 attributes.erase(attributes.begin() + pair.index());
336 LLVM::Linkage linkage = LLVM::Linkage::External;
337 if (funcOp->hasAttr(
"llvm.linkage")) {
339 funcOp->getAttr(
"llvm.linkage").dyn_cast<mlir::LLVM::LinkageAttr>();
342 <<
"Contains llvm.linkage attribute not of type LLVM::LinkageAttr";
345 linkage = attr.getLinkage();
347 auto newFuncOp = rewriter.
create<LLVM::LLVMFuncOp>(
348 funcOp.getLoc(), funcOp.getName(), llvmType, linkage,
349 false, LLVM::CConv::C, attributes);
363 struct FuncOpConversion :
public FuncOpConversionBase {
365 : FuncOpConversionBase(converter) {}
368 matchAndRewrite(func::FuncOp funcOp, OpAdaptor adaptor,
370 auto newFuncOp = convertFuncOpToLLVMFuncOp(funcOp, rewriter);
374 if (funcOp->getAttrOfType<UnitAttr>(
375 LLVM::LLVMDialect::getEmitCWrapperAttrName())) {
376 if (newFuncOp.isVarArg())
377 return funcOp->emitError(
"C interface for variadic functions is not " 380 if (newFuncOp.isExternal())
395 struct BarePtrFuncOpConversion :
public FuncOpConversionBase {
396 using FuncOpConversionBase::FuncOpConversionBase;
399 matchAndRewrite(func::FuncOp funcOp, OpAdaptor adaptor,
410 llvm::to_vector<8>(funcOp.getFunctionType().getInputs());
412 auto newFuncOp = convertFuncOpToLLVMFuncOp(funcOp, rewriter);
415 if (newFuncOp.getBody().empty()) {
423 Block *entryBlock = &newFuncOp.getBody().
front();
425 assert(blockArgs.size() == oldArgTypes.size() &&
426 "The number of arguments and types doesn't match");
430 for (
auto it : llvm::zip(blockArgs, oldArgTypes)) {
432 Type argTy = std::get<1>(it);
438 "Unranked memref is not supported");
439 auto memrefTy = argTy.dyn_cast<MemRefType>();
450 auto placeholder = rewriter.
create<LLVM::UndefOp>(
451 loc, getTypeConverter()->convertType(memrefTy));
455 rewriter, loc, *getTypeConverter(), memrefTy, arg);
468 matchAndRewrite(func::ConstantOp op, OpAdaptor adaptor,
470 auto type = typeConverter->convertType(op.getResult().getType());
475 rewriter.
create<LLVM::AddressOfOp>(op.getLoc(), type, op.getValue());
477 if (attr.getName().strref() ==
"value")
479 newOp->
setAttr(attr.getName(), attr.getValue());
481 rewriter.
replaceOp(op, newOp->getResults());
488 template <
typename CallOpType>
491 using Super = CallOpInterfaceLowering<CallOpType>;
495 matchAndRewrite(CallOpType callOp,
typename CallOpType::Adaptor adaptor,
498 Type packedResult =
nullptr;
499 unsigned numResults = callOp.getNumResults();
500 auto resultTypes = llvm::to_vector<4>(callOp.getResultTypes());
502 if (numResults != 0) {
504 this->getTypeConverter()->packFunctionResults(resultTypes)))
508 auto promoted = this->getTypeConverter()->promoteOperands(
509 callOp.getLoc(), callOp->getOperands(),
510 adaptor.getOperands(), rewriter);
511 auto newOp = rewriter.
create<LLVM::CallOp>(
512 callOp.getLoc(), packedResult ?
TypeRange(packedResult) : TypeRange(),
513 promoted, callOp->getAttrs());
516 if (numResults < 2) {
518 results.append(newOp.result_begin(), newOp.result_end());
522 results.reserve(numResults);
523 for (
unsigned i = 0; i < numResults; ++i) {
525 this->typeConverter->convertType(callOp.getResult(i).getType());
526 results.push_back(rewriter.
create<LLVM::ExtractValueOp>(
527 callOp.getLoc(), type, newOp->getResult(0),
532 if (this->getTypeConverter()->getOptions().useBarePtrCallConv) {
535 assert(results.size() == resultTypes.size() &&
536 "The number of arguments and types doesn't match");
537 this->getTypeConverter()->promoteBarePtrsToDescriptors(
538 rewriter, callOp.getLoc(), resultTypes, results);
539 }
else if (
failed(this->copyUnrankedDescriptors(rewriter, callOp.getLoc(),
540 resultTypes, results,
550 struct CallOpLowering :
public CallOpInterfaceLowering<func::CallOp> {
554 struct CallIndirectOpLowering
555 :
public CallOpInterfaceLowering<func::CallIndirectOp> {
559 struct UnrealizedConversionCastOpLowering
565 matchAndRewrite(UnrealizedConversionCastOp op, OpAdaptor adaptor,
568 if (
succeeded(typeConverter->convertTypes(op.getOutputs().getTypes(),
570 convertedTypes == adaptor.getInputs().getTypes()) {
571 rewriter.
replaceOp(op, adaptor.getInputs());
575 convertedTypes.clear();
576 if (
succeeded(typeConverter->convertTypes(adaptor.getInputs().getTypes(),
578 convertedTypes == op.getOutputs().getType()) {
579 rewriter.
replaceOp(op, adaptor.getInputs());
596 matchAndRewrite(func::ReturnOp op, OpAdaptor adaptor,
599 unsigned numArguments = op.getNumOperands();
602 if (getTypeConverter()->getOptions().useBarePtrCallConv) {
605 for (
auto it : llvm::zip(op->getOperands(), adaptor.getOperands())) {
606 Type oldTy = std::get<0>(it).getType();
607 Value newOperand = std::get<1>(it);
608 if (oldTy.
isa<MemRefType>() && getTypeConverter()->canConvertToBarePtr(
611 newOperand = memrefDesc.
alignedPtr(rewriter, loc);
617 updatedOperands.push_back(newOperand);
620 updatedOperands = llvm::to_vector<4>(adaptor.getOperands());
621 (
void)copyUnrankedDescriptors(rewriter, loc, op.getOperands().getTypes(),
627 if (numArguments == 0) {
632 if (numArguments == 1) {
634 op,
TypeRange(), updatedOperands, op->getAttrs());
640 auto packedType = getTypeConverter()->packFunctionResults(
641 llvm::to_vector<4>(op.getOperandTypes()));
643 Value packed = rewriter.
create<LLVM::UndefOp>(loc, packedType);
644 for (
unsigned i = 0; i < numArguments; ++i) {
645 packed = rewriter.
create<LLVM::InsertValueOp>(
646 loc, packedType, packed, updatedOperands[i],
659 patterns.
add<BarePtrFuncOpConversion>(converter);
661 patterns.
add<FuncOpConversion>(converter);
669 CallIndirectOpLowering,
672 ReturnOpLowering>(converter);
678 struct ConvertFuncToLLVMPass
679 :
public ConvertFuncToLLVMBase<ConvertFuncToLLVMPass> {
680 ConvertFuncToLLVMPass() =
default;
681 ConvertFuncToLLVMPass(
bool useBarePtrCallConv,
unsigned indexBitwidth,
682 bool useAlignedAlloc,
683 const llvm::DataLayout &dataLayout) {
684 this->useBarePtrCallConv = useBarePtrCallConv;
685 this->indexBitwidth = indexBitwidth;
686 this->dataLayout = dataLayout.getStringRepresentation();
690 void runOnOperation()
override {
691 if (
failed(LLVM::LLVMDialect::verifyDataLayoutString(
692 this->dataLayout, [
this](
const Twine &message) {
693 getOperation().emitError() << message.str();
699 ModuleOp m = getOperation();
700 const auto &dataLayoutAnalysis = getAnalysis<DataLayoutAnalysis>();
703 dataLayoutAnalysis.getAtOrAbove(m));
704 options.useBarePtrCallConv = useBarePtrCallConv;
706 options.overrideIndexBitwidth(indexBitwidth);
707 options.dataLayout = llvm::DataLayout(this->dataLayout);
710 &dataLayoutAnalysis);
723 m->setAttr(LLVM::LLVMDialect::getDataLayoutAttrName(),
724 StringAttr::get(m.getContext(), this->dataLayout));
730 return std::make_unique<ConvertFuncToLLVMPass>();
733 std::unique_ptr<OperationPass<ModuleOp>>
739 "ConvertFuncToLLVMPass doesn't support AllocLowering::None");
740 bool useAlignedAlloc =
742 return std::make_unique<ConvertFuncToLLVMPass>(
TODO: Remove this file when SCCP and integer range analysis have been ported to the new framework...
Utility class for operation conversions targeting the LLVM dialect that match exactly one source oper...
Do not lower heap allocations.
StringRef getResultDictAttrName()
Return the name of the attribute used for function argument attributes.
static StringRef getSymbolAttrName()
Return the name of the attribute used for symbol names.
MLIRContext * getContext() const
const LowerToLLVMOptions & getOptions() const
LogicalResult applyPartialConversion(ArrayRef< Operation *> ops, ConversionTarget &target, const FrozenRewritePatternSet &patterns, DenseSet< Operation *> *unconvertedOps=nullptr)
Below we define several entry points for operation conversion.
Attribute getValue() const
Return the value of the attribute.
StringRef getArgDictAttrName()
Return the name of the attribute used for function argument attributes.
LogicalResult convertType(Type t, SmallVectorImpl< Type > &results)
Convert the given type.
Block represents an ordered list of Operations.
void replaceUsesOfBlockArgument(BlockArgument from, Value to)
Replace all the uses of the block argument from with value to.
static void copy(Location loc, Value dst, Value src, Value size, OpBuilder &builder)
Copies the given number of bytes from src to dst pointers.
static unsigned getNumUnpackedValues(MemRefType type)
Returns the number of non-aggregate values that would be produced by unpack.
bool failed(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a failure value...
LLVM dialect function type.
Derived class that automatically populates legalization information for different LLVM ops...
static Value pack(OpBuilder &builder, Location loc, LLVMTypeConverter &converter, MemRefType type, ValueRange values)
Builds IR populating a MemRef descriptor structure from a list of individual values composing that de...
void populateArithmeticToLLVMConversionPatterns(LLVMTypeConverter &converter, RewritePatternSet &patterns)
llvm::DataLayout dataLayout
The data layout of the module to produce.
bool succeeded(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a success value...
static void prependResAttrsToArgAttrs(OpBuilder &builder, SmallVectorImpl< NamedAttribute > &attributes, size_t numArguments)
Combines all result attributes into a single DictionaryAttr and prepends to argument attrs...
StringRef getTypeAttrName()
Return the name of the attribute used for function types.
NamedAttribute getNamedAttr(StringRef name, Attribute val)
ArrayAttr getI64ArrayAttr(ArrayRef< int64_t > values)
unsigned getIndexBitwidth() const
Get the index bitwidth.
void replaceOp(Operation *op, ValueRange newValues) override
PatternRewriter hook for replacing the results of an operation.
static void filterFuncAttributes(ArrayRef< NamedAttribute > attrs, bool filterArgAndResAttrs, SmallVectorImpl< NamedAttribute > &result)
Only retain those attributes that are not constructed by LLVMFuncOp::build.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
NamedAttribute represents a combination of a name and an Attribute value.
LogicalResult success(bool isSuccess=true)
Utility function to generate a LogicalResult.
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
This class represents an efficient way to signal success or failure.
static auto wrapAsStructAttrs(OpBuilder &b, ArrayAttr attrs)
Helper function for wrapping all attributes into a single DictionaryAttr.
LogicalResult failure(bool isFailure=true)
Utility function to generate a LogicalResult.
This class provides all of the information necessary to convert a type signature. ...
bool isCompatibleType(Type type)
Returns true if the given type is compatible with the LLVM dialect.
static void wrapForExternalCallers(OpBuilder &rewriter, Location loc, LLVMTypeConverter &typeConverter, func::FuncOp funcOp, LLVM::LLVMFuncOp newFuncOp)
Creates an auxiliary function with pointer-to-memref-descriptor-struct arguments instead of unpacked ...
IntegerAttr getIntegerAttr(Type type, int64_t value)
static void wrapExternalFunction(OpBuilder &builder, Location loc, LLVMTypeConverter &typeConverter, func::FuncOp funcOp, LLVM::LLVMFuncOp newFuncOp)
Creates an auxiliary function with pointer-to-memref-descriptor-struct arguments instead of unpacked ...
Helper class to produce LLVM dialect operations extracting or inserting elements of a MemRef descript...
Special case of IntegerAttr to represent boolean integers, i.e., signless i1 integers.
std::pair< Type, bool > convertFunctionTypeCWrapper(FunctionType type)
Converts the function type to a C-compatible format, in particular using pointers to memref descripto...
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
static MemRefDescriptor fromStaticShape(OpBuilder &builder, Location loc, LLVMTypeConverter &typeConverter, MemRefType type, Value memory)
Builds IR creating a MemRef descriptor that represents type and populates it with static shape and st...
Use aligned_alloc for heap allocations.
unsigned getNumParams()
Returns the number of arguments to the function.
static LLVMPointerType get(MLIRContext *context, unsigned addressSpace=0)
Gets or creates an instance of LLVM dialect pointer type pointing to an object of pointee type in the...
This class provides an abstraction over the various different ranges of value types.
LogicalResult notifyMatchFailure(Location loc, function_ref< void(Diagnostic &)> reasonCallback) override
PatternRewriter hook for notifying match failure reasons.
BlockArgListType getArguments()
This class represents an argument of a Block.
Eliminates variable at the specified position using Fourier-Motzkin variable elimination.
void inlineRegionBefore(Region ®ion, Region &parent, Region::iterator before) override
PatternRewriter hook for moving blocks out of a region.
void populateFuncToLLVMFuncOpConversionPattern(LLVMTypeConverter &converter, RewritePatternSet &patterns)
Collect the default pattern to convert a FuncOp to the LLVM dialect.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
static unsigned getNumUnpackedValues()
Returns the number of non-aggregate values that would be produced by unpack.
static constexpr unsigned kDeriveIndexBitwidthFromDataLayout
Value to pass as bitwidth for the index type when the converter is expected to derive the bitwidth fr...
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
std::unique_ptr< OperationPass< ModuleOp > > createConvertFuncToLLVMPass()
Creates a pass to convert the Func dialect into the LLVMIR dialect.
static llvm::ManagedStatic< PassManagerOptions > options
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
RAII guard to reset the insertion point of the builder when destroyed.
void setAttr(StringAttr name, Attribute value)
If the an attribute exists with the specified name, change it to the new value.
Value alignedPtr(OpBuilder &builder, Location loc)
Builds IR extracting the aligned pointer from the descriptor.
Type getType() const
Return the type of this value.
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&... args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments...
This class provides a shared interface for ranked and unranked memref types.
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replaces the result op with a new op that is created without verification.
Conversion from types to the LLVM IR dialect.
static void unpack(OpBuilder &builder, Location loc, Value packed, MemRefType type, SmallVectorImpl< Value > &results)
Builds IR extracting individual elements of a MemRef descriptor structure and returning them as resul...
Options to control the LLVM lowering.
This class implements a pattern rewriter for use with ConversionPatterns.
void eraseOp(Operation *op) override
PatternRewriter hook for erasing a dead operation.
void populateFuncToLLVMConversionPatterns(LLVMTypeConverter &converter, RewritePatternSet &patterns)
Collect the patterns to convert from the Func dialect to LLVM.
static Value pack(OpBuilder &builder, Location loc, LLVMTypeConverter &converter, UnrankedMemRefType type, ValueRange values)
Builds IR populating an unranked MemRef descriptor structure from a list of individual constituent va...
void populateControlFlowToLLVMConversionPatterns(LLVMTypeConverter &converter, RewritePatternSet &patterns)
Collect the patterns to convert from the ControlFlow dialect to LLVM.
AllocLowering allocLowering
This class helps build Operations.
ArrayAttr getArrayAttr(ArrayRef< Attribute > value)
This class provides an abstraction over the different types of ranges over Values.
static void unpack(OpBuilder &builder, Location loc, Value packed, SmallVectorImpl< Value > &results)
Builds IR extracting individual elements that compose an unranked memref descriptor and returns them ...
FailureOr< Block * > convertRegionTypes(Region *region, TypeConverter &converter, TypeConverter::SignatureConversion *entryConversion=nullptr)
Convert the types of block arguments within the given region.