[Mlir-commits] [mlir] [mlir][ptr] Lower vector pointer addition to LLVM GEP (PR #231080)

Alexander Shaposhnikov llvmlistbot at llvm.org
Mon Oct 12 11:57:59 PDT 2026


https://github.com/alexander-shaposhnikov updated https://github.com/llvm/llvm-project/pull/231080

>From d25a8537adf1b5f08211cfa26f4456135410bfc6 Mon Sep 17 00:00:00 2001
From: Alexander Shaposhnikov <alexander.v.shaposhnikov at gmail.com>
Date: Sun, 11 Oct 2026 23:38:37 -0700
Subject: [PATCH 1/2] [mlir][ptr] Lower vector pointer addition to LLVM GEP

The ptr.ptr_add conversion only accepts scalar LLVM pointer bases and uses
the base type as the GEP result type. This leaves vector-base additions
unconverted and produces a scalar GEP with an unresolved conversion cast
when a vector offset broadcasts a scalar base.

Accept vector pointer bases and derive the GEP result type from the
converted operation result. Require a scalar or vector LLVM pointer result
so aggregate types are not used as GEP results.

Add tests for scalar/vector operand combinations with fixed and scalable
vectors, including index offsets and preservation of no-wrap flags.
---
 mlir/lib/Conversion/PtrToLLVM/PtrToLLVM.cpp | 12 +++--
 mlir/test/Conversion/PtrToLLVM/ptr-add.mlir | 59 +++++++++++++++++++++
 2 files changed, 68 insertions(+), 3 deletions(-)
 create mode 100644 mlir/test/Conversion/PtrToLLVM/ptr-add.mlir

diff --git a/mlir/lib/Conversion/PtrToLLVM/PtrToLLVM.cpp b/mlir/lib/Conversion/PtrToLLVM/PtrToLLVM.cpp
index cc1b3242c2619..b7da737e0c615 100644
--- a/mlir/lib/Conversion/PtrToLLVM/PtrToLLVM.cpp
+++ b/mlir/lib/Conversion/PtrToLLVM/PtrToLLVM.cpp
@@ -299,9 +299,15 @@ PtrAddOpConversion::matchAndRewrite(ptr::PtrAddOp op, OpAdaptor adaptor,
                                     ConversionPatternRewriter &rewriter) const {
   // Get and check the base.
   Value base = adaptor.getBase();
-  if (!isa<LLVM::LLVMPointerType>(base.getType()))
+  if (!isa<LLVM::LLVMPointerType>(getElementTypeOrSelf(base.getType())))
     return rewriter.notifyMatchFailure(op, "Incompatible pointer type");
 
+  // A vector offset can broadcast a scalar base to a vector result.
+  Type resultType = getTypeConverter()->convertType(op.getType());
+  if (!resultType ||
+      !isa<LLVM::LLVMPointerType>(getElementTypeOrSelf(resultType)))
+    return rewriter.notifyMatchFailure(op, "Incompatible result type");
+
   // Get the offset.
   Value offset = adaptor.getOffset();
 
@@ -326,8 +332,8 @@ PtrAddOpConversion::matchAndRewrite(ptr::PtrAddOp op, OpAdaptor adaptor,
   }
 
   // Create the GEP operation with appropriate arguments
-  rewriter.replaceOpWithNewOp<LLVM::GEPOp>(op, base.getType(), elementType,
-                                           base, ValueRange{offset}, flags);
+  rewriter.replaceOpWithNewOp<LLVM::GEPOp>(op, resultType, elementType, base,
+                                           ValueRange{offset}, flags);
   return success();
 }
 
diff --git a/mlir/test/Conversion/PtrToLLVM/ptr-add.mlir b/mlir/test/Conversion/PtrToLLVM/ptr-add.mlir
new file mode 100644
index 0000000000000..07fc6a196c657
--- /dev/null
+++ b/mlir/test/Conversion/PtrToLLVM/ptr-add.mlir
@@ -0,0 +1,59 @@
+// RUN: mlir-opt %s -convert-to-llvm | FileCheck %s
+
+// Broadcast a scalar base to the shape of the offsets.
+// CHECK-LABEL: llvm.func @scalar_base_vector_offset(
+// CHECK-SAME: %[[BASE:.*]]: !llvm.ptr, %[[OFFSET:.*]]: vector<4xi64>) -> vector<4x!llvm.ptr>
+// CHECK-NEXT: %[[RESULT:.*]] = llvm.getelementptr %[[BASE]][%[[OFFSET]]] : (!llvm.ptr, vector<4xi64>) -> vector<4x!llvm.ptr>, i8
+// CHECK-NEXT: llvm.return %[[RESULT]] : vector<4x!llvm.ptr>
+func.func @scalar_base_vector_offset(%base: !ptr.ptr<#ptr.generic_space>, %offset: vector<4xi64>) -> vector<4x!ptr.ptr<#ptr.generic_space>> {
+  %result = ptr.ptr_add %base, %offset : !ptr.ptr<#ptr.generic_space>, vector<4xi64>
+  return %result : vector<4x!ptr.ptr<#ptr.generic_space>>
+}
+
+// Broadcast a scalar offset to the shape of the base; preserve the flags.
+// CHECK-LABEL: llvm.func @vector_base_scalar_offset(
+// CHECK-SAME: %[[BASE:.*]]: vector<4x!llvm.ptr>, %[[OFFSET:.*]]: i32) -> vector<4x!llvm.ptr>
+// CHECK-NEXT: %[[RESULT:.*]] = llvm.getelementptr nusw %[[BASE]][%[[OFFSET]]] : (vector<4x!llvm.ptr>, i32) -> vector<4x!llvm.ptr>, i8
+// CHECK-NEXT: llvm.return %[[RESULT]] : vector<4x!llvm.ptr>
+func.func @vector_base_scalar_offset(%base: vector<4x!ptr.ptr<#ptr.generic_space>>, %offset: i32) -> vector<4x!ptr.ptr<#ptr.generic_space>> {
+  %result = ptr.ptr_add nusw %base, %offset : vector<4x!ptr.ptr<#ptr.generic_space>>, i32
+  return %result : vector<4x!ptr.ptr<#ptr.generic_space>>
+}
+
+// Convert index elements while preserving element-wise pointer addition.
+// CHECK-LABEL: llvm.func @vector_base_vector_offset(
+// CHECK-SAME: %[[BASE:.*]]: vector<4x!llvm.ptr>, %[[OFFSET:.*]]: vector<4xi64>) -> vector<4x!llvm.ptr>
+// CHECK-NEXT: %[[RESULT:.*]] = llvm.getelementptr nuw %[[BASE]][%[[OFFSET]]] : (vector<4x!llvm.ptr>, vector<4xi64>) -> vector<4x!llvm.ptr>, i8
+// CHECK-NEXT: llvm.return %[[RESULT]] : vector<4x!llvm.ptr>
+func.func @vector_base_vector_offset(%base: vector<4x!ptr.ptr<#ptr.generic_space>>, %offset: vector<4xindex>) -> vector<4x!ptr.ptr<#ptr.generic_space>> {
+  %result = ptr.ptr_add nuw %base, %offset : vector<4x!ptr.ptr<#ptr.generic_space>>, vector<4xindex>
+  return %result : vector<4x!ptr.ptr<#ptr.generic_space>>
+}
+
+// The same combinations work for scalable vectors.
+// CHECK-LABEL: llvm.func @scalar_base_scalable_offset(
+// CHECK-SAME: %[[BASE:.*]]: !llvm.ptr, %[[OFFSET:.*]]: vector<[4]xi64>) -> vector<[4]x!llvm.ptr>
+// CHECK-NEXT: %[[RESULT:.*]] = llvm.getelementptr inbounds %[[BASE]][%[[OFFSET]]] : (!llvm.ptr, vector<[4]xi64>) -> vector<[4]x!llvm.ptr>, i8
+// CHECK-NEXT: llvm.return %[[RESULT]] : vector<[4]x!llvm.ptr>
+func.func @scalar_base_scalable_offset(%base: !ptr.ptr<#ptr.generic_space>, %offset: vector<[4]xindex>) -> vector<[4]x!ptr.ptr<#ptr.generic_space>> {
+  %result = ptr.ptr_add inbounds %base, %offset : !ptr.ptr<#ptr.generic_space>, vector<[4]xindex>
+  return %result : vector<[4]x!ptr.ptr<#ptr.generic_space>>
+}
+
+// CHECK-LABEL: llvm.func @scalable_base_scalar_offset(
+// CHECK-SAME: %[[BASE:.*]]: vector<[4]x!llvm.ptr>, %[[OFFSET:.*]]: i64) -> vector<[4]x!llvm.ptr>
+// CHECK-NEXT: %[[RESULT:.*]] = llvm.getelementptr %[[BASE]][%[[OFFSET]]] : (vector<[4]x!llvm.ptr>, i64) -> vector<[4]x!llvm.ptr>, i8
+// CHECK-NEXT: llvm.return %[[RESULT]] : vector<[4]x!llvm.ptr>
+func.func @scalable_base_scalar_offset(%base: vector<[4]x!ptr.ptr<#ptr.generic_space>>, %offset: i64) -> vector<[4]x!ptr.ptr<#ptr.generic_space>> {
+  %result = ptr.ptr_add %base, %offset : vector<[4]x!ptr.ptr<#ptr.generic_space>>, i64
+  return %result : vector<[4]x!ptr.ptr<#ptr.generic_space>>
+}
+
+// CHECK-LABEL: llvm.func @scalable_base_scalable_offset(
+// CHECK-SAME: %[[BASE:.*]]: vector<[4]x!llvm.ptr>, %[[OFFSET:.*]]: vector<[4]xi32>) -> vector<[4]x!llvm.ptr>
+// CHECK-NEXT: %[[RESULT:.*]] = llvm.getelementptr %[[BASE]][%[[OFFSET]]] : (vector<[4]x!llvm.ptr>, vector<[4]xi32>) -> vector<[4]x!llvm.ptr>, i8
+// CHECK-NEXT: llvm.return %[[RESULT]] : vector<[4]x!llvm.ptr>
+func.func @scalable_base_scalable_offset(%base: vector<[4]x!ptr.ptr<#ptr.generic_space>>, %offset: vector<[4]xi32>) -> vector<[4]x!ptr.ptr<#ptr.generic_space>> {
+  %result = ptr.ptr_add %base, %offset : vector<[4]x!ptr.ptr<#ptr.generic_space>>, vector<[4]xi32>
+  return %result : vector<[4]x!ptr.ptr<#ptr.generic_space>>
+}

>From 60e25e8db713b0409d69bbaab8f788201e02361f Mon Sep 17 00:00:00 2001
From: Alexander Shaposhnikov <alexander.v.shaposhnikov at gmail.com>
Date: Mon, 12 Oct 2026 11:57:46 -0700
Subject: [PATCH 2/2] Address comments

---
 mlir/test/Conversion/PtrToLLVM/ptr-add.mlir | 26 +++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/mlir/test/Conversion/PtrToLLVM/ptr-add.mlir b/mlir/test/Conversion/PtrToLLVM/ptr-add.mlir
index 07fc6a196c657..ee86bebbd19e3 100644
--- a/mlir/test/Conversion/PtrToLLVM/ptr-add.mlir
+++ b/mlir/test/Conversion/PtrToLLVM/ptr-add.mlir
@@ -57,3 +57,29 @@ func.func @scalable_base_scalable_offset(%base: vector<[4]x!ptr.ptr<#ptr.generic
   %result = ptr.ptr_add %base, %offset : vector<[4]x!ptr.ptr<#ptr.generic_space>>, vector<[4]xi32>
   return %result : vector<[4]x!ptr.ptr<#ptr.generic_space>>
 }
+
+// Multidimensional vectors lower to LLVM arrays, which cannot be GEP operands
+// or results. Partial conversion leaves these pointer additions unconverted.
+// CHECK-LABEL: llvm.func @multidimensional_base_scalar_offset(
+// CHECK-SAME: %[[BASE:.*]]: !llvm.array<2 x vector<4x!llvm.ptr>>, %[[OFFSET:.*]]: i64) -> !llvm.array<2 x vector<4x!llvm.ptr>>
+// CHECK-NEXT: %[[CAST_BASE:.*]] = builtin.unrealized_conversion_cast %[[BASE]] : !llvm.array<2 x vector<4x!llvm.ptr>> to vector<2x4x!ptr.ptr<#ptr.generic_space>>
+// CHECK-NEXT: %[[RESULT:.*]] = ptr.ptr_add %[[CAST_BASE]], %[[OFFSET]] : vector<2x4x!ptr.ptr<#ptr.generic_space>>, i64
+// CHECK-NEXT: %[[CAST_RESULT:.*]] = builtin.unrealized_conversion_cast %[[RESULT]] : vector<2x4x!ptr.ptr<#ptr.generic_space>> to !llvm.array<2 x vector<4x!llvm.ptr>>
+// CHECK-NEXT: llvm.return %[[CAST_RESULT]] : !llvm.array<2 x vector<4x!llvm.ptr>>
+func.func @multidimensional_base_scalar_offset(%base: vector<2x4x!ptr.ptr<#ptr.generic_space>>, %offset: i64) -> vector<2x4x!ptr.ptr<#ptr.generic_space>> {
+  %result = ptr.ptr_add %base, %offset : vector<2x4x!ptr.ptr<#ptr.generic_space>>, i64
+  return %result : vector<2x4x!ptr.ptr<#ptr.generic_space>>
+}
+
+// A scalar base is a valid GEP operand, but the multidimensional result is not.
+// CHECK-LABEL: llvm.func @scalar_base_multidimensional_offset(
+// CHECK-SAME: %[[BASE:.*]]: !llvm.ptr, %[[OFFSET:.*]]: !llvm.array<2 x vector<4xi64>>) -> !llvm.array<2 x vector<4x!llvm.ptr>>
+// CHECK-NEXT: %[[CAST_OFFSET:.*]] = builtin.unrealized_conversion_cast %[[OFFSET]] : !llvm.array<2 x vector<4xi64>> to vector<2x4xi64>
+// CHECK-NEXT: %[[CAST_BASE:.*]] = builtin.unrealized_conversion_cast %[[BASE]] : !llvm.ptr to !ptr.ptr<#ptr.generic_space>
+// CHECK-NEXT: %[[RESULT:.*]] = ptr.ptr_add %[[CAST_BASE]], %[[CAST_OFFSET]] : !ptr.ptr<#ptr.generic_space>, vector<2x4xi64>
+// CHECK-NEXT: %[[CAST_RESULT:.*]] = builtin.unrealized_conversion_cast %[[RESULT]] : vector<2x4x!ptr.ptr<#ptr.generic_space>> to !llvm.array<2 x vector<4x!llvm.ptr>>
+// CHECK-NEXT: llvm.return %[[CAST_RESULT]] : !llvm.array<2 x vector<4x!llvm.ptr>>
+func.func @scalar_base_multidimensional_offset(%base: !ptr.ptr<#ptr.generic_space>, %offset: vector<2x4xi64>) -> vector<2x4x!ptr.ptr<#ptr.generic_space>> {
+  %result = ptr.ptr_add %base, %offset : !ptr.ptr<#ptr.generic_space>, vector<2x4xi64>
+  return %result : vector<2x4x!ptr.ptr<#ptr.generic_space>>
+}



More information about the Mlir-commits mailing list