[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
Mon Aug 18 09:38:40 PDT 2025
https://github.com/fhahn updated https://github.com/llvm/llvm-project/pull/153789
>From 536cbe6a4deea6e6c2221f1968b101deb5be9588 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/3] [GloablOpt] Add tests comparing global with ptrtoint.
---
.../globals-compares-with-ptrtoint.ll | 168 ++++++++++++++++++
1 file changed, 168 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..254b367897916
--- /dev/null
+++ b/llvm/test/Transforms/GlobalOpt/globals-compares-with-ptrtoint.ll
@@ -0,0 +1,168 @@
+; 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
+
+ at J = internal global [2 x ptr] 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
+; CHECK: @J = internal constant [2 x ptr] zeroinitializer
+;.
+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
+}
+
+define ptr @compare_arg(ptr %a) {
+; CHECK-LABEL: define ptr @compare_arg(
+; CHECK-SAME: ptr [[A:%.*]]) local_unnamed_addr {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C:%.*]] = icmp eq ptr [[A]], @J
+; CHECK-NEXT: br i1 [[C]], label %[[THEN:.*]], label %[[ELSE:.*]]
+; CHECK: [[THEN]]:
+; CHECK-NEXT: ret ptr null
+; CHECK: [[ELSE]]:
+; CHECK-NEXT: ret ptr null
+;
+entry:
+ %c = icmp eq ptr %a, @J
+ br i1 %c, label %then, label %else
+
+then:
+ %l = load ptr, ptr @J, align 8
+ ret ptr %l
+
+else:
+ ret ptr null
+}
>From fe37d44196989265bd15f606396b09355ea642ab 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/3] [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 | 45 +++++++++++++++++++
.../globals-compares-with-ptrtoint.ll | 18 ++++----
2 files changed, 55 insertions(+), 8 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/GlobalStatus.cpp b/llvm/lib/Transforms/Utils/GlobalStatus.cpp
index 0b3016a86e287..f82b45ac8a406 100644
--- a/llvm/lib/Transforms/Utils/GlobalStatus.cpp
+++ b/llvm/lib/Transforms/Utils/GlobalStatus.cpp
@@ -61,6 +61,45 @@ 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 +197,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 254b367897916..ec115dde5c273 100644
--- a/llvm/test/Transforms/GlobalOpt/globals-compares-with-ptrtoint.ll
+++ b/llvm/test/Transforms/GlobalOpt/globals-compares-with-ptrtoint.ll
@@ -17,12 +17,10 @@
;.
; 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: @J = internal constant [2 x ptr] zeroinitializer
;.
define i64 @A_and_B_cmp_ptrtoint_constant_expr() {
; CHECK-LABEL: define i64 @A_and_B_cmp_ptrtoint_constant_expr() local_unnamed_addr {
@@ -34,7 +32,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
@@ -61,7 +60,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
@@ -91,7 +91,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
@@ -124,7 +125,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
>From 05c75e6f8239842941103a31b0a704095cc03760 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Sat, 16 Aug 2025 11:16:32 +0100
Subject: [PATCH 3/3] !fixup check underlying obj
---
llvm/lib/Transforms/Utils/GlobalStatus.cpp | 55 ++++---------------
.../globals-compares-with-ptrtoint.ll | 5 +-
2 files changed, 16 insertions(+), 44 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/GlobalStatus.cpp b/llvm/lib/Transforms/Utils/GlobalStatus.cpp
index f82b45ac8a406..57f57899b9ff9 100644
--- a/llvm/lib/Transforms/Utils/GlobalStatus.cpp
+++ b/llvm/lib/Transforms/Utils/GlobalStatus.cpp
@@ -8,6 +8,7 @@
#include "llvm/Transforms/Utils/GlobalStatus.h"
#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/Analysis/ValueTracking.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constant.h"
#include "llvm/IR/Constants.h"
@@ -17,6 +18,7 @@
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/PatternMatch.h"
#include "llvm/IR/Use.h"
#include "llvm/IR/User.h"
#include "llvm/IR/Value.h"
@@ -26,6 +28,7 @@
#include <cassert>
using namespace llvm;
+using namespace llvm::PatternMatch;
/// Return the stronger of the two ordering. If the two orderings are acquire
/// and release, then return AcquireRelease.
@@ -61,45 +64,6 @@ 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))
@@ -198,10 +162,15 @@ static bool analyzeGlobalAux(const Value *V, GlobalStatus &GS,
} 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;
+ const Value *UO = nullptr;
+ for (Value *Op : I->operands()) {
+ if (match(Op, m_Zero()))
+ continue;
+ const Value *OpUO = getUnderlyingObject(Op);
+ if (!UO)
+ UO = OpUO;
+ if (!OpUO || UO != OpUO)
+ return true;
}
} else if (const MemTransferInst *MTI = dyn_cast<MemTransferInst>(I)) {
if (MTI->isVolatile())
diff --git a/llvm/test/Transforms/GlobalOpt/globals-compares-with-ptrtoint.ll b/llvm/test/Transforms/GlobalOpt/globals-compares-with-ptrtoint.ll
index ec115dde5c273..e3daa2bb26ca3 100644
--- a/llvm/test/Transforms/GlobalOpt/globals-compares-with-ptrtoint.ll
+++ b/llvm/test/Transforms/GlobalOpt/globals-compares-with-ptrtoint.ll
@@ -21,6 +21,8 @@
; CHECK: @C = internal global i64 0
; CHECK: @D = internal global i64 0
; CHECK: @G = internal global i64 0
+; CHECK: @H = internal global i64 0
+; CHECK: @J = internal global [2 x ptr] zeroinitializer
;.
define i64 @A_and_B_cmp_ptrtoint_constant_expr() {
; CHECK-LABEL: define i64 @A_and_B_cmp_ptrtoint_constant_expr() local_unnamed_addr {
@@ -153,7 +155,8 @@ define ptr @compare_arg(ptr %a) {
; CHECK-NEXT: [[C:%.*]] = icmp eq ptr [[A]], @J
; CHECK-NEXT: br i1 [[C]], label %[[THEN:.*]], label %[[ELSE:.*]]
; CHECK: [[THEN]]:
-; CHECK-NEXT: ret ptr null
+; CHECK-NEXT: [[L:%.*]] = load ptr, ptr @J, align 8
+; CHECK-NEXT: ret ptr [[L]]
; CHECK: [[ELSE]]:
; CHECK-NEXT: ret ptr null
;
More information about the llvm-commits
mailing list