15a7b9194SRiver Riddle //===- FuncToLLVM.cpp - Func to LLVM dialect conversion -------------------===//
25a7b9194SRiver Riddle //
35a7b9194SRiver Riddle // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45a7b9194SRiver Riddle // See https://llvm.org/LICENSE.txt for license information.
55a7b9194SRiver Riddle // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65a7b9194SRiver Riddle //
75a7b9194SRiver Riddle //===----------------------------------------------------------------------===//
85a7b9194SRiver Riddle //
95a7b9194SRiver Riddle // This file implements a pass to convert MLIR Func and builtin dialects
105a7b9194SRiver Riddle // into the LLVM IR dialect.
115a7b9194SRiver Riddle //
125a7b9194SRiver Riddle //===----------------------------------------------------------------------===//
135a7b9194SRiver Riddle
145a7b9194SRiver Riddle #include "../PassDetail.h"
155a7b9194SRiver Riddle #include "mlir/Analysis/DataLayoutAnalysis.h"
165a7b9194SRiver Riddle #include "mlir/Conversion/ArithmeticToLLVM/ArithmeticToLLVM.h"
175a7b9194SRiver Riddle #include "mlir/Conversion/ControlFlowToLLVM/ControlFlowToLLVM.h"
185a7b9194SRiver Riddle #include "mlir/Conversion/FuncToLLVM/ConvertFuncToLLVM.h"
195a7b9194SRiver Riddle #include "mlir/Conversion/FuncToLLVM/ConvertFuncToLLVMPass.h"
205a7b9194SRiver Riddle #include "mlir/Conversion/LLVMCommon/ConversionTarget.h"
215a7b9194SRiver Riddle #include "mlir/Conversion/LLVMCommon/Pattern.h"
225a7b9194SRiver Riddle #include "mlir/Conversion/LLVMCommon/VectorPattern.h"
235a7b9194SRiver Riddle #include "mlir/Dialect/Func/IR/FuncOps.h"
245a7b9194SRiver Riddle #include "mlir/Dialect/LLVMIR/FunctionCallUtils.h"
255a7b9194SRiver Riddle #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
265a7b9194SRiver Riddle #include "mlir/Dialect/Utils/StaticValueUtils.h"
275a7b9194SRiver Riddle #include "mlir/IR/Attributes.h"
285a7b9194SRiver Riddle #include "mlir/IR/BlockAndValueMapping.h"
295a7b9194SRiver Riddle #include "mlir/IR/Builders.h"
305a7b9194SRiver Riddle #include "mlir/IR/BuiltinOps.h"
315a7b9194SRiver Riddle #include "mlir/IR/PatternMatch.h"
325a7b9194SRiver Riddle #include "mlir/IR/TypeUtilities.h"
335a7b9194SRiver Riddle #include "mlir/Support/LogicalResult.h"
345a7b9194SRiver Riddle #include "mlir/Support/MathExtras.h"
355a7b9194SRiver Riddle #include "mlir/Transforms/DialectConversion.h"
365a7b9194SRiver Riddle #include "mlir/Transforms/Passes.h"
375a7b9194SRiver Riddle #include "llvm/ADT/TypeSwitch.h"
385a7b9194SRiver Riddle #include "llvm/IR/DerivedTypes.h"
395a7b9194SRiver Riddle #include "llvm/IR/IRBuilder.h"
405a7b9194SRiver Riddle #include "llvm/IR/Type.h"
415a7b9194SRiver Riddle #include "llvm/Support/CommandLine.h"
425a7b9194SRiver Riddle #include "llvm/Support/FormatVariadic.h"
43541d89b0SSam Carroll #include <algorithm>
445a7b9194SRiver Riddle #include <functional>
455a7b9194SRiver Riddle
465a7b9194SRiver Riddle using namespace mlir;
475a7b9194SRiver Riddle
485a7b9194SRiver Riddle #define PASS_NAME "convert-func-to-llvm"
495a7b9194SRiver Riddle
505a7b9194SRiver Riddle /// Only retain those attributes that are not constructed by
515a7b9194SRiver Riddle /// `LLVMFuncOp::build`. If `filterArgAttrs` is set, also filter out argument
525a7b9194SRiver Riddle /// attributes.
filterFuncAttributes(ArrayRef<NamedAttribute> attrs,bool filterArgAndResAttrs,SmallVectorImpl<NamedAttribute> & result)535a7b9194SRiver Riddle static void filterFuncAttributes(ArrayRef<NamedAttribute> attrs,
54541d89b0SSam Carroll bool filterArgAndResAttrs,
555a7b9194SRiver Riddle SmallVectorImpl<NamedAttribute> &result) {
565a7b9194SRiver Riddle for (const auto &attr : attrs) {
575a7b9194SRiver Riddle if (attr.getName() == SymbolTable::getSymbolAttrName() ||
585a7b9194SRiver Riddle attr.getName() == FunctionOpInterface::getTypeAttrName() ||
595a7b9194SRiver Riddle attr.getName() == "func.varargs" ||
60541d89b0SSam Carroll (filterArgAndResAttrs &&
61541d89b0SSam Carroll (attr.getName() == FunctionOpInterface::getArgDictAttrName() ||
62541d89b0SSam Carroll attr.getName() == FunctionOpInterface::getResultDictAttrName())))
635a7b9194SRiver Riddle continue;
645a7b9194SRiver Riddle result.push_back(attr);
655a7b9194SRiver Riddle }
665a7b9194SRiver Riddle }
675a7b9194SRiver Riddle
68541d89b0SSam Carroll /// Helper function for wrapping all attributes into a single DictionaryAttr
wrapAsStructAttrs(OpBuilder & b,ArrayAttr attrs)69541d89b0SSam Carroll static auto wrapAsStructAttrs(OpBuilder &b, ArrayAttr attrs) {
70541d89b0SSam Carroll return DictionaryAttr::get(
71541d89b0SSam Carroll b.getContext(),
72541d89b0SSam Carroll b.getNamedAttr(LLVM::LLVMDialect::getStructAttrsAttrName(), attrs));
73541d89b0SSam Carroll }
74541d89b0SSam Carroll
75541d89b0SSam Carroll /// Combines all result attributes into a single DictionaryAttr
76541d89b0SSam Carroll /// and prepends to argument attrs.
77541d89b0SSam Carroll /// This is intended to be used to format the attributes for a C wrapper
78541d89b0SSam Carroll /// function when the result(s) is converted to the first function argument
79541d89b0SSam Carroll /// (in the multiple return case, all returns get wrapped into a single
80541d89b0SSam Carroll /// argument). The total number of argument attributes should be equal to
81541d89b0SSam Carroll /// (number of function arguments) + 1.
82541d89b0SSam Carroll static void
prependResAttrsToArgAttrs(OpBuilder & builder,SmallVectorImpl<NamedAttribute> & attributes,size_t numArguments)83541d89b0SSam Carroll prependResAttrsToArgAttrs(OpBuilder &builder,
84541d89b0SSam Carroll SmallVectorImpl<NamedAttribute> &attributes,
85541d89b0SSam Carroll size_t numArguments) {
86541d89b0SSam Carroll auto allAttrs = SmallVector<Attribute>(
87541d89b0SSam Carroll numArguments + 1, DictionaryAttr::get(builder.getContext()));
88541d89b0SSam Carroll NamedAttribute *argAttrs = nullptr;
89dacb2713SMehdi Amini for (auto *it = attributes.begin(); it != attributes.end();) {
90541d89b0SSam Carroll if (it->getName() == FunctionOpInterface::getArgDictAttrName()) {
91541d89b0SSam Carroll auto arrayAttrs = it->getValue().cast<ArrayAttr>();
92541d89b0SSam Carroll assert(arrayAttrs.size() == numArguments &&
93541d89b0SSam Carroll "Number of arg attrs and args should match");
94541d89b0SSam Carroll std::copy(arrayAttrs.begin(), arrayAttrs.end(), allAttrs.begin() + 1);
95541d89b0SSam Carroll argAttrs = it;
96541d89b0SSam Carroll } else if (it->getName() == FunctionOpInterface::getResultDictAttrName()) {
97541d89b0SSam Carroll auto arrayAttrs = it->getValue().cast<ArrayAttr>();
98541d89b0SSam Carroll assert(!arrayAttrs.empty() && "expected array to be non-empty");
99541d89b0SSam Carroll allAttrs[0] = (arrayAttrs.size() == 1)
100541d89b0SSam Carroll ? arrayAttrs[0]
101541d89b0SSam Carroll : wrapAsStructAttrs(builder, arrayAttrs);
102541d89b0SSam Carroll it = attributes.erase(it);
103541d89b0SSam Carroll continue;
104541d89b0SSam Carroll }
105541d89b0SSam Carroll it++;
106541d89b0SSam Carroll }
107541d89b0SSam Carroll
108541d89b0SSam Carroll auto newArgAttrs =
109541d89b0SSam Carroll builder.getNamedAttr(FunctionOpInterface::getArgDictAttrName(),
110541d89b0SSam Carroll builder.getArrayAttr(allAttrs));
111541d89b0SSam Carroll if (!argAttrs) {
112541d89b0SSam Carroll attributes.emplace_back(newArgAttrs);
113541d89b0SSam Carroll return;
114541d89b0SSam Carroll }
115541d89b0SSam Carroll *argAttrs = newArgAttrs;
116541d89b0SSam Carroll }
117541d89b0SSam Carroll
1185a7b9194SRiver Riddle /// Creates an auxiliary function with pointer-to-memref-descriptor-struct
1195a7b9194SRiver Riddle /// arguments instead of unpacked arguments. This function can be called from C
1205a7b9194SRiver Riddle /// by passing a pointer to a C struct corresponding to a memref descriptor.
1215a7b9194SRiver Riddle /// Similarly, returned memrefs are passed via pointers to a C struct that is
1225a7b9194SRiver Riddle /// passed as additional argument.
1235a7b9194SRiver Riddle /// Internally, the auxiliary function unpacks the descriptor into individual
1245a7b9194SRiver Riddle /// components and forwards them to `newFuncOp` and forwards the results to
1255a7b9194SRiver Riddle /// the extra arguments.
wrapForExternalCallers(OpBuilder & rewriter,Location loc,LLVMTypeConverter & typeConverter,func::FuncOp funcOp,LLVM::LLVMFuncOp newFuncOp)1265a7b9194SRiver Riddle static void wrapForExternalCallers(OpBuilder &rewriter, Location loc,
1275a7b9194SRiver Riddle LLVMTypeConverter &typeConverter,
12858ceae95SRiver Riddle func::FuncOp funcOp,
12958ceae95SRiver Riddle LLVM::LLVMFuncOp newFuncOp) {
1304a3460a7SRiver Riddle auto type = funcOp.getFunctionType();
1315a7b9194SRiver Riddle SmallVector<NamedAttribute, 4> attributes;
132541d89b0SSam Carroll filterFuncAttributes(funcOp->getAttrs(), /*filterArgAndResAttrs=*/false,
1335a7b9194SRiver Riddle attributes);
1345a7b9194SRiver Riddle Type wrapperFuncType;
1355a7b9194SRiver Riddle bool resultIsNowArg;
1365a7b9194SRiver Riddle std::tie(wrapperFuncType, resultIsNowArg) =
1375a7b9194SRiver Riddle typeConverter.convertFunctionTypeCWrapper(type);
138541d89b0SSam Carroll if (resultIsNowArg)
139541d89b0SSam Carroll prependResAttrsToArgAttrs(rewriter, attributes, funcOp.getNumArguments());
1405a7b9194SRiver Riddle auto wrapperFuncOp = rewriter.create<LLVM::LLVMFuncOp>(
1415a7b9194SRiver Riddle loc, llvm::formatv("_mlir_ciface_{0}", funcOp.getName()).str(),
1420252357bSAlexander Batashev wrapperFuncType, LLVM::Linkage::External, /*dsoLocal*/ false,
1430252357bSAlexander Batashev /*cconv*/ LLVM::CConv::C, attributes);
1445a7b9194SRiver Riddle
1455a7b9194SRiver Riddle OpBuilder::InsertionGuard guard(rewriter);
1465a7b9194SRiver Riddle rewriter.setInsertionPointToStart(wrapperFuncOp.addEntryBlock());
1475a7b9194SRiver Riddle
1485a7b9194SRiver Riddle SmallVector<Value, 8> args;
1495a7b9194SRiver Riddle size_t argOffset = resultIsNowArg ? 1 : 0;
1505a7b9194SRiver Riddle for (auto &en : llvm::enumerate(type.getInputs())) {
1515a7b9194SRiver Riddle Value arg = wrapperFuncOp.getArgument(en.index() + argOffset);
1525a7b9194SRiver Riddle if (auto memrefType = en.value().dyn_cast<MemRefType>()) {
1535a7b9194SRiver Riddle Value loaded = rewriter.create<LLVM::LoadOp>(loc, arg);
1545a7b9194SRiver Riddle MemRefDescriptor::unpack(rewriter, loc, loaded, memrefType, args);
1555a7b9194SRiver Riddle continue;
1565a7b9194SRiver Riddle }
1575a7b9194SRiver Riddle if (en.value().isa<UnrankedMemRefType>()) {
1585a7b9194SRiver Riddle Value loaded = rewriter.create<LLVM::LoadOp>(loc, arg);
1595a7b9194SRiver Riddle UnrankedMemRefDescriptor::unpack(rewriter, loc, loaded, args);
1605a7b9194SRiver Riddle continue;
1615a7b9194SRiver Riddle }
1625a7b9194SRiver Riddle
1635a7b9194SRiver Riddle args.push_back(arg);
1645a7b9194SRiver Riddle }
1655a7b9194SRiver Riddle
1665a7b9194SRiver Riddle auto call = rewriter.create<LLVM::CallOp>(loc, newFuncOp, args);
1675a7b9194SRiver Riddle
1685a7b9194SRiver Riddle if (resultIsNowArg) {
1695a7b9194SRiver Riddle rewriter.create<LLVM::StoreOp>(loc, call.getResult(0),
1705a7b9194SRiver Riddle wrapperFuncOp.getArgument(0));
1715a7b9194SRiver Riddle rewriter.create<LLVM::ReturnOp>(loc, ValueRange{});
1725a7b9194SRiver Riddle } else {
1735a7b9194SRiver Riddle rewriter.create<LLVM::ReturnOp>(loc, call.getResults());
1745a7b9194SRiver Riddle }
1755a7b9194SRiver Riddle }
1765a7b9194SRiver Riddle
1775a7b9194SRiver Riddle /// Creates an auxiliary function with pointer-to-memref-descriptor-struct
1785a7b9194SRiver Riddle /// arguments instead of unpacked arguments. Creates a body for the (external)
1795a7b9194SRiver Riddle /// `newFuncOp` that allocates a memref descriptor on stack, packs the
1805a7b9194SRiver Riddle /// individual arguments into this descriptor and passes a pointer to it into
1815a7b9194SRiver Riddle /// the auxiliary function. If the result of the function cannot be directly
1825a7b9194SRiver Riddle /// returned, we write it to a special first argument that provides a pointer
1835a7b9194SRiver Riddle /// to a corresponding struct. This auxiliary external function is now
1845a7b9194SRiver Riddle /// compatible with functions defined in C using pointers to C structs
1855a7b9194SRiver Riddle /// corresponding to a memref descriptor.
wrapExternalFunction(OpBuilder & builder,Location loc,LLVMTypeConverter & typeConverter,func::FuncOp funcOp,LLVM::LLVMFuncOp newFuncOp)1865a7b9194SRiver Riddle static void wrapExternalFunction(OpBuilder &builder, Location loc,
1875a7b9194SRiver Riddle LLVMTypeConverter &typeConverter,
18858ceae95SRiver Riddle func::FuncOp funcOp,
18958ceae95SRiver Riddle LLVM::LLVMFuncOp newFuncOp) {
1905a7b9194SRiver Riddle OpBuilder::InsertionGuard guard(builder);
1915a7b9194SRiver Riddle
1925a7b9194SRiver Riddle Type wrapperType;
1935a7b9194SRiver Riddle bool resultIsNowArg;
1945a7b9194SRiver Riddle std::tie(wrapperType, resultIsNowArg) =
1954a3460a7SRiver Riddle typeConverter.convertFunctionTypeCWrapper(funcOp.getFunctionType());
1965a7b9194SRiver Riddle // This conversion can only fail if it could not convert one of the argument
1975a7b9194SRiver Riddle // types. But since it has been applied to a non-wrapper function before, it
1985a7b9194SRiver Riddle // should have failed earlier and not reach this point at all.
1995a7b9194SRiver Riddle assert(wrapperType && "unexpected type conversion failure");
2005a7b9194SRiver Riddle
2015a7b9194SRiver Riddle SmallVector<NamedAttribute, 4> attributes;
202541d89b0SSam Carroll filterFuncAttributes(funcOp->getAttrs(), /*filterArgAndResAttrs=*/false,
2035a7b9194SRiver Riddle attributes);
2045a7b9194SRiver Riddle
205541d89b0SSam Carroll if (resultIsNowArg)
206541d89b0SSam Carroll prependResAttrsToArgAttrs(builder, attributes, funcOp.getNumArguments());
2075a7b9194SRiver Riddle // Create the auxiliary function.
2085a7b9194SRiver Riddle auto wrapperFunc = builder.create<LLVM::LLVMFuncOp>(
2095a7b9194SRiver Riddle loc, llvm::formatv("_mlir_ciface_{0}", funcOp.getName()).str(),
2100252357bSAlexander Batashev wrapperType, LLVM::Linkage::External, /*dsoLocal*/ false,
2110252357bSAlexander Batashev /*cconv*/ LLVM::CConv::C, attributes);
2125a7b9194SRiver Riddle
2135a7b9194SRiver Riddle builder.setInsertionPointToStart(newFuncOp.addEntryBlock());
2145a7b9194SRiver Riddle
2155a7b9194SRiver Riddle // Get a ValueRange containing arguments.
2164a3460a7SRiver Riddle FunctionType type = funcOp.getFunctionType();
2175a7b9194SRiver Riddle SmallVector<Value, 8> args;
2185a7b9194SRiver Riddle args.reserve(type.getNumInputs());
2195a7b9194SRiver Riddle ValueRange wrapperArgsRange(newFuncOp.getArguments());
2205a7b9194SRiver Riddle
2215a7b9194SRiver Riddle if (resultIsNowArg) {
2225a7b9194SRiver Riddle // Allocate the struct on the stack and pass the pointer.
2235a7b9194SRiver Riddle Type resultType =
2245a7b9194SRiver Riddle wrapperType.cast<LLVM::LLVMFunctionType>().getParamType(0);
2255a7b9194SRiver Riddle Value one = builder.create<LLVM::ConstantOp>(
2265a7b9194SRiver Riddle loc, typeConverter.convertType(builder.getIndexType()),
2275a7b9194SRiver Riddle builder.getIntegerAttr(builder.getIndexType(), 1));
2285a7b9194SRiver Riddle Value result = builder.create<LLVM::AllocaOp>(loc, resultType, one);
2295a7b9194SRiver Riddle args.push_back(result);
2305a7b9194SRiver Riddle }
2315a7b9194SRiver Riddle
2325a7b9194SRiver Riddle // Iterate over the inputs of the original function and pack values into
2335a7b9194SRiver Riddle // memref descriptors if the original type is a memref.
2345a7b9194SRiver Riddle for (auto &en : llvm::enumerate(type.getInputs())) {
2355a7b9194SRiver Riddle Value arg;
2365a7b9194SRiver Riddle int numToDrop = 1;
2375a7b9194SRiver Riddle auto memRefType = en.value().dyn_cast<MemRefType>();
2385a7b9194SRiver Riddle auto unrankedMemRefType = en.value().dyn_cast<UnrankedMemRefType>();
2395a7b9194SRiver Riddle if (memRefType || unrankedMemRefType) {
2405a7b9194SRiver Riddle numToDrop = memRefType
2415a7b9194SRiver Riddle ? MemRefDescriptor::getNumUnpackedValues(memRefType)
2425a7b9194SRiver Riddle : UnrankedMemRefDescriptor::getNumUnpackedValues();
2435a7b9194SRiver Riddle Value packed =
2445a7b9194SRiver Riddle memRefType
2455a7b9194SRiver Riddle ? MemRefDescriptor::pack(builder, loc, typeConverter, memRefType,
2465a7b9194SRiver Riddle wrapperArgsRange.take_front(numToDrop))
2475a7b9194SRiver Riddle : UnrankedMemRefDescriptor::pack(
2485a7b9194SRiver Riddle builder, loc, typeConverter, unrankedMemRefType,
2495a7b9194SRiver Riddle wrapperArgsRange.take_front(numToDrop));
2505a7b9194SRiver Riddle
2515a7b9194SRiver Riddle auto ptrTy = LLVM::LLVMPointerType::get(packed.getType());
2525a7b9194SRiver Riddle Value one = builder.create<LLVM::ConstantOp>(
2535a7b9194SRiver Riddle loc, typeConverter.convertType(builder.getIndexType()),
2545a7b9194SRiver Riddle builder.getIntegerAttr(builder.getIndexType(), 1));
2555a7b9194SRiver Riddle Value allocated =
2565a7b9194SRiver Riddle builder.create<LLVM::AllocaOp>(loc, ptrTy, one, /*alignment=*/0);
2575a7b9194SRiver Riddle builder.create<LLVM::StoreOp>(loc, packed, allocated);
2585a7b9194SRiver Riddle arg = allocated;
2595a7b9194SRiver Riddle } else {
2605a7b9194SRiver Riddle arg = wrapperArgsRange[0];
2615a7b9194SRiver Riddle }
2625a7b9194SRiver Riddle
2635a7b9194SRiver Riddle args.push_back(arg);
2645a7b9194SRiver Riddle wrapperArgsRange = wrapperArgsRange.drop_front(numToDrop);
2655a7b9194SRiver Riddle }
2665a7b9194SRiver Riddle assert(wrapperArgsRange.empty() && "did not map some of the arguments");
2675a7b9194SRiver Riddle
2685a7b9194SRiver Riddle auto call = builder.create<LLVM::CallOp>(loc, wrapperFunc, args);
2695a7b9194SRiver Riddle
2705a7b9194SRiver Riddle if (resultIsNowArg) {
2715a7b9194SRiver Riddle Value result = builder.create<LLVM::LoadOp>(loc, args.front());
2725a7b9194SRiver Riddle builder.create<LLVM::ReturnOp>(loc, ValueRange{result});
2735a7b9194SRiver Riddle } else {
2745a7b9194SRiver Riddle builder.create<LLVM::ReturnOp>(loc, call.getResults());
2755a7b9194SRiver Riddle }
2765a7b9194SRiver Riddle }
2775a7b9194SRiver Riddle
2785a7b9194SRiver Riddle namespace {
2795a7b9194SRiver Riddle
28058ceae95SRiver Riddle struct FuncOpConversionBase : public ConvertOpToLLVMPattern<func::FuncOp> {
2815a7b9194SRiver Riddle protected:
28258ceae95SRiver Riddle using ConvertOpToLLVMPattern<func::FuncOp>::ConvertOpToLLVMPattern;
2835a7b9194SRiver Riddle
2845a7b9194SRiver Riddle // Convert input FuncOp to LLVMFuncOp by using the LLVMTypeConverter provided
2855a7b9194SRiver Riddle // to this legalization pattern.
2865a7b9194SRiver Riddle LLVM::LLVMFuncOp
convertFuncOpToLLVMFuncOp__anon290525710111::FuncOpConversionBase28758ceae95SRiver Riddle convertFuncOpToLLVMFuncOp(func::FuncOp funcOp,
2885a7b9194SRiver Riddle ConversionPatternRewriter &rewriter) const {
2895a7b9194SRiver Riddle // Convert the original function arguments. They are converted using the
2905a7b9194SRiver Riddle // LLVMTypeConverter provided to this legalization pattern.
2915a7b9194SRiver Riddle auto varargsAttr = funcOp->getAttrOfType<BoolAttr>("func.varargs");
2925a7b9194SRiver Riddle TypeConverter::SignatureConversion result(funcOp.getNumArguments());
2935a7b9194SRiver Riddle auto llvmType = getTypeConverter()->convertFunctionSignature(
2944a3460a7SRiver Riddle funcOp.getFunctionType(), varargsAttr && varargsAttr.getValue(),
2954a3460a7SRiver Riddle result);
2965a7b9194SRiver Riddle if (!llvmType)
2975a7b9194SRiver Riddle return nullptr;
2985a7b9194SRiver Riddle
299541d89b0SSam Carroll // Propagate argument/result attributes to all converted arguments/result
300541d89b0SSam Carroll // obtained after converting a given original argument/result.
3015a7b9194SRiver Riddle SmallVector<NamedAttribute, 4> attributes;
302541d89b0SSam Carroll filterFuncAttributes(funcOp->getAttrs(), /*filterArgAndResAttrs=*/true,
3035a7b9194SRiver Riddle attributes);
304541d89b0SSam Carroll if (ArrayAttr resAttrDicts = funcOp.getAllResultAttrs()) {
305541d89b0SSam Carroll assert(!resAttrDicts.empty() && "expected array to be non-empty");
306541d89b0SSam Carroll auto newResAttrDicts =
307541d89b0SSam Carroll (funcOp.getNumResults() == 1)
308541d89b0SSam Carroll ? resAttrDicts
309541d89b0SSam Carroll : rewriter.getArrayAttr(
310541d89b0SSam Carroll {wrapAsStructAttrs(rewriter, resAttrDicts)});
311541d89b0SSam Carroll attributes.push_back(rewriter.getNamedAttr(
312541d89b0SSam Carroll FunctionOpInterface::getResultDictAttrName(), newResAttrDicts));
313541d89b0SSam Carroll }
3145a7b9194SRiver Riddle if (ArrayAttr argAttrDicts = funcOp.getAllArgAttrs()) {
3155a7b9194SRiver Riddle SmallVector<Attribute, 4> newArgAttrs(
3165a7b9194SRiver Riddle llvmType.cast<LLVM::LLVMFunctionType>().getNumParams());
3175a7b9194SRiver Riddle for (unsigned i = 0, e = funcOp.getNumArguments(); i < e; ++i) {
3185a7b9194SRiver Riddle auto mapping = result.getInputMapping(i);
319ad7ce1e7SKazu Hirata assert(mapping && "unexpected deletion of function argument");
3205a7b9194SRiver Riddle for (size_t j = 0; j < mapping->size; ++j)
3215a7b9194SRiver Riddle newArgAttrs[mapping->inputNo + j] = argAttrDicts[i];
3225a7b9194SRiver Riddle }
3235a7b9194SRiver Riddle attributes.push_back(
3245a7b9194SRiver Riddle rewriter.getNamedAttr(FunctionOpInterface::getArgDictAttrName(),
3255a7b9194SRiver Riddle rewriter.getArrayAttr(newArgAttrs)));
3265a7b9194SRiver Riddle }
3275a7b9194SRiver Riddle for (const auto &pair : llvm::enumerate(attributes)) {
3285a7b9194SRiver Riddle if (pair.value().getName() == "llvm.linkage") {
3295a7b9194SRiver Riddle attributes.erase(attributes.begin() + pair.index());
3305a7b9194SRiver Riddle break;
3315a7b9194SRiver Riddle }
3325a7b9194SRiver Riddle }
3335a7b9194SRiver Riddle
3345a7b9194SRiver Riddle // Create an LLVM function, use external linkage by default until MLIR
3355a7b9194SRiver Riddle // functions have linkage.
3365a7b9194SRiver Riddle LLVM::Linkage linkage = LLVM::Linkage::External;
3375a7b9194SRiver Riddle if (funcOp->hasAttr("llvm.linkage")) {
3385a7b9194SRiver Riddle auto attr =
3395a7b9194SRiver Riddle funcOp->getAttr("llvm.linkage").dyn_cast<mlir::LLVM::LinkageAttr>();
3405a7b9194SRiver Riddle if (!attr) {
3415a7b9194SRiver Riddle funcOp->emitError()
3425a7b9194SRiver Riddle << "Contains llvm.linkage attribute not of type LLVM::LinkageAttr";
3435a7b9194SRiver Riddle return nullptr;
3445a7b9194SRiver Riddle }
3455a7b9194SRiver Riddle linkage = attr.getLinkage();
3465a7b9194SRiver Riddle }
3475a7b9194SRiver Riddle auto newFuncOp = rewriter.create<LLVM::LLVMFuncOp>(
3485a7b9194SRiver Riddle funcOp.getLoc(), funcOp.getName(), llvmType, linkage,
3490252357bSAlexander Batashev /*dsoLocal*/ false, /*cconv*/ LLVM::CConv::C, attributes);
3505a7b9194SRiver Riddle rewriter.inlineRegionBefore(funcOp.getBody(), newFuncOp.getBody(),
3515a7b9194SRiver Riddle newFuncOp.end());
3525a7b9194SRiver Riddle if (failed(rewriter.convertRegionTypes(&newFuncOp.getBody(), *typeConverter,
3535a7b9194SRiver Riddle &result)))
3545a7b9194SRiver Riddle return nullptr;
3555a7b9194SRiver Riddle
3565a7b9194SRiver Riddle return newFuncOp;
3575a7b9194SRiver Riddle }
3585a7b9194SRiver Riddle };
3595a7b9194SRiver Riddle
3605a7b9194SRiver Riddle /// FuncOp legalization pattern that converts MemRef arguments to pointers to
3615a7b9194SRiver Riddle /// MemRef descriptors (LLVM struct data types) containing all the MemRef type
3625a7b9194SRiver Riddle /// information.
3635a7b9194SRiver Riddle struct FuncOpConversion : public FuncOpConversionBase {
FuncOpConversion__anon290525710111::FuncOpConversion3645a7b9194SRiver Riddle FuncOpConversion(LLVMTypeConverter &converter)
3655a7b9194SRiver Riddle : FuncOpConversionBase(converter) {}
3665a7b9194SRiver Riddle
3675a7b9194SRiver Riddle LogicalResult
matchAndRewrite__anon290525710111::FuncOpConversion36858ceae95SRiver Riddle matchAndRewrite(func::FuncOp funcOp, OpAdaptor adaptor,
3695a7b9194SRiver Riddle ConversionPatternRewriter &rewriter) const override {
3705a7b9194SRiver Riddle auto newFuncOp = convertFuncOpToLLVMFuncOp(funcOp, rewriter);
3715a7b9194SRiver Riddle if (!newFuncOp)
3725a7b9194SRiver Riddle return failure();
3735a7b9194SRiver Riddle
374610139d2SAlex Zinenko if (funcOp->getAttrOfType<UnitAttr>(
375610139d2SAlex Zinenko LLVM::LLVMDialect::getEmitCWrapperAttrName())) {
376*856056d1SMin-Yih Hsu if (newFuncOp.isVarArg())
377*856056d1SMin-Yih Hsu return funcOp->emitError("C interface for variadic functions is not "
378*856056d1SMin-Yih Hsu "supported yet.");
379*856056d1SMin-Yih Hsu
3805a7b9194SRiver Riddle if (newFuncOp.isExternal())
3815a7b9194SRiver Riddle wrapExternalFunction(rewriter, funcOp.getLoc(), *getTypeConverter(),
3825a7b9194SRiver Riddle funcOp, newFuncOp);
3835a7b9194SRiver Riddle else
3845a7b9194SRiver Riddle wrapForExternalCallers(rewriter, funcOp.getLoc(), *getTypeConverter(),
3855a7b9194SRiver Riddle funcOp, newFuncOp);
3865a7b9194SRiver Riddle }
3875a7b9194SRiver Riddle
3885a7b9194SRiver Riddle rewriter.eraseOp(funcOp);
3895a7b9194SRiver Riddle return success();
3905a7b9194SRiver Riddle }
3915a7b9194SRiver Riddle };
3925a7b9194SRiver Riddle
3935a7b9194SRiver Riddle /// FuncOp legalization pattern that converts MemRef arguments to bare pointers
3945a7b9194SRiver Riddle /// to the MemRef element type. This will impact the calling convention and ABI.
3955a7b9194SRiver Riddle struct BarePtrFuncOpConversion : public FuncOpConversionBase {
3965a7b9194SRiver Riddle using FuncOpConversionBase::FuncOpConversionBase;
3975a7b9194SRiver Riddle
3985a7b9194SRiver Riddle LogicalResult
matchAndRewrite__anon290525710111::BarePtrFuncOpConversion39958ceae95SRiver Riddle matchAndRewrite(func::FuncOp funcOp, OpAdaptor adaptor,
4005a7b9194SRiver Riddle ConversionPatternRewriter &rewriter) const override {
4015a7b9194SRiver Riddle
4025a7b9194SRiver Riddle // TODO: bare ptr conversion could be handled by argument materialization
4035a7b9194SRiver Riddle // and most of the code below would go away. But to do this, we would need a
4045a7b9194SRiver Riddle // way to distinguish between FuncOp and other regions in the
4055a7b9194SRiver Riddle // addArgumentMaterialization hook.
4065a7b9194SRiver Riddle
4075a7b9194SRiver Riddle // Store the type of memref-typed arguments before the conversion so that we
4085a7b9194SRiver Riddle // can promote them to MemRef descriptor at the beginning of the function.
4095a7b9194SRiver Riddle SmallVector<Type, 8> oldArgTypes =
4104a3460a7SRiver Riddle llvm::to_vector<8>(funcOp.getFunctionType().getInputs());
4115a7b9194SRiver Riddle
4125a7b9194SRiver Riddle auto newFuncOp = convertFuncOpToLLVMFuncOp(funcOp, rewriter);
4135a7b9194SRiver Riddle if (!newFuncOp)
4145a7b9194SRiver Riddle return failure();
4155a7b9194SRiver Riddle if (newFuncOp.getBody().empty()) {
4165a7b9194SRiver Riddle rewriter.eraseOp(funcOp);
4175a7b9194SRiver Riddle return success();
4185a7b9194SRiver Riddle }
4195a7b9194SRiver Riddle
4205a7b9194SRiver Riddle // Promote bare pointers from memref arguments to memref descriptors at the
4215a7b9194SRiver Riddle // beginning of the function so that all the memrefs in the function have a
4225a7b9194SRiver Riddle // uniform representation.
4235a7b9194SRiver Riddle Block *entryBlock = &newFuncOp.getBody().front();
4245a7b9194SRiver Riddle auto blockArgs = entryBlock->getArguments();
4255a7b9194SRiver Riddle assert(blockArgs.size() == oldArgTypes.size() &&
4265a7b9194SRiver Riddle "The number of arguments and types doesn't match");
4275a7b9194SRiver Riddle
4285a7b9194SRiver Riddle OpBuilder::InsertionGuard guard(rewriter);
4295a7b9194SRiver Riddle rewriter.setInsertionPointToStart(entryBlock);
4305a7b9194SRiver Riddle for (auto it : llvm::zip(blockArgs, oldArgTypes)) {
4315a7b9194SRiver Riddle BlockArgument arg = std::get<0>(it);
4325a7b9194SRiver Riddle Type argTy = std::get<1>(it);
4335a7b9194SRiver Riddle
4345a7b9194SRiver Riddle // Unranked memrefs are not supported in the bare pointer calling
4355a7b9194SRiver Riddle // convention. We should have bailed out before in the presence of
4365a7b9194SRiver Riddle // unranked memrefs.
4375a7b9194SRiver Riddle assert(!argTy.isa<UnrankedMemRefType>() &&
4385a7b9194SRiver Riddle "Unranked memref is not supported");
4395a7b9194SRiver Riddle auto memrefTy = argTy.dyn_cast<MemRefType>();
4405a7b9194SRiver Riddle if (!memrefTy)
4415a7b9194SRiver Riddle continue;
4425a7b9194SRiver Riddle
4435a7b9194SRiver Riddle // Replace barePtr with a placeholder (undef), promote barePtr to a ranked
4445a7b9194SRiver Riddle // or unranked memref descriptor and replace placeholder with the last
4455a7b9194SRiver Riddle // instruction of the memref descriptor.
4465a7b9194SRiver Riddle // TODO: The placeholder is needed to avoid replacing barePtr uses in the
4475a7b9194SRiver Riddle // MemRef descriptor instructions. We may want to have a utility in the
4485a7b9194SRiver Riddle // rewriter to properly handle this use case.
4495a7b9194SRiver Riddle Location loc = funcOp.getLoc();
4505a7b9194SRiver Riddle auto placeholder = rewriter.create<LLVM::UndefOp>(
4515a7b9194SRiver Riddle loc, getTypeConverter()->convertType(memrefTy));
4525a7b9194SRiver Riddle rewriter.replaceUsesOfBlockArgument(arg, placeholder);
4535a7b9194SRiver Riddle
4545a7b9194SRiver Riddle Value desc = MemRefDescriptor::fromStaticShape(
4555a7b9194SRiver Riddle rewriter, loc, *getTypeConverter(), memrefTy, arg);
4565a7b9194SRiver Riddle rewriter.replaceOp(placeholder, {desc});
4575a7b9194SRiver Riddle }
4585a7b9194SRiver Riddle
4595a7b9194SRiver Riddle rewriter.eraseOp(funcOp);
4605a7b9194SRiver Riddle return success();
4615a7b9194SRiver Riddle }
4625a7b9194SRiver Riddle };
4635a7b9194SRiver Riddle
4645a7b9194SRiver Riddle struct ConstantOpLowering : public ConvertOpToLLVMPattern<func::ConstantOp> {
4655a7b9194SRiver Riddle using ConvertOpToLLVMPattern<func::ConstantOp>::ConvertOpToLLVMPattern;
4665a7b9194SRiver Riddle
4675a7b9194SRiver Riddle LogicalResult
matchAndRewrite__anon290525710111::ConstantOpLowering4685a7b9194SRiver Riddle matchAndRewrite(func::ConstantOp op, OpAdaptor adaptor,
4695a7b9194SRiver Riddle ConversionPatternRewriter &rewriter) const override {
4705a7b9194SRiver Riddle auto type = typeConverter->convertType(op.getResult().getType());
4715a7b9194SRiver Riddle if (!type || !LLVM::isCompatibleType(type))
4725a7b9194SRiver Riddle return rewriter.notifyMatchFailure(op, "failed to convert result type");
4735a7b9194SRiver Riddle
4745a7b9194SRiver Riddle auto newOp =
4755a7b9194SRiver Riddle rewriter.create<LLVM::AddressOfOp>(op.getLoc(), type, op.getValue());
4765a7b9194SRiver Riddle for (const NamedAttribute &attr : op->getAttrs()) {
4775a7b9194SRiver Riddle if (attr.getName().strref() == "value")
4785a7b9194SRiver Riddle continue;
4795a7b9194SRiver Riddle newOp->setAttr(attr.getName(), attr.getValue());
4805a7b9194SRiver Riddle }
4815a7b9194SRiver Riddle rewriter.replaceOp(op, newOp->getResults());
4825a7b9194SRiver Riddle return success();
4835a7b9194SRiver Riddle }
4845a7b9194SRiver Riddle };
4855a7b9194SRiver Riddle
4865a7b9194SRiver Riddle // A CallOp automatically promotes MemRefType to a sequence of alloca/store and
4875a7b9194SRiver Riddle // passes the pointer to the MemRef across function boundaries.
4885a7b9194SRiver Riddle template <typename CallOpType>
4895a7b9194SRiver Riddle struct CallOpInterfaceLowering : public ConvertOpToLLVMPattern<CallOpType> {
4905a7b9194SRiver Riddle using ConvertOpToLLVMPattern<CallOpType>::ConvertOpToLLVMPattern;
4915a7b9194SRiver Riddle using Super = CallOpInterfaceLowering<CallOpType>;
4925a7b9194SRiver Riddle using Base = ConvertOpToLLVMPattern<CallOpType>;
4935a7b9194SRiver Riddle
4945a7b9194SRiver Riddle LogicalResult
matchAndRewrite__anon290525710111::CallOpInterfaceLowering4955a7b9194SRiver Riddle matchAndRewrite(CallOpType callOp, typename CallOpType::Adaptor adaptor,
4965a7b9194SRiver Riddle ConversionPatternRewriter &rewriter) const override {
4975a7b9194SRiver Riddle // Pack the result types into a struct.
4985a7b9194SRiver Riddle Type packedResult = nullptr;
4995a7b9194SRiver Riddle unsigned numResults = callOp.getNumResults();
5005a7b9194SRiver Riddle auto resultTypes = llvm::to_vector<4>(callOp.getResultTypes());
5015a7b9194SRiver Riddle
5025a7b9194SRiver Riddle if (numResults != 0) {
5035a7b9194SRiver Riddle if (!(packedResult =
5045a7b9194SRiver Riddle this->getTypeConverter()->packFunctionResults(resultTypes)))
5055a7b9194SRiver Riddle return failure();
5065a7b9194SRiver Riddle }
5075a7b9194SRiver Riddle
5085a7b9194SRiver Riddle auto promoted = this->getTypeConverter()->promoteOperands(
5095a7b9194SRiver Riddle callOp.getLoc(), /*opOperands=*/callOp->getOperands(),
5105a7b9194SRiver Riddle adaptor.getOperands(), rewriter);
5115a7b9194SRiver Riddle auto newOp = rewriter.create<LLVM::CallOp>(
5125a7b9194SRiver Riddle callOp.getLoc(), packedResult ? TypeRange(packedResult) : TypeRange(),
5135a7b9194SRiver Riddle promoted, callOp->getAttrs());
5145a7b9194SRiver Riddle
5155a7b9194SRiver Riddle SmallVector<Value, 4> results;
5165a7b9194SRiver Riddle if (numResults < 2) {
5175a7b9194SRiver Riddle // If < 2 results, packing did not do anything and we can just return.
5185a7b9194SRiver Riddle results.append(newOp.result_begin(), newOp.result_end());
5195a7b9194SRiver Riddle } else {
5205a7b9194SRiver Riddle // Otherwise, it had been converted to an operation producing a structure.
5215a7b9194SRiver Riddle // Extract individual results from the structure and return them as list.
5225a7b9194SRiver Riddle results.reserve(numResults);
5235a7b9194SRiver Riddle for (unsigned i = 0; i < numResults; ++i) {
5245a7b9194SRiver Riddle auto type =
5255a7b9194SRiver Riddle this->typeConverter->convertType(callOp.getResult(i).getType());
5265a7b9194SRiver Riddle results.push_back(rewriter.create<LLVM::ExtractValueOp>(
5275a7b9194SRiver Riddle callOp.getLoc(), type, newOp->getResult(0),
5285a7b9194SRiver Riddle rewriter.getI64ArrayAttr(i)));
5295a7b9194SRiver Riddle }
5305a7b9194SRiver Riddle }
5315a7b9194SRiver Riddle
5325a7b9194SRiver Riddle if (this->getTypeConverter()->getOptions().useBarePtrCallConv) {
5335a7b9194SRiver Riddle // For the bare-ptr calling convention, promote memref results to
5345a7b9194SRiver Riddle // descriptors.
5355a7b9194SRiver Riddle assert(results.size() == resultTypes.size() &&
5365a7b9194SRiver Riddle "The number of arguments and types doesn't match");
5375a7b9194SRiver Riddle this->getTypeConverter()->promoteBarePtrsToDescriptors(
5385a7b9194SRiver Riddle rewriter, callOp.getLoc(), resultTypes, results);
5395a7b9194SRiver Riddle } else if (failed(this->copyUnrankedDescriptors(rewriter, callOp.getLoc(),
5405a7b9194SRiver Riddle resultTypes, results,
5415a7b9194SRiver Riddle /*toDynamic=*/false))) {
5425a7b9194SRiver Riddle return failure();
5435a7b9194SRiver Riddle }
5445a7b9194SRiver Riddle
5455a7b9194SRiver Riddle rewriter.replaceOp(callOp, results);
5465a7b9194SRiver Riddle return success();
5475a7b9194SRiver Riddle }
5485a7b9194SRiver Riddle };
5495a7b9194SRiver Riddle
5505a7b9194SRiver Riddle struct CallOpLowering : public CallOpInterfaceLowering<func::CallOp> {
5515a7b9194SRiver Riddle using Super::Super;
5525a7b9194SRiver Riddle };
5535a7b9194SRiver Riddle
5545a7b9194SRiver Riddle struct CallIndirectOpLowering
5555a7b9194SRiver Riddle : public CallOpInterfaceLowering<func::CallIndirectOp> {
5565a7b9194SRiver Riddle using Super::Super;
5575a7b9194SRiver Riddle };
5585a7b9194SRiver Riddle
5595a7b9194SRiver Riddle struct UnrealizedConversionCastOpLowering
5605a7b9194SRiver Riddle : public ConvertOpToLLVMPattern<UnrealizedConversionCastOp> {
5615a7b9194SRiver Riddle using ConvertOpToLLVMPattern<
5625a7b9194SRiver Riddle UnrealizedConversionCastOp>::ConvertOpToLLVMPattern;
5635a7b9194SRiver Riddle
5645a7b9194SRiver Riddle LogicalResult
matchAndRewrite__anon290525710111::UnrealizedConversionCastOpLowering5655a7b9194SRiver Riddle matchAndRewrite(UnrealizedConversionCastOp op, OpAdaptor adaptor,
5665a7b9194SRiver Riddle ConversionPatternRewriter &rewriter) const override {
5675a7b9194SRiver Riddle SmallVector<Type> convertedTypes;
568f8d5c73cSRiver Riddle if (succeeded(typeConverter->convertTypes(op.getOutputs().getTypes(),
5695a7b9194SRiver Riddle convertedTypes)) &&
570f8d5c73cSRiver Riddle convertedTypes == adaptor.getInputs().getTypes()) {
571f8d5c73cSRiver Riddle rewriter.replaceOp(op, adaptor.getInputs());
5725a7b9194SRiver Riddle return success();
5735a7b9194SRiver Riddle }
5745a7b9194SRiver Riddle
5755a7b9194SRiver Riddle convertedTypes.clear();
576f8d5c73cSRiver Riddle if (succeeded(typeConverter->convertTypes(adaptor.getInputs().getTypes(),
5775a7b9194SRiver Riddle convertedTypes)) &&
578f8d5c73cSRiver Riddle convertedTypes == op.getOutputs().getType()) {
579f8d5c73cSRiver Riddle rewriter.replaceOp(op, adaptor.getInputs());
5805a7b9194SRiver Riddle return success();
5815a7b9194SRiver Riddle }
5825a7b9194SRiver Riddle return failure();
5835a7b9194SRiver Riddle }
5845a7b9194SRiver Riddle };
5855a7b9194SRiver Riddle
5865a7b9194SRiver Riddle // Special lowering pattern for `ReturnOps`. Unlike all other operations,
5875a7b9194SRiver Riddle // `ReturnOp` interacts with the function signature and must have as many
5885a7b9194SRiver Riddle // operands as the function has return values. Because in LLVM IR, functions
5895a7b9194SRiver Riddle // can only return 0 or 1 value, we pack multiple values into a structure type.
5905a7b9194SRiver Riddle // Emit `UndefOp` followed by `InsertValueOp`s to create such structure if
5915a7b9194SRiver Riddle // necessary before returning it
5925a7b9194SRiver Riddle struct ReturnOpLowering : public ConvertOpToLLVMPattern<func::ReturnOp> {
5935a7b9194SRiver Riddle using ConvertOpToLLVMPattern<func::ReturnOp>::ConvertOpToLLVMPattern;
5945a7b9194SRiver Riddle
5955a7b9194SRiver Riddle LogicalResult
matchAndRewrite__anon290525710111::ReturnOpLowering5965a7b9194SRiver Riddle matchAndRewrite(func::ReturnOp op, OpAdaptor adaptor,
5975a7b9194SRiver Riddle ConversionPatternRewriter &rewriter) const override {
5985a7b9194SRiver Riddle Location loc = op.getLoc();
5995a7b9194SRiver Riddle unsigned numArguments = op.getNumOperands();
6005a7b9194SRiver Riddle SmallVector<Value, 4> updatedOperands;
6015a7b9194SRiver Riddle
6025a7b9194SRiver Riddle if (getTypeConverter()->getOptions().useBarePtrCallConv) {
6035a7b9194SRiver Riddle // For the bare-ptr calling convention, extract the aligned pointer to
6045a7b9194SRiver Riddle // be returned from the memref descriptor.
6055a7b9194SRiver Riddle for (auto it : llvm::zip(op->getOperands(), adaptor.getOperands())) {
6065a7b9194SRiver Riddle Type oldTy = std::get<0>(it).getType();
6075a7b9194SRiver Riddle Value newOperand = std::get<1>(it);
608ae0fb613SLorenzo Chelini if (oldTy.isa<MemRefType>() && getTypeConverter()->canConvertToBarePtr(
609ae0fb613SLorenzo Chelini oldTy.cast<BaseMemRefType>())) {
6105a7b9194SRiver Riddle MemRefDescriptor memrefDesc(newOperand);
6115a7b9194SRiver Riddle newOperand = memrefDesc.alignedPtr(rewriter, loc);
6125a7b9194SRiver Riddle } else if (oldTy.isa<UnrankedMemRefType>()) {
6135a7b9194SRiver Riddle // Unranked memref is not supported in the bare pointer calling
6145a7b9194SRiver Riddle // convention.
6155a7b9194SRiver Riddle return failure();
6165a7b9194SRiver Riddle }
6175a7b9194SRiver Riddle updatedOperands.push_back(newOperand);
6185a7b9194SRiver Riddle }
6195a7b9194SRiver Riddle } else {
6205a7b9194SRiver Riddle updatedOperands = llvm::to_vector<4>(adaptor.getOperands());
6215a7b9194SRiver Riddle (void)copyUnrankedDescriptors(rewriter, loc, op.getOperands().getTypes(),
6225a7b9194SRiver Riddle updatedOperands,
6235a7b9194SRiver Riddle /*toDynamic=*/true);
6245a7b9194SRiver Riddle }
6255a7b9194SRiver Riddle
6265a7b9194SRiver Riddle // If ReturnOp has 0 or 1 operand, create it and return immediately.
6275a7b9194SRiver Riddle if (numArguments == 0) {
6285a7b9194SRiver Riddle rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, TypeRange(), ValueRange(),
6295a7b9194SRiver Riddle op->getAttrs());
6305a7b9194SRiver Riddle return success();
6315a7b9194SRiver Riddle }
6325a7b9194SRiver Riddle if (numArguments == 1) {
6335a7b9194SRiver Riddle rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(
6345a7b9194SRiver Riddle op, TypeRange(), updatedOperands, op->getAttrs());
6355a7b9194SRiver Riddle return success();
6365a7b9194SRiver Riddle }
6375a7b9194SRiver Riddle
6385a7b9194SRiver Riddle // Otherwise, we need to pack the arguments into an LLVM struct type before
6395a7b9194SRiver Riddle // returning.
6405a7b9194SRiver Riddle auto packedType = getTypeConverter()->packFunctionResults(
6415a7b9194SRiver Riddle llvm::to_vector<4>(op.getOperandTypes()));
6425a7b9194SRiver Riddle
6435a7b9194SRiver Riddle Value packed = rewriter.create<LLVM::UndefOp>(loc, packedType);
6445a7b9194SRiver Riddle for (unsigned i = 0; i < numArguments; ++i) {
6455a7b9194SRiver Riddle packed = rewriter.create<LLVM::InsertValueOp>(
6465a7b9194SRiver Riddle loc, packedType, packed, updatedOperands[i],
6475a7b9194SRiver Riddle rewriter.getI64ArrayAttr(i));
6485a7b9194SRiver Riddle }
6495a7b9194SRiver Riddle rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, TypeRange(), packed,
6505a7b9194SRiver Riddle op->getAttrs());
6515a7b9194SRiver Riddle return success();
6525a7b9194SRiver Riddle }
6535a7b9194SRiver Riddle };
6545a7b9194SRiver Riddle } // namespace
6555a7b9194SRiver Riddle
populateFuncToLLVMFuncOpConversionPattern(LLVMTypeConverter & converter,RewritePatternSet & patterns)6565a7b9194SRiver Riddle void mlir::populateFuncToLLVMFuncOpConversionPattern(
6575a7b9194SRiver Riddle LLVMTypeConverter &converter, RewritePatternSet &patterns) {
6585a7b9194SRiver Riddle if (converter.getOptions().useBarePtrCallConv)
6595a7b9194SRiver Riddle patterns.add<BarePtrFuncOpConversion>(converter);
6605a7b9194SRiver Riddle else
6615a7b9194SRiver Riddle patterns.add<FuncOpConversion>(converter);
6625a7b9194SRiver Riddle }
6635a7b9194SRiver Riddle
populateFuncToLLVMConversionPatterns(LLVMTypeConverter & converter,RewritePatternSet & patterns)6645a7b9194SRiver Riddle void mlir::populateFuncToLLVMConversionPatterns(LLVMTypeConverter &converter,
6655a7b9194SRiver Riddle RewritePatternSet &patterns) {
6665a7b9194SRiver Riddle populateFuncToLLVMFuncOpConversionPattern(converter, patterns);
6675a7b9194SRiver Riddle // clang-format off
6685a7b9194SRiver Riddle patterns.add<
6695a7b9194SRiver Riddle CallIndirectOpLowering,
6705a7b9194SRiver Riddle CallOpLowering,
6715a7b9194SRiver Riddle ConstantOpLowering,
6725a7b9194SRiver Riddle ReturnOpLowering>(converter);
6735a7b9194SRiver Riddle // clang-format on
6745a7b9194SRiver Riddle }
6755a7b9194SRiver Riddle
6765a7b9194SRiver Riddle namespace {
6775a7b9194SRiver Riddle /// A pass converting Func operations into the LLVM IR dialect.
6785a7b9194SRiver Riddle struct ConvertFuncToLLVMPass
6795a7b9194SRiver Riddle : public ConvertFuncToLLVMBase<ConvertFuncToLLVMPass> {
6805a7b9194SRiver Riddle ConvertFuncToLLVMPass() = default;
ConvertFuncToLLVMPass__anon290525710211::ConvertFuncToLLVMPass681610139d2SAlex Zinenko ConvertFuncToLLVMPass(bool useBarePtrCallConv, unsigned indexBitwidth,
682610139d2SAlex Zinenko bool useAlignedAlloc,
6835a7b9194SRiver Riddle const llvm::DataLayout &dataLayout) {
6845a7b9194SRiver Riddle this->useBarePtrCallConv = useBarePtrCallConv;
6855a7b9194SRiver Riddle this->indexBitwidth = indexBitwidth;
6865a7b9194SRiver Riddle this->dataLayout = dataLayout.getStringRepresentation();
6875a7b9194SRiver Riddle }
6885a7b9194SRiver Riddle
6895a7b9194SRiver Riddle /// Run the dialect converter on the module.
runOnOperation__anon290525710211::ConvertFuncToLLVMPass6905a7b9194SRiver Riddle void runOnOperation() override {
6915a7b9194SRiver Riddle if (failed(LLVM::LLVMDialect::verifyDataLayoutString(
6925a7b9194SRiver Riddle this->dataLayout, [this](const Twine &message) {
6935a7b9194SRiver Riddle getOperation().emitError() << message.str();
6945a7b9194SRiver Riddle }))) {
6955a7b9194SRiver Riddle signalPassFailure();
6965a7b9194SRiver Riddle return;
6975a7b9194SRiver Riddle }
6985a7b9194SRiver Riddle
6995a7b9194SRiver Riddle ModuleOp m = getOperation();
7005a7b9194SRiver Riddle const auto &dataLayoutAnalysis = getAnalysis<DataLayoutAnalysis>();
7015a7b9194SRiver Riddle
7025a7b9194SRiver Riddle LowerToLLVMOptions options(&getContext(),
7035a7b9194SRiver Riddle dataLayoutAnalysis.getAtOrAbove(m));
7045a7b9194SRiver Riddle options.useBarePtrCallConv = useBarePtrCallConv;
7055a7b9194SRiver Riddle if (indexBitwidth != kDeriveIndexBitwidthFromDataLayout)
7065a7b9194SRiver Riddle options.overrideIndexBitwidth(indexBitwidth);
7075a7b9194SRiver Riddle options.dataLayout = llvm::DataLayout(this->dataLayout);
7085a7b9194SRiver Riddle
7095a7b9194SRiver Riddle LLVMTypeConverter typeConverter(&getContext(), options,
7105a7b9194SRiver Riddle &dataLayoutAnalysis);
7115a7b9194SRiver Riddle
7125a7b9194SRiver Riddle RewritePatternSet patterns(&getContext());
7135a7b9194SRiver Riddle populateFuncToLLVMConversionPatterns(typeConverter, patterns);
7145a7b9194SRiver Riddle
7155a7b9194SRiver Riddle // TODO: Remove these in favor of their dedicated conversion passes.
7165a7b9194SRiver Riddle arith::populateArithmeticToLLVMConversionPatterns(typeConverter, patterns);
7175a7b9194SRiver Riddle cf::populateControlFlowToLLVMConversionPatterns(typeConverter, patterns);
7185a7b9194SRiver Riddle
7195a7b9194SRiver Riddle LLVMConversionTarget target(getContext());
7205a7b9194SRiver Riddle if (failed(applyPartialConversion(m, target, std::move(patterns))))
7215a7b9194SRiver Riddle signalPassFailure();
7225a7b9194SRiver Riddle
7235a7b9194SRiver Riddle m->setAttr(LLVM::LLVMDialect::getDataLayoutAttrName(),
7245a7b9194SRiver Riddle StringAttr::get(m.getContext(), this->dataLayout));
7255a7b9194SRiver Riddle }
7265a7b9194SRiver Riddle };
7275a7b9194SRiver Riddle } // namespace
7285a7b9194SRiver Riddle
createConvertFuncToLLVMPass()7295a7b9194SRiver Riddle std::unique_ptr<OperationPass<ModuleOp>> mlir::createConvertFuncToLLVMPass() {
7305a7b9194SRiver Riddle return std::make_unique<ConvertFuncToLLVMPass>();
7315a7b9194SRiver Riddle }
7325a7b9194SRiver Riddle
7335a7b9194SRiver Riddle std::unique_ptr<OperationPass<ModuleOp>>
createConvertFuncToLLVMPass(const LowerToLLVMOptions & options)7345a7b9194SRiver Riddle mlir::createConvertFuncToLLVMPass(const LowerToLLVMOptions &options) {
7355a7b9194SRiver Riddle auto allocLowering = options.allocLowering;
7365a7b9194SRiver Riddle // There is no way to provide additional patterns for pass, so
7375a7b9194SRiver Riddle // AllocLowering::None will always fail.
7385a7b9194SRiver Riddle assert(allocLowering != LowerToLLVMOptions::AllocLowering::None &&
7395a7b9194SRiver Riddle "ConvertFuncToLLVMPass doesn't support AllocLowering::None");
7405a7b9194SRiver Riddle bool useAlignedAlloc =
7415a7b9194SRiver Riddle (allocLowering == LowerToLLVMOptions::AllocLowering::AlignedAlloc);
7425a7b9194SRiver Riddle return std::make_unique<ConvertFuncToLLVMPass>(
743610139d2SAlex Zinenko options.useBarePtrCallConv, options.getIndexBitwidth(), useAlignedAlloc,
744610139d2SAlex Zinenko options.dataLayout);
7455a7b9194SRiver Riddle }
746