MLIR 23.0.0git
FuncToLLVM.cpp
Go to the documentation of this file.
1//===- FuncToLLVM.cpp - Func to LLVM dialect conversion -------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements a pass to convert MLIR Func and builtin dialects
10// into the LLVM IR dialect.
11//
12//===----------------------------------------------------------------------===//
13
15
28#include "mlir/IR/Attributes.h"
29#include "mlir/IR/Builders.h"
31#include "mlir/IR/BuiltinOps.h"
33#include "mlir/IR/SymbolTable.h"
37#include "llvm/ADT/SmallVector.h"
38#include "llvm/IR/Type.h"
39#include "llvm/Support/DebugLog.h"
40#include "llvm/Support/FormatVariadic.h"
41#include <optional>
42
43namespace mlir {
44#define GEN_PASS_DEF_CONVERTFUNCTOLLVMPASS
45#define GEN_PASS_DEF_SETLLVMMODULEDATALAYOUTPASS
46#include "mlir/Conversion/Passes.h.inc"
47} // namespace mlir
48
49using namespace mlir;
50
51#define PASS_NAME "convert-func-to-llvm"
52#define DEBUG_TYPE PASS_NAME
53
54static constexpr StringRef varargsAttrName = "func.varargs";
55static constexpr StringRef linkageAttrName = "llvm.linkage";
56static constexpr StringRef barePtrAttrName = "llvm.bareptr";
57
58/// Return `true` if the `op` should use bare pointer calling convention.
60 const LLVMTypeConverter *typeConverter) {
61 return (op && op->hasAttr(barePtrAttrName)) ||
62 typeConverter->getOptions().useBarePtrCallConv;
63}
64
65static bool isDiscardableAttr(StringRef name) {
66 return name == linkageAttrName || name == varargsAttrName ||
67 name == LLVM::LLVMDialect::getReadnoneAttrName();
68}
69
70/// Only retain those attributes that are not constructed by
71/// `LLVMFuncOp::build`.
72static void filterFuncAttributes(FunctionOpInterface func,
74 for (const NamedAttribute &attr : func->getDiscardableAttrs()) {
75 if (isDiscardableAttr(attr.getName().strref()))
76 continue;
77 result.push_back(attr);
78 }
79}
80
81/// Propagate argument/results attributes.
82static void propagateArgResAttrs(OpBuilder &builder, bool resultStructType,
83 FunctionOpInterface funcOp,
84 LLVM::LLVMFuncOp wrapperFuncOp) {
85 auto argAttrs = funcOp.getAllArgAttrs();
86 if (!resultStructType) {
87 if (auto resAttrs = funcOp.getAllResultAttrs())
88 wrapperFuncOp.setAllResultAttrs(resAttrs);
89 if (argAttrs)
90 wrapperFuncOp.setAllArgAttrs(argAttrs);
91 } else {
92 SmallVector<Attribute> argAttributes;
93 // Only modify the argument and result attributes when the result is now
94 // an argument.
95 if (argAttrs) {
96 argAttributes.push_back(builder.getDictionaryAttr({}));
97 argAttributes.append(argAttrs.begin(), argAttrs.end());
98 wrapperFuncOp.setAllArgAttrs(argAttributes);
99 }
100 }
101 cast<FunctionOpInterface>(wrapperFuncOp.getOperation())
102 .setVisibility(funcOp.getVisibility());
103}
104
105/// Creates an auxiliary function with pointer-to-memref-descriptor-struct
106/// arguments instead of unpacked arguments. This function can be called from C
107/// by passing a pointer to a C struct corresponding to a memref descriptor.
108/// Similarly, returned memrefs are passed via pointers to a C struct that is
109/// passed as additional argument.
110/// Internally, the auxiliary function unpacks the descriptor into individual
111/// components and forwards them to `newFuncOp` and forwards the results to
112/// the extra arguments.
113static void wrapForExternalCallers(OpBuilder &rewriter, Location loc,
114 const LLVMTypeConverter &typeConverter,
115 FunctionOpInterface funcOp,
116 LLVM::LLVMFuncOp newFuncOp) {
117 auto type = cast<FunctionType>(funcOp.getFunctionType());
118 auto [wrapperFuncType, resultStructType] =
119 typeConverter.convertFunctionTypeCWrapper(type);
120
122 filterFuncAttributes(funcOp, attributes);
123
124 auto wrapperFuncOp = LLVM::LLVMFuncOp::create(
125 rewriter, loc, llvm::formatv("_mlir_ciface_{0}", funcOp.getName()).str(),
126 wrapperFuncType, LLVM::Linkage::External, /*dsoLocal=*/false,
127 /*cconv=*/LLVM::CConv::C, /*comdat=*/nullptr, attributes);
128 propagateArgResAttrs(rewriter, !!resultStructType, funcOp, wrapperFuncOp);
129
130 OpBuilder::InsertionGuard guard(rewriter);
131 rewriter.setInsertionPointToStart(wrapperFuncOp.addEntryBlock(rewriter));
132
134 size_t argOffset = resultStructType ? 1 : 0;
135 for (auto [index, argType] : llvm::enumerate(type.getInputs())) {
136 Value arg = wrapperFuncOp.getArgument(index + argOffset);
137 if (auto memrefType = dyn_cast<MemRefType>(argType)) {
138 Value loaded = LLVM::LoadOp::create(
139 rewriter, loc, typeConverter.convertType(memrefType), arg);
140 MemRefDescriptor::unpack(rewriter, loc, loaded, memrefType, args);
141 continue;
142 }
143 if (isa<UnrankedMemRefType>(argType)) {
144 Value loaded = LLVM::LoadOp::create(
145 rewriter, loc, typeConverter.convertType(argType), arg);
146 UnrankedMemRefDescriptor::unpack(rewriter, loc, loaded, args);
147 continue;
148 }
149
150 args.push_back(arg);
151 }
152
153 auto call = LLVM::CallOp::create(rewriter, loc, newFuncOp, args);
154
155 if (resultStructType) {
156 LLVM::StoreOp::create(rewriter, loc, call.getResult(),
157 wrapperFuncOp.getArgument(0));
158 LLVM::ReturnOp::create(rewriter, loc, ValueRange{});
159 } else {
160 LLVM::ReturnOp::create(rewriter, loc, call.getResults());
161 }
162}
163
164/// Creates an auxiliary function with pointer-to-memref-descriptor-struct
165/// arguments instead of unpacked arguments. Creates a body for the (external)
166/// `newFuncOp` that allocates a memref descriptor on stack, packs the
167/// individual arguments into this descriptor and passes a pointer to it into
168/// the auxiliary function. If the result of the function cannot be directly
169/// returned, we write it to a special first argument that provides a pointer
170/// to a corresponding struct. This auxiliary external function is now
171/// compatible with functions defined in C using pointers to C structs
172/// corresponding to a memref descriptor.
173static void wrapExternalFunction(OpBuilder &builder, Location loc,
174 const LLVMTypeConverter &typeConverter,
175 FunctionOpInterface funcOp,
176 LLVM::LLVMFuncOp newFuncOp) {
177 OpBuilder::InsertionGuard guard(builder);
178
179 auto [wrapperType, resultStructType] =
180 typeConverter.convertFunctionTypeCWrapper(
181 cast<FunctionType>(funcOp.getFunctionType()));
182 // This conversion can only fail if it could not convert one of the argument
183 // types. But since it has been applied to a non-wrapper function before, it
184 // should have failed earlier and not reach this point at all.
185 assert(wrapperType && "unexpected type conversion failure");
186
188 filterFuncAttributes(funcOp, attributes);
189
190 // Create the auxiliary function.
191 auto wrapperFunc = LLVM::LLVMFuncOp::create(
192 builder, loc, llvm::formatv("_mlir_ciface_{0}", funcOp.getName()).str(),
193 wrapperType, LLVM::Linkage::External, /*dsoLocal=*/false,
194 /*cconv=*/LLVM::CConv::C, /*comdat=*/nullptr, attributes);
195 propagateArgResAttrs(builder, !!resultStructType, funcOp, wrapperFunc);
196
197 // The wrapper that we synthetize here should only be visible in this module.
198 newFuncOp.setLinkage(LLVM::Linkage::Private);
199 builder.setInsertionPointToStart(newFuncOp.addEntryBlock(builder));
200
201 // Get a ValueRange containing arguments.
202 FunctionType type = cast<FunctionType>(funcOp.getFunctionType());
204 args.reserve(type.getNumInputs());
205 ValueRange wrapperArgsRange(newFuncOp.getArguments());
206
207 if (resultStructType) {
208 // Allocate the struct on the stack and pass the pointer.
209 Type resultType = cast<LLVM::LLVMFunctionType>(wrapperType).getParamType(0);
210 Value one = LLVM::ConstantOp::create(
211 builder, loc, typeConverter.convertType(builder.getIndexType()),
212 builder.getIntegerAttr(builder.getIndexType(), 1));
213 Value result =
214 LLVM::AllocaOp::create(builder, loc, resultType, resultStructType, one);
215 args.push_back(result);
216 }
217
218 // Iterate over the inputs of the original function and pack values into
219 // memref descriptors if the original type is a memref.
220 for (Type input : type.getInputs()) {
221 Value arg;
222 int numToDrop = 1;
223 auto memRefType = dyn_cast<MemRefType>(input);
224 auto unrankedMemRefType = dyn_cast<UnrankedMemRefType>(input);
225 if (memRefType || unrankedMemRefType) {
226 numToDrop = memRefType
229 Value packed =
230 memRefType
231 ? MemRefDescriptor::pack(builder, loc, typeConverter, memRefType,
232 wrapperArgsRange.take_front(numToDrop))
234 builder, loc, typeConverter, unrankedMemRefType,
235 wrapperArgsRange.take_front(numToDrop));
236
237 auto ptrTy = LLVM::LLVMPointerType::get(builder.getContext());
238 Value one = LLVM::ConstantOp::create(
239 builder, loc, typeConverter.convertType(builder.getIndexType()),
240 builder.getIntegerAttr(builder.getIndexType(), 1));
241 Value allocated = LLVM::AllocaOp::create(
242 builder, loc, ptrTy, packed.getType(), one, /*alignment=*/0);
243 LLVM::StoreOp::create(builder, loc, packed, allocated);
244 arg = allocated;
245 } else {
246 arg = wrapperArgsRange[0];
247 }
248
249 args.push_back(arg);
250 wrapperArgsRange = wrapperArgsRange.drop_front(numToDrop);
251 }
252 assert(wrapperArgsRange.empty() && "did not map some of the arguments");
253
254 auto call = LLVM::CallOp::create(builder, loc, wrapperFunc, args);
255
256 if (resultStructType) {
257 Value result =
258 LLVM::LoadOp::create(builder, loc, resultStructType, args.front());
259 LLVM::ReturnOp::create(builder, loc, result);
260 } else {
261 LLVM::ReturnOp::create(builder, loc, call.getResults());
262 }
263}
264
265/// Inserts `llvm.load` ops in the function body to restore the expected pointee
266/// value from `llvm.byval`/`llvm.byref` function arguments that were converted
267/// to LLVM pointer types.
269 ConversionPatternRewriter &rewriter, const LLVMTypeConverter &typeConverter,
270 ArrayRef<std::optional<NamedAttribute>> byValRefNonPtrAttrs,
271 LLVM::LLVMFuncOp funcOp) {
272 // Nothing to do for function declarations.
273 if (funcOp.isExternal())
274 return;
275
276 ConversionPatternRewriter::InsertionGuard guard(rewriter);
277 rewriter.setInsertionPointToStart(&funcOp.getFunctionBody().front());
278
279 for (const auto &[arg, byValRefAttr] :
280 llvm::zip(funcOp.getArguments(), byValRefNonPtrAttrs)) {
281 // Skip argument if no `llvm.byval` or `llvm.byref` attribute.
282 if (!byValRefAttr)
283 continue;
284
285 // Insert load to retrieve the actual argument passed by value/reference.
286 assert(isa<LLVM::LLVMPointerType>(arg.getType()) &&
287 "Expected LLVM pointer type for argument with "
288 "`llvm.byval`/`llvm.byref` attribute");
289 Type resTy = typeConverter.convertType(
290 cast<TypeAttr>(byValRefAttr->getValue()).getValue());
291
292 Value valueArg = LLVM::LoadOp::create(rewriter, arg.getLoc(), resTy, arg);
293 rewriter.replaceAllUsesWith(arg, valueArg);
294 }
295}
296
297static FailureOr<LLVM::LLVMFunctionType> convertFuncSignature(
298 FunctionOpInterface funcOp, const LLVMTypeConverter &converter,
299 bool useBarePtrCallConv, TypeConverter::SignatureConversion &result,
300 SmallVectorImpl<std::optional<NamedAttribute>> &byValRefNonPtrAttrs) {
301 auto varargsAttr = funcOp->getAttrOfType<BoolAttr>(varargsAttrName);
302 auto llvmType = dyn_cast_or_null<LLVM::LLVMFunctionType>(
303 converter.convertFunctionSignature(
304 funcOp, varargsAttr && varargsAttr.getValue(), useBarePtrCallConv,
305 result, byValRefNonPtrAttrs));
306 if (!llvmType)
307 return failure();
308 return llvmType;
309}
310
311static LLVM::LLVMFuncOp createLLVMFuncOp(FunctionOpInterface funcOp,
312 ConversionPatternRewriter &rewriter,
313 LLVM::LLVMFunctionType llvmType,
314 LoweredLLVMFuncAttrs &loweredAttrs,
315 SymbolTableCollection *symbolTables) {
316 Operation *symbolTableOp = funcOp->getParentWithTrait<OpTrait::SymbolTable>();
317 if (symbolTables && symbolTableOp) {
318 SymbolTable &symbolTable = symbolTables->getSymbolTable(symbolTableOp);
319 symbolTable.remove(funcOp);
320 }
321 loweredAttrs.properties.setCConv(
322 LLVM::CConvAttr::get(rewriter.getContext(), LLVM::CConv::C));
323 auto newFuncOp = LLVM::LLVMFuncOp::create(rewriter, funcOp.getLoc(),
324 loweredAttrs.properties,
325 loweredAttrs.discardableAttrs);
326
327 if (symbolTables && symbolTableOp) {
328 auto ip = rewriter.getInsertionPoint();
329 SymbolTable &symbolTable = symbolTables->getSymbolTable(symbolTableOp);
330 symbolTable.insert(newFuncOp, ip);
331 }
332
333 cast<FunctionOpInterface>(newFuncOp.getOperation())
334 .setVisibility(funcOp.getVisibility());
335
336 // Set readnone memory effects
337 if (funcOp->hasAttr(LLVM::LLVMDialect::getReadnoneAttrName())) {
338 auto memoryAttr = LLVM::MemoryEffectsAttr::get(
339 rewriter.getContext(), {/*other=*/LLVM::ModRefInfo::NoModRef,
340 /*argMem=*/LLVM::ModRefInfo::NoModRef,
341 /*inaccessibleMem=*/LLVM::ModRefInfo::NoModRef,
342 /*errnoMem=*/LLVM::ModRefInfo::NoModRef,
343 /*targetMem0=*/LLVM::ModRefInfo::NoModRef,
344 /*targetMem1=*/LLVM::ModRefInfo::NoModRef});
345 newFuncOp.setMemoryEffectsAttr(memoryAttr);
346 }
347
348 return newFuncOp;
349}
350
352convertArgumentAttributes(DictionaryAttr attrsDict,
353 ConversionPatternRewriter &rewriter,
354 const LLVMTypeConverter &converter) {
355 SmallVector<NamedAttribute> convertedAttrs;
356 convertedAttrs.reserve(attrsDict.size());
357 for (const NamedAttribute &attr : attrsDict) {
358 const auto convert = [&](const NamedAttribute &attr) {
359 return TypeAttr::get(
360 converter.convertType(cast<TypeAttr>(attr.getValue()).getValue()));
361 };
362 if (attr.getName().getValue() == LLVM::LLVMDialect::getByValAttrName()) {
363 convertedAttrs.push_back(rewriter.getNamedAttr(
364 LLVM::LLVMDialect::getByValAttrName(), convert(attr)));
365 } else if (attr.getName().getValue() ==
366 LLVM::LLVMDialect::getByRefAttrName()) {
367 convertedAttrs.push_back(rewriter.getNamedAttr(
368 LLVM::LLVMDialect::getByRefAttrName(), convert(attr)));
369 } else if (attr.getName().getValue() ==
370 LLVM::LLVMDialect::getStructRetAttrName()) {
371 convertedAttrs.push_back(rewriter.getNamedAttr(
372 LLVM::LLVMDialect::getStructRetAttrName(), convert(attr)));
373 } else if (attr.getName().getValue() ==
374 LLVM::LLVMDialect::getInAllocaAttrName()) {
375 convertedAttrs.push_back(rewriter.getNamedAttr(
376 LLVM::LLVMDialect::getInAllocaAttrName(), convert(attr)));
377 } else {
378 convertedAttrs.push_back(attr);
379 }
380 }
381 return convertedAttrs;
382}
383
385 FunctionOpInterface funcOp, ConversionPatternRewriter &rewriter,
386 const LLVMTypeConverter &converter, TypeConverter::SignatureConversion &sig,
387 LLVM::LLVMFunctionType llvmType, LLVM::LLVMFuncOp newFuncOp) {
388 // Propagate argument/result attributes to all converted arguments/result
389 // obtained after converting a given original argument/result.
390 if (ArrayAttr resAttrDicts = funcOp.getAllResultAttrs()) {
391 assert(!resAttrDicts.empty() && "expected array to be non-empty");
392 if (funcOp.getNumResults() == 1)
393 newFuncOp.setAllResultAttrs(resAttrDicts);
394 }
395 if (ArrayAttr argAttrDicts = funcOp.getAllArgAttrs()) {
396 SmallVector<Attribute> newArgAttrs(llvmType.getNumParams());
397 for (unsigned i = 0, e = funcOp.getNumArguments(); i < e; ++i) {
398 // Some LLVM IR attribute have a type attached to them. During FuncOp ->
399 // LLVMFuncOp conversion these types may have changed. Account for that
400 // change by converting attributes' types as well.
401 auto attrsDict = cast<DictionaryAttr>(argAttrDicts[i]);
402 SmallVector<NamedAttribute, 4> convertedAttrs =
403 convertArgumentAttributes(attrsDict, rewriter, converter);
404 auto mapping = sig.getInputMapping(i);
405 assert(mapping && "unexpected deletion of function argument");
406 // Only attach the new argument attributes if there is a one-to-one
407 // mapping from old to new types. Otherwise, attributes might be
408 // attached to types that they do not support.
409 if (mapping->size == 1) {
410 newArgAttrs[mapping->inputNo] =
411 DictionaryAttr::get(rewriter.getContext(), convertedAttrs);
412 continue;
413 }
414 // TODO: Implement custom handling for types that expand to multiple
415 // function arguments.
416 for (size_t j = 0; j < mapping->size; ++j)
417 newArgAttrs[mapping->inputNo + j] =
418 DictionaryAttr::get(rewriter.getContext(), {});
419 }
420 if (!newArgAttrs.empty())
421 newFuncOp.setAllArgAttrs(rewriter.getArrayAttr(newArgAttrs));
422 }
423}
424
425static void wrapWithCInterface(FunctionOpInterface funcOp,
426 ConversionPatternRewriter &rewriter,
427 const LLVMTypeConverter &converter,
428 LLVM::LLVMFuncOp newFuncOp) {
429 if (newFuncOp.isExternal())
430 wrapExternalFunction(rewriter, funcOp->getLoc(), converter, funcOp,
431 newFuncOp);
432 else
433 wrapForExternalCallers(rewriter, funcOp->getLoc(), converter, funcOp,
434 newFuncOp);
435}
436
437/// Conversion steps
438/// - Validate function type
439/// - Convert signature
440/// - Validate C wrapper varargs constraint
441/// - Lower function attrs
442/// - Create llvm.func
443/// - Propagate arg/result attrs
444/// - Inline body + signature conversion
445/// - Restore byval/byref pointee types
446/// - C-wrapper handling
447FailureOr<LLVM::LLVMFuncOp> mlir::convertFuncOpToLLVMFuncOp(
448 FunctionOpInterface funcOp, ConversionPatternRewriter &rewriter,
449 const LLVMTypeConverter &converter, SymbolTableCollection *symbolTables) {
450 // Check the funcOp has `FunctionType`.
451 auto funcTy = dyn_cast<FunctionType>(funcOp.getFunctionType());
452 if (!funcTy)
453 return rewriter.notifyMatchFailure(
454 funcOp, "Only support FunctionOpInterface with FunctionType");
455
456 bool useBarePtrCallConv = shouldUseBarePtrCallConv(funcOp, &converter);
457 // Convert the original function arguments. They are converted using the
458 // LLVMTypeConverter provided to this legalization pattern.
459 // Gather `llvm.byval` and `llvm.byref` arguments whose type convertion was
460 // overriden with an LLVM pointer type for later processing.
462 TypeConverter::SignatureConversion result(funcOp.getNumArguments());
463 FailureOr<LLVM::LLVMFunctionType> llvmType = convertFuncSignature(
464 funcOp, converter, useBarePtrCallConv, result, byValRefNonPtrAttrs);
465 if (failed(llvmType))
466 return rewriter.notifyMatchFailure(funcOp, "signature conversion failed");
467
468 // Validate C wrapper varargs constraint
469 bool emitCWrapper = funcOp->hasAttrOfType<UnitAttr>(
470 LLVM::LLVMDialect::getEmitCWrapperAttrName());
471 if (!useBarePtrCallConv && emitCWrapper && llvmType->isVarArg())
472 return funcOp.emitError("C interface for variadic functions is not "
473 "supported yet.");
474
475 // Lower function attrs
476 FailureOr<LoweredLLVMFuncAttrs> loweredAttrs =
477 lowerDiscardableAttrsForLLVMFunc(funcOp, *llvmType);
478 if (failed(loweredAttrs))
479 return rewriter.notifyMatchFailure(funcOp,
480 "failed to lower func attributes");
481
482 // Create llvm.func
483 auto newFuncOp = createLLVMFuncOp(funcOp, rewriter, *llvmType, *loweredAttrs,
484 symbolTables);
485
486 // Propagate arg/result attrs
487 propagateFunctionArgResAttrs(funcOp, rewriter, converter, result, *llvmType,
488 newFuncOp);
489
490 // Inline body + signature conversion
491 rewriter.inlineRegionBefore(funcOp.getFunctionBody(), newFuncOp.getBody(),
492 newFuncOp.end());
493 // Convert just the entry block. The remaining unstructured control flow is
494 // converted by ControlFlowToLLVM.
495 if (!newFuncOp.getBody().empty())
496 rewriter.applySignatureConversion(&newFuncOp.getBody().front(), result,
497 &converter);
498
499 // Restore byval/byref pointee types
500 // Fix the type mismatch between the materialized `llvm.ptr` and the expected
501 // pointee type in the function body when converting `llvm.byval`/`llvm.byref`
502 // function arguments.
503 restoreByValRefArgumentType(rewriter, converter, byValRefNonPtrAttrs,
504 newFuncOp);
505
506 // C-wrapper handling
507 if (!useBarePtrCallConv && emitCWrapper)
508 wrapWithCInterface(funcOp, rewriter, converter, newFuncOp);
509
510 return newFuncOp;
511}
512
513namespace {
514
515/// FuncOp legalization pattern that converts MemRef arguments to pointers to
516/// MemRef descriptors (LLVM struct data types) containing all the MemRef type
517/// information.
518class FuncOpConversion : public ConvertOpToLLVMPattern<func::FuncOp> {
519 SymbolTableCollection *symbolTables = nullptr;
520
521public:
522 explicit FuncOpConversion(const LLVMTypeConverter &converter,
523 SymbolTableCollection *symbolTables = nullptr)
524 : ConvertOpToLLVMPattern(converter), symbolTables(symbolTables) {}
525
526 LogicalResult
527 matchAndRewrite(func::FuncOp funcOp, OpAdaptor adaptor,
528 ConversionPatternRewriter &rewriter) const override {
529 FailureOr<LLVM::LLVMFuncOp> newFuncOp = mlir::convertFuncOpToLLVMFuncOp(
530 cast<FunctionOpInterface>(funcOp.getOperation()), rewriter,
531 *getTypeConverter(), symbolTables);
532 if (failed(newFuncOp))
533 return rewriter.notifyMatchFailure(funcOp, "Could not convert funcop");
534
535 rewriter.eraseOp(funcOp);
536 return success();
537 }
538};
539
540struct ConstantOpLowering : public ConvertOpToLLVMPattern<func::ConstantOp> {
541 using ConvertOpToLLVMPattern<func::ConstantOp>::ConvertOpToLLVMPattern;
542
543 LogicalResult
544 matchAndRewrite(func::ConstantOp op, OpAdaptor adaptor,
545 ConversionPatternRewriter &rewriter) const override {
546 auto type = typeConverter->convertType(op.getResult().getType());
547 if (!type || !LLVM::isCompatibleType(type))
548 return rewriter.notifyMatchFailure(op, "failed to convert result type");
549
550 auto newOp =
551 LLVM::AddressOfOp::create(rewriter, op.getLoc(), type, op.getValue());
552 for (const NamedAttribute &attr : op->getAttrs()) {
553 if (attr.getName().strref() == "value")
554 continue;
555 newOp->setAttr(attr.getName(), attr.getValue());
556 }
557 rewriter.replaceOp(op, newOp->getResults());
558 return success();
559 }
560};
561
562// A CallOp automatically promotes MemRefType to a sequence of alloca/store and
563// passes the pointer to the MemRef across function boundaries.
564template <typename CallOpType>
565struct CallOpInterfaceLowering : public ConvertOpToLLVMPattern<CallOpType> {
566 using ConvertOpToLLVMPattern<CallOpType>::ConvertOpToLLVMPattern;
567 using Super = CallOpInterfaceLowering<CallOpType>;
568 using Base = ConvertOpToLLVMPattern<CallOpType>;
570
571 LogicalResult matchAndRewriteImpl(CallOpType callOp, Adaptor adaptor,
572 ConversionPatternRewriter &rewriter,
573 bool useBarePtrCallConv = false) const {
574 // Pack the result types into a struct.
575 Type packedResult = nullptr;
576 SmallVector<SmallVector<Type>> groupedResultTypes;
577 unsigned numResults = callOp.getNumResults();
578 auto resultTypes = llvm::to_vector<4>(callOp.getResultTypes());
579 int64_t numConvertedTypes = 0;
580 if (numResults != 0) {
581 if (!(packedResult = this->getTypeConverter()->packFunctionResults(
582 resultTypes, useBarePtrCallConv, &groupedResultTypes,
583 &numConvertedTypes)))
584 return failure();
585 }
586
587 if (useBarePtrCallConv) {
588 for (auto it : callOp->getOperands()) {
589 Type operandType = it.getType();
590 if (isa<UnrankedMemRefType>(operandType)) {
591 // Unranked memref is not supported in the bare pointer calling
592 // convention.
593 return failure();
594 }
595 }
596 }
597 auto promoted = this->getTypeConverter()->promoteOperands(
598 callOp.getLoc(), /*opOperands=*/callOp->getOperands(),
599 adaptor.getOperands(), rewriter, useBarePtrCallConv);
600 auto newOp = LLVM::CallOp::create(rewriter, callOp.getLoc(),
601 packedResult ? TypeRange(packedResult)
602 : TypeRange(),
603 promoted, callOp->getAttrs());
604
605 newOp.getProperties().operandSegmentSizes = {
606 static_cast<int32_t>(promoted.size()), 0};
607 newOp.getProperties().op_bundle_sizes = rewriter.getDenseI32ArrayAttr({});
608
609 // Helper function that extracts an individual result from the return value
610 // of the new call op. llvm.call ops support only 0 or 1 result. In case of
611 // 2 or more results, the results are packed into a structure.
612 //
613 // The new call op may have more than 2 results because:
614 // a. The original call op has more than 2 results.
615 // b. An original op result type-converted to more than 1 result.
616 auto getUnpackedResult = [&](unsigned i) -> Value {
617 assert(numConvertedTypes > 0 && "convert op has no results");
618 if (numConvertedTypes == 1) {
619 assert(i == 0 && "out of bounds: converted op has only one result");
620 return newOp->getResult(0);
621 }
622 // Results have been converted to a structure. Extract individual results
623 // from the structure.
624 return LLVM::ExtractValueOp::create(rewriter, callOp.getLoc(),
625 newOp->getResult(0), i);
626 };
627
628 // Group the results into a vector of vectors, such that it is clear which
629 // original op result is replaced with which range of values. (In case of a
630 // 1:N conversion, there can be multiple replacements for a single result.)
631 SmallVector<SmallVector<Value>> results;
632 results.reserve(numResults);
633 unsigned counter = 0;
634 for (unsigned i = 0; i < numResults; ++i) {
635 SmallVector<Value> &group = results.emplace_back();
636 for (unsigned j = 0, e = groupedResultTypes[i].size(); j < e; ++j)
637 group.push_back(getUnpackedResult(counter++));
638 }
639
640 // Special handling for MemRef types.
641 for (unsigned i = 0; i < numResults; ++i) {
642 Type origType = resultTypes[i];
643 auto memrefType = dyn_cast<MemRefType>(origType);
644 auto unrankedMemrefType = dyn_cast<UnrankedMemRefType>(origType);
645 if (useBarePtrCallConv && memrefType) {
646 // For the bare-ptr calling convention, promote memref results to
647 // descriptors.
648 assert(results[i].size() == 1 && "expected one converted result");
649 results[i].front() = MemRefDescriptor::fromStaticShape(
650 rewriter, callOp.getLoc(), *this->getTypeConverter(), memrefType,
651 results[i].front());
652 }
653 if (unrankedMemrefType) {
654 assert(!useBarePtrCallConv && "unranked memref is not supported in the "
655 "bare-ptr calling convention");
656 assert(results[i].size() == 1 && "expected one converted result");
657 Value desc = this->copyUnrankedDescriptor(
658 rewriter, callOp.getLoc(), unrankedMemrefType, results[i].front(),
659 /*toDynamic=*/false);
660 if (!desc)
661 return failure();
662 results[i].front() = desc;
663 }
664 }
665
666 rewriter.replaceOpWithMultiple(callOp, results);
667 return success();
668 }
669};
670
671class CallOpLowering : public CallOpInterfaceLowering<func::CallOp> {
672public:
673 explicit CallOpLowering(const LLVMTypeConverter &typeConverter,
674 SymbolTableCollection *symbolTables = nullptr,
675 PatternBenefit benefit = 1)
676 : CallOpInterfaceLowering<func::CallOp>(typeConverter, benefit),
677 symbolTables(symbolTables) {}
678
679 LogicalResult
680 matchAndRewrite(func::CallOp callOp, OneToNOpAdaptor adaptor,
681 ConversionPatternRewriter &rewriter) const override {
682 bool useBarePtrCallConv = false;
683 if (getTypeConverter()->getOptions().useBarePtrCallConv) {
684 useBarePtrCallConv = true;
685 } else if (symbolTables != nullptr) {
686 // Fast lookup.
687 Operation *callee =
688 symbolTables->lookupNearestSymbolFrom(callOp, callOp.getCalleeAttr());
689 useBarePtrCallConv =
690 callee != nullptr && callee->hasAttr(barePtrAttrName);
691 } else {
692 // Warning: This is a linear lookup.
693 Operation *callee =
694 SymbolTable::lookupNearestSymbolFrom(callOp, callOp.getCalleeAttr());
695 useBarePtrCallConv =
696 callee != nullptr && callee->hasAttr(barePtrAttrName);
697 }
698 return matchAndRewriteImpl(callOp, adaptor, rewriter, useBarePtrCallConv);
699 }
700
701private:
702 SymbolTableCollection *symbolTables = nullptr;
703};
704
705struct CallIndirectOpLowering
706 : public CallOpInterfaceLowering<func::CallIndirectOp> {
707 using Super::Super;
708
709 LogicalResult
710 matchAndRewrite(func::CallIndirectOp callIndirectOp, OneToNOpAdaptor adaptor,
711 ConversionPatternRewriter &rewriter) const override {
712 return matchAndRewriteImpl(callIndirectOp, adaptor, rewriter);
713 }
714};
715
716struct UnrealizedConversionCastOpLowering
717 : public ConvertOpToLLVMPattern<UnrealizedConversionCastOp> {
718 using ConvertOpToLLVMPattern<
719 UnrealizedConversionCastOp>::ConvertOpToLLVMPattern;
720
721 LogicalResult
722 matchAndRewrite(UnrealizedConversionCastOp op, OpAdaptor adaptor,
723 ConversionPatternRewriter &rewriter) const override {
724 SmallVector<Type> convertedTypes;
725 if (succeeded(typeConverter->convertTypes(op.getOutputs().getTypes(),
726 convertedTypes)) &&
727 convertedTypes == adaptor.getInputs().getTypes()) {
728 rewriter.replaceOp(op, adaptor.getInputs());
729 return success();
730 }
731
732 convertedTypes.clear();
733 if (succeeded(typeConverter->convertTypes(adaptor.getInputs().getTypes(),
734 convertedTypes)) &&
735 convertedTypes == op.getOutputs().getType()) {
736 rewriter.replaceOp(op, adaptor.getInputs());
737 return success();
738 }
739 return failure();
740 }
741};
742
743// Special lowering pattern for `ReturnOps`. Unlike all other operations,
744// `ReturnOp` interacts with the function signature and must have as many
745// operands as the function has return values. Because in LLVM IR, functions
746// can only return 0 or 1 value, we pack multiple values into a structure type.
747// Emit `PoisonOp` followed by `InsertValueOp`s to create such structure if
748// necessary before returning it
749struct ReturnOpLowering : public ConvertOpToLLVMPattern<func::ReturnOp> {
750 using ConvertOpToLLVMPattern<func::ReturnOp>::ConvertOpToLLVMPattern;
751
752 LogicalResult
753 matchAndRewrite(func::ReturnOp op, OneToNOpAdaptor adaptor,
754 ConversionPatternRewriter &rewriter) const override {
755 Location loc = op.getLoc();
756 SmallVector<Value, 4> updatedOperands;
757
758 auto funcOp = op->getParentOfType<LLVM::LLVMFuncOp>();
759 bool useBarePtrCallConv =
760 shouldUseBarePtrCallConv(funcOp, this->getTypeConverter());
761
762 for (auto [oldOperand, newOperands] :
763 llvm::zip_equal(op->getOperands(), adaptor.getOperands())) {
764 Type oldTy = oldOperand.getType();
765 if (auto memRefType = dyn_cast<MemRefType>(oldTy)) {
766 assert(newOperands.size() == 1 && "expected one converted result");
767 if (useBarePtrCallConv &&
768 getTypeConverter()->canConvertToBarePtr(memRefType)) {
769 // For the bare-ptr calling convention, extract the aligned pointer to
770 // be returned from the memref descriptor.
771 MemRefDescriptor memrefDesc(newOperands.front());
772 updatedOperands.push_back(memrefDesc.allocatedPtr(rewriter, loc));
773 continue;
774 }
775 } else if (auto unrankedMemRefType =
776 dyn_cast<UnrankedMemRefType>(oldTy)) {
777 assert(newOperands.size() == 1 && "expected one converted result");
778 if (useBarePtrCallConv) {
779 // Unranked memref is not supported in the bare pointer calling
780 // convention.
781 return failure();
782 }
783 Value updatedDesc =
784 copyUnrankedDescriptor(rewriter, loc, unrankedMemRefType,
785 newOperands.front(), /*toDynamic=*/true);
786 if (!updatedDesc)
787 return failure();
788 updatedOperands.push_back(updatedDesc);
789 continue;
790 }
791
792 llvm::append_range(updatedOperands, newOperands);
793 }
794
795 // If ReturnOp has 0 or 1 operand, create it and return immediately.
796 if (updatedOperands.size() <= 1) {
797 rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(
798 op, TypeRange(), updatedOperands, op->getAttrs());
799 return success();
800 }
801
802 // Otherwise, we need to pack the arguments into an LLVM struct type before
803 // returning.
804 auto packedType = getTypeConverter()->packFunctionResults(
805 op.getOperandTypes(), useBarePtrCallConv);
806 if (!packedType) {
807 return rewriter.notifyMatchFailure(op, "could not convert result types");
808 }
809
810 Value packed = LLVM::PoisonOp::create(rewriter, loc, packedType);
811 for (auto [idx, operand] : llvm::enumerate(updatedOperands)) {
812 packed = LLVM::InsertValueOp::create(rewriter, loc, packed, operand, idx);
813 }
814 rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, TypeRange(), packed,
815 op->getAttrs());
816 return success();
817 }
818};
819} // namespace
820
822 const LLVMTypeConverter &converter, RewritePatternSet &patterns,
823 SymbolTableCollection *symbolTables) {
824 patterns.add<FuncOpConversion>(converter, symbolTables);
825}
826
828 const LLVMTypeConverter &converter, RewritePatternSet &patterns,
829 SymbolTableCollection *symbolTables) {
830 populateFuncToLLVMFuncOpConversionPattern(converter, patterns, symbolTables);
831 patterns.add<CallIndirectOpLowering>(converter);
832 patterns.add<CallOpLowering>(converter, symbolTables);
833 patterns.add<ConstantOpLowering>(converter);
834 patterns.add<ReturnOpLowering>(converter);
835}
836
837namespace {
838/// A pass converting Func operations into the LLVM IR dialect.
839struct ConvertFuncToLLVMPass
840 : public impl::ConvertFuncToLLVMPassBase<ConvertFuncToLLVMPass> {
841 using Base::Base;
842
843 /// Run the dialect converter on the module.
844 void runOnOperation() override {
845 ModuleOp m = getOperation();
846 StringRef dataLayout;
847 auto dataLayoutAttr = dyn_cast_or_null<StringAttr>(
848 m->getAttr(LLVM::LLVMDialect::getDataLayoutAttrName()));
849 if (dataLayoutAttr)
850 dataLayout = dataLayoutAttr.getValue();
851
852 if (failed(LLVM::LLVMDialect::verifyDataLayoutString(
853 dataLayout, [this](const Twine &message) {
854 getOperation().emitError() << message.str();
855 }))) {
856 signalPassFailure();
857 return;
858 }
859
860 const auto &dataLayoutAnalysis = getAnalysis<DataLayoutAnalysis>();
861
862 LowerToLLVMOptions options(&getContext(),
863 dataLayoutAnalysis.getAtOrAbove(m));
864 options.useBarePtrCallConv = useBarePtrCallConv;
865 if (indexBitwidth != kDeriveIndexBitwidthFromDataLayout)
866 options.overrideIndexBitwidth(indexBitwidth);
867 options.dataLayout = llvm::DataLayout(dataLayout);
868
869 LLVMTypeConverter typeConverter(&getContext(), options,
870 &dataLayoutAnalysis);
871
872 RewritePatternSet patterns(&getContext());
873 SymbolTableCollection symbolTables;
874
875 populateFuncToLLVMConversionPatterns(typeConverter, patterns,
876 &symbolTables);
877
878 LLVMConversionTarget target(getContext());
879 if (failed(applyPartialConversion(m, target, std::move(patterns))))
880 signalPassFailure();
881 }
882};
883
884struct SetLLVMModuleDataLayoutPass
886 SetLLVMModuleDataLayoutPass> {
887 using Base::Base;
888
889 /// Run the dialect converter on the module.
890 void runOnOperation() override {
891 if (failed(LLVM::LLVMDialect::verifyDataLayoutString(
892 this->dataLayout, [this](const Twine &message) {
893 getOperation().emitError() << message.str();
894 }))) {
895 signalPassFailure();
896 return;
897 }
898 ModuleOp m = getOperation();
899 m->setAttr(LLVM::LLVMDialect::getDataLayoutAttrName(),
900 StringAttr::get(m.getContext(), this->dataLayout));
901 }
902};
903} // namespace
904
905//===----------------------------------------------------------------------===//
906// ConvertToLLVMPatternInterface implementation
907//===----------------------------------------------------------------------===//
908
909namespace {
910/// Implement the interface to convert Func to LLVM.
911struct FuncToLLVMDialectInterface : public ConvertToLLVMPatternInterface {
912 FuncToLLVMDialectInterface(Dialect *dialect)
913 : ConvertToLLVMPatternInterface(dialect) {}
914 /// Hook for derived dialect interface to provide conversion patterns
915 /// and mark dialect legal for the conversion target.
916 void populateConvertToLLVMConversionPatterns(
917 ConversionTarget &target, LLVMTypeConverter &typeConverter,
918 RewritePatternSet &patterns) const final {
919 populateFuncToLLVMConversionPatterns(typeConverter, patterns);
920 }
921};
922} // namespace
923
925 registry.addExtension(+[](MLIRContext *ctx, func::FuncDialect *dialect) {
926 dialect->addInterfaces<FuncToLLVMDialectInterface>();
927 });
928}
return success()
static FailureOr< LLVM::LLVMFunctionType > convertFuncSignature(FunctionOpInterface funcOp, const LLVMTypeConverter &converter, bool useBarePtrCallConv, TypeConverter::SignatureConversion &result, SmallVectorImpl< std::optional< NamedAttribute > > &byValRefNonPtrAttrs)
static void restoreByValRefArgumentType(ConversionPatternRewriter &rewriter, const LLVMTypeConverter &typeConverter, ArrayRef< std::optional< NamedAttribute > > byValRefNonPtrAttrs, LLVM::LLVMFuncOp funcOp)
Inserts llvm.load ops in the function body to restore the expected pointee value from llvm....
static LLVM::LLVMFuncOp createLLVMFuncOp(FunctionOpInterface funcOp, ConversionPatternRewriter &rewriter, LLVM::LLVMFunctionType llvmType, LoweredLLVMFuncAttrs &loweredAttrs, SymbolTableCollection *symbolTables)
static void propagateArgResAttrs(OpBuilder &builder, bool resultStructType, FunctionOpInterface funcOp, LLVM::LLVMFuncOp wrapperFuncOp)
Propagate argument/results attributes.
static SmallVector< NamedAttribute > convertArgumentAttributes(DictionaryAttr attrsDict, ConversionPatternRewriter &rewriter, const LLVMTypeConverter &converter)
static bool isDiscardableAttr(StringRef name)
static constexpr StringRef barePtrAttrName
static constexpr StringRef varargsAttrName
static constexpr StringRef linkageAttrName
static void filterFuncAttributes(FunctionOpInterface func, SmallVectorImpl< NamedAttribute > &result)
Only retain those attributes that are not constructed by LLVMFuncOp::build.
static void propagateFunctionArgResAttrs(FunctionOpInterface funcOp, ConversionPatternRewriter &rewriter, const LLVMTypeConverter &converter, TypeConverter::SignatureConversion &sig, LLVM::LLVMFunctionType llvmType, LLVM::LLVMFuncOp newFuncOp)
static bool shouldUseBarePtrCallConv(Operation *op, const LLVMTypeConverter *typeConverter)
Return true if the op should use bare pointer calling convention.
static void wrapExternalFunction(OpBuilder &builder, Location loc, const LLVMTypeConverter &typeConverter, FunctionOpInterface funcOp, LLVM::LLVMFuncOp newFuncOp)
Creates an auxiliary function with pointer-to-memref-descriptor-struct arguments instead of unpacked ...
static void wrapForExternalCallers(OpBuilder &rewriter, Location loc, const LLVMTypeConverter &typeConverter, FunctionOpInterface funcOp, LLVM::LLVMFuncOp newFuncOp)
Creates an auxiliary function with pointer-to-memref-descriptor-struct arguments instead of unpacked ...
static void wrapWithCInterface(FunctionOpInterface funcOp, ConversionPatternRewriter &rewriter, const LLVMTypeConverter &converter, LLVM::LLVMFuncOp newFuncOp)
ArrayAttr()
b getContext())
static llvm::ManagedStatic< PassManagerOptions > options
Special case of IntegerAttr to represent boolean integers, i.e., signless i1 integers.
IntegerAttr getIntegerAttr(Type type, int64_t value)
Definition Builders.cpp:232
MLIRContext * getContext() const
Definition Builders.h:56
IndexType getIndexType()
Definition Builders.cpp:55
DictionaryAttr getDictionaryAttr(ArrayRef< NamedAttribute > value)
Definition Builders.cpp:108
Utility class for operation conversions targeting the LLVM dialect that match exactly one source oper...
Definition Pattern.h:227
typename SourceOp::template GenericAdaptor< ArrayRef< ValueRange > > OneToNOpAdaptor
Definition Pattern.h:230
The DialectRegistry maps a dialect namespace to a constructor for the matching dialect.
bool addExtension(TypeID extensionID, std::unique_ptr< DialectExtensionBase > extension)
Add the given extension to the registry.
Conversion from types to the LLVM IR dialect.
Type convertFunctionSignature(FunctionType funcTy, bool isVariadic, bool useBarePtrCallConv, SignatureConversion &result) const
Convert a function type.
const LowerToLLVMOptions & getOptions() const
std::pair< LLVM::LLVMFunctionType, LLVM::LLVMStructType > convertFunctionTypeCWrapper(FunctionType type) const
Converts the function type to a C-compatible format, in particular using pointers to memref descripto...
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
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...
static unsigned getNumUnpackedValues(MemRefType type)
Returns the number of non-aggregate values that would be produced by unpack.
static Value pack(OpBuilder &builder, Location loc, const LLVMTypeConverter &converter, MemRefType type, ValueRange values)
Builds IR populating a MemRef descriptor structure from a list of individual values composing that de...
NamedAttribute represents a combination of a name and an Attribute value.
Definition Attributes.h:164
RAII guard to reset the insertion point of the builder when destroyed.
Definition Builders.h:350
This class helps build Operations.
Definition Builders.h:209
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
Definition Builders.h:433
A trait used to provide symbol table functionalities to a region operation.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:88
bool hasAttr(StringAttr name)
Return true if the operation has an attribute with the provided name, false otherwise.
Definition Operation.h:586
Operation * getParentWithTrait()
Returns the closest surrounding parent operation with trait Trait.
Definition Operation.h:274
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 represents a collection of SymbolTables.
virtual SymbolTable & getSymbolTable(Operation *op)
Lookup, or create, a symbol table for an operation.
This class allows for representing and managing the symbol table used by operations with the 'SymbolT...
Definition SymbolTable.h:24
static Operation * lookupNearestSymbolFrom(Operation *from, StringAttr symbol)
Returns the operation registered with the given symbol name within the closest parent operation of,...
StringAttr insert(Operation *symbol, Block::iterator insertPt={})
Insert a new symbol into the table, and rename it as necessary to avoid collisions.
void remove(Operation *op)
Remove the given symbol from the table, without deleting it.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
static Value pack(OpBuilder &builder, Location loc, const LLVMTypeConverter &converter, UnrankedMemRefType type, ValueRange values)
Builds IR populating an unranked MemRef descriptor structure from a list of individual constituent va...
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 ...
static unsigned getNumUnpackedValues()
Returns the number of non-aggregate values that would be produced by unpack.
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:389
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
bool isCompatibleType(Type type)
Returns true if the given type is compatible with the LLVM dialect.
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:717
Include the generated interface declarations.
static constexpr unsigned kDeriveIndexBitwidthFromDataLayout
Value to pass as bitwidth for the index type when the converter is expected to derive the bitwidth fr...
void registerConvertFuncToLLVMInterface(DialectRegistry &registry)
void populateFuncToLLVMConversionPatterns(const LLVMTypeConverter &converter, RewritePatternSet &patterns, SymbolTableCollection *symbolTables=nullptr)
Collect the patterns to convert from the Func dialect to LLVM.
void populateFuncToLLVMFuncOpConversionPattern(const LLVMTypeConverter &converter, RewritePatternSet &patterns, SymbolTableCollection *symbolTables=nullptr)
Collect the default pattern to convert a FuncOp to the LLVM dialect.
FailureOr< LoweredLLVMFuncAttrs > lowerDiscardableAttrsForLLVMFunc(FunctionOpInterface funcOp, Type llvmFuncType)
Partition funcOp's discardables for llvm.func: sym_name, function_type, and typed properties from llv...
FailureOr< LLVM::LLVMFuncOp > convertFuncOpToLLVMFuncOp(FunctionOpInterface funcOp, ConversionPatternRewriter &rewriter, const LLVMTypeConverter &converter, SymbolTableCollection *symbolTables=nullptr)
Convert input FunctionOpInterface operation to LLVMFuncOp by using the provided LLVMTypeConverter.
Result of lowering discardable attributes from a FunctionOpInterface to what llvm....
LLVM::LLVMFuncOp::Properties properties
Eliminates variable at the specified position using Fourier-Motzkin variable elimination.