[llvm] [GlobalOpt] Do not fold away addrspacecasts which may be runtime operations (PR #153753)
    Owen Anderson via llvm-commits 
    llvm-commits at lists.llvm.org
       
    Fri Aug 15 03:20:12 PDT 2025
    
    
  
https://github.com/resistor updated https://github.com/llvm/llvm-project/pull/153753
>From 7db799a857978e7aca11e03d62e28eb05d402eeb Mon Sep 17 00:00:00 2001
From: Owen Anderson <resistor at mac.com>
Date: Fri, 15 Aug 2025 14:36:49 +0800
Subject: [PATCH 1/2] Prevent GlobalOpt from folding away addrspacecasts which
 may be runtime operations.
Specifically in the context of the once-stored transformation, GlobalOpt would strip
all pointer casts unconditionally, even though addrspacecasts might be runtime operations.
This manifested particularly on CHERI targets.
This patch was inspired by an existing change in CHERI LLVM (https://github.com/CHERIoT-Platform/llvm-project/commit/91afa60f17ea1b91e5cdba21a5c90400024b2b6a), but has been reimplemented with updated conventions, and a testcase constructed from scratch.
---
 llvm/lib/Transforms/IPO/GlobalOpt.cpp         |  5 +--
 .../GlobalOpt/stored-once-addrspacecast.ll    | 31 +++++++++++++++++++
 2 files changed, 34 insertions(+), 2 deletions(-)
 create mode 100644 llvm/test/Transforms/GlobalOpt/stored-once-addrspacecast.ll
diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
index bdda4980c1005..89a811e3f74d5 100644
--- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp
+++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
@@ -1133,8 +1133,9 @@ static bool
 optimizeOnceStoredGlobal(GlobalVariable *GV, Value *StoredOnceVal,
                          const DataLayout &DL,
                          function_ref<TargetLibraryInfo &(Function &)> GetTLI) {
-  // Ignore no-op GEPs and bitcasts.
-  StoredOnceVal = StoredOnceVal->stripPointerCasts();
+  // Ignore no-op GEPs and bitcasts, but not addrspacecasts that may be runtime
+  // operations.
+  StoredOnceVal = StoredOnceVal->stripPointerCastsSameRepresentation();
 
   // If we are dealing with a pointer global that is initialized to null and
   // only has one (non-null) value stored into it, then we can optimize any
diff --git a/llvm/test/Transforms/GlobalOpt/stored-once-addrspacecast.ll b/llvm/test/Transforms/GlobalOpt/stored-once-addrspacecast.ll
new file mode 100644
index 0000000000000..cb2fcce51a7ae
--- /dev/null
+++ b/llvm/test/Transforms/GlobalOpt/stored-once-addrspacecast.ll
@@ -0,0 +1,31 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals
+; RUN: opt -passes=globalopt < %s -S | FileCheck %s
+
+; Test that we do not fold away addresscasts when optimizing once-stored
+; globals, as these may be runtime operations.
+
+%T = type { ptr }
+
+ at 0 = internal global ptr null
+ at 1 = internal addrspace(1) global i32 0
+
+;.
+; CHECK: @[[GLOB0:[0-9]+]] = internal unnamed_addr global ptr null
+; CHECK: @[[GLOB1:[0-9]+]] = internal addrspace(1) global i32 0
+;.
+define void @a() {
+; CHECK-LABEL: @a(
+; CHECK-NEXT:    [[TMP1:%.*]] = addrspacecast ptr addrspace(1) @[[GLOB1]] to ptr
+; CHECK-NEXT:    store ptr [[TMP1]], ptr @[[GLOB0]], align 8
+; CHECK-NEXT:    [[TMP2:%.*]] = load ptr, ptr @[[GLOB0]], align 8
+; CHECK-NEXT:    [[TMP3:%.*]] = load atomic i64, ptr [[TMP2]] acquire, align 8
+; CHECK-NEXT:    ret void
+;
+  %1 = addrspacecast ptr addrspace(1) @1 to ptr
+  store ptr %1, ptr @0, align 8
+  %2 = load ptr, ptr @0, align 8
+  %3 = load atomic i64, ptr %2 acquire, align 8
+  ret void
+}
+
+declare ptr @_Znwm(i64)
>From f54a98b28919ee48b534ff4d4f8796be833a9021 Mon Sep 17 00:00:00 2001
From: Owen Anderson <resistor at mac.com>
Date: Fri, 15 Aug 2025 18:19:52 +0800
Subject: [PATCH 2/2] Update for review feedback.
---
 llvm/lib/Transforms/IPO/GlobalOpt.cpp         |  4 --
 .../GlobalOpt/stored-once-addrspacecast.ll    | 47 ++++++++++++-------
 2 files changed, 29 insertions(+), 22 deletions(-)
diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
index 89a811e3f74d5..d7edd1288309b 100644
--- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp
+++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
@@ -1133,10 +1133,6 @@ static bool
 optimizeOnceStoredGlobal(GlobalVariable *GV, Value *StoredOnceVal,
                          const DataLayout &DL,
                          function_ref<TargetLibraryInfo &(Function &)> GetTLI) {
-  // Ignore no-op GEPs and bitcasts, but not addrspacecasts that may be runtime
-  // operations.
-  StoredOnceVal = StoredOnceVal->stripPointerCastsSameRepresentation();
-
   // If we are dealing with a pointer global that is initialized to null and
   // only has one (non-null) value stored into it, then we can optimize any
   // users of the loaded value (often calls and loads) that would trap if the
diff --git a/llvm/test/Transforms/GlobalOpt/stored-once-addrspacecast.ll b/llvm/test/Transforms/GlobalOpt/stored-once-addrspacecast.ll
index cb2fcce51a7ae..31349e830b2d1 100644
--- a/llvm/test/Transforms/GlobalOpt/stored-once-addrspacecast.ll
+++ b/llvm/test/Transforms/GlobalOpt/stored-once-addrspacecast.ll
@@ -4,28 +4,39 @@
 ; Test that we do not fold away addresscasts when optimizing once-stored
 ; globals, as these may be runtime operations.
 
-%T = type { ptr }
-
- at 0 = internal global ptr null
- at 1 = internal addrspace(1) global i32 0
+ at g1 = internal global ptr null
+ at g2 = internal addrspace(1) global i32 0
 
 ;.
-; CHECK: @[[GLOB0:[0-9]+]] = internal unnamed_addr global ptr null
-; CHECK: @[[GLOB1:[0-9]+]] = internal addrspace(1) global i32 0
+; CHECK: @g1 = internal unnamed_addr global ptr null
+; CHECK: @g2 = internal addrspace(1) global i32 0
+; CHECK: @g4 = local_unnamed_addr addrspace(1) global i32 0
 ;.
-define void @a() {
-; CHECK-LABEL: @a(
-; CHECK-NEXT:    [[TMP1:%.*]] = addrspacecast ptr addrspace(1) @[[GLOB1]] to ptr
-; CHECK-NEXT:    store ptr [[TMP1]], ptr @[[GLOB0]], align 8
-; CHECK-NEXT:    [[TMP2:%.*]] = load ptr, ptr @[[GLOB0]], align 8
+define i64 @test1() {
+; CHECK-LABEL: @test1(
+; CHECK-NEXT:    [[TMP1:%.*]] = addrspacecast ptr addrspace(1) @g2 to ptr
+; CHECK-NEXT:    store ptr [[TMP1]], ptr @g1, align 8
+; CHECK-NEXT:    [[TMP2:%.*]] = load ptr, ptr @g1, align 8
 ; CHECK-NEXT:    [[TMP3:%.*]] = load atomic i64, ptr [[TMP2]] acquire, align 8
-; CHECK-NEXT:    ret void
+; CHECK-NEXT:    ret i64 [[TMP3]]
 ;
-  %1 = addrspacecast ptr addrspace(1) @1 to ptr
-  store ptr %1, ptr @0, align 8
-  %2 = load ptr, ptr @0, align 8
-  %3 = load atomic i64, ptr %2 acquire, align 8
-  ret void
+  %l1 = addrspacecast ptr addrspace(1) @g2 to ptr
+  store ptr %l1, ptr @g1, align 8
+  %l2 = load ptr, ptr @g1, align 8
+  %l3 = load atomic i64, ptr %l2 acquire, align 8
+  ret i64 %l3
 }
 
-declare ptr @_Znwm(i64)
+ at g3 = internal global ptr null
+ at g4 = addrspace(1) global i32 0
+
+define i64 @test2() {
+; CHECK-LABEL: @test2(
+; CHECK-NEXT:    [[TMP1:%.*]] = load atomic i64, ptr addrspacecast (ptr addrspace(1) @g4 to ptr) acquire, align 8
+; CHECK-NEXT:    ret i64 [[TMP1]]
+;
+  store ptr addrspacecast (ptr addrspace(1) @g4 to ptr), ptr @g3, align 8
+  %l1 = load ptr, ptr @g3, align 8
+  %l2 = load atomic i64, ptr %l1 acquire, align 8
+  ret i64 %l2
+}
    
    
More information about the llvm-commits
mailing list