[PSA] In-tree conversion passes can now be used with LLVM opaque-pointers; Please switch your downstream projects!

Hello everyone!

As part of the RFC “Switching the LLVM dialect and dialect lowerings to opaque-pointers” all in-tree conversion passes are now capable of emitting opaque pointers instead of typed pointers.
This means that all users of MLIR are now able to switch their downstream projects to using opaque-pointers. Since LLVM 17 officially does not support typed pointers anymore it is important for any downstream projects to switch to using opaque pointers.

For immediate source compatiblity, opaque-pointers are currently turned off by default, therefore requiring users to opt-in at the time or writing. Use of opaque-pointers will be turned on by default at a later time however. See timeline at the end of this post.

Migration guide for users of in-tree conversion passes and patterns

All in-tree conversion passes that make use of patterns have a new pass option called use-opaque-pointers. When enabled, it automatically causes the pass and conversion patterns used by the path to emit opaque pointers instead of typed pointers.

Example calling mlir-opt in the testsuite:

// RUN: mlir-opt %s -convert-vector-to-llvm='use-opaque-pointers=1' | FileCheck %s

When adding such a pass in C++ you can enable this option by setting the field useOpaquePointers of the passes option struct to true.

Example making use of a generic enableOpaquePointers lambda:

auto enableOpaquePointers = [](auto options) {
    // Enables opaque pointers in any struct with the option. 
    options.useOpaquePointers = true;
    return options;

// Creates the passes with the default values for options, except enabling opaque pointers.
// Note: The option struct is auto-generated by TableGen. See
// https://mlir.llvm.org/docs/PassManagement/#declarative-pass-specification for more details.

Users of conversion patterns instead of passes can switch to using opaque pointers through the simple use of a flag in LowerToLLVMOptions, which must then be passed to the LLVMTypeConverter. All in-tree conversion patterns will automatically emit opaque-pointers when the LLVMTypeConverter they were constructed with has the option enabled.

Example configuring the LLVMTypeConverter:

LowerToLLVMOptions options(&getContext());
// Enables the use of opaque pointers.
options.useOpaquePointers = true;
// Don't forget to construct the `LLVMTypeConverter` using `options`!
LLVMTypeConverter converter(&getContext(), options);
// The patterns in `FinalizeMemRefToLLVM` will now emit opaque-pointers.
populateFinalizeMemRefToLLVMConversionPatterns(converter, patterns);

“I only make use of in-tree conversion passes, can I just wait for the default to be switched?”

I’d highly encourage you to enable opaque pointers within your pass pipeline as soon as possible to find any regressions as fast as possible. Ideally you should not notice any difference when turning on opaque pointers (except when dumping the IR).

Migration guide for users of the LLVM Dialect

The bulk of the changes required have to be done by any users of the LLVM Dialect. For architectural differences I’d like to refer you to LLVMs documentation of opaque pointers: Opaque Pointers — LLVM 17.0.0git documentation

Changes in Dialect conversions

See the paragraph about the use of LowerToLLVMOptions and LLVMTypeConverter above. This causes the LLVMTypeConverter to emit opaque pointers for all built in type conversions.

Changes in C++ code

  • To create an opaque pointer type instead of a typed pointer type use the LLVMPointerType::get(MLIRContext* context, unsigned addressSpace = 0) method.
  • LLVM::LLVMPointerType::getElementType() will return a null attribute for opaque pointers. In an opaque-pointer world, you should never call this method. You must get the element type from elsewhere. During lowering of e.g. ops using MemRefs, this can be done by using the type converter on the element type of the MemRef. Analog for other composite types.
  • You must now use the build methods of GEPOp with explicit basePtrType parameter as second parameter. Since GEPOp does indexing operations based on the pointers element type, the element type that was previously part of the pointer type has to now be passed as additional type parameter to GEPOp.
  • You must now use the build method of LoadOp with the explicit return type. Since the pointer passed to LoadOp has no element type anymore, the type that should be loaded from the pointer has to be passed explicitly as the result type of the op.
  • You must now use the build method of AllocaOp with the explicit elementType as second parameter. Since the result type of the AllocaOp no longer contains an element type it is necessary to pass the type that should be allocated as the second parameter explicitly.
  • For the time being you mustn’t use the build methods of AddressOfOp taking GlobalOp or LLVMFuncOp. These currently lead to typed pointers being used as result types. Once opaque-pointers are the default, they’ll be changed to using opaque-pointers as result types.
  • BitcastOp from a pointer to another pointer type are now noops.

Note that all the build that should be used are also compatible with typed pointers. You can therefore do these changes in a more incremental fashion as well. The old build methods of GEP, load and alloca will be marked deprecated in C++ as soon as possible.

“I’d like to support both opaque-pointers and typed pointers for the time being. Is this possible?”

For code wishing to support both for the time being LLVMTypeConverter offers the getPointerType method to create either a typed or opaque pointer depending on the LowerToLLVMOptions used at construction time.
The useOpaquePointers() method can be used to check whether opaque pointers are currently active. This is e.g. useful to avoid creating noop bitcasts when using opaque pointers.

Changes in IR Syntax

Typed Pointers Opaque Pointers Description
!llvm.ptr<i32>, !llvm.ptr<f32, 5> !llvm.ptr, !llvm.ptr<5> The new syntax for pointer types no longer contains the element type and only requires brackets for non-default address spaces
%0 = llvm.load %ptr : !llvm.ptr<i32> %0 = llvm.load %ptr : !llvm.ptr -> i32 The new syntax for llvm.load now has an explicit result type, replacing the element type of the typed pointer
llvm.store %val, %ptr : !llvm.ptr<f32> llvm.store %val, %ptr : f32, !llvm.ptr The new syntax for llvm.store now explicitly states the type of the value being stored. This is now necessary since the pointer element type is no longer equal.
llvm.getelementptr %ptr[%idx] : (!llvm.ptr<f32>, i64) -> !llvm.ptr<f32> llvm.getelementptr %ptr[%idx] : (!llvm.ptr, i64) -> !llvm.ptr, f32 The element type used for indexing purposes by GEP is now printed behind the result type. It should be equal to the element type of %ptr previously.

Note that the changes to the pointer type syntax will also be visible in every operation printing the type.

Migration Timeline

As of writing this, the use-opaque-pointers options of passes and corresponding field in LowerToLLVMOptions is still turned off for immediate source compatibility. Following changes will happen in the future:

  • In roughly two weeks use-opaque-pointers and LowerToLLVMOptions will default to being turned on. Any users still relying on typed pointers will have to either switch or explicitly disable the use of opaque-pointers.
  • One month after the default switch, typed pointers will be unsupported within the LLVM Dialect entirely.

(Exact time of these may vary depending on regressions or other unforssen consequences)

I will be posting additional PSAs for each of these steps.

Thanks for reading!


We recently created a page on the website to track this: Deprecations & Current Refactoring - MLIR ; shall we add this there?

Also I’m wondering if a sub-category would be useful on Discourse for subscribing specifically to deprecation and refactoring announcement like this one.

Thank you, I wasn’t aware of that page! I opened Add deprecation of typed pointers in the LLVM dialect by zero9178 · Pull Request #139 · llvm/mlir-www · GitHub to add it to the list.

I believe such a sub-category would be useful to avoid anncouncements being lost inbetween regular posts.

1 Like