MLIR  19.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 
29 #include "mlir/IR/Attributes.h"
30 #include "mlir/IR/Builders.h"
33 #include "mlir/IR/BuiltinOps.h"
34 #include "mlir/IR/IRMapping.h"
35 #include "mlir/IR/PatternMatch.h"
36 #include "mlir/IR/SymbolTable.h"
37 #include "mlir/IR/TypeUtilities.h"
41 #include "mlir/Transforms/Passes.h"
42 #include "llvm/ADT/SmallVector.h"
43 #include "llvm/ADT/TypeSwitch.h"
44 #include "llvm/IR/DerivedTypes.h"
45 #include "llvm/IR/IRBuilder.h"
46 #include "llvm/IR/Type.h"
47 #include "llvm/Support/Casting.h"
48 #include "llvm/Support/CommandLine.h"
49 #include "llvm/Support/FormatVariadic.h"
50 #include <algorithm>
51 #include <functional>
52 #include <optional>
53 
54 namespace mlir {
55 #define GEN_PASS_DEF_CONVERTFUNCTOLLVMPASS
56 #define GEN_PASS_DEF_SETLLVMMODULEDATALAYOUTPASS
57 #include "mlir/Conversion/Passes.h.inc"
58 } // namespace mlir
59 
60 using namespace mlir;
61 
62 #define PASS_NAME "convert-func-to-llvm"
63 
64 static constexpr StringRef varargsAttrName = "func.varargs";
65 static constexpr StringRef linkageAttrName = "llvm.linkage";
66 static constexpr StringRef barePtrAttrName = "llvm.bareptr";
67 
68 /// Return `true` if the `op` should use bare pointer calling convention.
70  const LLVMTypeConverter *typeConverter) {
71  return (op && op->hasAttr(barePtrAttrName)) ||
72  typeConverter->getOptions().useBarePtrCallConv;
73 }
74 
75 /// Only retain those attributes that are not constructed by
76 /// `LLVMFuncOp::build`.
77 static void filterFuncAttributes(FunctionOpInterface func,
79  for (const NamedAttribute &attr : func->getDiscardableAttrs()) {
80  if (attr.getName() == linkageAttrName ||
81  attr.getName() == varargsAttrName ||
82  attr.getName() == LLVM::LLVMDialect::getReadnoneAttrName())
83  continue;
84  result.push_back(attr);
85  }
86 }
87 
88 /// Propagate argument/results attributes.
89 static void propagateArgResAttrs(OpBuilder &builder, bool resultStructType,
90  FunctionOpInterface funcOp,
91  LLVM::LLVMFuncOp wrapperFuncOp) {
92  auto argAttrs = funcOp.getAllArgAttrs();
93  if (!resultStructType) {
94  if (auto resAttrs = funcOp.getAllResultAttrs())
95  wrapperFuncOp.setAllResultAttrs(resAttrs);
96  if (argAttrs)
97  wrapperFuncOp.setAllArgAttrs(argAttrs);
98  } else {
99  SmallVector<Attribute> argAttributes;
100  // Only modify the argument and result attributes when the result is now
101  // an argument.
102  if (argAttrs) {
103  argAttributes.push_back(builder.getDictionaryAttr({}));
104  argAttributes.append(argAttrs.begin(), argAttrs.end());
105  wrapperFuncOp.setAllArgAttrs(argAttributes);
106  }
107  }
108  cast<FunctionOpInterface>(wrapperFuncOp.getOperation())
109  .setVisibility(funcOp.getVisibility());
110 }
111 
112 /// Creates an auxiliary function with pointer-to-memref-descriptor-struct
113 /// arguments instead of unpacked arguments. This function can be called from C
114 /// by passing a pointer to a C struct corresponding to a memref descriptor.
115 /// Similarly, returned memrefs are passed via pointers to a C struct that is
116 /// passed as additional argument.
117 /// Internally, the auxiliary function unpacks the descriptor into individual
118 /// components and forwards them to `newFuncOp` and forwards the results to
119 /// the extra arguments.
120 static void wrapForExternalCallers(OpBuilder &rewriter, Location loc,
121  const LLVMTypeConverter &typeConverter,
122  FunctionOpInterface funcOp,
123  LLVM::LLVMFuncOp newFuncOp) {
124  auto type = cast<FunctionType>(funcOp.getFunctionType());
125  auto [wrapperFuncType, resultStructType] =
126  typeConverter.convertFunctionTypeCWrapper(type);
127 
128  SmallVector<NamedAttribute> attributes;
129  filterFuncAttributes(funcOp, attributes);
130 
131  auto wrapperFuncOp = rewriter.create<LLVM::LLVMFuncOp>(
132  loc, llvm::formatv("_mlir_ciface_{0}", funcOp.getName()).str(),
133  wrapperFuncType, LLVM::Linkage::External, /*dsoLocal=*/false,
134  /*cconv=*/LLVM::CConv::C, /*comdat=*/nullptr, attributes);
135  propagateArgResAttrs(rewriter, !!resultStructType, funcOp, wrapperFuncOp);
136 
137  OpBuilder::InsertionGuard guard(rewriter);
138  rewriter.setInsertionPointToStart(wrapperFuncOp.addEntryBlock(rewriter));
139 
141  size_t argOffset = resultStructType ? 1 : 0;
142  for (auto [index, argType] : llvm::enumerate(type.getInputs())) {
143  Value arg = wrapperFuncOp.getArgument(index + argOffset);
144  if (auto memrefType = dyn_cast<MemRefType>(argType)) {
145  Value loaded = rewriter.create<LLVM::LoadOp>(
146  loc, typeConverter.convertType(memrefType), arg);
147  MemRefDescriptor::unpack(rewriter, loc, loaded, memrefType, args);
148  continue;
149  }
150  if (isa<UnrankedMemRefType>(argType)) {
151  Value loaded = rewriter.create<LLVM::LoadOp>(
152  loc, typeConverter.convertType(argType), arg);
153  UnrankedMemRefDescriptor::unpack(rewriter, loc, loaded, args);
154  continue;
155  }
156 
157  args.push_back(arg);
158  }
159 
160  auto call = rewriter.create<LLVM::CallOp>(loc, newFuncOp, args);
161 
162  if (resultStructType) {
163  rewriter.create<LLVM::StoreOp>(loc, call.getResult(),
164  wrapperFuncOp.getArgument(0));
165  rewriter.create<LLVM::ReturnOp>(loc, ValueRange{});
166  } else {
167  rewriter.create<LLVM::ReturnOp>(loc, call.getResults());
168  }
169 }
170 
171 /// Creates an auxiliary function with pointer-to-memref-descriptor-struct
172 /// arguments instead of unpacked arguments. Creates a body for the (external)
173 /// `newFuncOp` that allocates a memref descriptor on stack, packs the
174 /// individual arguments into this descriptor and passes a pointer to it into
175 /// the auxiliary function. If the result of the function cannot be directly
176 /// returned, we write it to a special first argument that provides a pointer
177 /// to a corresponding struct. This auxiliary external function is now
178 /// compatible with functions defined in C using pointers to C structs
179 /// corresponding to a memref descriptor.
180 static void wrapExternalFunction(OpBuilder &builder, Location loc,
181  const LLVMTypeConverter &typeConverter,
182  FunctionOpInterface funcOp,
183  LLVM::LLVMFuncOp newFuncOp) {
184  OpBuilder::InsertionGuard guard(builder);
185 
186  auto [wrapperType, resultStructType] =
187  typeConverter.convertFunctionTypeCWrapper(
188  cast<FunctionType>(funcOp.getFunctionType()));
189  // This conversion can only fail if it could not convert one of the argument
190  // types. But since it has been applied to a non-wrapper function before, it
191  // should have failed earlier and not reach this point at all.
192  assert(wrapperType && "unexpected type conversion failure");
193 
195  filterFuncAttributes(funcOp, attributes);
196 
197  // Create the auxiliary function.
198  auto wrapperFunc = builder.create<LLVM::LLVMFuncOp>(
199  loc, llvm::formatv("_mlir_ciface_{0}", funcOp.getName()).str(),
200  wrapperType, LLVM::Linkage::External, /*dsoLocal=*/false,
201  /*cconv=*/LLVM::CConv::C, /*comdat=*/nullptr, attributes);
202  propagateArgResAttrs(builder, !!resultStructType, funcOp, wrapperFunc);
203 
204  // The wrapper that we synthetize here should only be visible in this module.
205  newFuncOp.setLinkage(LLVM::Linkage::Private);
206  builder.setInsertionPointToStart(newFuncOp.addEntryBlock(builder));
207 
208  // Get a ValueRange containing arguments.
209  FunctionType type = cast<FunctionType>(funcOp.getFunctionType());
211  args.reserve(type.getNumInputs());
212  ValueRange wrapperArgsRange(newFuncOp.getArguments());
213 
214  if (resultStructType) {
215  // Allocate the struct on the stack and pass the pointer.
216  Type resultType = cast<LLVM::LLVMFunctionType>(wrapperType).getParamType(0);
217  Value one = builder.create<LLVM::ConstantOp>(
218  loc, typeConverter.convertType(builder.getIndexType()),
219  builder.getIntegerAttr(builder.getIndexType(), 1));
220  Value result =
221  builder.create<LLVM::AllocaOp>(loc, resultType, resultStructType, one);
222  args.push_back(result);
223  }
224 
225  // Iterate over the inputs of the original function and pack values into
226  // memref descriptors if the original type is a memref.
227  for (Type input : type.getInputs()) {
228  Value arg;
229  int numToDrop = 1;
230  auto memRefType = dyn_cast<MemRefType>(input);
231  auto unrankedMemRefType = dyn_cast<UnrankedMemRefType>(input);
232  if (memRefType || unrankedMemRefType) {
233  numToDrop = memRefType
236  Value packed =
237  memRefType
238  ? MemRefDescriptor::pack(builder, loc, typeConverter, memRefType,
239  wrapperArgsRange.take_front(numToDrop))
241  builder, loc, typeConverter, unrankedMemRefType,
242  wrapperArgsRange.take_front(numToDrop));
243 
244  auto ptrTy = LLVM::LLVMPointerType::get(builder.getContext());
245  Value one = builder.create<LLVM::ConstantOp>(
246  loc, typeConverter.convertType(builder.getIndexType()),
247  builder.getIntegerAttr(builder.getIndexType(), 1));
248  Value allocated = builder.create<LLVM::AllocaOp>(
249  loc, ptrTy, packed.getType(), one, /*alignment=*/0);
250  builder.create<LLVM::StoreOp>(loc, packed, allocated);
251  arg = allocated;
252  } else {
253  arg = wrapperArgsRange[0];
254  }
255 
256  args.push_back(arg);
257  wrapperArgsRange = wrapperArgsRange.drop_front(numToDrop);
258  }
259  assert(wrapperArgsRange.empty() && "did not map some of the arguments");
260 
261  auto call = builder.create<LLVM::CallOp>(loc, wrapperFunc, args);
262 
263  if (resultStructType) {
264  Value result =
265  builder.create<LLVM::LoadOp>(loc, resultStructType, args.front());
266  builder.create<LLVM::ReturnOp>(loc, result);
267  } else {
268  builder.create<LLVM::ReturnOp>(loc, call.getResults());
269  }
270 }
271 
272 /// Modifies the body of the function to construct the `MemRefDescriptor` from
273 /// the bare pointer calling convention lowering of `memref` types.
275  ConversionPatternRewriter &rewriter, Location loc,
276  const LLVMTypeConverter &typeConverter, LLVM::LLVMFuncOp funcOp,
277  TypeRange oldArgTypes) {
278  if (funcOp.getBody().empty())
279  return;
280 
281  // Promote bare pointers from memref arguments to memref descriptors at the
282  // beginning of the function so that all the memrefs in the function have a
283  // uniform representation.
284  Block *entryBlock = &funcOp.getBody().front();
285  auto blockArgs = entryBlock->getArguments();
286  assert(blockArgs.size() == oldArgTypes.size() &&
287  "The number of arguments and types doesn't match");
288 
289  OpBuilder::InsertionGuard guard(rewriter);
290  rewriter.setInsertionPointToStart(entryBlock);
291  for (auto it : llvm::zip(blockArgs, oldArgTypes)) {
292  BlockArgument arg = std::get<0>(it);
293  Type argTy = std::get<1>(it);
294 
295  // Unranked memrefs are not supported in the bare pointer calling
296  // convention. We should have bailed out before in the presence of
297  // unranked memrefs.
298  assert(!isa<UnrankedMemRefType>(argTy) &&
299  "Unranked memref is not supported");
300  auto memrefTy = dyn_cast<MemRefType>(argTy);
301  if (!memrefTy)
302  continue;
303 
304  // Replace barePtr with a placeholder (undef), promote barePtr to a ranked
305  // or unranked memref descriptor and replace placeholder with the last
306  // instruction of the memref descriptor.
307  // TODO: The placeholder is needed to avoid replacing barePtr uses in the
308  // MemRef descriptor instructions. We may want to have a utility in the
309  // rewriter to properly handle this use case.
310  Location loc = funcOp.getLoc();
311  auto placeholder = rewriter.create<LLVM::UndefOp>(
312  loc, typeConverter.convertType(memrefTy));
313  rewriter.replaceUsesOfBlockArgument(arg, placeholder);
314 
315  Value desc = MemRefDescriptor::fromStaticShape(rewriter, loc, typeConverter,
316  memrefTy, arg);
317  rewriter.replaceOp(placeholder, {desc});
318  }
319 }
320 
322 mlir::convertFuncOpToLLVMFuncOp(FunctionOpInterface funcOp,
323  ConversionPatternRewriter &rewriter,
324  const LLVMTypeConverter &converter) {
325  // Check the funcOp has `FunctionType`.
326  auto funcTy = dyn_cast<FunctionType>(funcOp.getFunctionType());
327  if (!funcTy)
328  return rewriter.notifyMatchFailure(
329  funcOp, "Only support FunctionOpInterface with FunctionType");
330 
331  // Convert the original function arguments. They are converted using the
332  // LLVMTypeConverter provided to this legalization pattern.
333  auto varargsAttr = funcOp->getAttrOfType<BoolAttr>(varargsAttrName);
334  TypeConverter::SignatureConversion result(funcOp.getNumArguments());
335  auto llvmType = converter.convertFunctionSignature(
336  funcTy, varargsAttr && varargsAttr.getValue(),
337  shouldUseBarePtrCallConv(funcOp, &converter), result);
338  if (!llvmType)
339  return rewriter.notifyMatchFailure(funcOp, "signature conversion failed");
340 
341  // Create an LLVM function, use external linkage by default until MLIR
342  // functions have linkage.
343  LLVM::Linkage linkage = LLVM::Linkage::External;
344  if (funcOp->hasAttr(linkageAttrName)) {
345  auto attr =
346  dyn_cast<mlir::LLVM::LinkageAttr>(funcOp->getAttr(linkageAttrName));
347  if (!attr) {
348  funcOp->emitError() << "Contains " << linkageAttrName
349  << " attribute not of type LLVM::LinkageAttr";
350  return rewriter.notifyMatchFailure(
351  funcOp, "Contains linkage attribute not of type LLVM::LinkageAttr");
352  }
353  linkage = attr.getLinkage();
354  }
355 
357  filterFuncAttributes(funcOp, attributes);
358  auto newFuncOp = rewriter.create<LLVM::LLVMFuncOp>(
359  funcOp.getLoc(), funcOp.getName(), llvmType, linkage,
360  /*dsoLocal=*/false, /*cconv=*/LLVM::CConv::C, /*comdat=*/nullptr,
361  attributes);
362  cast<FunctionOpInterface>(newFuncOp.getOperation())
363  .setVisibility(funcOp.getVisibility());
364 
365  // Create a memory effect attribute corresponding to readnone.
366  StringRef readnoneAttrName = LLVM::LLVMDialect::getReadnoneAttrName();
367  if (funcOp->hasAttr(readnoneAttrName)) {
368  auto attr = funcOp->getAttrOfType<UnitAttr>(readnoneAttrName);
369  if (!attr) {
370  funcOp->emitError() << "Contains " << readnoneAttrName
371  << " attribute not of type UnitAttr";
372  return rewriter.notifyMatchFailure(
373  funcOp, "Contains readnone attribute not of type UnitAttr");
374  }
375  auto memoryAttr = LLVM::MemoryEffectsAttr::get(
376  rewriter.getContext(),
377  {LLVM::ModRefInfo::NoModRef, LLVM::ModRefInfo::NoModRef,
378  LLVM::ModRefInfo::NoModRef});
379  newFuncOp.setMemoryAttr(memoryAttr);
380  }
381 
382  // Propagate argument/result attributes to all converted arguments/result
383  // obtained after converting a given original argument/result.
384  if (ArrayAttr resAttrDicts = funcOp.getAllResultAttrs()) {
385  assert(!resAttrDicts.empty() && "expected array to be non-empty");
386  if (funcOp.getNumResults() == 1)
387  newFuncOp.setAllResultAttrs(resAttrDicts);
388  }
389  if (ArrayAttr argAttrDicts = funcOp.getAllArgAttrs()) {
390  SmallVector<Attribute> newArgAttrs(
391  cast<LLVM::LLVMFunctionType>(llvmType).getNumParams());
392  for (unsigned i = 0, e = funcOp.getNumArguments(); i < e; ++i) {
393  // Some LLVM IR attribute have a type attached to them. During FuncOp ->
394  // LLVMFuncOp conversion these types may have changed. Account for that
395  // change by converting attributes' types as well.
396  SmallVector<NamedAttribute, 4> convertedAttrs;
397  auto attrsDict = cast<DictionaryAttr>(argAttrDicts[i]);
398  convertedAttrs.reserve(attrsDict.size());
399  for (const NamedAttribute &attr : attrsDict) {
400  const auto convert = [&](const NamedAttribute &attr) {
401  return TypeAttr::get(converter.convertType(
402  cast<TypeAttr>(attr.getValue()).getValue()));
403  };
404  if (attr.getName().getValue() ==
405  LLVM::LLVMDialect::getByValAttrName()) {
406  convertedAttrs.push_back(rewriter.getNamedAttr(
407  LLVM::LLVMDialect::getByValAttrName(), convert(attr)));
408  } else if (attr.getName().getValue() ==
409  LLVM::LLVMDialect::getByRefAttrName()) {
410  convertedAttrs.push_back(rewriter.getNamedAttr(
411  LLVM::LLVMDialect::getByRefAttrName(), convert(attr)));
412  } else if (attr.getName().getValue() ==
413  LLVM::LLVMDialect::getStructRetAttrName()) {
414  convertedAttrs.push_back(rewriter.getNamedAttr(
415  LLVM::LLVMDialect::getStructRetAttrName(), convert(attr)));
416  } else if (attr.getName().getValue() ==
417  LLVM::LLVMDialect::getInAllocaAttrName()) {
418  convertedAttrs.push_back(rewriter.getNamedAttr(
419  LLVM::LLVMDialect::getInAllocaAttrName(), convert(attr)));
420  } else {
421  convertedAttrs.push_back(attr);
422  }
423  }
424  auto mapping = result.getInputMapping(i);
425  assert(mapping && "unexpected deletion of function argument");
426  // Only attach the new argument attributes if there is a one-to-one
427  // mapping from old to new types. Otherwise, attributes might be
428  // attached to types that they do not support.
429  if (mapping->size == 1) {
430  newArgAttrs[mapping->inputNo] =
431  DictionaryAttr::get(rewriter.getContext(), convertedAttrs);
432  continue;
433  }
434  // TODO: Implement custom handling for types that expand to multiple
435  // function arguments.
436  for (size_t j = 0; j < mapping->size; ++j)
437  newArgAttrs[mapping->inputNo + j] =
438  DictionaryAttr::get(rewriter.getContext(), {});
439  }
440  if (!newArgAttrs.empty())
441  newFuncOp.setAllArgAttrs(rewriter.getArrayAttr(newArgAttrs));
442  }
443 
444  rewriter.inlineRegionBefore(funcOp.getFunctionBody(), newFuncOp.getBody(),
445  newFuncOp.end());
446  if (failed(rewriter.convertRegionTypes(&newFuncOp.getBody(), converter,
447  &result))) {
448  return rewriter.notifyMatchFailure(funcOp,
449  "region types conversion failed");
450  }
451 
452  return newFuncOp;
453 }
454 
455 namespace {
456 
457 struct FuncOpConversionBase : public ConvertOpToLLVMPattern<func::FuncOp> {
458 protected:
460 
461  // Convert input FuncOp to LLVMFuncOp by using the LLVMTypeConverter provided
462  // to this legalization pattern.
464  convertFuncOpToLLVMFuncOp(func::FuncOp funcOp,
465  ConversionPatternRewriter &rewriter) const {
467  cast<FunctionOpInterface>(funcOp.getOperation()), rewriter,
468  *getTypeConverter());
469  }
470 };
471 
472 /// FuncOp legalization pattern that converts MemRef arguments to pointers to
473 /// MemRef descriptors (LLVM struct data types) containing all the MemRef type
474 /// information.
475 struct FuncOpConversion : public FuncOpConversionBase {
476  FuncOpConversion(const LLVMTypeConverter &converter)
477  : FuncOpConversionBase(converter) {}
478 
480  matchAndRewrite(func::FuncOp funcOp, OpAdaptor adaptor,
481  ConversionPatternRewriter &rewriter) const override {
482  FailureOr<LLVM::LLVMFuncOp> newFuncOp =
483  convertFuncOpToLLVMFuncOp(funcOp, rewriter);
484  if (failed(newFuncOp))
485  return rewriter.notifyMatchFailure(funcOp, "Could not convert funcop");
486 
487  if (!shouldUseBarePtrCallConv(funcOp, this->getTypeConverter())) {
488  if (funcOp->getAttrOfType<UnitAttr>(
489  LLVM::LLVMDialect::getEmitCWrapperAttrName())) {
490  if (newFuncOp->isVarArg())
491  return funcOp->emitError("C interface for variadic functions is not "
492  "supported yet.");
493 
494  if (newFuncOp->isExternal())
495  wrapExternalFunction(rewriter, funcOp->getLoc(), *getTypeConverter(),
496  funcOp, *newFuncOp);
497  else
498  wrapForExternalCallers(rewriter, funcOp->getLoc(),
499  *getTypeConverter(), funcOp, *newFuncOp);
500  }
501  } else {
502  modifyFuncOpToUseBarePtrCallingConv(rewriter, funcOp->getLoc(),
503  *getTypeConverter(), *newFuncOp,
504  funcOp.getFunctionType().getInputs());
505  }
506 
507  rewriter.eraseOp(funcOp);
508  return success();
509  }
510 };
511 
512 struct ConstantOpLowering : public ConvertOpToLLVMPattern<func::ConstantOp> {
514 
516  matchAndRewrite(func::ConstantOp op, OpAdaptor adaptor,
517  ConversionPatternRewriter &rewriter) const override {
518  auto type = typeConverter->convertType(op.getResult().getType());
519  if (!type || !LLVM::isCompatibleType(type))
520  return rewriter.notifyMatchFailure(op, "failed to convert result type");
521 
522  auto newOp =
523  rewriter.create<LLVM::AddressOfOp>(op.getLoc(), type, op.getValue());
524  for (const NamedAttribute &attr : op->getAttrs()) {
525  if (attr.getName().strref() == "value")
526  continue;
527  newOp->setAttr(attr.getName(), attr.getValue());
528  }
529  rewriter.replaceOp(op, newOp->getResults());
530  return success();
531  }
532 };
533 
534 // A CallOp automatically promotes MemRefType to a sequence of alloca/store and
535 // passes the pointer to the MemRef across function boundaries.
536 template <typename CallOpType>
537 struct CallOpInterfaceLowering : public ConvertOpToLLVMPattern<CallOpType> {
539  using Super = CallOpInterfaceLowering<CallOpType>;
541 
542  LogicalResult matchAndRewriteImpl(CallOpType callOp,
543  typename CallOpType::Adaptor adaptor,
544  ConversionPatternRewriter &rewriter,
545  bool useBarePtrCallConv = false) const {
546  // Pack the result types into a struct.
547  Type packedResult = nullptr;
548  unsigned numResults = callOp.getNumResults();
549  auto resultTypes = llvm::to_vector<4>(callOp.getResultTypes());
550 
551  if (numResults != 0) {
552  if (!(packedResult = this->getTypeConverter()->packFunctionResults(
553  resultTypes, useBarePtrCallConv)))
554  return failure();
555  }
556 
557  if (useBarePtrCallConv) {
558  for (auto it : callOp->getOperands()) {
559  Type operandType = it.getType();
560  if (isa<UnrankedMemRefType>(operandType)) {
561  // Unranked memref is not supported in the bare pointer calling
562  // convention.
563  return failure();
564  }
565  }
566  }
567  auto promoted = this->getTypeConverter()->promoteOperands(
568  callOp.getLoc(), /*opOperands=*/callOp->getOperands(),
569  adaptor.getOperands(), rewriter, useBarePtrCallConv);
570  auto newOp = rewriter.create<LLVM::CallOp>(
571  callOp.getLoc(), packedResult ? TypeRange(packedResult) : TypeRange(),
572  promoted, callOp->getAttrs());
573 
574  SmallVector<Value, 4> results;
575  if (numResults < 2) {
576  // If < 2 results, packing did not do anything and we can just return.
577  results.append(newOp.result_begin(), newOp.result_end());
578  } else {
579  // Otherwise, it had been converted to an operation producing a structure.
580  // Extract individual results from the structure and return them as list.
581  results.reserve(numResults);
582  for (unsigned i = 0; i < numResults; ++i) {
583  results.push_back(rewriter.create<LLVM::ExtractValueOp>(
584  callOp.getLoc(), newOp->getResult(0), i));
585  }
586  }
587 
588  if (useBarePtrCallConv) {
589  // For the bare-ptr calling convention, promote memref results to
590  // descriptors.
591  assert(results.size() == resultTypes.size() &&
592  "The number of arguments and types doesn't match");
593  this->getTypeConverter()->promoteBarePtrsToDescriptors(
594  rewriter, callOp.getLoc(), resultTypes, results);
595  } else if (failed(this->copyUnrankedDescriptors(rewriter, callOp.getLoc(),
596  resultTypes, results,
597  /*toDynamic=*/false))) {
598  return failure();
599  }
600 
601  rewriter.replaceOp(callOp, results);
602  return success();
603  }
604 };
605 
606 class CallOpLowering : public CallOpInterfaceLowering<func::CallOp> {
607 public:
608  CallOpLowering(const LLVMTypeConverter &typeConverter,
609  // Can be nullptr.
610  const SymbolTable *symbolTable, PatternBenefit benefit = 1)
611  : CallOpInterfaceLowering<func::CallOp>(typeConverter, benefit),
612  symbolTable(symbolTable) {}
613 
615  matchAndRewrite(func::CallOp callOp, OpAdaptor adaptor,
616  ConversionPatternRewriter &rewriter) const override {
617  bool useBarePtrCallConv = false;
618  if (getTypeConverter()->getOptions().useBarePtrCallConv) {
619  useBarePtrCallConv = true;
620  } else if (symbolTable != nullptr) {
621  // Fast lookup.
622  Operation *callee =
623  symbolTable->lookup(callOp.getCalleeAttr().getValue());
624  useBarePtrCallConv =
625  callee != nullptr && callee->hasAttr(barePtrAttrName);
626  } else {
627  // Warning: This is a linear lookup.
628  Operation *callee =
629  SymbolTable::lookupNearestSymbolFrom(callOp, callOp.getCalleeAttr());
630  useBarePtrCallConv =
631  callee != nullptr && callee->hasAttr(barePtrAttrName);
632  }
633  return matchAndRewriteImpl(callOp, adaptor, rewriter, useBarePtrCallConv);
634  }
635 
636 private:
637  const SymbolTable *symbolTable = nullptr;
638 };
639 
640 struct CallIndirectOpLowering
641  : public CallOpInterfaceLowering<func::CallIndirectOp> {
642  using Super::Super;
643 
645  matchAndRewrite(func::CallIndirectOp callIndirectOp, OpAdaptor adaptor,
646  ConversionPatternRewriter &rewriter) const override {
647  return matchAndRewriteImpl(callIndirectOp, adaptor, rewriter);
648  }
649 };
650 
651 struct UnrealizedConversionCastOpLowering
652  : public ConvertOpToLLVMPattern<UnrealizedConversionCastOp> {
654  UnrealizedConversionCastOp>::ConvertOpToLLVMPattern;
655 
657  matchAndRewrite(UnrealizedConversionCastOp op, OpAdaptor adaptor,
658  ConversionPatternRewriter &rewriter) const override {
659  SmallVector<Type> convertedTypes;
660  if (succeeded(typeConverter->convertTypes(op.getOutputs().getTypes(),
661  convertedTypes)) &&
662  convertedTypes == adaptor.getInputs().getTypes()) {
663  rewriter.replaceOp(op, adaptor.getInputs());
664  return success();
665  }
666 
667  convertedTypes.clear();
668  if (succeeded(typeConverter->convertTypes(adaptor.getInputs().getTypes(),
669  convertedTypes)) &&
670  convertedTypes == op.getOutputs().getType()) {
671  rewriter.replaceOp(op, adaptor.getInputs());
672  return success();
673  }
674  return failure();
675  }
676 };
677 
678 // Special lowering pattern for `ReturnOps`. Unlike all other operations,
679 // `ReturnOp` interacts with the function signature and must have as many
680 // operands as the function has return values. Because in LLVM IR, functions
681 // can only return 0 or 1 value, we pack multiple values into a structure type.
682 // Emit `UndefOp` followed by `InsertValueOp`s to create such structure if
683 // necessary before returning it
684 struct ReturnOpLowering : public ConvertOpToLLVMPattern<func::ReturnOp> {
686 
688  matchAndRewrite(func::ReturnOp op, OpAdaptor adaptor,
689  ConversionPatternRewriter &rewriter) const override {
690  Location loc = op.getLoc();
691  unsigned numArguments = op.getNumOperands();
692  SmallVector<Value, 4> updatedOperands;
693 
694  auto funcOp = op->getParentOfType<LLVM::LLVMFuncOp>();
695  bool useBarePtrCallConv =
696  shouldUseBarePtrCallConv(funcOp, this->getTypeConverter());
697  if (useBarePtrCallConv) {
698  // For the bare-ptr calling convention, extract the aligned pointer to
699  // be returned from the memref descriptor.
700  for (auto it : llvm::zip(op->getOperands(), adaptor.getOperands())) {
701  Type oldTy = std::get<0>(it).getType();
702  Value newOperand = std::get<1>(it);
703  if (isa<MemRefType>(oldTy) && getTypeConverter()->canConvertToBarePtr(
704  cast<BaseMemRefType>(oldTy))) {
705  MemRefDescriptor memrefDesc(newOperand);
706  newOperand = memrefDesc.allocatedPtr(rewriter, loc);
707  } else if (isa<UnrankedMemRefType>(oldTy)) {
708  // Unranked memref is not supported in the bare pointer calling
709  // convention.
710  return failure();
711  }
712  updatedOperands.push_back(newOperand);
713  }
714  } else {
715  updatedOperands = llvm::to_vector<4>(adaptor.getOperands());
716  (void)copyUnrankedDescriptors(rewriter, loc, op.getOperands().getTypes(),
717  updatedOperands,
718  /*toDynamic=*/true);
719  }
720 
721  // If ReturnOp has 0 or 1 operand, create it and return immediately.
722  if (numArguments <= 1) {
723  rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(
724  op, TypeRange(), updatedOperands, op->getAttrs());
725  return success();
726  }
727 
728  // Otherwise, we need to pack the arguments into an LLVM struct type before
729  // returning.
730  auto packedType = getTypeConverter()->packFunctionResults(
731  op.getOperandTypes(), useBarePtrCallConv);
732  if (!packedType) {
733  return rewriter.notifyMatchFailure(op, "could not convert result types");
734  }
735 
736  Value packed = rewriter.create<LLVM::UndefOp>(loc, packedType);
737  for (auto [idx, operand] : llvm::enumerate(updatedOperands)) {
738  packed = rewriter.create<LLVM::InsertValueOp>(loc, packed, operand, idx);
739  }
740  rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, TypeRange(), packed,
741  op->getAttrs());
742  return success();
743  }
744 };
745 } // namespace
746 
748  LLVMTypeConverter &converter, RewritePatternSet &patterns) {
749  patterns.add<FuncOpConversion>(converter);
750 }
751 
753  LLVMTypeConverter &converter, RewritePatternSet &patterns,
754  const SymbolTable *symbolTable) {
755  populateFuncToLLVMFuncOpConversionPattern(converter, patterns);
756  patterns.add<CallIndirectOpLowering>(converter);
757  patterns.add<CallOpLowering>(converter, symbolTable);
758  patterns.add<ConstantOpLowering>(converter);
759  patterns.add<ReturnOpLowering>(converter);
760 }
761 
762 namespace {
763 /// A pass converting Func operations into the LLVM IR dialect.
764 struct ConvertFuncToLLVMPass
765  : public impl::ConvertFuncToLLVMPassBase<ConvertFuncToLLVMPass> {
766  using Base::Base;
767 
768  /// Run the dialect converter on the module.
769  void runOnOperation() override {
770  ModuleOp m = getOperation();
771  StringRef dataLayout;
772  auto dataLayoutAttr = dyn_cast_or_null<StringAttr>(
773  m->getAttr(LLVM::LLVMDialect::getDataLayoutAttrName()));
774  if (dataLayoutAttr)
775  dataLayout = dataLayoutAttr.getValue();
776 
777  if (failed(LLVM::LLVMDialect::verifyDataLayoutString(
778  dataLayout, [this](const Twine &message) {
779  getOperation().emitError() << message.str();
780  }))) {
781  signalPassFailure();
782  return;
783  }
784 
785  const auto &dataLayoutAnalysis = getAnalysis<DataLayoutAnalysis>();
786 
788  dataLayoutAnalysis.getAtOrAbove(m));
789  options.useBarePtrCallConv = useBarePtrCallConv;
790  if (indexBitwidth != kDeriveIndexBitwidthFromDataLayout)
791  options.overrideIndexBitwidth(indexBitwidth);
792  options.dataLayout = llvm::DataLayout(dataLayout);
793 
794  LLVMTypeConverter typeConverter(&getContext(), options,
795  &dataLayoutAnalysis);
796 
797  std::optional<SymbolTable> optSymbolTable = std::nullopt;
798  const SymbolTable *symbolTable = nullptr;
799  if (!options.useBarePtrCallConv) {
800  optSymbolTable.emplace(m);
801  symbolTable = &optSymbolTable.value();
802  }
803 
804  RewritePatternSet patterns(&getContext());
805  populateFuncToLLVMConversionPatterns(typeConverter, patterns, symbolTable);
806 
807  // TODO(https://github.com/llvm/llvm-project/issues/70982): Remove these in
808  // favor of their dedicated conversion passes.
809  arith::populateArithToLLVMConversionPatterns(typeConverter, patterns);
810  cf::populateControlFlowToLLVMConversionPatterns(typeConverter, patterns);
811 
813  if (failed(applyPartialConversion(m, target, std::move(patterns))))
814  signalPassFailure();
815  }
816 };
817 
818 struct SetLLVMModuleDataLayoutPass
819  : public impl::SetLLVMModuleDataLayoutPassBase<
820  SetLLVMModuleDataLayoutPass> {
821  using Base::Base;
822 
823  /// Run the dialect converter on the module.
824  void runOnOperation() override {
825  if (failed(LLVM::LLVMDialect::verifyDataLayoutString(
826  this->dataLayout, [this](const Twine &message) {
827  getOperation().emitError() << message.str();
828  }))) {
829  signalPassFailure();
830  return;
831  }
832  ModuleOp m = getOperation();
833  m->setAttr(LLVM::LLVMDialect::getDataLayoutAttrName(),
834  StringAttr::get(m.getContext(), this->dataLayout));
835  }
836 };
837 } // namespace
838 
839 //===----------------------------------------------------------------------===//
840 // ConvertToLLVMPatternInterface implementation
841 //===----------------------------------------------------------------------===//
842 
843 namespace {
844 /// Implement the interface to convert Func to LLVM.
845 struct FuncToLLVMDialectInterface : public ConvertToLLVMPatternInterface {
847  /// Hook for derived dialect interface to provide conversion patterns
848  /// and mark dialect legal for the conversion target.
849  void populateConvertToLLVMConversionPatterns(
850  ConversionTarget &target, LLVMTypeConverter &typeConverter,
851  RewritePatternSet &patterns) const final {
852  populateFuncToLLVMConversionPatterns(typeConverter, patterns);
853  }
854 };
855 } // namespace
856 
858  registry.addExtension(+[](MLIRContext *ctx, func::FuncDialect *dialect) {
859  dialect->addInterfaces<FuncToLLVMDialectInterface>();
860  });
861 }
static void modifyFuncOpToUseBarePtrCallingConv(ConversionPatternRewriter &rewriter, Location loc, const LLVMTypeConverter &typeConverter, LLVM::LLVMFuncOp funcOp, TypeRange oldArgTypes)
Modifies the body of the function to construct the MemRefDescriptor from the bare pointer calling con...
Definition: FuncToLLVM.cpp:274
static void propagateArgResAttrs(OpBuilder &builder, bool resultStructType, FunctionOpInterface funcOp, LLVM::LLVMFuncOp wrapperFuncOp)
Propagate argument/results attributes.
Definition: FuncToLLVM.cpp:89
static constexpr StringRef barePtrAttrName
Definition: FuncToLLVM.cpp:66
static constexpr StringRef varargsAttrName
Definition: FuncToLLVM.cpp:64
static constexpr StringRef linkageAttrName
Definition: FuncToLLVM.cpp:65
static void filterFuncAttributes(FunctionOpInterface func, SmallVectorImpl< NamedAttribute > &result)
Only retain those attributes that are not constructed by LLVMFuncOp::build.
Definition: FuncToLLVM.cpp:77
static bool shouldUseBarePtrCallConv(Operation *op, const LLVMTypeConverter *typeConverter)
Return true if the op should use bare pointer calling convention.
Definition: FuncToLLVM.cpp:69
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 ...
Definition: FuncToLLVM.cpp:180
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 ...
Definition: FuncToLLVM.cpp:120
static MLIRContext * getContext(OpFoldResult val)
static llvm::ManagedStatic< PassManagerOptions > options
This class represents an argument of a Block.
Definition: Value.h:315
Block represents an ordered list of Operations.
Definition: Block.h:30
BlockArgListType getArguments()
Definition: Block.h:84
Operation & front()
Definition: Block.h:150
Special case of IntegerAttr to represent boolean integers, i.e., signless i1 integers.
IntegerAttr getIntegerAttr(Type type, int64_t value)
Definition: Builders.cpp:238
MLIRContext * getContext() const
Definition: Builders.h:55
ArrayAttr getArrayAttr(ArrayRef< Attribute > value)
Definition: Builders.cpp:273
IndexType getIndexType()
Definition: Builders.cpp:71
DictionaryAttr getDictionaryAttr(ArrayRef< NamedAttribute > value)
Definition: Builders.cpp:120
NamedAttribute getNamedAttr(StringRef name, Attribute val)
Definition: Builders.cpp:110
This class implements a pattern rewriter for use with ConversionPatterns.
void replaceOp(Operation *op, ValueRange newValues) override
PatternRewriter hook for replacing an operation.
FailureOr< Block * > convertRegionTypes(Region *region, const TypeConverter &converter, TypeConverter::SignatureConversion *entryConversion=nullptr)
Convert the types of block arguments within the given region.
void eraseOp(Operation *op) override
PatternRewriter hook for erasing a dead operation.
void replaceUsesOfBlockArgument(BlockArgument from, Value to)
Replace all the uses of the block argument from with value to.
This class describes a specific conversion target.
Utility class for operation conversions targeting the LLVM dialect that match exactly one source oper...
Definition: Pattern.h:139
Base class for dialect interfaces providing translation to LLVM IR.
ConvertToLLVMPatternInterface(Dialect *dialect)
The DialectRegistry maps a dialect namespace to a constructor for the matching dialect.
void addExtension(std::unique_ptr< DialectExtensionBase > extension)
Add the given extension to the registry.
This class provides support for representing a failure result, or a valid value of type T.
Definition: LogicalResult.h:78
Derived class that automatically populates legalization information for different LLVM ops.
Conversion from types to the LLVM IR dialect.
Definition: TypeConverter.h:34
const LowerToLLVMOptions & getOptions() const
Definition: TypeConverter.h:94
Type convertFunctionSignature(FunctionType funcTy, bool isVariadic, bool useBarePtrCallConv, SignatureConversion &result) const
Convert a function type.
LogicalResult convertType(Type t, SmallVectorImpl< Type > &results) const
Convert the given type.
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:63
Options to control the LLVM lowering.
MLIRContext is the top-level object for a collection of MLIR operations.
Definition: MLIRContext.h:60
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, const 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 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 MemRefDescriptor fromStaticShape(OpBuilder &builder, Location loc, const LLVMTypeConverter &typeConverter, MemRefType type, Value memory)
Builds IR creating a MemRef descriptor that represents type and populates it with static shape and st...
NamedAttribute represents a combination of a name and an Attribute value.
Definition: Attributes.h:202
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
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
Definition: Builders.cpp:464
type_range getTypes() const
Definition: ValueRange.cpp:26
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:555
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition: Operation.h:402
Location getLoc()
The source location the operation was defined or derived from.
Definition: Operation.h:223
unsigned getNumOperands()
Definition: Operation.h:341
ArrayRef< NamedAttribute > getAttrs()
Return all of the attributes on this operation.
Definition: Operation.h:507
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
Definition: Operation.h:238
void setAttr(StringAttr name, Attribute value)
If the an attribute exists with the specified name, change it to the new value.
Definition: Operation.h:577
operand_type_range getOperandTypes()
Definition: Operation.h:392
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition: Operation.h:373
This class represents the benefit of a pattern match in a unitless scheme that ranges from 0 (very li...
Definition: PatternMatch.h:33
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
Definition: PatternMatch.h:809
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the rewriter that the IR failed to be rewritten because of a match failure,...
Definition: PatternMatch.h:685
void inlineRegionBefore(Region &region, Region &parent, Region::iterator before)
Move the blocks that belong to "region" before the given position in another region "parent".
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replaces the result op with a new op that is created without verification.
Definition: PatternMatch.h:537
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,...
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
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:378
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:125
bool isCompatibleType(Type type)
Returns true if the given type is compatible with the LLVM dialect.
Definition: LLVMTypes.cpp:845
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:285
Include the generated interface declarations.
LogicalResult failure(bool isFailure=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:62
void populateFuncToLLVMConversionPatterns(LLVMTypeConverter &converter, RewritePatternSet &patterns, const SymbolTable *symbolTable=nullptr)
Collect the patterns to convert from the Func dialect to LLVM.
Definition: FuncToLLVM.cpp:752
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)
Definition: FuncToLLVM.cpp:857
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
FailureOr< LLVM::LLVMFuncOp > convertFuncOpToLLVMFuncOp(FunctionOpInterface funcOp, ConversionPatternRewriter &rewriter, const LLVMTypeConverter &converter)
Convert input FunctionOpInterface operation to LLVMFuncOp by using the provided LLVMTypeConverter.
Definition: FuncToLLVM.cpp:322
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
LogicalResult applyPartialConversion(ArrayRef< Operation * > ops, const ConversionTarget &target, const FrozenRewritePatternSet &patterns, ConversionConfig config=ConversionConfig())
Below we define several entry points for operation conversion.
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:747
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.