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

llvmlistbot at llvm.org llvmlistbot at llvm.org
Tue Oct 13 10:05:54 PDT 2026


Author: Alexander Shaposhnikov
Date: 2026-10-13T10:05:48-07:00
New Revision: 7419d48987d79eca2b617cc94b5af521de4f2154

URL: https://github.com/llvm/llvm-project/commit/7419d48987d79eca2b617cc94b5af521de4f2154
DIFF: https://github.com/llvm/llvm-project/commit/7419d48987d79eca2b617cc94b5af521de4f2154.diff

LOG: [mlir][ptr] Lower vector pointer addition to LLVM GEP (#231080)

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.

Added: 
    mlir/test/Conversion/PtrToLLVM/ptr-add.mlir

Modified: 
    mlir/lib/Conversion/PtrToLLVM/PtrToLLVM.cpp

Removed: 
    


################################################################################
diff  --git a/mlir/lib/Conversion/PtrToLLVM/PtrToLLVM.cpp b/mlir/lib/Conversion/PtrToLLVM/PtrToLLVM.cpp
index cc1b3242c26191..b7da737e0c6152 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 00000000000000..ee86bebbd19e3f
--- /dev/null
+++ b/mlir/test/Conversion/PtrToLLVM/ptr-add.mlir
@@ -0,0 +1,85 @@
+// 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>>
+}
+
+// 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