MLIR  17.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"
32 #include "mlir/IR/BuiltinOps.h"
33 #include "mlir/IR/IRMapping.h"
34 #include "mlir/IR/PatternMatch.h"
35 #include "mlir/IR/TypeUtilities.h"
39 #include "mlir/Transforms/Passes.h"
40 #include "llvm/ADT/SmallVector.h"
41 #include "llvm/ADT/TypeSwitch.h"
42 #include "llvm/IR/DerivedTypes.h"
43 #include "llvm/IR/IRBuilder.h"
44 #include "llvm/IR/Type.h"
45 #include "llvm/Support/CommandLine.h"
46 #include "llvm/Support/FormatVariadic.h"
47 #include <algorithm>
48 #include <functional>
49 
50 namespace mlir {
51 #define GEN_PASS_DEF_CONVERTFUNCTOLLVM
52 #include "mlir/Conversion/Passes.h.inc"
53 } // namespace mlir
54 
55 using namespace mlir;
56 
57 #define PASS_NAME "convert-func-to-llvm"
58 
59 static constexpr StringRef varargsAttrName = "func.varargs";
60 static constexpr StringRef linkageAttrName = "llvm.linkage";
61 
62 /// Only retain those attributes that are not constructed by
63 /// `LLVMFuncOp::build`. If `filterArgAttrs` is set, also filter out argument
64 /// attributes.
65 static void filterFuncAttributes(func::FuncOp func, bool filterArgAndResAttrs,
67  for (const NamedAttribute &attr : func->getAttrs()) {
68  if (attr.getName() == SymbolTable::getSymbolAttrName() ||
69  attr.getName() == func.getFunctionTypeAttrName() ||
70  attr.getName() == linkageAttrName ||
71  attr.getName() == varargsAttrName ||
72  attr.getName() == LLVM::LLVMDialect::getReadnoneAttrName() ||
73  (filterArgAndResAttrs &&
74  (attr.getName() == func.getArgAttrsAttrName() ||
75  attr.getName() == func.getResAttrsAttrName())))
76  continue;
77  result.push_back(attr);
78  }
79 }
80 
81 /// Helper function for wrapping all attributes into a single DictionaryAttr
82 static auto wrapAsStructAttrs(OpBuilder &b, ArrayAttr attrs) {
83  return DictionaryAttr::get(
84  b.getContext(),
85  b.getNamedAttr(LLVM::LLVMDialect::getStructAttrsAttrName(), attrs));
86 }
87 
88 /// Combines all result attributes into a single DictionaryAttr
89 /// and prepends to argument attrs.
90 /// This is intended to be used to format the attributes for a C wrapper
91 /// function when the result(s) is converted to the first function argument
92 /// (in the multiple return case, all returns get wrapped into a single
93 /// argument). The total number of argument attributes should be equal to
94 /// (number of function arguments) + 1.
95 static void
98  func::FuncOp func) {
99  size_t numArguments = func.getNumArguments();
100  auto allAttrs = SmallVector<Attribute>(
101  numArguments + 1, DictionaryAttr::get(builder.getContext()));
102  NamedAttribute *argAttrs = nullptr;
103  for (auto *it = attributes.begin(); it != attributes.end();) {
104  if (it->getName() == func.getArgAttrsAttrName()) {
105  auto arrayAttrs = it->getValue().cast<ArrayAttr>();
106  assert(arrayAttrs.size() == numArguments &&
107  "Number of arg attrs and args should match");
108  std::copy(arrayAttrs.begin(), arrayAttrs.end(), allAttrs.begin() + 1);
109  argAttrs = it;
110  } else if (it->getName() == func.getResAttrsAttrName()) {
111  auto arrayAttrs = it->getValue().cast<ArrayAttr>();
112  assert(!arrayAttrs.empty() && "expected array to be non-empty");
113  allAttrs[0] = (arrayAttrs.size() == 1)
114  ? arrayAttrs[0]
115  : wrapAsStructAttrs(builder, arrayAttrs);
116  it = attributes.erase(it);
117  continue;
118  }
119  it++;
120  }
121 
122  auto newArgAttrs = builder.getNamedAttr(func.getArgAttrsAttrName(),
123  builder.getArrayAttr(allAttrs));
124  if (!argAttrs) {
125  attributes.emplace_back(newArgAttrs);
126  return;
127  }
128  *argAttrs = newArgAttrs;
129 }
130 
131 /// Creates an auxiliary function with pointer-to-memref-descriptor-struct
132 /// arguments instead of unpacked arguments. This function can be called from C
133 /// by passing a pointer to a C struct corresponding to a memref descriptor.
134 /// Similarly, returned memrefs are passed via pointers to a C struct that is
135 /// passed as additional argument.
136 /// Internally, the auxiliary function unpacks the descriptor into individual
137 /// components and forwards them to `newFuncOp` and forwards the results to
138 /// the extra arguments.
139 static void wrapForExternalCallers(OpBuilder &rewriter, Location loc,
140  LLVMTypeConverter &typeConverter,
141  func::FuncOp funcOp,
142  LLVM::LLVMFuncOp newFuncOp) {
143  auto type = funcOp.getFunctionType();
145  filterFuncAttributes(funcOp, /*filterArgAndResAttrs=*/false, attributes);
146  auto [wrapperFuncType, resultIsNowArg] =
147  typeConverter.convertFunctionTypeCWrapper(type);
148  if (resultIsNowArg)
149  prependResAttrsToArgAttrs(rewriter, attributes, funcOp);
150  auto wrapperFuncOp = rewriter.create<LLVM::LLVMFuncOp>(
151  loc, llvm::formatv("_mlir_ciface_{0}", funcOp.getName()).str(),
152  wrapperFuncType, LLVM::Linkage::External, /*dsoLocal*/ false,
153  /*cconv*/ LLVM::CConv::C, attributes);
154 
155  OpBuilder::InsertionGuard guard(rewriter);
156  rewriter.setInsertionPointToStart(wrapperFuncOp.addEntryBlock());
157 
159  size_t argOffset = resultIsNowArg ? 1 : 0;
160  for (auto &en : llvm::enumerate(type.getInputs())) {
161  Value arg = wrapperFuncOp.getArgument(en.index() + argOffset);
162  if (auto memrefType = en.value().dyn_cast<MemRefType>()) {
163  Value loaded = rewriter.create<LLVM::LoadOp>(loc, arg);
164  MemRefDescriptor::unpack(rewriter, loc, loaded, memrefType, args);
165  continue;
166  }
167  if (en.value().isa<UnrankedMemRefType>()) {
168  Value loaded = rewriter.create<LLVM::LoadOp>(loc, arg);
169  UnrankedMemRefDescriptor::unpack(rewriter, loc, loaded, args);
170  continue;
171  }
172 
173  args.push_back(arg);
174  }
175 
176  auto call = rewriter.create<LLVM::CallOp>(loc, newFuncOp, args);
177 
178  if (resultIsNowArg) {
179  rewriter.create<LLVM::StoreOp>(loc, call.getResult(),
180  wrapperFuncOp.getArgument(0));
181  rewriter.create<LLVM::ReturnOp>(loc, ValueRange{});
182  } else {
183  rewriter.create<LLVM::ReturnOp>(loc, call.getResults());
184  }
185 }
186 
187 /// Creates an auxiliary function with pointer-to-memref-descriptor-struct
188 /// arguments instead of unpacked arguments. Creates a body for the (external)
189 /// `newFuncOp` that allocates a memref descriptor on stack, packs the
190 /// individual arguments into this descriptor and passes a pointer to it into
191 /// the auxiliary function. If the result of the function cannot be directly
192 /// returned, we write it to a special first argument that provides a pointer
193 /// to a corresponding struct. This auxiliary external function is now
194 /// compatible with functions defined in C using pointers to C structs
195 /// corresponding to a memref descriptor.
196 static void wrapExternalFunction(OpBuilder &builder, Location loc,
197  LLVMTypeConverter &typeConverter,
198  func::FuncOp funcOp,
199  LLVM::LLVMFuncOp newFuncOp) {
200  OpBuilder::InsertionGuard guard(builder);
201 
202  auto [wrapperType, resultIsNowArg] =
203  typeConverter.convertFunctionTypeCWrapper(funcOp.getFunctionType());
204  // This conversion can only fail if it could not convert one of the argument
205  // types. But since it has been applied to a non-wrapper function before, it
206  // should have failed earlier and not reach this point at all.
207  assert(wrapperType && "unexpected type conversion failure");
208 
210  filterFuncAttributes(funcOp, /*filterArgAndResAttrs=*/false, attributes);
211 
212  if (resultIsNowArg)
213  prependResAttrsToArgAttrs(builder, attributes, funcOp);
214  // Create the auxiliary function.
215  auto wrapperFunc = builder.create<LLVM::LLVMFuncOp>(
216  loc, llvm::formatv("_mlir_ciface_{0}", funcOp.getName()).str(),
217  wrapperType, LLVM::Linkage::External, /*dsoLocal*/ false,
218  /*cconv*/ LLVM::CConv::C, attributes);
219 
220  builder.setInsertionPointToStart(newFuncOp.addEntryBlock());
221 
222  // Get a ValueRange containing arguments.
223  FunctionType type = funcOp.getFunctionType();
225  args.reserve(type.getNumInputs());
226  ValueRange wrapperArgsRange(newFuncOp.getArguments());
227 
228  if (resultIsNowArg) {
229  // Allocate the struct on the stack and pass the pointer.
230  Type resultType =
231  wrapperType.cast<LLVM::LLVMFunctionType>().getParamType(0);
232  Value one = builder.create<LLVM::ConstantOp>(
233  loc, typeConverter.convertType(builder.getIndexType()),
234  builder.getIntegerAttr(builder.getIndexType(), 1));
235  Value result = builder.create<LLVM::AllocaOp>(loc, resultType, one);
236  args.push_back(result);
237  }
238 
239  // Iterate over the inputs of the original function and pack values into
240  // memref descriptors if the original type is a memref.
241  for (auto &en : llvm::enumerate(type.getInputs())) {
242  Value arg;
243  int numToDrop = 1;
244  auto memRefType = en.value().dyn_cast<MemRefType>();
245  auto unrankedMemRefType = en.value().dyn_cast<UnrankedMemRefType>();
246  if (memRefType || unrankedMemRefType) {
247  numToDrop = memRefType
250  Value packed =
251  memRefType
252  ? MemRefDescriptor::pack(builder, loc, typeConverter, memRefType,
253  wrapperArgsRange.take_front(numToDrop))
255  builder, loc, typeConverter, unrankedMemRefType,
256  wrapperArgsRange.take_front(numToDrop));
257 
258  auto ptrTy = LLVM::LLVMPointerType::get(packed.getType());
259  Value one = builder.create<LLVM::ConstantOp>(
260  loc, typeConverter.convertType(builder.getIndexType()),
261  builder.getIntegerAttr(builder.getIndexType(), 1));
262  Value allocated =
263  builder.create<LLVM::AllocaOp>(loc, ptrTy, one, /*alignment=*/0);
264  builder.create<LLVM::StoreOp>(loc, packed, allocated);
265  arg = allocated;
266  } else {
267  arg = wrapperArgsRange[0];
268  }
269 
270  args.push_back(arg);
271  wrapperArgsRange = wrapperArgsRange.drop_front(numToDrop);
272  }
273  assert(wrapperArgsRange.empty() && "did not map some of the arguments");
274 
275  auto call = builder.create<LLVM::CallOp>(loc, wrapperFunc, args);
276 
277  if (resultIsNowArg) {
278  Value result = builder.create<LLVM::LoadOp>(loc, args.front());
279  builder.create<LLVM::ReturnOp>(loc, result);
280  } else {
281  builder.create<LLVM::ReturnOp>(loc, call.getResults());
282  }
283 }
284 
285 namespace {
286 
287 struct FuncOpConversionBase : public ConvertOpToLLVMPattern<func::FuncOp> {
288 protected:
290 
291  // Convert input FuncOp to LLVMFuncOp by using the LLVMTypeConverter provided
292  // to this legalization pattern.
293  LLVM::LLVMFuncOp
294  convertFuncOpToLLVMFuncOp(func::FuncOp funcOp,
295  ConversionPatternRewriter &rewriter) const {
296  // Convert the original function arguments. They are converted using the
297  // LLVMTypeConverter provided to this legalization pattern.
298  auto varargsAttr = funcOp->getAttrOfType<BoolAttr>(varargsAttrName);
299  TypeConverter::SignatureConversion result(funcOp.getNumArguments());
300  auto llvmType = getTypeConverter()->convertFunctionSignature(
301  funcOp.getFunctionType(), varargsAttr && varargsAttr.getValue(),
302  result);
303  if (!llvmType)
304  return nullptr;
305 
306  // Propagate argument/result attributes to all converted arguments/result
307  // obtained after converting a given original argument/result.
309  filterFuncAttributes(funcOp, /*filterArgAndResAttrs=*/true, attributes);
310  if (ArrayAttr resAttrDicts = funcOp.getAllResultAttrs()) {
311  assert(!resAttrDicts.empty() && "expected array to be non-empty");
312  auto newResAttrDicts =
313  (funcOp.getNumResults() == 1)
314  ? resAttrDicts
315  : rewriter.getArrayAttr(
316  {wrapAsStructAttrs(rewriter, resAttrDicts)});
317  attributes.push_back(
318  rewriter.getNamedAttr(funcOp.getResAttrsAttrName(), newResAttrDicts));
319  }
320  if (ArrayAttr argAttrDicts = funcOp.getAllArgAttrs()) {
321  SmallVector<Attribute, 4> newArgAttrs(
322  llvmType.cast<LLVM::LLVMFunctionType>().getNumParams());
323  for (unsigned i = 0, e = funcOp.getNumArguments(); i < e; ++i) {
324  // Some LLVM IR attribute have a type attached to them. During FuncOp ->
325  // LLVMFuncOp conversion these types may have changed. Account for that
326  // change by converting attributes' types as well.
327  SmallVector<NamedAttribute, 4> convertedAttrs;
328  auto attrsDict = argAttrDicts[i].cast<DictionaryAttr>();
329  convertedAttrs.reserve(attrsDict.size());
330  for (const NamedAttribute &attr : attrsDict) {
331  const auto convert = [&](const NamedAttribute &attr) {
332  return TypeAttr::get(getTypeConverter()->convertType(
333  attr.getValue().cast<TypeAttr>().getValue()));
334  };
335  if (attr.getName().getValue() ==
336  LLVM::LLVMDialect::getByValAttrName()) {
337  convertedAttrs.push_back(rewriter.getNamedAttr(
338  LLVM::LLVMDialect::getByValAttrName(), convert(attr)));
339  } else if (attr.getName().getValue() ==
340  LLVM::LLVMDialect::getByRefAttrName()) {
341  convertedAttrs.push_back(rewriter.getNamedAttr(
342  LLVM::LLVMDialect::getByRefAttrName(), convert(attr)));
343  } else if (attr.getName().getValue() ==
344  LLVM::LLVMDialect::getStructRetAttrName()) {
345  convertedAttrs.push_back(rewriter.getNamedAttr(
346  LLVM::LLVMDialect::getStructRetAttrName(), convert(attr)));
347  } else if (attr.getName().getValue() ==
348  LLVM::LLVMDialect::getInAllocaAttrName()) {
349  convertedAttrs.push_back(rewriter.getNamedAttr(
350  LLVM::LLVMDialect::getInAllocaAttrName(), convert(attr)));
351  } else {
352  convertedAttrs.push_back(attr);
353  }
354  }
355  auto mapping = result.getInputMapping(i);
356  assert(mapping && "unexpected deletion of function argument");
357  // Only attach the new argument attributes if there is a one-to-one
358  // mapping from old to new types. Otherwise, attributes might be
359  // attached to types that they do not support.
360  if (mapping->size == 1) {
361  newArgAttrs[mapping->inputNo] =
362  DictionaryAttr::get(rewriter.getContext(), convertedAttrs);
363  continue;
364  }
365  // TODO: Implement custom handling for types that expand to multiple
366  // function arguments.
367  for (size_t j = 0; j < mapping->size; ++j)
368  newArgAttrs[mapping->inputNo + j] =
369  DictionaryAttr::get(rewriter.getContext(), {});
370  }
371  attributes.push_back(rewriter.getNamedAttr(
372  funcOp.getArgAttrsAttrName(), rewriter.getArrayAttr(newArgAttrs)));
373  }
374 
375  // Create an LLVM function, use external linkage by default until MLIR
376  // functions have linkage.
377  LLVM::Linkage linkage = LLVM::Linkage::External;
378  if (funcOp->hasAttr(linkageAttrName)) {
379  auto attr =
380  funcOp->getAttr(linkageAttrName).dyn_cast<mlir::LLVM::LinkageAttr>();
381  if (!attr) {
382  funcOp->emitError() << "Contains " << linkageAttrName
383  << " attribute not of type LLVM::LinkageAttr";
384  return nullptr;
385  }
386  linkage = attr.getLinkage();
387  }
388 
389  // Create a memory effect attribute corresponding to readnone.
390  StringRef readnoneAttrName = LLVM::LLVMDialect::getReadnoneAttrName();
391  LLVM::MemoryEffectsAttr memoryAttr = {};
392  if (funcOp->hasAttr(readnoneAttrName)) {
393  auto attr = funcOp->getAttrOfType<UnitAttr>(readnoneAttrName);
394  if (!attr) {
395  funcOp->emitError() << "Contains " << readnoneAttrName
396  << " attribute not of type UnitAttr";
397  return nullptr;
398  }
399  memoryAttr = LLVM::MemoryEffectsAttr::get(rewriter.getContext(),
400  {LLVM::ModRefInfo::NoModRef,
401  LLVM::ModRefInfo::NoModRef,
402  LLVM::ModRefInfo::NoModRef});
403  }
404  auto newFuncOp = rewriter.create<LLVM::LLVMFuncOp>(
405  funcOp.getLoc(), funcOp.getName(), llvmType, linkage,
406  /*dsoLocal*/ false, /*cconv*/ LLVM::CConv::C, attributes);
407  // If the memory attribute was created, add it to the function.
408  if (memoryAttr)
409  newFuncOp.setMemoryAttr(memoryAttr);
410  rewriter.inlineRegionBefore(funcOp.getBody(), newFuncOp.getBody(),
411  newFuncOp.end());
412  if (failed(rewriter.convertRegionTypes(&newFuncOp.getBody(), *typeConverter,
413  &result)))
414  return nullptr;
415 
416  return newFuncOp;
417  }
418 };
419 
420 /// FuncOp legalization pattern that converts MemRef arguments to pointers to
421 /// MemRef descriptors (LLVM struct data types) containing all the MemRef type
422 /// information.
423 struct FuncOpConversion : public FuncOpConversionBase {
424  FuncOpConversion(LLVMTypeConverter &converter)
425  : FuncOpConversionBase(converter) {}
426 
428  matchAndRewrite(func::FuncOp funcOp, OpAdaptor adaptor,
429  ConversionPatternRewriter &rewriter) const override {
430  auto newFuncOp = convertFuncOpToLLVMFuncOp(funcOp, rewriter);
431  if (!newFuncOp)
432  return failure();
433 
434  if (funcOp->getAttrOfType<UnitAttr>(
435  LLVM::LLVMDialect::getEmitCWrapperAttrName())) {
436  if (newFuncOp.isVarArg())
437  return funcOp->emitError("C interface for variadic functions is not "
438  "supported yet.");
439 
440  if (newFuncOp.isExternal())
441  wrapExternalFunction(rewriter, funcOp.getLoc(), *getTypeConverter(),
442  funcOp, newFuncOp);
443  else
444  wrapForExternalCallers(rewriter, funcOp.getLoc(), *getTypeConverter(),
445  funcOp, newFuncOp);
446  }
447 
448  rewriter.eraseOp(funcOp);
449  return success();
450  }
451 };
452 
453 /// FuncOp legalization pattern that converts MemRef arguments to bare pointers
454 /// to the MemRef element type. This will impact the calling convention and ABI.
455 struct BarePtrFuncOpConversion : public FuncOpConversionBase {
456  using FuncOpConversionBase::FuncOpConversionBase;
457 
459  matchAndRewrite(func::FuncOp funcOp, OpAdaptor adaptor,
460  ConversionPatternRewriter &rewriter) const override {
461 
462  // TODO: bare ptr conversion could be handled by argument materialization
463  // and most of the code below would go away. But to do this, we would need a
464  // way to distinguish between FuncOp and other regions in the
465  // addArgumentMaterialization hook.
466 
467  // Store the type of memref-typed arguments before the conversion so that we
468  // can promote them to MemRef descriptor at the beginning of the function.
469  SmallVector<Type, 8> oldArgTypes =
470  llvm::to_vector<8>(funcOp.getFunctionType().getInputs());
471 
472  auto newFuncOp = convertFuncOpToLLVMFuncOp(funcOp, rewriter);
473  if (!newFuncOp)
474  return failure();
475  if (newFuncOp.getBody().empty()) {
476  rewriter.eraseOp(funcOp);
477  return success();
478  }
479 
480  // Promote bare pointers from memref arguments to memref descriptors at the
481  // beginning of the function so that all the memrefs in the function have a
482  // uniform representation.
483  Block *entryBlock = &newFuncOp.getBody().front();
484  auto blockArgs = entryBlock->getArguments();
485  assert(blockArgs.size() == oldArgTypes.size() &&
486  "The number of arguments and types doesn't match");
487 
488  OpBuilder::InsertionGuard guard(rewriter);
489  rewriter.setInsertionPointToStart(entryBlock);
490  for (auto it : llvm::zip(blockArgs, oldArgTypes)) {
491  BlockArgument arg = std::get<0>(it);
492  Type argTy = std::get<1>(it);
493 
494  // Unranked memrefs are not supported in the bare pointer calling
495  // convention. We should have bailed out before in the presence of
496  // unranked memrefs.
497  assert(!argTy.isa<UnrankedMemRefType>() &&
498  "Unranked memref is not supported");
499  auto memrefTy = argTy.dyn_cast<MemRefType>();
500  if (!memrefTy)
501  continue;
502 
503  // Replace barePtr with a placeholder (undef), promote barePtr to a ranked
504  // or unranked memref descriptor and replace placeholder with the last
505  // instruction of the memref descriptor.
506  // TODO: The placeholder is needed to avoid replacing barePtr uses in the
507  // MemRef descriptor instructions. We may want to have a utility in the
508  // rewriter to properly handle this use case.
509  Location loc = funcOp.getLoc();
510  auto placeholder = rewriter.create<LLVM::UndefOp>(
511  loc, getTypeConverter()->convertType(memrefTy));
512  rewriter.replaceUsesOfBlockArgument(arg, placeholder);
513 
515  rewriter, loc, *getTypeConverter(), memrefTy, arg);
516  rewriter.replaceOp(placeholder, {desc});
517  }
518 
519  rewriter.eraseOp(funcOp);
520  return success();
521  }
522 };
523 
524 struct ConstantOpLowering : public ConvertOpToLLVMPattern<func::ConstantOp> {
526 
528  matchAndRewrite(func::ConstantOp op, OpAdaptor adaptor,
529  ConversionPatternRewriter &rewriter) const override {
530  auto type = typeConverter->convertType(op.getResult().getType());
531  if (!type || !LLVM::isCompatibleType(type))
532  return rewriter.notifyMatchFailure(op, "failed to convert result type");
533 
534  auto newOp =
535  rewriter.create<LLVM::AddressOfOp>(op.getLoc(), type, op.getValue());
536  for (const NamedAttribute &attr : op->getAttrs()) {
537  if (attr.getName().strref() == "value")
538  continue;
539  newOp->setAttr(attr.getName(), attr.getValue());
540  }
541  rewriter.replaceOp(op, newOp->getResults());
542  return success();
543  }
544 };
545 
546 // A CallOp automatically promotes MemRefType to a sequence of alloca/store and
547 // passes the pointer to the MemRef across function boundaries.
548 template <typename CallOpType>
549 struct CallOpInterfaceLowering : public ConvertOpToLLVMPattern<CallOpType> {
551  using Super = CallOpInterfaceLowering<CallOpType>;
553 
555  matchAndRewrite(CallOpType callOp, typename CallOpType::Adaptor adaptor,
556  ConversionPatternRewriter &rewriter) const override {
557  // Pack the result types into a struct.
558  Type packedResult = nullptr;
559  unsigned numResults = callOp.getNumResults();
560  auto resultTypes = llvm::to_vector<4>(callOp.getResultTypes());
561 
562  if (numResults != 0) {
563  if (!(packedResult =
564  this->getTypeConverter()->packFunctionResults(resultTypes)))
565  return failure();
566  }
567 
568  auto promoted = this->getTypeConverter()->promoteOperands(
569  callOp.getLoc(), /*opOperands=*/callOp->getOperands(),
570  adaptor.getOperands(), rewriter);
571  auto newOp = rewriter.create<LLVM::CallOp>(
572  callOp.getLoc(), packedResult ? TypeRange(packedResult) : TypeRange(),
573  promoted, callOp->getAttrs());
574 
575  SmallVector<Value, 4> results;
576  if (numResults < 2) {
577  // If < 2 results, packing did not do anything and we can just return.
578  results.append(newOp.result_begin(), newOp.result_end());
579  } else {
580  // Otherwise, it had been converted to an operation producing a structure.
581  // Extract individual results from the structure and return them as list.
582  results.reserve(numResults);
583  for (unsigned i = 0; i < numResults; ++i) {
584  results.push_back(rewriter.create<LLVM::ExtractValueOp>(
585  callOp.getLoc(), newOp->getResult(0), i));
586  }
587  }
588 
589  if (this->getTypeConverter()->getOptions().useBarePtrCallConv) {
590  // For the bare-ptr calling convention, promote memref results to
591  // descriptors.
592  assert(results.size() == resultTypes.size() &&
593  "The number of arguments and types doesn't match");
594  this->getTypeConverter()->promoteBarePtrsToDescriptors(
595  rewriter, callOp.getLoc(), resultTypes, results);
596  } else if (failed(this->copyUnrankedDescriptors(rewriter, callOp.getLoc(),
597  resultTypes, results,
598  /*toDynamic=*/false))) {
599  return failure();
600  }
601 
602  rewriter.replaceOp(callOp, results);
603  return success();
604  }
605 };
606 
607 struct CallOpLowering : public CallOpInterfaceLowering<func::CallOp> {
608  using Super::Super;
609 };
610 
611 struct CallIndirectOpLowering
612  : public CallOpInterfaceLowering<func::CallIndirectOp> {
613  using Super::Super;
614 };
615 
616 struct UnrealizedConversionCastOpLowering
617  : public ConvertOpToLLVMPattern<UnrealizedConversionCastOp> {
619  UnrealizedConversionCastOp>::ConvertOpToLLVMPattern;
620 
622  matchAndRewrite(UnrealizedConversionCastOp op, OpAdaptor adaptor,
623  ConversionPatternRewriter &rewriter) const override {
624  SmallVector<Type> convertedTypes;
625  if (succeeded(typeConverter->convertTypes(op.getOutputs().getTypes(),
626  convertedTypes)) &&
627  convertedTypes == adaptor.getInputs().getTypes()) {
628  rewriter.replaceOp(op, adaptor.getInputs());
629  return success();
630  }
631 
632  convertedTypes.clear();
633  if (succeeded(typeConverter->convertTypes(adaptor.getInputs().getTypes(),
634  convertedTypes)) &&
635  convertedTypes == op.getOutputs().getType()) {
636  rewriter.replaceOp(op, adaptor.getInputs());
637  return success();
638  }
639  return failure();
640  }
641 };
642 
643 // Special lowering pattern for `ReturnOps`. Unlike all other operations,
644 // `ReturnOp` interacts with the function signature and must have as many
645 // operands as the function has return values. Because in LLVM IR, functions
646 // can only return 0 or 1 value, we pack multiple values into a structure type.
647 // Emit `UndefOp` followed by `InsertValueOp`s to create such structure if
648 // necessary before returning it
649 struct ReturnOpLowering : public ConvertOpToLLVMPattern<func::ReturnOp> {
651 
653  matchAndRewrite(func::ReturnOp op, OpAdaptor adaptor,
654  ConversionPatternRewriter &rewriter) const override {
655  Location loc = op.getLoc();
656  unsigned numArguments = op.getNumOperands();
657  SmallVector<Value, 4> updatedOperands;
658 
659  if (getTypeConverter()->getOptions().useBarePtrCallConv) {
660  // For the bare-ptr calling convention, extract the aligned pointer to
661  // be returned from the memref descriptor.
662  for (auto it : llvm::zip(op->getOperands(), adaptor.getOperands())) {
663  Type oldTy = std::get<0>(it).getType();
664  Value newOperand = std::get<1>(it);
665  if (oldTy.isa<MemRefType>() && getTypeConverter()->canConvertToBarePtr(
666  oldTy.cast<BaseMemRefType>())) {
667  MemRefDescriptor memrefDesc(newOperand);
668  newOperand = memrefDesc.alignedPtr(rewriter, loc);
669  } else if (oldTy.isa<UnrankedMemRefType>()) {
670  // Unranked memref is not supported in the bare pointer calling
671  // convention.
672  return failure();
673  }
674  updatedOperands.push_back(newOperand);
675  }
676  } else {
677  updatedOperands = llvm::to_vector<4>(adaptor.getOperands());
678  (void)copyUnrankedDescriptors(rewriter, loc, op.getOperands().getTypes(),
679  updatedOperands,
680  /*toDynamic=*/true);
681  }
682 
683  // If ReturnOp has 0 or 1 operand, create it and return immediately.
684  if (numArguments <= 1) {
685  rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(
686  op, TypeRange(), updatedOperands, op->getAttrs());
687  return success();
688  }
689 
690  // Otherwise, we need to pack the arguments into an LLVM struct type before
691  // returning.
692  auto packedType =
693  getTypeConverter()->packFunctionResults(op.getOperandTypes());
694 
695  Value packed = rewriter.create<LLVM::UndefOp>(loc, packedType);
696  for (auto &it : llvm::enumerate(updatedOperands)) {
697  packed = rewriter.create<LLVM::InsertValueOp>(loc, packed, it.value(),
698  it.index());
699  }
700  rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, TypeRange(), packed,
701  op->getAttrs());
702  return success();
703  }
704 };
705 } // namespace
706 
708  LLVMTypeConverter &converter, RewritePatternSet &patterns) {
709  if (converter.getOptions().useBarePtrCallConv)
710  patterns.add<BarePtrFuncOpConversion>(converter);
711  else
712  patterns.add<FuncOpConversion>(converter);
713 }
714 
716  RewritePatternSet &patterns) {
717  populateFuncToLLVMFuncOpConversionPattern(converter, patterns);
718  // clang-format off
719  patterns.add<
720  CallIndirectOpLowering,
721  CallOpLowering,
722  ConstantOpLowering,
723  ReturnOpLowering>(converter);
724  // clang-format on
725 }
726 
727 namespace {
728 /// A pass converting Func operations into the LLVM IR dialect.
729 struct ConvertFuncToLLVMPass
730  : public impl::ConvertFuncToLLVMBase<ConvertFuncToLLVMPass> {
731  ConvertFuncToLLVMPass() = default;
732  ConvertFuncToLLVMPass(bool useBarePtrCallConv, unsigned indexBitwidth,
733  bool useAlignedAlloc,
734  const llvm::DataLayout &dataLayout) {
735  this->useBarePtrCallConv = useBarePtrCallConv;
736  this->indexBitwidth = indexBitwidth;
737  this->dataLayout = dataLayout.getStringRepresentation();
738  }
739 
740  /// Run the dialect converter on the module.
741  void runOnOperation() override {
742  if (failed(LLVM::LLVMDialect::verifyDataLayoutString(
743  this->dataLayout, [this](const Twine &message) {
744  getOperation().emitError() << message.str();
745  }))) {
746  signalPassFailure();
747  return;
748  }
749 
750  ModuleOp m = getOperation();
751  const auto &dataLayoutAnalysis = getAnalysis<DataLayoutAnalysis>();
752 
753  LowerToLLVMOptions options(&getContext(),
754  dataLayoutAnalysis.getAtOrAbove(m));
755  options.useBarePtrCallConv = useBarePtrCallConv;
756  if (indexBitwidth != kDeriveIndexBitwidthFromDataLayout)
757  options.overrideIndexBitwidth(indexBitwidth);
758  options.dataLayout = llvm::DataLayout(this->dataLayout);
759 
760  LLVMTypeConverter typeConverter(&getContext(), options,
761  &dataLayoutAnalysis);
762 
763  RewritePatternSet patterns(&getContext());
764  populateFuncToLLVMConversionPatterns(typeConverter, patterns);
765 
766  // TODO: Remove these in favor of their dedicated conversion passes.
767  arith::populateArithToLLVMConversionPatterns(typeConverter, patterns);
768  cf::populateControlFlowToLLVMConversionPatterns(typeConverter, patterns);
769 
770  LLVMConversionTarget target(getContext());
771  if (failed(applyPartialConversion(m, target, std::move(patterns))))
772  signalPassFailure();
773 
774  m->setAttr(LLVM::LLVMDialect::getDataLayoutAttrName(),
775  StringAttr::get(m.getContext(), this->dataLayout));
776  }
777 };
778 } // namespace
779 
780 std::unique_ptr<OperationPass<ModuleOp>> mlir::createConvertFuncToLLVMPass() {
781  return std::make_unique<ConvertFuncToLLVMPass>();
782 }
783 
784 std::unique_ptr<OperationPass<ModuleOp>>
786  auto allocLowering = options.allocLowering;
787  // There is no way to provide additional patterns for pass, so
788  // AllocLowering::None will always fail.
789  assert(allocLowering != LowerToLLVMOptions::AllocLowering::None &&
790  "ConvertFuncToLLVMPass doesn't support AllocLowering::None");
791  bool useAlignedAlloc =
792  (allocLowering == LowerToLLVMOptions::AllocLowering::AlignedAlloc);
793  return std::make_unique<ConvertFuncToLLVMPass>(
794  options.useBarePtrCallConv, options.getIndexBitwidth(), useAlignedAlloc,
795  options.dataLayout);
796 }
static void copy(Location loc, Value dst, Value src, Value size, OpBuilder &builder)
Copies the given number of bytes from src to dst pointers.
static void prependResAttrsToArgAttrs(OpBuilder &builder, SmallVectorImpl< NamedAttribute > &attributes, func::FuncOp func)
Combines all result attributes into a single DictionaryAttr and prepends to argument attrs.
Definition: FuncToLLVM.cpp:96
static auto wrapAsStructAttrs(OpBuilder &b, ArrayAttr attrs)
Helper function for wrapping all attributes into a single DictionaryAttr.
Definition: FuncToLLVM.cpp:82
static void wrapExternalFunction(OpBuilder &builder, Location loc, LLVMTypeConverter &typeConverter, func::FuncOp funcOp, LLVM::LLVMFuncOp newFuncOp)
Creates an auxiliary function with pointer-to-memref-descriptor-struct arguments instead of unpacked ...
Definition: FuncToLLVM.cpp:196
static constexpr StringRef varargsAttrName
Definition: FuncToLLVM.cpp:59
static constexpr StringRef linkageAttrName
Definition: FuncToLLVM.cpp:60
static void wrapForExternalCallers(OpBuilder &rewriter, Location loc, LLVMTypeConverter &typeConverter, func::FuncOp funcOp, LLVM::LLVMFuncOp newFuncOp)
Creates an auxiliary function with pointer-to-memref-descriptor-struct arguments instead of unpacked ...
Definition: FuncToLLVM.cpp:139
static void filterFuncAttributes(func::FuncOp func, bool filterArgAndResAttrs, SmallVectorImpl< NamedAttribute > &result)
Only retain those attributes that are not constructed by LLVMFuncOp::build.
Definition: FuncToLLVM.cpp:65
@ None
static llvm::ManagedStatic< PassManagerOptions > options
U cast() const
Definition: Attributes.h:179
This class provides a shared interface for ranked and unranked memref types.
Definition: BuiltinTypes.h:113
This class represents an argument of a Block.
Definition: Value.h:304
Block represents an ordered list of Operations.
Definition: Block.h:30
BlockArgListType getArguments()
Definition: Block.h:76
Operation & front()
Definition: Block.h:142
Special case of IntegerAttr to represent boolean integers, i.e., signless i1 integers.
IntegerAttr getIntegerAttr(Type type, int64_t value)
Definition: Builders.cpp:212
MLIRContext * getContext() const
Definition: Builders.h:55
ArrayAttr getArrayAttr(ArrayRef< Attribute > value)
Definition: Builders.cpp:247
IndexType getIndexType()
Definition: Builders.cpp:56
NamedAttribute getNamedAttr(StringRef name, Attribute val)
Definition: Builders.cpp:95
This class implements a pattern rewriter for use with ConversionPatterns.
void replaceOp(Operation *op, ValueRange newValues) override
PatternRewriter hook for replacing the results of an operation.
LogicalResult notifyMatchFailure(Location loc, function_ref< void(Diagnostic &)> reasonCallback) override
PatternRewriter hook for notifying match failure reasons.
void inlineRegionBefore(Region &region, Region &parent, Region::iterator before) override
PatternRewriter hook for moving blocks out of a region.
void eraseOp(Operation *op) override
PatternRewriter hook for erasing a dead operation.
FailureOr< Block * > convertRegionTypes(Region *region, TypeConverter &converter, TypeConverter::SignatureConversion *entryConversion=nullptr)
Convert the types of block arguments within the given region.
void replaceUsesOfBlockArgument(BlockArgument from, Value to)
Replace all the uses of the block argument from with value to.
Utility class for operation conversions targeting the LLVM dialect that match exactly one source oper...
Definition: Pattern.h:135
Derived class that automatically populates legalization information for different LLVM ops.
Conversion from types to the LLVM IR dialect.
Definition: TypeConverter.h:30
const LowerToLLVMOptions & getOptions() const
Definition: TypeConverter.h:81
LogicalResult convertType(Type t, SmallVectorImpl< Type > &results)
Convert the given type.
std::pair< Type, bool > convertFunctionTypeCWrapper(FunctionType type)
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:63
Options to control the LLVM lowering.
Helper class to produce LLVM dialect operations extracting or inserting elements of a MemRef descript...
Definition: MemRefBuilder.h:33
static Value pack(OpBuilder &builder, Location loc, LLVMTypeConverter &converter, MemRefType type, ValueRange values)
Builds IR populating a MemRef descriptor structure from a list of individual values composing that de...
static unsigned getNumUnpackedValues(MemRefType type)
Returns the number of non-aggregate values that would be produced by unpack.
static MemRefDescriptor fromStaticShape(OpBuilder &builder, Location loc, LLVMTypeConverter &typeConverter, MemRefType type, Value memory)
Builds IR creating a MemRef descriptor that represents type and populates it with static shape and st...
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...
NamedAttribute represents a combination of a name and an Attribute value.
Definition: Attributes.h:192
Attribute getValue() const
Return the value of the attribute.
Definition: Attributes.h:206
RAII guard to reset the insertion point of the builder when destroyed.
Definition: Builders.h:301
This class helps build Operations.
Definition: Builders.h:199
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
Definition: Builders.h:384
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
Definition: Builders.cpp:422
void setAttr(StringAttr name, Attribute value)
If the an attribute exists with the specified name, change it to the new value.
Definition: Operation.h:439
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replaces the result op with a new op that is created without verification.
Definition: PatternMatch.h:451
static StringRef getSymbolAttrName()
Return the name of the attribute used for symbol names.
Definition: SymbolTable.h:58
This class provides all of the information necessary to convert a type signature.
This class provides an abstraction over the various different ranges of value types.
Definition: TypeRange.h:36
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition: Types.h:74
U cast() const
Definition: Types.h:321
U dyn_cast() const
Definition: Types.h:311
bool isa() const
Definition: Types.h:301
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 Value pack(OpBuilder &builder, Location loc, LLVMTypeConverter &converter, UnrankedMemRefType type, ValueRange values)
Builds IR populating an unranked MemRef descriptor structure from a list of individual constituent va...
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:350
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition: Value.h:93
Type getType() const
Return the type of this value.
Definition: Value.h:122
U dyn_cast() const
Definition: Value.h:103
bool isCompatibleType(Type type)
Returns true if the given type is compatible with the LLVM dialect.
Definition: LLVMTypes.cpp:837
void populateArithToLLVMConversionPatterns(LLVMTypeConverter &converter, RewritePatternSet &patterns)
void populateControlFlowToLLVMConversionPatterns(LLVMTypeConverter &converter, RewritePatternSet &patterns)
Collect the patterns to convert from the ControlFlow dialect to LLVM.
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
Definition: Matchers.h:223
Include the generated interface declarations.
std::unique_ptr< OperationPass< ModuleOp > > createConvertFuncToLLVMPass()
Creates a pass to convert the Func dialect into the LLVMIR dialect.
Definition: FuncToLLVM.cpp:780
LogicalResult failure(bool isFailure=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:62
LogicalResult applyPartialConversion(ArrayRef< Operation * > ops, ConversionTarget &target, const FrozenRewritePatternSet &patterns, DenseSet< Operation * > *unconvertedOps=nullptr)
Below we define several entry points for operation conversion.
static constexpr unsigned kDeriveIndexBitwidthFromDataLayout
Value to pass as bitwidth for the index type when the converter is expected to derive the bitwidth fr...
bool succeeded(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a success value.
Definition: LogicalResult.h:68
LogicalResult success(bool isSuccess=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:56
void populateFuncToLLVMConversionPatterns(LLVMTypeConverter &converter, RewritePatternSet &patterns)
Collect the patterns to convert from the Func dialect to LLVM.
Definition: FuncToLLVM.cpp:715
bool failed(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a failure value.
Definition: LogicalResult.h:72
void populateFuncToLLVMFuncOpConversionPattern(LLVMTypeConverter &converter, RewritePatternSet &patterns)
Collect the default pattern to convert a FuncOp to the LLVM dialect.
Definition: FuncToLLVM.cpp:707
This class represents an efficient way to signal success or failure.
Definition: LogicalResult.h:26
Eliminates variable at the specified position using Fourier-Motzkin variable elimination.