[Mlir-commits] [mlir] [mlir][LowerToLLVM] Pass DataLayoutAnalysis to LowerToLLVMOptions in `ArithToLLVM`, `ControlFlowToLLVM`, and `VectorToLLVM` passes (PR #206380)
Federico Bruzzone
llvmlistbot at llvm.org
Wed Aug 5 03:17:08 PDT 2026
https://github.com/FedericoBruzzone updated https://github.com/llvm/llvm-project/pull/206380
>From 305f638bb0911c2e2926a76e262710bac00e1746 Mon Sep 17 00:00:00 2001
From: Federico Bruzzone <federico.bruzzone.i at gmail.com>
Date: Mon, 29 Jun 2026 01:54:40 +0200
Subject: [PATCH 1/3] [mlir][conversion] Pass DataLayoutAnalysis to
LowerToLLVMOptions in `ArithToLLVM`, `ControlFlowToLLVM`, and `VectorToLLVM`
passes
Signed-off-by: Federico Bruzzone <federico.bruzzone.i at gmail.com>
---
.../Conversion/ArithToLLVM/ArithToLLVM.cpp | 7 +-
.../lib/Conversion/ArithToLLVM/CMakeLists.txt | 2 +
.../ControlFlowToLLVM/CMakeLists.txt | 1 +
.../ControlFlowToLLVM/ControlFlowToLLVM.cpp | 7 +-
.../Conversion/VectorToLLVM/CMakeLists.txt | 2 +
.../VectorToLLVM/ConvertVectorToLLVMPass.cpp | 10 ++-
.../arith-to-llvm-32bit-index.mlir | 74 ++++++++++++++++++
.../ControlFlowToLLVM/branch-32bit-index.mlir | 65 ++++++++++++++++
...vector-load-store-32bit-index-to-llvm.mlir | 76 +++++++++++++++++++
9 files changed, 238 insertions(+), 6 deletions(-)
create mode 100644 mlir/test/Conversion/ArithToLLVM/arith-to-llvm-32bit-index.mlir
create mode 100644 mlir/test/Conversion/ControlFlowToLLVM/branch-32bit-index.mlir
create mode 100644 mlir/test/Conversion/VectorToLLVM/vector-load-store-32bit-index-to-llvm.mlir
diff --git a/mlir/lib/Conversion/ArithToLLVM/ArithToLLVM.cpp b/mlir/lib/Conversion/ArithToLLVM/ArithToLLVM.cpp
index 6bf0fe85bb62f..64f6358dd938d 100644
--- a/mlir/lib/Conversion/ArithToLLVM/ArithToLLVM.cpp
+++ b/mlir/lib/Conversion/ArithToLLVM/ArithToLLVM.cpp
@@ -8,6 +8,7 @@
#include "mlir/Conversion/ArithToLLVM/ArithToLLVM.h"
+#include "mlir/Analysis/DataLayoutAnalysis.h"
#include "mlir/Conversion/ArithCommon/AttrToLLVMConverter.h"
#include "mlir/Conversion/ConvertToLLVM/ToLLVMInterface.h"
#include "mlir/Conversion/LLVMCommon/ConversionTarget.h"
@@ -710,11 +711,13 @@ struct ArithToLLVMConversionPass
LLVMConversionTarget target(getContext());
RewritePatternSet patterns(&getContext());
- LowerToLLVMOptions options(&getContext());
+ const auto &dataLayoutAnalysis = getAnalysis<DataLayoutAnalysis>();
+ LowerToLLVMOptions options(&getContext(),
+ dataLayoutAnalysis.getAtOrAbove(getOperation()));
if (indexBitwidth != kDeriveIndexBitwidthFromDataLayout)
options.overrideIndexBitwidth(indexBitwidth);
- LLVMTypeConverter converter(&getContext(), options);
+ LLVMTypeConverter converter(&getContext(), options, &dataLayoutAnalysis);
arith::populateCeilFloorDivExpandOpsPatterns(patterns);
arith::populateArithToLLVMConversionPatterns(converter, patterns);
diff --git a/mlir/lib/Conversion/ArithToLLVM/CMakeLists.txt b/mlir/lib/Conversion/ArithToLLVM/CMakeLists.txt
index 0a0e25e18b47a..8fdfa991a35d3 100644
--- a/mlir/lib/Conversion/ArithToLLVM/CMakeLists.txt
+++ b/mlir/lib/Conversion/ArithToLLVM/CMakeLists.txt
@@ -14,6 +14,8 @@ add_mlir_conversion_library(MLIRArithToLLVM
MLIRArithAttrToLLVMConversion
MLIRArithDialect
MLIRArithTransforms
+ MLIRAnalysis
+ MLIRDataLayoutInterfaces
MLIRLLVMCommonConversion
MLIRLLVMDialect
)
diff --git a/mlir/lib/Conversion/ControlFlowToLLVM/CMakeLists.txt b/mlir/lib/Conversion/ControlFlowToLLVM/CMakeLists.txt
index dcc78c8367a43..b36c85165d234 100644
--- a/mlir/lib/Conversion/ControlFlowToLLVM/CMakeLists.txt
+++ b/mlir/lib/Conversion/ControlFlowToLLVM/CMakeLists.txt
@@ -13,6 +13,7 @@ add_mlir_conversion_library(MLIRControlFlowToLLVM
LINK_LIBS PUBLIC
MLIRAnalysis
+ MLIRDataLayoutInterfaces
MLIRControlFlowDialect
MLIRLLVMCommonConversion
MLIRLLVMDialect
diff --git a/mlir/lib/Conversion/ControlFlowToLLVM/ControlFlowToLLVM.cpp b/mlir/lib/Conversion/ControlFlowToLLVM/ControlFlowToLLVM.cpp
index fef78a46d69fc..e3d26cb68859b 100644
--- a/mlir/lib/Conversion/ControlFlowToLLVM/ControlFlowToLLVM.cpp
+++ b/mlir/lib/Conversion/ControlFlowToLLVM/ControlFlowToLLVM.cpp
@@ -13,6 +13,7 @@
#include "mlir/Conversion/ControlFlowToLLVM/ControlFlowToLLVM.h"
+#include "mlir/Analysis/DataLayoutAnalysis.h"
#include "mlir/Conversion/ConvertToLLVM/ToLLVMInterface.h"
#include "mlir/Conversion/LLVMCommon/ConversionTarget.h"
#include "mlir/Conversion/LLVMCommon/Pattern.h"
@@ -273,11 +274,13 @@ struct ConvertControlFlowToLLVM
ctx->getLoadedDialect<cf::ControlFlowDialect>();
});
- LowerToLLVMOptions options(ctx);
+ const auto &dataLayoutAnalysis = getAnalysis<DataLayoutAnalysis>();
+ LowerToLLVMOptions options(ctx,
+ dataLayoutAnalysis.getAtOrAbove(getOperation()));
if (indexBitwidth != kDeriveIndexBitwidthFromDataLayout)
options.overrideIndexBitwidth(indexBitwidth);
- LLVMTypeConverter converter(ctx, options);
+ LLVMTypeConverter converter(ctx, options, &dataLayoutAnalysis);
RewritePatternSet patterns(ctx);
mlir::cf::populateControlFlowToLLVMConversionPatterns(converter, patterns);
mlir::cf::populateAssertToLLVMConversionPattern(converter, patterns);
diff --git a/mlir/lib/Conversion/VectorToLLVM/CMakeLists.txt b/mlir/lib/Conversion/VectorToLLVM/CMakeLists.txt
index 0d700ea65eb4e..ea1a1de75f9bf 100644
--- a/mlir/lib/Conversion/VectorToLLVM/CMakeLists.txt
+++ b/mlir/lib/Conversion/VectorToLLVM/CMakeLists.txt
@@ -38,6 +38,8 @@ add_mlir_conversion_library(MLIRVectorToLLVMPass
MLIRArmNeonTransforms
MLIRArmSVEDialect
MLIRArmSVETransforms
+ MLIRAnalysis
+ MLIRDataLayoutInterfaces
MLIRX86Dialect
MLIRX86Transforms
)
diff --git a/mlir/lib/Conversion/VectorToLLVM/ConvertVectorToLLVMPass.cpp b/mlir/lib/Conversion/VectorToLLVM/ConvertVectorToLLVMPass.cpp
index 2b358d312dcfe..d80769ec4dc0b 100644
--- a/mlir/lib/Conversion/VectorToLLVM/ConvertVectorToLLVMPass.cpp
+++ b/mlir/lib/Conversion/VectorToLLVM/ConvertVectorToLLVMPass.cpp
@@ -8,6 +8,7 @@
#include "mlir/Conversion/VectorToLLVM/ConvertVectorToLLVMPass.h"
+#include "mlir/Analysis/DataLayoutAnalysis.h"
#include "mlir/Conversion/LLVMCommon/ConversionTarget.h"
#include "mlir/Conversion/LLVMCommon/TypeConverter.h"
#include "mlir/Dialect/Arith/IR/Arith.h"
@@ -108,8 +109,13 @@ void ConvertVectorToLLVMPass::runOnOperation() {
}
// Convert to the LLVM IR dialect.
- LowerToLLVMOptions options(&getContext());
- LLVMTypeConverter converter(&getContext(), options);
+ // Use the module's data layout so that the index bitwidth is derived from the
+ // target (e.g. i32 on 32-bit targets) rather than hard-wired to i64, which
+ // mirrors what FinalizeMemRefToLLVMConversionPass does.
+ const auto &dataLayoutAnalysis = getAnalysis<DataLayoutAnalysis>();
+ LowerToLLVMOptions options(&getContext(),
+ dataLayoutAnalysis.getAtOrAbove(getOperation()));
+ LLVMTypeConverter converter(&getContext(), options, &dataLayoutAnalysis);
RewritePatternSet patterns(&getContext());
populateVectorTransferLoweringPatterns(patterns);
populateVectorToLLVMConversionPatterns(
diff --git a/mlir/test/Conversion/ArithToLLVM/arith-to-llvm-32bit-index.mlir b/mlir/test/Conversion/ArithToLLVM/arith-to-llvm-32bit-index.mlir
new file mode 100644
index 0000000000000..85b0324388b8a
--- /dev/null
+++ b/mlir/test/Conversion/ArithToLLVM/arith-to-llvm-32bit-index.mlir
@@ -0,0 +1,74 @@
+// RUN: mlir-opt %s --convert-arith-to-llvm -split-input-file | FileCheck %s
+
+// Verify that ArithToLLVMConversionPass respects the module's data layout
+// when deriving the index type. When the module declares a 32-bit index via
+// dlti.dl_spec, index constants and index arithmetic ops must be emitted as
+// i32 instead of the default i64.
+
+// -----
+
+// 32-bit data layout: arith.constant with index type -> i32 constant.
+
+module attributes { dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<index, 32>> } {
+
+func.func @constant_index_32bit() -> index {
+ %c0 = arith.constant 0 : index
+ return %c0 : index
+}
+
+}
+
+// CHECK-LABEL: func @constant_index_32bit
+// CHECK: llvm.mlir.constant(0 : index) : i32
+
+// -----
+
+// 32-bit data layout: arith.cmpi on index type -> icmp on i32.
+
+module attributes { dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<index, 32>> } {
+
+func.func @cmpi_index_32bit(%a: index, %b: index) -> i1 {
+ %cmp = arith.cmpi slt, %a, %b : index
+ return %cmp : i1
+}
+
+}
+
+// CHECK-LABEL: func @cmpi_index_32bit
+// CHECK: builtin.unrealized_conversion_cast %{{.*}} : index to i32
+// CHECK: builtin.unrealized_conversion_cast %{{.*}} : index to i32
+// CHECK: llvm.icmp "slt" %{{.*}}, %{{.*}} : i32
+
+// -----
+
+// 32-bit data layout: arith.addi on index type -> add on i32.
+
+module attributes { dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<index, 32>> } {
+
+func.func @addi_index_32bit(%a: index, %b: index) -> index {
+ %add = arith.addi %a, %b : index
+ return %add : index
+}
+
+}
+
+// CHECK-LABEL: func @addi_index_32bit
+// CHECK: builtin.unrealized_conversion_cast %{{.*}} : index to i32
+// CHECK: builtin.unrealized_conversion_cast %{{.*}} : index to i32
+// CHECK: llvm.add %{{.*}}, %{{.*}} : i32
+
+// -----
+
+// Without dlti.dl_spec the default index width (i64) is preserved.
+
+module {
+
+func.func @constant_index_default() -> index {
+ %c0 = arith.constant 0 : index
+ return %c0 : index
+}
+
+}
+
+// CHECK-LABEL: func @constant_index_default
+// CHECK: llvm.mlir.constant(0 : index) : i64
diff --git a/mlir/test/Conversion/ControlFlowToLLVM/branch-32bit-index.mlir b/mlir/test/Conversion/ControlFlowToLLVM/branch-32bit-index.mlir
new file mode 100644
index 0000000000000..fcb32bc86a69d
--- /dev/null
+++ b/mlir/test/Conversion/ControlFlowToLLVM/branch-32bit-index.mlir
@@ -0,0 +1,65 @@
+// RUN: mlir-opt %s --convert-cf-to-llvm -split-input-file | FileCheck %s
+
+// Verify that ConvertControlFlowToLLVMPass respects the module's data layout
+// when deriving the index type for block arguments. When the module declares
+// a 32-bit index via dlti.dl_spec, cf.br and cf.cond_br must pass index-typed
+// block arguments as i32 instead of the default i64.
+
+// -----
+
+// 32-bit data layout: cf.br with index block argument -> i32 branch arg.
+
+module attributes { dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<index, 32>> } {
+
+func.func @cf_br_index_32bit(%arg0: index) -> index {
+ cf.br ^bb1(%arg0 : index)
+^bb1(%a: index):
+ return %a : index
+}
+
+}
+
+// CHECK-LABEL: func @cf_br_index_32bit
+// CHECK: builtin.unrealized_conversion_cast %{{.*}} : index to i32
+// CHECK: llvm.br ^{{.*}}(%{{.*}} : i32)
+// CHECK: ^{{.*}}(%{{.*}}: i32):
+
+// -----
+
+// 32-bit data layout: cf.cond_br with index block arguments -> i32 branch args.
+
+module attributes { dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<index, 32>> } {
+
+func.func @cf_cond_br_index_32bit(%cond: i1, %a: index, %b: index) -> index {
+ cf.cond_br %cond, ^bb1(%a : index), ^bb2(%b : index)
+^bb1(%x: index):
+ return %x : index
+^bb2(%y: index):
+ return %y : index
+}
+
+}
+
+// CHECK-LABEL: func @cf_cond_br_index_32bit
+// CHECK: builtin.unrealized_conversion_cast %{{.*}} : index to i32
+// CHECK: builtin.unrealized_conversion_cast %{{.*}} : index to i32
+// CHECK: llvm.cond_br %{{.*}}, ^{{.*}}(%{{.*}} : i32), ^{{.*}}(%{{.*}} : i32)
+
+// -----
+
+// Without dlti.dl_spec the default index width (i64) is preserved.
+
+module {
+
+func.func @cf_br_index_default(%arg0: index) -> index {
+ cf.br ^bb1(%arg0 : index)
+^bb1(%a: index):
+ return %a : index
+}
+
+}
+
+// CHECK-LABEL: func @cf_br_index_default
+// CHECK: builtin.unrealized_conversion_cast %{{.*}} : index to i64
+// CHECK: llvm.br ^{{.*}}(%{{.*}} : i64)
+// CHECK: ^{{.*}}(%{{.*}}: i64):
diff --git a/mlir/test/Conversion/VectorToLLVM/vector-load-store-32bit-index-to-llvm.mlir b/mlir/test/Conversion/VectorToLLVM/vector-load-store-32bit-index-to-llvm.mlir
new file mode 100644
index 0000000000000..29496be37322f
--- /dev/null
+++ b/mlir/test/Conversion/VectorToLLVM/vector-load-store-32bit-index-to-llvm.mlir
@@ -0,0 +1,76 @@
+// RUN: mlir-opt %s -convert-vector-to-llvm -split-input-file | FileCheck %s --check-prefixes=ALL,DEFAULT
+// RUN: mlir-opt %s -convert-vector-to-llvm='enable-gep-inbounds-nuw=1' -split-input-file | FileCheck %s --check-prefixes=ALL,INBOUNDS
+
+// Verify that ConvertVectorToLLVMPass respects the module's data layout when
+// deriving the index type. When the module declares a 32-bit index (via
+// dlti.dl_spec), GEP arithmetic should be emitted in i32 instead of the
+// default i64. This also makes enable-gep-inbounds-nuw meaningful on 32-bit
+// targets: the flag emits nsw/nuw on the *narrow* i32 multiply, allowing SCEV
+// to form a clean 32-bit AddRec and enabling LSR to use direct pointer
+// induction instead of per-iteration sign/zero extension.
+
+// -----
+
+// 32-bit data layout: index -> i32 in GEP arithmetic.
+
+module attributes { dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<index, 32>> } {
+
+func.func @load_32bit_index(%memref : memref<200x100xf32>, %i : index, %j : index) -> vector<8xf32> {
+ %0 = vector.load %memref[%i, %j] : memref<200x100xf32>, vector<8xf32>
+ return %0 : vector<8xf32>
+}
+
+}
+
+// ALL-LABEL: func @load_32bit_index
+// ALL: %[[C100:.*]] = llvm.mlir.constant(100 : index) : i32
+// ALL: %[[MUL:.*]] = llvm.mul %{{.*}}, %[[C100]]
+// ALL: %[[ADD:.*]] = llvm.add %[[MUL]], %{{.*}}
+// DEFAULT: %[[GEP:.*]] = llvm.getelementptr %{{.*}}[%[[ADD]]] : (!llvm.ptr, i32) -> !llvm.ptr, f32
+// INBOUNDS: %[[GEP:.*]] = llvm.getelementptr inbounds|nuw %{{.*}}[%[[ADD]]] : (!llvm.ptr, i32) -> !llvm.ptr, f32
+// ALL: llvm.load %[[GEP]] {alignment = 4 : i64} : !llvm.ptr -> vector<8xf32>
+
+// -----
+
+// With enable-gep-inbounds-nuw, the narrow i32 multiply and add carry
+// nsw/nuw flags, enabling SCEV to form a clean 32-bit AddRec.
+
+module attributes { dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<index, 32>> } {
+
+func.func @load_32bit_index_nuw_mul(%memref : memref<200x100xf32>, %i : index, %j : index) -> vector<8xf32> {
+ %0 = vector.load %memref[%i, %j] : memref<200x100xf32>, vector<8xf32>
+ return %0 : vector<8xf32>
+}
+
+}
+
+// DEFAULT-LABEL: func @load_32bit_index_nuw_mul
+// DEFAULT: llvm.mul %{{.*}}, %{{.*}} : i32
+// DEFAULT: llvm.add %{{.*}}, %{{.*}} : i32
+// DEFAULT: llvm.getelementptr %{{.*}} : (!llvm.ptr, i32) -> !llvm.ptr, f32
+
+// INBOUNDS-LABEL: func @load_32bit_index_nuw_mul
+// INBOUNDS: llvm.mul %{{.*}}, %{{.*}} overflow<nsw, nuw> : i32
+// INBOUNDS: llvm.add %{{.*}}, %{{.*}} overflow<nsw, nuw> : i32
+// INBOUNDS: llvm.getelementptr inbounds|nuw %{{.*}} : (!llvm.ptr, i32) -> !llvm.ptr, f32
+
+// -----
+
+// Same test for store.
+
+module attributes { dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<index, 32>> } {
+
+func.func @store_32bit_index(%memref : memref<200x100xf32>, %i : index, %j : index, %val : vector<8xf32>) {
+ vector.store %val, %memref[%i, %j] : memref<200x100xf32>, vector<8xf32>
+ return
+}
+
+}
+
+// ALL-LABEL: func @store_32bit_index
+// ALL: %[[C100:.*]] = llvm.mlir.constant(100 : index) : i32
+// ALL: %[[MUL:.*]] = llvm.mul %{{.*}}, %[[C100]]
+// ALL: %[[ADD:.*]] = llvm.add %[[MUL]], %{{.*}}
+// DEFAULT: %[[GEP:.*]] = llvm.getelementptr %{{.*}}[%[[ADD]]] : (!llvm.ptr, i32) -> !llvm.ptr, f32
+// INBOUNDS: %[[GEP:.*]] = llvm.getelementptr inbounds|nuw %{{.*}}[%[[ADD]]] : (!llvm.ptr, i32) -> !llvm.ptr, f32
+// ALL: llvm.store %{{.*}}, %[[GEP]] {alignment = 4 : i64} : vector<8xf32>, !llvm.ptr
>From 0b60d5342adf276f64ae763ad1baf3d5a2ce3e64 Mon Sep 17 00:00:00 2001
From: Federico Bruzzone <federico.bruzzone.i at gmail.com>
Date: Sat, 25 Jul 2026 11:55:16 +0200
Subject: [PATCH 2/3] Address comments
Signed-off-by: Federico Bruzzone <federico.bruzzone.i at gmail.com>
---
.../arith-to-llvm-32bit-index.mlir | 74 -----------------
.../Conversion/ArithToLLVM/arith-to-llvm.mlir | 83 +++++++++++++++++--
.../ControlFlowToLLVM/branch-32bit-index.mlir | 65 ---------------
.../Conversion/ControlFlowToLLVM/branch.mlir | 64 ++++++++++++++
...vector-load-store-32bit-index-to-llvm.mlir | 76 -----------------
.../vector-load-store-to-llvm.mlir | 70 ++++++++++++++++
6 files changed, 212 insertions(+), 220 deletions(-)
delete mode 100644 mlir/test/Conversion/ArithToLLVM/arith-to-llvm-32bit-index.mlir
delete mode 100644 mlir/test/Conversion/ControlFlowToLLVM/branch-32bit-index.mlir
delete mode 100644 mlir/test/Conversion/VectorToLLVM/vector-load-store-32bit-index-to-llvm.mlir
diff --git a/mlir/test/Conversion/ArithToLLVM/arith-to-llvm-32bit-index.mlir b/mlir/test/Conversion/ArithToLLVM/arith-to-llvm-32bit-index.mlir
deleted file mode 100644
index 85b0324388b8a..0000000000000
--- a/mlir/test/Conversion/ArithToLLVM/arith-to-llvm-32bit-index.mlir
+++ /dev/null
@@ -1,74 +0,0 @@
-// RUN: mlir-opt %s --convert-arith-to-llvm -split-input-file | FileCheck %s
-
-// Verify that ArithToLLVMConversionPass respects the module's data layout
-// when deriving the index type. When the module declares a 32-bit index via
-// dlti.dl_spec, index constants and index arithmetic ops must be emitted as
-// i32 instead of the default i64.
-
-// -----
-
-// 32-bit data layout: arith.constant with index type -> i32 constant.
-
-module attributes { dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<index, 32>> } {
-
-func.func @constant_index_32bit() -> index {
- %c0 = arith.constant 0 : index
- return %c0 : index
-}
-
-}
-
-// CHECK-LABEL: func @constant_index_32bit
-// CHECK: llvm.mlir.constant(0 : index) : i32
-
-// -----
-
-// 32-bit data layout: arith.cmpi on index type -> icmp on i32.
-
-module attributes { dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<index, 32>> } {
-
-func.func @cmpi_index_32bit(%a: index, %b: index) -> i1 {
- %cmp = arith.cmpi slt, %a, %b : index
- return %cmp : i1
-}
-
-}
-
-// CHECK-LABEL: func @cmpi_index_32bit
-// CHECK: builtin.unrealized_conversion_cast %{{.*}} : index to i32
-// CHECK: builtin.unrealized_conversion_cast %{{.*}} : index to i32
-// CHECK: llvm.icmp "slt" %{{.*}}, %{{.*}} : i32
-
-// -----
-
-// 32-bit data layout: arith.addi on index type -> add on i32.
-
-module attributes { dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<index, 32>> } {
-
-func.func @addi_index_32bit(%a: index, %b: index) -> index {
- %add = arith.addi %a, %b : index
- return %add : index
-}
-
-}
-
-// CHECK-LABEL: func @addi_index_32bit
-// CHECK: builtin.unrealized_conversion_cast %{{.*}} : index to i32
-// CHECK: builtin.unrealized_conversion_cast %{{.*}} : index to i32
-// CHECK: llvm.add %{{.*}}, %{{.*}} : i32
-
-// -----
-
-// Without dlti.dl_spec the default index width (i64) is preserved.
-
-module {
-
-func.func @constant_index_default() -> index {
- %c0 = arith.constant 0 : index
- return %c0 : index
-}
-
-}
-
-// CHECK-LABEL: func @constant_index_default
-// CHECK: llvm.mlir.constant(0 : index) : i64
diff --git a/mlir/test/Conversion/ArithToLLVM/arith-to-llvm.mlir b/mlir/test/Conversion/ArithToLLVM/arith-to-llvm.mlir
index 43f21561e6544..db4a2b6f0cee7 100644
--- a/mlir/test/Conversion/ArithToLLVM/arith-to-llvm.mlir
+++ b/mlir/test/Conversion/ArithToLLVM/arith-to-llvm.mlir
@@ -1,4 +1,4 @@
-// RUN: mlir-opt -pass-pipeline="builtin.module(func.func(convert-arith-to-llvm))" %s -split-input-file | FileCheck %s
+// RUN: mlir-opt -pass-pipeline="builtin.module(func.func(convert-arith-to-llvm))" %s -split-input-file | FileCheck %s --check-prefixes=CHECK,CHECK-DERIVE
// Same below, but using the `ConvertToLLVMPatternInterface` entry point
// and the generic `convert-to-llvm` pass.
@@ -969,11 +969,11 @@ func.func @unsupported_fp_type(%arg0: f4E2M1FN, %arg1: vector<4xf4E2M1FN>, %arg2
// -----
-// CHECK-LABEL: func @supported_fp_type
-// CHECK: llvm.fadd {{.*}} : f32
-// CHECK: llvm.fadd {{.*}} : vector<4xf32>
+// CHECK-LABEL: func @supported_fp_type
+// CHECK: llvm.fadd {{.*}} : f32
+// CHECK: llvm.fadd {{.*}} : vector<4xf32>
// CHECK-COUNT-4: llvm.fadd {{.*}} : vector<8xf32>
-// CHECK: llvm.fcmp {{.*}} : f32
+// CHECK: llvm.fcmp {{.*}} : f32
func.func @supported_fp_type(%arg0: f32, %arg1: vector<4xf32>, %arg2: vector<4x8xf32>, %arg3: f32) {
%0 = arith.addf %arg0, %arg0 : f32
%1 = arith.addf %arg1, %arg1 : vector<4xf32>
@@ -982,3 +982,76 @@ func.func @supported_fp_type(%arg0: f32, %arg1: vector<4xf32>, %arg2: vector<4x8
return
}
+// -----
+
+// Verify that the pass respects the module's data layout when deriving the
+// index type. When the module declares a 32-bit index via dlti.dl_spec,
+// index constants and index arithmetic ops must be emitted as i32 instead of
+// the default i64.
+
+// 32-bit data layout: arith.constant with index type -> i32 constant.
+
+module attributes { dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<index, 32>> } {
+
+func.func @constant_index_32bit() -> index {
+ %c0 = arith.constant 0 : index
+ return %c0 : index
+}
+
+}
+
+// CHECK-DERIVE-LABEL: func @constant_index_32bit
+// CHECK-DERIVE: llvm.mlir.constant(0 : index) : i32
+
+// -----
+
+// 32-bit data layout: arith.cmpi on index type -> icmp on i32.
+
+module attributes { dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<index, 32>> } {
+
+func.func @cmpi_index_32bit(%a: index, %b: index) -> i1 {
+ %cmp = arith.cmpi slt, %a, %b : index
+ return %cmp : i1
+}
+
+}
+
+// CHECK-DERIVE-LABEL: func @cmpi_index_32bit
+// CHECK-DERIVE: builtin.unrealized_conversion_cast %{{.*}} : index to i32
+// CHECK-DERIVE: builtin.unrealized_conversion_cast %{{.*}} : index to i32
+// CHECK-DERIVE: llvm.icmp "slt" %{{.*}}, %{{.*}} : i32
+
+// -----
+
+// 32-bit data layout: arith.addi on index type -> add on i32.
+
+module attributes { dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<index, 32>> } {
+
+func.func @addi_index_32bit(%a: index, %b: index) -> index {
+ %add = arith.addi %a, %b : index
+ return %add : index
+}
+
+}
+
+// CHECK-DERIVE-LABEL: func @addi_index_32bit
+// CHECK-DERIVE: builtin.unrealized_conversion_cast %{{.*}} : index to i32
+// CHECK-DERIVE: builtin.unrealized_conversion_cast %{{.*}} : index to i32
+// CHECK-DERIVE: llvm.add %{{.*}}, %{{.*}} : i32
+
+// -----
+
+// Without dlti.dl_spec the default index width (i64) is preserved.
+
+module {
+
+func.func @constant_index_default() -> index {
+ %c0 = arith.constant 0 : index
+ return %c0 : index
+}
+
+}
+
+// CHECK-DERIVE-LABEL: func @constant_index_default
+// CHECK-DERIVE: llvm.mlir.constant(0 : index) : i64
+
diff --git a/mlir/test/Conversion/ControlFlowToLLVM/branch-32bit-index.mlir b/mlir/test/Conversion/ControlFlowToLLVM/branch-32bit-index.mlir
deleted file mode 100644
index fcb32bc86a69d..0000000000000
--- a/mlir/test/Conversion/ControlFlowToLLVM/branch-32bit-index.mlir
+++ /dev/null
@@ -1,65 +0,0 @@
-// RUN: mlir-opt %s --convert-cf-to-llvm -split-input-file | FileCheck %s
-
-// Verify that ConvertControlFlowToLLVMPass respects the module's data layout
-// when deriving the index type for block arguments. When the module declares
-// a 32-bit index via dlti.dl_spec, cf.br and cf.cond_br must pass index-typed
-// block arguments as i32 instead of the default i64.
-
-// -----
-
-// 32-bit data layout: cf.br with index block argument -> i32 branch arg.
-
-module attributes { dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<index, 32>> } {
-
-func.func @cf_br_index_32bit(%arg0: index) -> index {
- cf.br ^bb1(%arg0 : index)
-^bb1(%a: index):
- return %a : index
-}
-
-}
-
-// CHECK-LABEL: func @cf_br_index_32bit
-// CHECK: builtin.unrealized_conversion_cast %{{.*}} : index to i32
-// CHECK: llvm.br ^{{.*}}(%{{.*}} : i32)
-// CHECK: ^{{.*}}(%{{.*}}: i32):
-
-// -----
-
-// 32-bit data layout: cf.cond_br with index block arguments -> i32 branch args.
-
-module attributes { dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<index, 32>> } {
-
-func.func @cf_cond_br_index_32bit(%cond: i1, %a: index, %b: index) -> index {
- cf.cond_br %cond, ^bb1(%a : index), ^bb2(%b : index)
-^bb1(%x: index):
- return %x : index
-^bb2(%y: index):
- return %y : index
-}
-
-}
-
-// CHECK-LABEL: func @cf_cond_br_index_32bit
-// CHECK: builtin.unrealized_conversion_cast %{{.*}} : index to i32
-// CHECK: builtin.unrealized_conversion_cast %{{.*}} : index to i32
-// CHECK: llvm.cond_br %{{.*}}, ^{{.*}}(%{{.*}} : i32), ^{{.*}}(%{{.*}} : i32)
-
-// -----
-
-// Without dlti.dl_spec the default index width (i64) is preserved.
-
-module {
-
-func.func @cf_br_index_default(%arg0: index) -> index {
- cf.br ^bb1(%arg0 : index)
-^bb1(%a: index):
- return %a : index
-}
-
-}
-
-// CHECK-LABEL: func @cf_br_index_default
-// CHECK: builtin.unrealized_conversion_cast %{{.*}} : index to i64
-// CHECK: llvm.br ^{{.*}}(%{{.*}} : i64)
-// CHECK: ^{{.*}}(%{{.*}}: i64):
diff --git a/mlir/test/Conversion/ControlFlowToLLVM/branch.mlir b/mlir/test/Conversion/ControlFlowToLLVM/branch.mlir
index 7c78211d59010..7d2aa0bc5d86e 100644
--- a/mlir/test/Conversion/ControlFlowToLLVM/branch.mlir
+++ b/mlir/test/Conversion/ControlFlowToLLVM/branch.mlir
@@ -81,3 +81,67 @@ func.func @cf_cond_br_with_weights(%cond: i1, %a: index, %b: index) -> index {
^bb2(%arg2: index):
return %arg2 : index
}
+
+// -----
+
+// Verify that the pass respects the module's data layout when deriving the
+// index type for block arguments. When the module declares a 32-bit index
+// via dlti.dl_spec, cf.br and cf.cond_br must pass index-typed block
+// arguments as i32 instead of the default i64.
+
+// 32-bit data layout: cf.br with index block argument -> i32 branch arg.
+
+module attributes { dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<index, 32>> } {
+
+func.func @cf_br_index_32bit(%arg0: index) -> index {
+ cf.br ^bb1(%arg0 : index)
+^bb1(%a: index):
+ return %a : index
+}
+
+}
+
+// CHECK-LABEL: func @cf_br_index_32bit
+// CHECK: builtin.unrealized_conversion_cast %{{.*}} : index to i32
+// CHECK: llvm.br ^{{.*}}(%{{.*}} : i32)
+// CHECK: ^{{.*}}(%{{.*}}: i32):
+
+// -----
+
+// 32-bit data layout: cf.cond_br with index block arguments -> i32 branch args.
+
+module attributes { dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<index, 32>> } {
+
+func.func @cf_cond_br_index_32bit(%cond: i1, %a: index, %b: index) -> index {
+ cf.cond_br %cond, ^bb1(%a : index), ^bb2(%b : index)
+^bb1(%x: index):
+ return %x : index
+^bb2(%y: index):
+ return %y : index
+}
+
+}
+
+// CHECK-LABEL: func @cf_cond_br_index_32bit
+// CHECK: builtin.unrealized_conversion_cast %{{.*}} : index to i32
+// CHECK: builtin.unrealized_conversion_cast %{{.*}} : index to i32
+// CHECK: llvm.cond_br %{{.*}}, ^{{.*}}(%{{.*}} : i32), ^{{.*}}(%{{.*}} : i32)
+
+// -----
+
+// Without dlti.dl_spec the default index width (i64) is preserved.
+
+module {
+
+func.func @cf_br_index_default(%arg0: index) -> index {
+ cf.br ^bb1(%arg0 : index)
+^bb1(%a: index):
+ return %a : index
+}
+
+}
+
+// CHECK-LABEL: func @cf_br_index_default
+// CHECK: builtin.unrealized_conversion_cast %{{.*}} : index to i64
+// CHECK: llvm.br ^{{.*}}(%{{.*}} : i64)
+// CHECK: ^{{.*}}(%{{.*}}: i64):
diff --git a/mlir/test/Conversion/VectorToLLVM/vector-load-store-32bit-index-to-llvm.mlir b/mlir/test/Conversion/VectorToLLVM/vector-load-store-32bit-index-to-llvm.mlir
deleted file mode 100644
index 29496be37322f..0000000000000
--- a/mlir/test/Conversion/VectorToLLVM/vector-load-store-32bit-index-to-llvm.mlir
+++ /dev/null
@@ -1,76 +0,0 @@
-// RUN: mlir-opt %s -convert-vector-to-llvm -split-input-file | FileCheck %s --check-prefixes=ALL,DEFAULT
-// RUN: mlir-opt %s -convert-vector-to-llvm='enable-gep-inbounds-nuw=1' -split-input-file | FileCheck %s --check-prefixes=ALL,INBOUNDS
-
-// Verify that ConvertVectorToLLVMPass respects the module's data layout when
-// deriving the index type. When the module declares a 32-bit index (via
-// dlti.dl_spec), GEP arithmetic should be emitted in i32 instead of the
-// default i64. This also makes enable-gep-inbounds-nuw meaningful on 32-bit
-// targets: the flag emits nsw/nuw on the *narrow* i32 multiply, allowing SCEV
-// to form a clean 32-bit AddRec and enabling LSR to use direct pointer
-// induction instead of per-iteration sign/zero extension.
-
-// -----
-
-// 32-bit data layout: index -> i32 in GEP arithmetic.
-
-module attributes { dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<index, 32>> } {
-
-func.func @load_32bit_index(%memref : memref<200x100xf32>, %i : index, %j : index) -> vector<8xf32> {
- %0 = vector.load %memref[%i, %j] : memref<200x100xf32>, vector<8xf32>
- return %0 : vector<8xf32>
-}
-
-}
-
-// ALL-LABEL: func @load_32bit_index
-// ALL: %[[C100:.*]] = llvm.mlir.constant(100 : index) : i32
-// ALL: %[[MUL:.*]] = llvm.mul %{{.*}}, %[[C100]]
-// ALL: %[[ADD:.*]] = llvm.add %[[MUL]], %{{.*}}
-// DEFAULT: %[[GEP:.*]] = llvm.getelementptr %{{.*}}[%[[ADD]]] : (!llvm.ptr, i32) -> !llvm.ptr, f32
-// INBOUNDS: %[[GEP:.*]] = llvm.getelementptr inbounds|nuw %{{.*}}[%[[ADD]]] : (!llvm.ptr, i32) -> !llvm.ptr, f32
-// ALL: llvm.load %[[GEP]] {alignment = 4 : i64} : !llvm.ptr -> vector<8xf32>
-
-// -----
-
-// With enable-gep-inbounds-nuw, the narrow i32 multiply and add carry
-// nsw/nuw flags, enabling SCEV to form a clean 32-bit AddRec.
-
-module attributes { dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<index, 32>> } {
-
-func.func @load_32bit_index_nuw_mul(%memref : memref<200x100xf32>, %i : index, %j : index) -> vector<8xf32> {
- %0 = vector.load %memref[%i, %j] : memref<200x100xf32>, vector<8xf32>
- return %0 : vector<8xf32>
-}
-
-}
-
-// DEFAULT-LABEL: func @load_32bit_index_nuw_mul
-// DEFAULT: llvm.mul %{{.*}}, %{{.*}} : i32
-// DEFAULT: llvm.add %{{.*}}, %{{.*}} : i32
-// DEFAULT: llvm.getelementptr %{{.*}} : (!llvm.ptr, i32) -> !llvm.ptr, f32
-
-// INBOUNDS-LABEL: func @load_32bit_index_nuw_mul
-// INBOUNDS: llvm.mul %{{.*}}, %{{.*}} overflow<nsw, nuw> : i32
-// INBOUNDS: llvm.add %{{.*}}, %{{.*}} overflow<nsw, nuw> : i32
-// INBOUNDS: llvm.getelementptr inbounds|nuw %{{.*}} : (!llvm.ptr, i32) -> !llvm.ptr, f32
-
-// -----
-
-// Same test for store.
-
-module attributes { dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<index, 32>> } {
-
-func.func @store_32bit_index(%memref : memref<200x100xf32>, %i : index, %j : index, %val : vector<8xf32>) {
- vector.store %val, %memref[%i, %j] : memref<200x100xf32>, vector<8xf32>
- return
-}
-
-}
-
-// ALL-LABEL: func @store_32bit_index
-// ALL: %[[C100:.*]] = llvm.mlir.constant(100 : index) : i32
-// ALL: %[[MUL:.*]] = llvm.mul %{{.*}}, %[[C100]]
-// ALL: %[[ADD:.*]] = llvm.add %[[MUL]], %{{.*}}
-// DEFAULT: %[[GEP:.*]] = llvm.getelementptr %{{.*}}[%[[ADD]]] : (!llvm.ptr, i32) -> !llvm.ptr, f32
-// INBOUNDS: %[[GEP:.*]] = llvm.getelementptr inbounds|nuw %{{.*}}[%[[ADD]]] : (!llvm.ptr, i32) -> !llvm.ptr, f32
-// ALL: llvm.store %{{.*}}, %[[GEP]] {alignment = 4 : i64} : vector<8xf32>, !llvm.ptr
diff --git a/mlir/test/Conversion/VectorToLLVM/vector-load-store-to-llvm.mlir b/mlir/test/Conversion/VectorToLLVM/vector-load-store-to-llvm.mlir
index cab462efee2b5..3cddf0acaa89d 100644
--- a/mlir/test/Conversion/VectorToLLVM/vector-load-store-to-llvm.mlir
+++ b/mlir/test/Conversion/VectorToLLVM/vector-load-store-to-llvm.mlir
@@ -236,3 +236,73 @@ func.func @store_with_alignment(%memref : memref<200x100xf32>, %i : index, %j :
// ALL-LABEL: func @store_with_alignment
// ALL: llvm.store %{{.*}} {alignment = 8 : i64} : vector<4xf32>, !llvm.ptr
+
+// -----
+
+// Verify that the pass respects the module's data layout when deriving the
+// index type. When the module declares a 32-bit index (via dlti.dl_spec),
+// GEP arithmetic should be emitted in i32 instead of the default i64.
+
+// 32-bit data layout: index -> i32 in GEP arithmetic.
+
+module attributes { dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<index, 32>> } {
+
+func.func @load_32bit_index(%memref : memref<200x100xf32>, %i : index, %j : index) -> vector<8xf32> {
+ %0 = vector.load %memref[%i, %j] : memref<200x100xf32>, vector<8xf32>
+ return %0 : vector<8xf32>
+}
+
+}
+
+// ALL-LABEL: func @load_32bit_index
+// ALL: %[[C100:.*]] = llvm.mlir.constant(100 : index) : i32
+// ALL: %[[MUL:.*]] = llvm.mul %{{.*}}, %[[C100]]
+// ALL: %[[ADD:.*]] = llvm.add %[[MUL]], %{{.*}}
+// DEFAULT: %[[GEP:.*]] = llvm.getelementptr %{{.*}}[%[[ADD]]] : (!llvm.ptr, i32) -> !llvm.ptr, f32
+// INBOUNDS: %[[GEP:.*]] = llvm.getelementptr inbounds|nuw %{{.*}}[%[[ADD]]] : (!llvm.ptr, i32) -> !llvm.ptr, f32
+// ALL: llvm.load %[[GEP]] {alignment = 4 : i64} : !llvm.ptr -> vector<8xf32>
+
+// -----
+
+// With enable-gep-inbounds-nuw, the narrow i32 multiply and add carry
+// nsw/nuw flags, enabling SCEV to form a clean 32-bit AddRec.
+
+module attributes { dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<index, 32>> } {
+
+func.func @load_32bit_index_nuw_mul(%memref : memref<200x100xf32>, %i : index, %j : index) -> vector<8xf32> {
+ %0 = vector.load %memref[%i, %j] : memref<200x100xf32>, vector<8xf32>
+ return %0 : vector<8xf32>
+}
+
+}
+
+// DEFAULT-LABEL: func @load_32bit_index_nuw_mul
+// DEFAULT: llvm.mul %{{.*}}, %{{.*}} : i32
+// DEFAULT: llvm.add %{{.*}}, %{{.*}} : i32
+// DEFAULT: llvm.getelementptr %{{.*}} : (!llvm.ptr, i32) -> !llvm.ptr, f32
+
+// INBOUNDS-LABEL: func @load_32bit_index_nuw_mul
+// INBOUNDS: llvm.mul %{{.*}}, %{{.*}} overflow<nsw, nuw> : i32
+// INBOUNDS: llvm.add %{{.*}}, %{{.*}} overflow<nsw, nuw> : i32
+// INBOUNDS: llvm.getelementptr inbounds|nuw %{{.*}} : (!llvm.ptr, i32) -> !llvm.ptr, f32
+
+// -----
+
+// Same test for store.
+
+module attributes { dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry<index, 32>> } {
+
+func.func @store_32bit_index(%memref : memref<200x100xf32>, %i : index, %j : index, %val : vector<8xf32>) {
+ vector.store %val, %memref[%i, %j] : memref<200x100xf32>, vector<8xf32>
+ return
+}
+
+}
+
+// ALL-LABEL: func @store_32bit_index
+// ALL: %[[C100:.*]] = llvm.mlir.constant(100 : index) : i32
+// ALL: %[[MUL:.*]] = llvm.mul %{{.*}}, %[[C100]]
+// ALL: %[[ADD:.*]] = llvm.add %[[MUL]], %{{.*}}
+// DEFAULT: %[[GEP:.*]] = llvm.getelementptr %{{.*}}[%[[ADD]]] : (!llvm.ptr, i32) -> !llvm.ptr, f32
+// INBOUNDS: %[[GEP:.*]] = llvm.getelementptr inbounds|nuw %{{.*}}[%[[ADD]]] : (!llvm.ptr, i32) -> !llvm.ptr, f32
+// ALL: llvm.store %{{.*}}, %[[GEP]] {alignment = 4 : i64} : vector<8xf32>, !llvm.ptr
>From 055af22f0ffb49c80147bec87ecc1d165e7de232 Mon Sep 17 00:00:00 2001
From: Federico Bruzzone <federico.bruzzone.i at gmail.com>
Date: Wed, 5 Aug 2026 12:16:45 +0200
Subject: [PATCH 3/3] Address comments
Signed-off-by: Federico Bruzzone <federico.bruzzone.i at gmail.com>
---
.../lib/Conversion/VectorToLLVM/ConvertVectorToLLVMPass.cpp | 6 +++---
mlir/test/Conversion/ArithToLLVM/arith-to-llvm.mlir | 4 ++++
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/mlir/lib/Conversion/VectorToLLVM/ConvertVectorToLLVMPass.cpp b/mlir/lib/Conversion/VectorToLLVM/ConvertVectorToLLVMPass.cpp
index d80769ec4dc0b..e0355222cff54 100644
--- a/mlir/lib/Conversion/VectorToLLVM/ConvertVectorToLLVMPass.cpp
+++ b/mlir/lib/Conversion/VectorToLLVM/ConvertVectorToLLVMPass.cpp
@@ -109,9 +109,9 @@ void ConvertVectorToLLVMPass::runOnOperation() {
}
// Convert to the LLVM IR dialect.
- // Use the module's data layout so that the index bitwidth is derived from the
- // target (e.g. i32 on 32-bit targets) rather than hard-wired to i64, which
- // mirrors what FinalizeMemRefToLLVMConversionPass does.
+ // Use the data layout in scope (if any) so that the index bitwidth is taken
+ // from it rather than hard-wired to i64, mirroring what
+ // FinalizeMemRefToLLVMConversionPass does.
const auto &dataLayoutAnalysis = getAnalysis<DataLayoutAnalysis>();
LowerToLLVMOptions options(&getContext(),
dataLayoutAnalysis.getAtOrAbove(getOperation()));
diff --git a/mlir/test/Conversion/ArithToLLVM/arith-to-llvm.mlir b/mlir/test/Conversion/ArithToLLVM/arith-to-llvm.mlir
index db4a2b6f0cee7..a42b736ce2c24 100644
--- a/mlir/test/Conversion/ArithToLLVM/arith-to-llvm.mlir
+++ b/mlir/test/Conversion/ArithToLLVM/arith-to-llvm.mlir
@@ -988,6 +988,10 @@ func.func @supported_fp_type(%arg0: f32, %arg1: vector<4xf32>, %arg2: vector<4x8
// index type. When the module declares a 32-bit index via dlti.dl_spec,
// index constants and index arithmetic ops must be emitted as i32 instead of
// the default i64.
+//
+// These cases use the CHECK-DERIVE prefix: the `convert-to-llvm` RUN lines
+// above use the static pass implementation, which builds its type converter
+// without a data layout, so it always lowers `index` to i64.
// 32-bit data layout: arith.constant with index type -> i32 constant.
More information about the Mlir-commits
mailing list