[llvm] [GlobalOpt] Check if global gets compared to pointer of different obj. (PR #153789)

Florian Hahn via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 15 04:14:50 PDT 2025


https://github.com/fhahn created https://github.com/llvm/llvm-project/pull/153789

Add an extra check to the compare handling to make sure the other
compared pointer cannot be based on a different object.

For example, consider the IR below, which may write `@B` via a pointer constructed
from `@A` using PtrToInt and a compare against `@B`.

```
  %cmp = icmp eq ptr inttoptr (i64 add (i64 ptrtoint (ptr @A to i64), i64 8) to ptr) , @b
  br i1 %cmp, label %then, label %else

then:
  store i64 10, ptr inttoptr (i64 add (i64 ptrtoint (ptr @A to i64), i64 8) to ptr)
  br label %exit

else:
  br label %exit

exit:
  %l = load i64, ptr @b
  ret i64 %l
}
```

>From e9fcbfdbc10e5e839c41eee1e8ee047b2a139894 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Fri, 15 Aug 2025 10:54:56 +0100
Subject: [PATCH 1/2] [GloablOpt] Add tests comparing global with ptrtoint.

---
 .../globals-compares-with-ptrtoint.ll         | 144 ++++++++++++++++++
 1 file changed, 144 insertions(+)
 create mode 100644 llvm/test/Transforms/GlobalOpt/globals-compares-with-ptrtoint.ll

diff --git a/llvm/test/Transforms/GlobalOpt/globals-compares-with-ptrtoint.ll b/llvm/test/Transforms/GlobalOpt/globals-compares-with-ptrtoint.ll
new file mode 100644
index 0000000000000..640be038c4f9b
--- /dev/null
+++ b/llvm/test/Transforms/GlobalOpt/globals-compares-with-ptrtoint.ll
@@ -0,0 +1,144 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals all --version 5
+; RUN: opt -p globalopt -S %s | FileCheck %s
+
+ at A = internal global i64 zeroinitializer
+ at B = internal global i64 zeroinitializer
+
+ at C = internal global i64 zeroinitializer
+ at D = internal global i64 zeroinitializer
+
+ at E = internal global i64 zeroinitializer
+ at F = internal global i64 zeroinitializer
+
+ at G = internal global i64 zeroinitializer
+ at H = internal global i64 zeroinitializer
+
+
+
+;.
+; CHECK: @A = internal global i64 0
+; CHECK: @B = internal constant i64 0
+; CHECK: @C = internal global i64 0
+; CHECK: @D = internal constant i64 0
+; CHECK: @G = internal global i64 0
+; CHECK: @H = internal constant i64 0
+;.
+define i64 @A_and_B_cmp_ptrtoint_constant_expr() {
+; CHECK-LABEL: define i64 @A_and_B_cmp_ptrtoint_constant_expr() local_unnamed_addr {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq ptr inttoptr (i64 add (i64 ptrtoint (ptr @A to i64), i64 8) to ptr), @B
+; CHECK-NEXT:    br i1 [[CMP]], label %[[THEN:.*]], label %[[ELSE:.*]]
+; CHECK:       [[THEN]]:
+; CHECK-NEXT:    store i64 10, ptr inttoptr (i64 add (i64 ptrtoint (ptr @A to i64), i64 8) to ptr), align 4
+; CHECK-NEXT:    br label %[[EXIT:.*]]
+; CHECK:       [[ELSE]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret i64 0
+;
+  %cmp = icmp eq ptr inttoptr (i64 add (i64 ptrtoint (ptr @A to i64), i64 8) to ptr) , @B
+  br i1 %cmp, label %then, label %else
+
+then:
+  store i64 10, ptr inttoptr (i64 add (i64 ptrtoint (ptr @A to i64), i64 8) to ptr)
+  br label %exit
+
+else:
+  br label %exit
+
+exit:
+  %l = load i64, ptr @B
+  ret i64 %l
+}
+
+define i64 @G_and_H_cmp_ptrtoint_constant_expr_cmp_ops_swapped() {
+; CHECK-LABEL: define i64 @G_and_H_cmp_ptrtoint_constant_expr_cmp_ops_swapped() local_unnamed_addr {
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq ptr inttoptr (i64 add (i64 ptrtoint (ptr @G to i64), i64 8) to ptr), @H
+; CHECK-NEXT:    br i1 [[CMP]], label %[[THEN:.*]], label %[[ELSE:.*]]
+; CHECK:       [[THEN]]:
+; CHECK-NEXT:    store i64 10, ptr inttoptr (i64 add (i64 ptrtoint (ptr @G to i64), i64 8) to ptr), align 4
+; CHECK-NEXT:    br label %[[EXIT:.*]]
+; CHECK:       [[ELSE]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret i64 0
+;
+  %cmp = icmp eq ptr inttoptr (i64 add (i64 ptrtoint (ptr @G to i64), i64 8) to ptr) , @H
+  br i1 %cmp, label %then, label %else
+
+then:
+  store i64 10, ptr inttoptr (i64 add (i64 ptrtoint (ptr @G to i64), i64 8) to ptr)
+  br label %exit
+
+else:
+  br label %exit
+
+exit:
+  %l = load i64, ptr @H
+  ret i64 %l
+}
+
+define i64 @C_and_D_cmp_ptr_load() {
+; CHECK-LABEL: define i64 @C_and_D_cmp_ptr_load() local_unnamed_addr {
+; CHECK-NEXT:    [[P:%.*]] = alloca ptr, align 8
+; CHECK-NEXT:    store ptr inttoptr (i64 add (i64 ptrtoint (ptr @C to i64), i64 8) to ptr), ptr [[P]], align 8
+; CHECK-NEXT:    [[L_P:%.*]] = load ptr, ptr [[P]], align 8
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq ptr [[L_P]], @D
+; CHECK-NEXT:    br i1 [[CMP]], label %[[THEN:.*]], label %[[ELSE:.*]]
+; CHECK:       [[THEN]]:
+; CHECK-NEXT:    store i64 10, ptr inttoptr (i64 add (i64 ptrtoint (ptr @C to i64), i64 8) to ptr), align 4
+; CHECK-NEXT:    br label %[[EXIT:.*]]
+; CHECK:       [[ELSE]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret i64 0
+;
+  %p = alloca ptr
+  store ptr inttoptr (i64 add (i64 ptrtoint (ptr @C to i64), i64 8) to ptr), ptr %p
+  %l.p = load ptr, ptr %p
+  %cmp = icmp eq ptr %l.p, @D
+  br i1 %cmp, label %then, label %else
+
+then:
+  store i64 10, ptr inttoptr (i64 add (i64 ptrtoint (ptr @C to i64), i64 8) to ptr)
+  br label %exit
+
+else:
+  br label %exit
+
+exit:
+  %l = load i64, ptr @D
+  ret i64 %l
+}
+
+define i64 @D_and_E_cmp_ptrtoint_constant_expr() {
+; CHECK-LABEL: define i64 @D_and_E_cmp_ptrtoint_constant_expr() local_unnamed_addr {
+; CHECK-NEXT:    [[PTR2INT:%.*]] = ptrtoint ptr @A to i64
+; CHECK-NEXT:    [[ADD:%.*]] = add i64 [[PTR2INT]], 8
+; CHECK-NEXT:    [[INT2PTR:%.*]] = inttoptr i64 [[ADD]] to ptr
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq ptr [[INT2PTR]], @B
+; CHECK-NEXT:    br i1 [[CMP]], label %[[THEN:.*]], label %[[ELSE:.*]]
+; CHECK:       [[THEN]]:
+; CHECK-NEXT:    store i64 10, ptr inttoptr (i64 add (i64 ptrtoint (ptr @A to i64), i64 8) to ptr), align 4
+; CHECK-NEXT:    br label %[[EXIT:.*]]
+; CHECK:       [[ELSE]]:
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret i64 0
+;
+  %ptr2int = ptrtoint ptr @A to i64
+  %add = add i64 %ptr2int, 8
+  %int2ptr = inttoptr i64 %add to ptr
+  %cmp = icmp eq ptr %int2ptr , @B
+  br i1 %cmp, label %then, label %else
+
+then:
+  store i64 10, ptr inttoptr (i64 add (i64 ptrtoint (ptr @A to i64), i64 8) to ptr)
+  br label %exit
+
+else:
+  br label %exit
+
+exit:
+  %l = load i64, ptr @B
+  ret i64 %l
+}

>From fceb7b1f7e14d08dfef523d14cf3d80f474884c2 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Fri, 15 Aug 2025 10:47:32 +0100
Subject: [PATCH 2/2] [GlobalOpt] Check if global gets compared to pointer of
 different obj.

Add an extra check to the compare handling to make sure the other
compared pointer cannot be based on a different object.

For example, consider the IR below, which may write @B via a pointer constructed
from @A using PtrToInt and a compare against @B.

```
  %cmp = icmp eq ptr inttoptr (i64 add (i64 ptrtoint (ptr @A to i64), i64 8) to ptr) , @B
  br i1 %cmp, label %then, label %else

then:
  store i64 10, ptr inttoptr (i64 add (i64 ptrtoint (ptr @A to i64), i64 8) to ptr)
  br label %exit

else:
  br label %exit

exit:
  %l = load i64, ptr @B
  ret i64 %l
}
```
---
 llvm/lib/Transforms/Utils/GlobalStatus.cpp    | 42 +++++++++++++++++++
 .../globals-compares-with-ptrtoint.ll         | 18 ++++----
 2 files changed, 53 insertions(+), 7 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/GlobalStatus.cpp b/llvm/lib/Transforms/Utils/GlobalStatus.cpp
index 0b3016a86e287..2452737e97278 100644
--- a/llvm/lib/Transforms/Utils/GlobalStatus.cpp
+++ b/llvm/lib/Transforms/Utils/GlobalStatus.cpp
@@ -61,6 +61,42 @@ bool llvm::isSafeToDestroyConstant(const Constant *C) {
   return true;
 }
 
+static bool maybePointerToDifferentObjectRecursive(Value *V, SmallPtrSetImpl<Value *> &Visited) {
+  if (!Visited.insert(V).second)
+    return false;
+
+  if (Visited.size() > 32)
+    return true;
+
+  // PtrToInt may be used to construct a pointer to a different object. Loads and calls may return a pointer for a different object.
+  if (isa<PtrToIntInst, LoadInst, CallInst>(V))
+    return true;
+
+  if (auto *CE = dyn_cast<ConstantExpr>(V)) {
+    if (CE->getOpcode() == Instruction::PtrToInt)
+      return true;
+
+    for (auto &Op : CE->operands()) {
+      if (maybePointerToDifferentObjectRecursive(Op.get(), Visited))
+        return true;
+    }
+    return false;
+  }
+
+  if (auto *U = dyn_cast<User>(V)) {
+    for (auto &Op : U->operands()) {
+      if (maybePointerToDifferentObjectRecursive(Op.get(), Visited))
+        return true;
+    }
+  }
+  return false;
+}
+
+bool maybePointerToDifferentObject(Value *V) {
+  SmallPtrSet<Value *, 32> Visited;
+  return maybePointerToDifferentObjectRecursive(V, Visited);
+}
+
 static bool analyzeGlobalAux(const Value *V, GlobalStatus &GS,
                              SmallPtrSetImpl<const Value *> &VisitedUsers) {
   if (const GlobalVariable *GV = dyn_cast<GlobalVariable>(V))
@@ -158,6 +194,12 @@ static bool analyzeGlobalAux(const Value *V, GlobalStatus &GS,
             return true;
       } else if (isa<CmpInst>(I)) {
         GS.IsCompared = true;
+
+        if (VisitedUsers.insert(I).second) {
+          for (Value *Op : I->operands())
+            if ( Op != V && maybePointerToDifferentObject(Op))
+              return true;
+        }
       } else if (const MemTransferInst *MTI = dyn_cast<MemTransferInst>(I)) {
         if (MTI->isVolatile())
           return true;
diff --git a/llvm/test/Transforms/GlobalOpt/globals-compares-with-ptrtoint.ll b/llvm/test/Transforms/GlobalOpt/globals-compares-with-ptrtoint.ll
index 640be038c4f9b..25495aa6de559 100644
--- a/llvm/test/Transforms/GlobalOpt/globals-compares-with-ptrtoint.ll
+++ b/llvm/test/Transforms/GlobalOpt/globals-compares-with-ptrtoint.ll
@@ -17,11 +17,11 @@
 
 ;.
 ; CHECK: @A = internal global i64 0
-; CHECK: @B = internal constant i64 0
+; CHECK: @B = internal global i64 0
 ; CHECK: @C = internal global i64 0
-; CHECK: @D = internal constant i64 0
+; CHECK: @D = internal global i64 0
 ; CHECK: @G = internal global i64 0
-; CHECK: @H = internal constant i64 0
+; CHECK: @H = internal global i64 0
 ;.
 define i64 @A_and_B_cmp_ptrtoint_constant_expr() {
 ; CHECK-LABEL: define i64 @A_and_B_cmp_ptrtoint_constant_expr() local_unnamed_addr {
@@ -33,7 +33,8 @@ define i64 @A_and_B_cmp_ptrtoint_constant_expr() {
 ; CHECK:       [[ELSE]]:
 ; CHECK-NEXT:    br label %[[EXIT]]
 ; CHECK:       [[EXIT]]:
-; CHECK-NEXT:    ret i64 0
+; CHECK-NEXT:    [[L:%.*]] = load i64, ptr @B, align 4
+; CHECK-NEXT:    ret i64 [[L]]
 ;
   %cmp = icmp eq ptr inttoptr (i64 add (i64 ptrtoint (ptr @A to i64), i64 8) to ptr) , @B
   br i1 %cmp, label %then, label %else
@@ -60,7 +61,8 @@ define i64 @G_and_H_cmp_ptrtoint_constant_expr_cmp_ops_swapped() {
 ; CHECK:       [[ELSE]]:
 ; CHECK-NEXT:    br label %[[EXIT]]
 ; CHECK:       [[EXIT]]:
-; CHECK-NEXT:    ret i64 0
+; CHECK-NEXT:    [[L:%.*]] = load i64, ptr @H, align 4
+; CHECK-NEXT:    ret i64 [[L]]
 ;
   %cmp = icmp eq ptr inttoptr (i64 add (i64 ptrtoint (ptr @G to i64), i64 8) to ptr) , @H
   br i1 %cmp, label %then, label %else
@@ -90,7 +92,8 @@ define i64 @C_and_D_cmp_ptr_load() {
 ; CHECK:       [[ELSE]]:
 ; CHECK-NEXT:    br label %[[EXIT]]
 ; CHECK:       [[EXIT]]:
-; CHECK-NEXT:    ret i64 0
+; CHECK-NEXT:    [[L:%.*]] = load i64, ptr @D, align 4
+; CHECK-NEXT:    ret i64 [[L]]
 ;
   %p = alloca ptr
   store ptr inttoptr (i64 add (i64 ptrtoint (ptr @C to i64), i64 8) to ptr), ptr %p
@@ -123,7 +126,8 @@ define i64 @D_and_E_cmp_ptrtoint_constant_expr() {
 ; CHECK:       [[ELSE]]:
 ; CHECK-NEXT:    br label %[[EXIT]]
 ; CHECK:       [[EXIT]]:
-; CHECK-NEXT:    ret i64 0
+; CHECK-NEXT:    [[L:%.*]] = load i64, ptr @B, align 4
+; CHECK-NEXT:    ret i64 [[L]]
 ;
   %ptr2int = ptrtoint ptr @A to i64
   %add = add i64 %ptr2int, 8



More information about the llvm-commits mailing list