[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