[llvm] GlobalStatus: recognize aggregate subobject stores matching initializer (PR #203535)

何衍泽(He Yanze) via llvm-commits llvm-commits at lists.llvm.org
Fri Jun 12 07:20:40 PDT 2026


https://github.com/hyz231 created https://github.com/llvm/llvm-project/pull/203535

## Summary
Recognize aggregate global subobject stores that match the initializer
slice at a constant offset as `InitializerStored`. This allows GlobalOpt
to mark such globals constant and eliminate dead control flow.

Reduced from a Linux i8042 driver pattern where loads from a
zeroinitialized aggregate were not folded when stores only wrote
initializer-equivalent null values.

## Test plan
- Added `llvm/test/Transforms/GlobalOpt/aggregate-init-store.ll`
- Verified with `opt -passes='default<O2>'` that `@i8042_remove` folds to `ret void`

>From e652126617964e730713f81bdc4682e1ff32cbdc Mon Sep 17 00:00:00 2001
From: hyz231 <1756111705 at qq.com>
Date: Thu, 30 Apr 2026 13:33:56 +0800
Subject: [PATCH] GlobalStatus: recognize aggregate subobject stores matching
 initializer

---
 llvm/lib/Transforms/Utils/GlobalStatus.cpp    | 60 +++++++++++++++----
 .../GlobalOpt/aggregate-init-store.ll         | 36 +++++++++++
 2 files changed, 85 insertions(+), 11 deletions(-)
 create mode 100644 llvm/test/Transforms/GlobalOpt/aggregate-init-store.ll

diff --git a/llvm/lib/Transforms/Utils/GlobalStatus.cpp b/llvm/lib/Transforms/Utils/GlobalStatus.cpp
index 0b3016a86e287..c0efb7e3b6e0c 100644
--- a/llvm/lib/Transforms/Utils/GlobalStatus.cpp
+++ b/llvm/lib/Transforms/Utils/GlobalStatus.cpp
@@ -7,16 +7,20 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/Transforms/Utils/GlobalStatus.h"
+#include "llvm/ADT/APInt.h"
 #include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/Analysis/ConstantFolding.h"
 #include "llvm/IR/BasicBlock.h"
 #include "llvm/IR/Constant.h"
 #include "llvm/IR/Constants.h"
+#include "llvm/IR/DataLayout.h"
 #include "llvm/IR/GlobalValue.h"
 #include "llvm/IR/GlobalVariable.h"
 #include "llvm/IR/InstrTypes.h"
 #include "llvm/IR/Instruction.h"
 #include "llvm/IR/Instructions.h"
 #include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Module.h"
 #include "llvm/IR/Use.h"
 #include "llvm/IR/User.h"
 #include "llvm/IR/Value.h"
@@ -112,22 +116,56 @@ static bool analyzeGlobalAux(const Value *V, GlobalStatus &GS,
         // value, not an aggregate), keep more specific information about
         // stores.
         if (GS.StoredType != GlobalStatus::Stored) {
-          const Value *Ptr = SI->getPointerOperand()->stripPointerCasts();
-          if (const GlobalVariable *GV = dyn_cast<GlobalVariable>(Ptr)) {
+          const DataLayout &DL = SI->getModule()->getDataLayout();
+          const Value *StorePtr = SI->getPointerOperand();
+          const GlobalVariable *GV = nullptr;
+          APInt Offset(64, 0);
+          bool HasConstantOffset = false;
+
+          if (auto *PTy = dyn_cast<PointerType>(StorePtr->getType())) {
+            if (const auto *StoreGV = dyn_cast<GlobalVariable>(
+                    StorePtr->stripPointerCasts())) {
+              GV = StoreGV;
+              Offset = APInt(64, 0);
+              HasConstantOffset = true;
+            } else {
+              APInt APOffset(DL.getIndexTypeSizeInBits(PTy), 0);
+              const Value *Base = StorePtr->stripAndAccumulateConstantOffsets(
+                  DL, APOffset, /*AllowNonInbounds=*/true);
+              if (const auto *StoreGV = dyn_cast<GlobalVariable>(Base)) {
+                GV = StoreGV;
+                if (APOffset.getActiveBits() < 64) {
+                  Offset = APOffset.zextOrTrunc(64);
+                  HasConstantOffset = true;
+                }
+              }
+            }
+          }
+
+          if (GV) {
             Value *StoredVal = SI->getOperand(0);
+            Constant *StoredC = dyn_cast<Constant>(StoredVal);
 
-            if (Constant *C = dyn_cast<Constant>(StoredVal)) {
-              if (C->isThreadDependent()) {
-                // The stored value changes between threads; don't track it.
-                return true;
+            if (StoredC && StoredC->isThreadDependent()) {
+              // The stored value changes between threads; don't track it.
+              return true;
+            }
+
+            bool IsInitializerStore = false;
+            if (GV->hasInitializer()) {
+              if (StoredVal == GV->getInitializer()) {
+                IsInitializerStore = true;
+              } else if (StoredC && HasConstantOffset) {
+                if (Constant *InitSlice = ConstantFoldLoadFromConst(
+                        const_cast<Constant *>(GV->getInitializer()),
+                        StoredC->getType(), Offset, DL))
+                  IsInitializerStore = (InitSlice == StoredC);
               }
             }
 
-            if (GV->hasInitializer() && StoredVal == GV->getInitializer()) {
-              if (GS.StoredType < GlobalStatus::InitializerStored)
-                GS.StoredType = GlobalStatus::InitializerStored;
-            } else if (isa<LoadInst>(StoredVal) &&
-                       cast<LoadInst>(StoredVal)->getOperand(0) == GV) {
+            if (IsInitializerStore ||
+                (isa<LoadInst>(StoredVal) &&
+                 cast<LoadInst>(StoredVal)->getOperand(0) == GV)) {
               if (GS.StoredType < GlobalStatus::InitializerStored)
                 GS.StoredType = GlobalStatus::InitializerStored;
             } else if (GS.StoredType < GlobalStatus::StoredOnce) {
diff --git a/llvm/test/Transforms/GlobalOpt/aggregate-init-store.ll b/llvm/test/Transforms/GlobalOpt/aggregate-init-store.ll
new file mode 100644
index 0000000000000..e6d7c9355e758
--- /dev/null
+++ b/llvm/test/Transforms/GlobalOpt/aggregate-init-store.ll
@@ -0,0 +1,36 @@
+; RUN: opt -passes='default<O2>' -S %s | FileCheck %s
+;
+; Stores to aggregate globals that match the initializer slice at a constant
+; offset should be treated as InitializerStored, allowing GlobalOpt to mark
+; the global constant and fold dead control flow.
+
+%struct.i8042_port = type { ptr, i32, i8, i8, i8 }
+
+ at i8042_ports = internal unnamed_addr global [6 x %struct.i8042_port] zeroinitializer
+
+define void @i8042_remove() local_unnamed_addr {
+  br label %1
+
+1:
+  %2 = phi i64 [ 0, %0 ], [ %8, %7 ]
+  %3 = getelementptr [6 x %struct.i8042_port], ptr @i8042_ports, i64 0, i64 %2
+  %4 = load ptr, ptr %3, align 16
+  %5 = icmp eq ptr %4, null
+  br i1 %5, label %7, label %6
+
+6:
+  store ptr null, ptr @i8042_ports, align 16
+  br label %7
+
+7:
+  %8 = add nuw nsw i64 %2, 1
+  %9 = icmp eq i64 %8, 6
+  br i1 %9, label %10, label %1
+
+10:
+  ret void
+}
+
+; CHECK-LABEL: define void @i8042_remove
+; CHECK-NOT: br label
+; CHECK: ret void



More information about the llvm-commits mailing list