[llvm] [GlobalOpt] Use getGetElementPtr() overload accepting a DataLayout (PR #225789)

Nikita Popov via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 23 07:11:50 PDT 2026


https://github.com/nikic created https://github.com/llvm/llvm-project/pull/225789

To produce the GEP in canonical ptradd form. Unlike other uses,
this one has to take potential failure to create the constant
expression into account.

It also seems like the GEP path was entirely untested, so I added
some basic test coverage for it.

>From f287f8ca499e894930e93fc81a43c76699c3bafa Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Wed, 23 Sep 2026 15:50:14 +0200
Subject: [PATCH] [GlobalOpt] Use getGetElementPtr() overload accepting a
 DataLayout

To produce the GEP in canonical ptradd form. Unlike other uses,
this one has to take potential failure to create the constant
expression into account.

It also seems like the GEP path was entirely untested, so I added
some basic test coverage for it.
---
 llvm/lib/Transforms/IPO/GlobalOpt.cpp         |  9 +-
 .../test/Transforms/GlobalOpt/storepointer.ll | 88 +++++++++++++++++--
 2 files changed, 84 insertions(+), 13 deletions(-)

diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
index b16c58ad8f8c81..36563e25cc7f75 100644
--- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp
+++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
@@ -814,10 +814,11 @@ static bool OptimizeAwayTrappingUsesOfValue(Instruction *V, Constant *NewV) {
           Idxs.push_back(C);
         else
           break;
-      if (Idxs.size() == GEPI->getNumOperands()-1)
-        Changed |= OptimizeAwayTrappingUsesOfValue(
-            GEPI, ConstantExpr::getGetElementPtr(GEPI->getSourceElementType(),
-                                                 NewV, Idxs));
+      if (Idxs.size() == GEPI->getNumOperands() - 1) {
+        if (Constant *NewGEP = ConstantExpr::getGetElementPtr(
+                V->getDataLayout(), GEPI->getSourceElementType(), NewV, Idxs))
+          Changed |= OptimizeAwayTrappingUsesOfValue(GEPI, NewGEP);
+      }
       if (GEPI->use_empty()) {
         Changed = true;
         GEPI->eraseFromParent();
diff --git a/llvm/test/Transforms/GlobalOpt/storepointer.ll b/llvm/test/Transforms/GlobalOpt/storepointer.ll
index 9a88ee0a217639..502803682cced0 100644
--- a/llvm/test/Transforms/GlobalOpt/storepointer.ll
+++ b/llvm/test/Transforms/GlobalOpt/storepointer.ll
@@ -1,19 +1,89 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
 ; RUN: opt < %s -passes=globalopt -S | FileCheck %s
-; CHECK-NOT: global
 
- at G = internal global ptr null              ; <ptr> [#uses=2]
+ at G = internal global ptr null
+ at G2 = internal global ptr null
+ at glob = global { i32, i32 } zeroinitializer
 
 define internal void @Actual() {
-        ret void
+; CHECK-LABEL: define internal fastcc void @Actual() unnamed_addr {
+; CHECK-NEXT:    ret void
+;
+  ret void
 }
 
 define void @init() {
-        store ptr @Actual, ptr @G
-        ret void
+; CHECK-LABEL: define void @init() local_unnamed_addr {
+; CHECK-NEXT:    store ptr @glob, ptr @G2, align 8
+; CHECK-NEXT:    ret void
+;
+  store ptr @Actual, ptr @G
+  store ptr @glob, ptr @G2
+  ret void
 }
 
-define void @doit() {
-        %FP = load ptr, ptr @G         ; <ptr> [#uses=1]
-        call void %FP( )
-        ret void
+define void @call() {
+; CHECK-LABEL: define void @call() local_unnamed_addr {
+; CHECK-NEXT:    call fastcc void @Actual()
+; CHECK-NEXT:    ret void
+;
+  %FP = load ptr, ptr @G
+  call void %FP()
+  ret void
+}
+
+define i32 @load() {
+; CHECK-LABEL: define i32 @load() local_unnamed_addr {
+; CHECK-NEXT:    [[RES:%.*]] = load i32, ptr @glob, align 4
+; CHECK-NEXT:    [[B:%.*]] = load i32, ptr getelementptr (i8, ptr @glob, i64 4), align 4
+; CHECK-NEXT:    [[RES1:%.*]] = add i32 [[RES]], [[B]]
+; CHECK-NEXT:    ret i32 [[RES1]]
+;
+  %g = load ptr, ptr @G2
+  %a = load i32, ptr %g
+  %gep = getelementptr i8, ptr %g, i64 4
+  %b = load i32, ptr %gep
+  %res = add i32 %a, %b
+  ret i32 %res
+}
+
+define void @store() {
+; CHECK-LABEL: define void @store() local_unnamed_addr {
+; CHECK-NEXT:    store i32 1, ptr @glob, align 4
+; CHECK-NEXT:    store i32 2, ptr getelementptr (i8, ptr @glob, i64 4), align 4
+; CHECK-NEXT:    ret void
+;
+  %g = load ptr, ptr @G2
+  store i32 1, ptr %g
+  %gep = getelementptr i8, ptr %g, i64 4
+  store i32 2, ptr %gep
+  ret void
+}
+
+define void @gep_variable(i64 %offset) {
+; CHECK-LABEL: define void @gep_variable(
+; CHECK-SAME: i64 [[OFFSET:%.*]]) local_unnamed_addr {
+; CHECK-NEXT:    [[G:%.*]] = load ptr, ptr @G2, align 8
+; CHECK-NEXT:    [[GEP:%.*]] = getelementptr i8, ptr [[G]], i64 [[OFFSET]]
+; CHECK-NEXT:    store i32 0, ptr [[GEP]], align 4
+; CHECK-NEXT:    ret void
+;
+  %g = load ptr, ptr @G2
+  %gep = getelementptr i8, ptr %g, i64 %offset
+  store i32 0, ptr %gep
+  ret void
+}
+
+define void @gep_constexpr_complex(i64 %offset) {
+; CHECK-LABEL: define void @gep_constexpr_complex(
+; CHECK-SAME: i64 [[OFFSET:%.*]]) local_unnamed_addr {
+; CHECK-NEXT:    [[G:%.*]] = load ptr, ptr @G2, align 8
+; CHECK-NEXT:    [[GEP:%.*]] = getelementptr i32, ptr [[G]], i64 ptrtoint (ptr @glob to i64)
+; CHECK-NEXT:    store i32 0, ptr [[GEP]], align 4
+; CHECK-NEXT:    ret void
+;
+  %g = load ptr, ptr @G2
+  %gep = getelementptr i32, ptr %g, i64 ptrtoint (ptr @glob to i64)
+  store i32 0, ptr %gep
+  ret void
 }



More information about the llvm-commits mailing list