[llvm] [GlobalOpt] Fix alignments of globals introduced for allocations (PR #216480)

Ömer Sinan Ağacan via llvm-commits llvm-commits at lists.llvm.org
Sat Oct 3 04:42:35 PDT 2026


https://github.com/osa1 updated https://github.com/llvm/llvm-project/pull/216480

>From f5194e827a6f1d02cfa92cc069aeea1e5f07211e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= <omer at osa1.net>
Date: Mon, 17 Aug 2026 09:40:46 +0100
Subject: [PATCH 1/7] Add tests

---
 .../Transforms/GlobalOpt/global-align-1.ll    | 44 ++++++++++
 .../Transforms/GlobalOpt/global-align-2.ll    | 47 +++++++++++
 .../Transforms/GlobalOpt/global-align-3.ll    | 48 +++++++++++
 .../GlobalOpt/global-align-memset.ll          | 82 +++++++++++++++++++
 4 files changed, 221 insertions(+)
 create mode 100644 llvm/test/Transforms/GlobalOpt/global-align-1.ll
 create mode 100644 llvm/test/Transforms/GlobalOpt/global-align-2.ll
 create mode 100644 llvm/test/Transforms/GlobalOpt/global-align-3.ll
 create mode 100644 llvm/test/Transforms/GlobalOpt/global-align-memset.ll

diff --git a/llvm/test/Transforms/GlobalOpt/global-align-1.ll b/llvm/test/Transforms/GlobalOpt/global-align-1.ll
new file mode 100644
index 00000000000000..0e4eaa4b54f0b2
--- /dev/null
+++ b/llvm/test/Transforms/GlobalOpt/global-align-1.ll
@@ -0,0 +1,44 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals all --version 6
+; RUN: opt < %s -passes=globalopt -S | FileCheck %s
+
+; Test from issue #215533.
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+ at g10 = internal global ptr null, align 8
+ at g26 = dso_local global <4 x i32> zeroinitializer, align 16
+ at g5 = dso_local global i8 0, align 1
+
+;.
+; CHECK: @g26 = dso_local local_unnamed_addr global <4 x i32> zeroinitializer, align 16
+; CHECK: @g5 = dso_local local_unnamed_addr global i8 0, align 1
+; CHECK: @g10.body = internal unnamed_addr global [16 x i8] undef{{$}}
+;.
+define dso_local i32 @main() {
+; CHECK-LABEL: define dso_local i32 @main() local_unnamed_addr {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = load <4 x i32>, ptr @g26, align 16
+; CHECK-NEXT:    store <4 x i32> [[TMP0]], ptr @g10.body, align 16
+; CHECK-NEXT:    [[BB3_0_COPYLOAD:%.*]] = load i32, ptr @g10.body, align 1
+; CHECK-NEXT:    [[TOBOOL:%.*]] = icmp ne i32 [[BB3_0_COPYLOAD]], 0
+; CHECK-NEXT:    [[STOREDV:%.*]] = zext i1 [[TOBOOL]] to i8
+; CHECK-NEXT:    store i8 [[STOREDV]], ptr @g5, align 1
+; CHECK-NEXT:    ret i32 0
+;
+entry:
+  %call = call noalias align 16 ptr @aligned_alloc(i64 noundef 16, i64 noundef 16)
+  store ptr %call, ptr @g10, align 8
+  %0 = load <4 x i32>, ptr @g26, align 16
+  store <4 x i32> %0, ptr %call, align 16
+  %1 = load ptr, ptr @g10, align 8
+  %bb3.0.copyload = load i32, ptr %1, align 1
+  %tobool = icmp ne i32 %bb3.0.copyload, 0
+  %storedv = zext i1 %tobool to i8
+  store i8 %storedv, ptr @g5, align 1
+  ret i32 0
+}
+
+declare noalias noundef ptr @aligned_alloc(i64 allocalign noundef, i64 noundef) #0
+
+attributes #0 = { allockind("alloc,uninitialized,aligned") allocsize(1) }
diff --git a/llvm/test/Transforms/GlobalOpt/global-align-2.ll b/llvm/test/Transforms/GlobalOpt/global-align-2.ll
new file mode 100644
index 00000000000000..5e729275426eca
--- /dev/null
+++ b/llvm/test/Transforms/GlobalOpt/global-align-2.ll
@@ -0,0 +1,47 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals all --version 6
+; RUN: opt < %s -passes=globalopt -S | FileCheck %s
+
+; Use the declared alignment of the allocation as the global's alignment.
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+ at g = internal global ptr null
+
+;.
+; CHECK: @g.body = internal unnamed_addr global [64 x i8] undef{{$}}
+;.
+define void @init() {
+; CHECK-LABEL: define void @init() local_unnamed_addr {
+; CHECK-NEXT:    ret void
+;
+  %m = call noalias align 64 ptr @malloc(i64 64)
+  store ptr %m, ptr @g, align 8
+  ret void
+}
+
+define void @store(<8 x float> %v, i8 %c) {
+; CHECK-LABEL: define void @store(
+; CHECK-SAME: <8 x float> [[V:%.*]], i8 [[C:%.*]]) local_unnamed_addr {
+; CHECK-NEXT:    store <8 x float> [[V]], ptr @g.body, align 32
+; CHECK-NEXT:    store i8 [[C]], ptr getelementptr inbounds nuw (i8, ptr @g.body, i64 3), align 1
+; CHECK-NEXT:    ret void
+;
+  %p = load ptr, ptr @g, align 8
+  store <8 x float> %v, ptr %p, align 32
+  %q = getelementptr i8, ptr %p, i64 3
+  store i8 %c, ptr %q, align 1
+  ret void
+}
+
+define <8 x float> @load() {
+; CHECK-LABEL: define <8 x float> @load() local_unnamed_addr {
+; CHECK-NEXT:    [[V:%.*]] = load <8 x float>, ptr @g.body, align 32
+; CHECK-NEXT:    ret <8 x float> [[V]]
+;
+  %p = load ptr, ptr @g, align 8
+  %v = load <8 x float>, ptr %p, align 32
+  ret <8 x float> %v
+}
+
+declare noalias ptr @malloc(i64) allockind("alloc,uninitialized") allocsize(0)
diff --git a/llvm/test/Transforms/GlobalOpt/global-align-3.ll b/llvm/test/Transforms/GlobalOpt/global-align-3.ll
new file mode 100644
index 00000000000000..a899ab5761583e
--- /dev/null
+++ b/llvm/test/Transforms/GlobalOpt/global-align-3.ll
@@ -0,0 +1,48 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals all --version 6
+; RUN: opt < %s -passes=globalopt -S | FileCheck %s
+
+; Check the the alignment is not specified when it's smaller than the preferred
+; alignment of the type.
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+ at g = internal global ptr null
+
+;.
+; CHECK: @g.body = internal unnamed_addr global [32 x i8] undef{{$}}
+;.
+define void @init() {
+; CHECK-LABEL: define void @init() local_unnamed_addr {
+; CHECK-NEXT:    ret void
+;
+  %m = call noalias ptr @aligned_alloc(i64 8, i64 32)
+  store ptr %m, ptr @g, align 8
+  ret void
+}
+
+define void @store(<4 x float> %v, i8 %c) {
+; CHECK-LABEL: define void @store(
+; CHECK-SAME: <4 x float> [[V:%.*]], i8 [[C:%.*]]) local_unnamed_addr {
+; CHECK-NEXT:    store <4 x float> [[V]], ptr @g.body, align 16
+; CHECK-NEXT:    store i8 [[C]], ptr getelementptr inbounds nuw (i8, ptr @g.body, i64 3), align 1
+; CHECK-NEXT:    ret void
+;
+  %p = load ptr, ptr @g, align 8
+  store <4 x float> %v, ptr %p, align 16
+  %q = getelementptr i8, ptr %p, i64 3
+  store i8 %c, ptr %q, align 1
+  ret void
+}
+
+define <4 x float> @load() {
+; CHECK-LABEL: define <4 x float> @load() local_unnamed_addr {
+; CHECK-NEXT:    [[V:%.*]] = load <4 x float>, ptr @g.body, align 16
+; CHECK-NEXT:    ret <4 x float> [[V]]
+;
+  %p = load ptr, ptr @g, align 8
+  %v = load <4 x float>, ptr %p, align 16
+  ret <4 x float> %v
+}
+
+declare noalias ptr @aligned_alloc(i64 allocalign, i64) allockind("alloc,uninitialized,aligned") allocsize(1)
diff --git a/llvm/test/Transforms/GlobalOpt/global-align-memset.ll b/llvm/test/Transforms/GlobalOpt/global-align-memset.ll
new file mode 100644
index 00000000000000..344b6747135b08
--- /dev/null
+++ b/llvm/test/Transforms/GlobalOpt/global-align-memset.ll
@@ -0,0 +1,82 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals all --version 6
+; RUN: opt < %s -passes=globalopt -S | FileCheck %s
+
+; When initializing an aligned global pass the alignment to the memset call.
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+ at aligned = internal global ptr null
+ at unaligned = internal global ptr null
+
+;.
+; CHECK: @aligned.body = internal unnamed_addr global [64 x i8] undef{{$}}
+; CHECK: @unaligned.body = internal unnamed_addr global [64 x i8] undef{{$}}
+;.
+define void @init_aligned() {
+; CHECK-LABEL: define void @init_aligned() local_unnamed_addr {
+; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr @aligned.body, i8 0, i64 64, i1 false)
+; CHECK-NEXT:    ret void
+;
+  %m = call noalias align 32 ptr @aligned_calloc(i64 32, i64 64)
+  store ptr %m, ptr @aligned, align 8
+  ret void
+}
+
+define void @store_aligned(<8 x float> %v) {
+; CHECK-LABEL: define void @store_aligned(
+; CHECK-SAME: <8 x float> [[V:%.*]]) local_unnamed_addr {
+; CHECK-NEXT:    store <8 x float> [[V]], ptr @aligned.body, align 32
+; CHECK-NEXT:    ret void
+;
+  %p = load ptr, ptr @aligned, align 8
+  store <8 x float> %v, ptr %p, align 32
+  ret void
+}
+
+define <8 x float> @load_aligned() {
+; CHECK-LABEL: define <8 x float> @load_aligned() local_unnamed_addr {
+; CHECK-NEXT:    [[V:%.*]] = load <8 x float>, ptr @aligned.body, align 32
+; CHECK-NEXT:    ret <8 x float> [[V]]
+;
+  %p = load ptr, ptr @aligned, align 8
+  %v = load <8 x float>, ptr %p, align 32
+  ret <8 x float> %v
+}
+
+define void @init_unaligned() {
+; CHECK-LABEL: define void @init_unaligned() local_unnamed_addr {
+; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr @unaligned.body, i8 0, i64 64, i1 false)
+; CHECK-NEXT:    ret void
+;
+  %m = call noalias ptr @calloc(i64 1, i64 64)
+  store ptr %m, ptr @unaligned, align 8
+  ret void
+}
+
+define void @store_unaligned(i8 %c) {
+; CHECK-LABEL: define void @store_unaligned(
+; CHECK-SAME: i8 [[C:%.*]]) local_unnamed_addr {
+; CHECK-NEXT:    store i8 [[C]], ptr @unaligned.body, align 1
+; CHECK-NEXT:    ret void
+;
+  %p = load ptr, ptr @unaligned, align 8
+  store i8 %c, ptr %p, align 1
+  ret void
+}
+
+define i8 @load_unaligned() {
+; CHECK-LABEL: define i8 @load_unaligned() local_unnamed_addr {
+; CHECK-NEXT:    [[V:%.*]] = load i8, ptr @unaligned.body, align 1
+; CHECK-NEXT:    ret i8 [[V]]
+;
+  %p = load ptr, ptr @unaligned, align 8
+  %v = load i8, ptr %p, align 1
+  ret i8 %v
+}
+
+declare noalias ptr @aligned_calloc(i64, i64) allockind("alloc,zeroed,aligned") allocsize(1)
+declare noalias ptr @calloc(i64, i64) allockind("alloc,zeroed") allocsize(0,1)
+;.
+; CHECK: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(argmem: write) }
+;.

>From b6adc5fdd290f6e7613d78fae1a93b5bf5765e49 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= <omer at osa1.net>
Date: Mon, 17 Aug 2026 09:41:08 +0100
Subject: [PATCH 2/7] Fix and update tests

---
 llvm/lib/Transforms/IPO/GlobalOpt.cpp                | 12 ++++++++++--
 llvm/test/Transforms/GlobalOpt/calloc-promote.ll     |  2 +-
 llvm/test/Transforms/GlobalOpt/global-align-1.ll     |  2 +-
 llvm/test/Transforms/GlobalOpt/global-align-2.ll     |  2 +-
 .../test/Transforms/GlobalOpt/global-align-memset.ll |  4 ++--
 5 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
index 36563e25cc7f75..591050e21a6e97 100644
--- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp
+++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
@@ -929,6 +929,15 @@ OptimizeGlobalAddressOfAllocation(GlobalVariable *GV, CallInst *CI,
       UndefValue::get(GlobalType), GV->getName() + ".body", nullptr,
       GV->getThreadLocalMode());
 
+  // Alignment of the return value of the allocator call.
+  Align GVAlign = CI->getPointerAlignment(DL);
+
+  // Only specify the global alignment if it increases the preferred alignment.
+  // Otherwise leave it unset to allow other optimizations to increase it.
+  if (GVAlign > DL.getPreferredAlign(NewGV)) {
+    NewGV->setAlignment(GVAlign);
+  }
+
   // Initialize the global at the point of the original call.  Note that this
   // is a different point from the initialization referred to below for the
   // nullability handling.  Sublety: We have not proven the original global was
@@ -936,8 +945,7 @@ OptimizeGlobalAddressOfAllocation(GlobalVariable *GV, CallInst *CI,
   // of the new global as may need to re-init the storage multiple times.
   if (!isa<UndefValue>(InitVal)) {
     IRBuilder<> Builder(CI->getNextNode());
-    // TODO: Use alignment above if align!=1
-    Builder.CreateMemSet(NewGV, InitVal, AllocSize, std::nullopt);
+    Builder.CreateMemSet(NewGV, InitVal, AllocSize, NewGV->getAlign());
   }
 
   // Update users of the allocation to use the new global instead.
diff --git a/llvm/test/Transforms/GlobalOpt/calloc-promote.ll b/llvm/test/Transforms/GlobalOpt/calloc-promote.ll
index c369ed9b9fc117..d12b4a8f82cad5 100644
--- a/llvm/test/Transforms/GlobalOpt/calloc-promote.ll
+++ b/llvm/test/Transforms/GlobalOpt/calloc-promote.ll
@@ -6,7 +6,7 @@
 define signext i32 @f() local_unnamed_addr {
 ; CHECK-LABEL: @f(
 ; CHECK-NEXT:  entry:
-; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr @g.body, i8 0, i64 4, i1 false)
+; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr align 16 @g.body, i8 0, i64 4, i1 false)
 ; CHECK-NEXT:    store i16 -1, ptr @g.body, align 2
 ; CHECK-NEXT:    ret i32 0
 ;
diff --git a/llvm/test/Transforms/GlobalOpt/global-align-1.ll b/llvm/test/Transforms/GlobalOpt/global-align-1.ll
index 0e4eaa4b54f0b2..0ab7fae067c1dc 100644
--- a/llvm/test/Transforms/GlobalOpt/global-align-1.ll
+++ b/llvm/test/Transforms/GlobalOpt/global-align-1.ll
@@ -13,7 +13,7 @@ target triple = "x86_64-unknown-linux-gnu"
 ;.
 ; CHECK: @g26 = dso_local local_unnamed_addr global <4 x i32> zeroinitializer, align 16
 ; CHECK: @g5 = dso_local local_unnamed_addr global i8 0, align 1
-; CHECK: @g10.body = internal unnamed_addr global [16 x i8] undef{{$}}
+; CHECK: @g10.body = internal unnamed_addr global [16 x i8] undef, align 16
 ;.
 define dso_local i32 @main() {
 ; CHECK-LABEL: define dso_local i32 @main() local_unnamed_addr {
diff --git a/llvm/test/Transforms/GlobalOpt/global-align-2.ll b/llvm/test/Transforms/GlobalOpt/global-align-2.ll
index 5e729275426eca..9f8c6be68e4e44 100644
--- a/llvm/test/Transforms/GlobalOpt/global-align-2.ll
+++ b/llvm/test/Transforms/GlobalOpt/global-align-2.ll
@@ -9,7 +9,7 @@ target triple = "x86_64-unknown-linux-gnu"
 @g = internal global ptr null
 
 ;.
-; CHECK: @g.body = internal unnamed_addr global [64 x i8] undef{{$}}
+; CHECK: @g.body = internal unnamed_addr global [64 x i8] undef, align 64
 ;.
 define void @init() {
 ; CHECK-LABEL: define void @init() local_unnamed_addr {
diff --git a/llvm/test/Transforms/GlobalOpt/global-align-memset.ll b/llvm/test/Transforms/GlobalOpt/global-align-memset.ll
index 344b6747135b08..53716b733caada 100644
--- a/llvm/test/Transforms/GlobalOpt/global-align-memset.ll
+++ b/llvm/test/Transforms/GlobalOpt/global-align-memset.ll
@@ -10,12 +10,12 @@ target triple = "x86_64-unknown-linux-gnu"
 @unaligned = internal global ptr null
 
 ;.
-; CHECK: @aligned.body = internal unnamed_addr global [64 x i8] undef{{$}}
+; CHECK: @aligned.body = internal unnamed_addr global [64 x i8] undef, align 32
 ; CHECK: @unaligned.body = internal unnamed_addr global [64 x i8] undef{{$}}
 ;.
 define void @init_aligned() {
 ; CHECK-LABEL: define void @init_aligned() local_unnamed_addr {
-; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr @aligned.body, i8 0, i64 64, i1 false)
+; CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr align 32 @aligned.body, i8 0, i64 64, i1 false)
 ; CHECK-NEXT:    ret void
 ;
   %m = call noalias align 32 ptr @aligned_calloc(i64 32, i64 64)

>From 82bab7d28497d8dce208695bf188eb3145a765da Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= <omer at osa1.net>
Date: Sat, 3 Oct 2026 11:26:40 +0100
Subject: [PATCH 3/7] Align the global as high as necessary to not create UB

---
 llvm/lib/Transforms/IPO/GlobalOpt.cpp         | 29 +++++++++++++++++++
 .../GlobalOpt/malloc-promote-opaque-ptr.ll    | 14 ++++-----
 2 files changed, 36 insertions(+), 7 deletions(-)

diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
index 591050e21a6e97..22d069648fa8e1 100644
--- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp
+++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
@@ -932,6 +932,35 @@ OptimizeGlobalAddressOfAllocation(GlobalVariable *GV, CallInst *CI,
   // Alignment of the return value of the allocator call.
   Align GVAlign = CI->getPointerAlignment(DL);
 
+  // The allocation is in local `CI` and also stored in global `GV`. The
+  // alignment of the allocation should be at least as high as to satisfy the
+  // highest alignment of the uses of these, to avoid making the program less
+  // defined.
+  {
+    SmallPtrSet<const Value *, 4> Visited;
+    SmallVector<const Value *, 4> Worklist;
+    Worklist.push_back(CI);
+    Worklist.push_back(GV);
+
+    while (!Worklist.empty()) {
+      const Value *V = Worklist.pop_back_val();
+      if (!Visited.insert(V).second)
+        continue;
+
+      for (const Use &VUse : V->uses()) {
+        const User *U = VUse.getUser();
+        if (auto *LI = dyn_cast<LoadInst>(U)) {
+          GVAlign = std::max(GVAlign, LI->getAlign());
+        } else if (auto *SI = dyn_cast<StoreInst>(U)) {
+          GVAlign = std::max(GVAlign, SI->getAlign());
+        } else if (auto *GEPI = dyn_cast<GetElementPtrInst>(U)) {
+          Worklist.push_back(GEPI);
+          continue;
+        }
+      }
+    }
+  }
+
   // Only specify the global alignment if it increases the preferred alignment.
   // Otherwise leave it unset to allow other optimizations to increase it.
   if (GVAlign > DL.getPreferredAlign(NewGV)) {
diff --git a/llvm/test/Transforms/GlobalOpt/malloc-promote-opaque-ptr.ll b/llvm/test/Transforms/GlobalOpt/malloc-promote-opaque-ptr.ll
index 49163547098785..9fc58c51020e23 100644
--- a/llvm/test/Transforms/GlobalOpt/malloc-promote-opaque-ptr.ll
+++ b/llvm/test/Transforms/GlobalOpt/malloc-promote-opaque-ptr.ll
@@ -8,16 +8,16 @@
 declare noalias ptr @malloc(i64) allockind("alloc,uninitialized") allocsize(0)
 
 ;.
-; CHECK: @[[G1_BODY_0:[a-zA-Z0-9_$"\\.-]+]] = internal unnamed_addr global i64 undef
-; CHECK: @[[G2_BODY_0:[a-zA-Z0-9_$"\\.-]+]] = internal unnamed_addr global i32 undef
-; CHECK: @[[G2_BODY_1:[a-zA-Z0-9_$"\\.-]+]] = internal unnamed_addr global i32 undef
-; CHECK: @[[G2_BODY_2:[a-zA-Z0-9_$"\\.-]+]] = internal unnamed_addr global i32 undef
-; CHECK: @[[G3_BODY:[a-zA-Z0-9_$"\\.-]+]] = internal unnamed_addr global [8 x i8] undef
+; CHECK: @g1.body.0 = internal unnamed_addr global i64 undef, align 8
+; CHECK: @g2.body.0 = internal unnamed_addr global i32 undef, align 8
+; CHECK: @g2.body.1 = internal unnamed_addr global i32 undef, align 4
+; CHECK: @g2.body.2 = internal unnamed_addr global i32 undef, align 4
+; CHECK: @g3.body = internal unnamed_addr global [8 x i8] undef, align 8
 ;.
 define void @test_store(i64 %a, i32 %b) {
 ; CHECK-LABEL: @test_store(
 ; CHECK-NEXT:    store i64 [[A:%.*]], ptr @g1.body.0, align 8
-; CHECK-NEXT:    store i32 [[B:%.*]], ptr @g2.body.0, align 4
+; CHECK-NEXT:    store i32 [[B:%.*]], ptr @g2.body.0, align 8
 ; CHECK-NEXT:    store i32 [[B]], ptr @g2.body.1, align 4
 ; CHECK-NEXT:    store i32 [[B]], ptr @g2.body.2, align 4
 ; CHECK-NEXT:    store i64 [[A]], ptr @g3.body, align 4
@@ -55,7 +55,7 @@ define void @test_store(i64 %a, i32 %b) {
 define void @test_load() {
 ; CHECK-LABEL: @test_load(
 ; CHECK-NEXT:    [[TMP1:%.*]] = load i64, ptr @g1.body.0, align 8
-; CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr @g2.body.0, align 4
+; CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr @g2.body.0, align 8
 ; CHECK-NEXT:    [[TMP3:%.*]] = load i32, ptr @g2.body.1, align 4
 ; CHECK-NEXT:    [[TMP4:%.*]] = load i32, ptr @g2.body.2, align 4
 ; CHECK-NEXT:    [[TMP5:%.*]] = load i64, ptr @g3.body, align 4

>From 2afcd9b1a99f007dbbc4d7d2e3ea421453a4f4b1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= <omer at osa1.net>
Date: Sat, 3 Oct 2026 11:50:53 +0100
Subject: [PATCH 4/7] Only consider aligns loads of ptr from the ptr slot
 (global)

---
 llvm/lib/Transforms/IPO/GlobalOpt.cpp         | 19 +++++---
 .../Transforms/GlobalOpt/global-align-4.ll    | 48 +++++++++++++++++++
 2 files changed, 60 insertions(+), 7 deletions(-)
 create mode 100644 llvm/test/Transforms/GlobalOpt/global-align-4.ll

diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
index 22d069648fa8e1..f5ffb57d506a80 100644
--- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp
+++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
@@ -932,15 +932,22 @@ OptimizeGlobalAddressOfAllocation(GlobalVariable *GV, CallInst *CI,
   // Alignment of the return value of the allocator call.
   Align GVAlign = CI->getPointerAlignment(DL);
 
-  // The allocation is in local `CI` and also stored in global `GV`. The
-  // alignment of the allocation should be at least as high as to satisfy the
-  // highest alignment of the uses of these, to avoid making the program less
-  // defined.
+  // The allocation is in local `CI` and also stored in global `GV`.
+  // `GV` is set once with the allocation, checked by
+  // `valueIsOnlyUsedLocallyOrStoredToOneGlobal`.
+  // The global's alignment should be at least as large as to support the
+  // largest alignment of `CI`s loads and stores, and `GV`s loads' users.
+  SmallVector<Value *, 4> Guses;
+  allUsesOfLoadAndStores(GV, Guses);
+
   {
     SmallPtrSet<const Value *, 4> Visited;
     SmallVector<const Value *, 4> Worklist;
     Worklist.push_back(CI);
-    Worklist.push_back(GV);
+
+    for (Value *Guse : Guses)
+      if (isa<LoadInst>(Guse))
+        Worklist.push_back(Guse);
 
     while (!Worklist.empty()) {
       const Value *V = Worklist.pop_back_val();
@@ -989,8 +996,6 @@ OptimizeGlobalAddressOfAllocation(GlobalVariable *GV, CallInst *CI,
   bool InitBoolUsed = false;
 
   // Loop over all instruction uses of GV, processing them in turn.
-  SmallVector<Value *, 4> Guses;
-  allUsesOfLoadAndStores(GV, Guses);
   for (auto *U : Guses) {
     if (StoreInst *SI = dyn_cast<StoreInst>(U)) {
       // The global is initialized when the store to it occurs. If the stored
diff --git a/llvm/test/Transforms/GlobalOpt/global-align-4.ll b/llvm/test/Transforms/GlobalOpt/global-align-4.ll
new file mode 100644
index 00000000000000..b4c172e41439eb
--- /dev/null
+++ b/llvm/test/Transforms/GlobalOpt/global-align-4.ll
@@ -0,0 +1,48 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals all --version 6
+; RUN: opt < %s -passes=globalopt -S | FileCheck %s
+
+; Use the alignment of the accesses through the pointers loaded from the global
+; as the new global's alignment.
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+ at g = internal global ptr null
+
+;.
+; CHECK: @g.body = internal unnamed_addr global [64 x i8] undef, align 32
+;.
+define void @init() {
+; CHECK-LABEL: define void @init() local_unnamed_addr {
+; CHECK-NEXT:    ret void
+;
+  %m = call noalias ptr @malloc(i64 64)
+  store ptr %m, ptr @g, align 8
+  ret void
+}
+
+define void @store(<8 x float> %v, i8 %c) {
+; CHECK-LABEL: define void @store(
+; CHECK-SAME: <8 x float> [[V:%.*]], i8 [[C:%.*]]) local_unnamed_addr {
+; CHECK-NEXT:    store <8 x float> [[V]], ptr @g.body, align 32
+; CHECK-NEXT:    store i8 [[C]], ptr getelementptr inbounds nuw (i8, ptr @g.body, i64 3), align 1
+; CHECK-NEXT:    ret void
+;
+  %p = load ptr, ptr @g, align 8
+  store <8 x float> %v, ptr %p, align 32
+  %q = getelementptr i8, ptr %p, i64 3
+  store i8 %c, ptr %q, align 1
+  ret void
+}
+
+define <8 x float> @load() {
+; CHECK-LABEL: define <8 x float> @load() local_unnamed_addr {
+; CHECK-NEXT:    [[V:%.*]] = load <8 x float>, ptr @g.body, align 32
+; CHECK-NEXT:    ret <8 x float> [[V]]
+;
+  %p = load ptr, ptr @g, align 8
+  %v = load <8 x float>, ptr %p, align 32
+  ret <8 x float> %v
+}
+
+declare noalias ptr @malloc(i64) allockind("alloc,uninitialized") allocsize(0)

>From 65bcf11c959f04b26d963f992597b6eccf1405b7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= <omer at osa1.net>
Date: Sat, 3 Oct 2026 12:16:48 +0100
Subject: [PATCH 5/7] Fix use traversal, skip store to the original GV

---
 llvm/lib/Transforms/IPO/GlobalOpt.cpp         |  4 +-
 .../Transforms/GlobalOpt/global-align-5.ll    | 49 +++++++++++++++++++
 .../GlobalOpt/malloc-promote-opaque-ptr.ll    |  8 +--
 3 files changed, 56 insertions(+), 5 deletions(-)
 create mode 100644 llvm/test/Transforms/GlobalOpt/global-align-5.ll

diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
index f5ffb57d506a80..92c18df780ab88 100644
--- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp
+++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
@@ -959,7 +959,9 @@ OptimizeGlobalAddressOfAllocation(GlobalVariable *GV, CallInst *CI,
         if (auto *LI = dyn_cast<LoadInst>(U)) {
           GVAlign = std::max(GVAlign, LI->getAlign());
         } else if (auto *SI = dyn_cast<StoreInst>(U)) {
-          GVAlign = std::max(GVAlign, SI->getAlign());
+          // Skip the store that stores the allocation into GV.
+          if (SI->getPointerOperand()->stripPointerCasts() != GV)
+            GVAlign = std::max(GVAlign, SI->getAlign());
         } else if (auto *GEPI = dyn_cast<GetElementPtrInst>(U)) {
           Worklist.push_back(GEPI);
           continue;
diff --git a/llvm/test/Transforms/GlobalOpt/global-align-5.ll b/llvm/test/Transforms/GlobalOpt/global-align-5.ll
new file mode 100644
index 00000000000000..176b6721a24a3e
--- /dev/null
+++ b/llvm/test/Transforms/GlobalOpt/global-align-5.ll
@@ -0,0 +1,49 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals all --version 6
+; RUN: opt < %s -passes=globalopt -S | FileCheck %s
+
+; The alignment of the store of the allocation's address into the global is not
+; an access to the allocation, so it should not affect the new global's
+; alignment.
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+ at g = internal global ptr null
+
+;.
+; CHECK: @g.body = internal unnamed_addr global [8 x i8] undef
+;.
+define void @init() {
+; CHECK-LABEL: define void @init() local_unnamed_addr {
+; CHECK-NEXT:    ret void
+;
+  %m = call noalias ptr @malloc(i64 8)
+  store ptr %m, ptr @g, align 32
+  ret void
+}
+
+define void @store(i8 %c) {
+; CHECK-LABEL: define void @store(
+; CHECK-SAME: i8 [[C:%.*]]) local_unnamed_addr {
+; CHECK-NEXT:    store i8 [[C]], ptr @g.body, align 1
+; CHECK-NEXT:    ret void
+;
+  %p = load ptr, ptr @g, align 8
+  store i8 %c, ptr %p, align 1
+  ret void
+}
+
+define i8 @load(i64 %i) {
+; CHECK-LABEL: define i8 @load(
+; CHECK-SAME: i64 [[I:%.*]]) local_unnamed_addr {
+; CHECK-NEXT:    [[Q:%.*]] = getelementptr i8, ptr @g.body, i64 [[I]]
+; CHECK-NEXT:    [[V:%.*]] = load i8, ptr [[Q]], align 1
+; CHECK-NEXT:    ret i8 [[V]]
+;
+  %p = load ptr, ptr @g, align 8
+  %q = getelementptr i8, ptr %p, i64 %i
+  %v = load i8, ptr %q, align 1
+  ret i8 %v
+}
+
+declare noalias ptr @malloc(i64) allockind("alloc,uninitialized") allocsize(0)
diff --git a/llvm/test/Transforms/GlobalOpt/malloc-promote-opaque-ptr.ll b/llvm/test/Transforms/GlobalOpt/malloc-promote-opaque-ptr.ll
index 9fc58c51020e23..5650bb231aa581 100644
--- a/llvm/test/Transforms/GlobalOpt/malloc-promote-opaque-ptr.ll
+++ b/llvm/test/Transforms/GlobalOpt/malloc-promote-opaque-ptr.ll
@@ -9,15 +9,15 @@ declare noalias ptr @malloc(i64) allockind("alloc,uninitialized") allocsize(0)
 
 ;.
 ; CHECK: @g1.body.0 = internal unnamed_addr global i64 undef, align 8
-; CHECK: @g2.body.0 = internal unnamed_addr global i32 undef, align 8
+; CHECK: @g2.body.0 = internal unnamed_addr global i32 undef, align 4
 ; CHECK: @g2.body.1 = internal unnamed_addr global i32 undef, align 4
 ; CHECK: @g2.body.2 = internal unnamed_addr global i32 undef, align 4
-; CHECK: @g3.body = internal unnamed_addr global [8 x i8] undef, align 8
+; CHECK: @g3.body = internal unnamed_addr global [8 x i8] undef, align 4
 ;.
 define void @test_store(i64 %a, i32 %b) {
 ; CHECK-LABEL: @test_store(
 ; CHECK-NEXT:    store i64 [[A:%.*]], ptr @g1.body.0, align 8
-; CHECK-NEXT:    store i32 [[B:%.*]], ptr @g2.body.0, align 8
+; CHECK-NEXT:    store i32 [[B:%.*]], ptr @g2.body.0, align 4
 ; CHECK-NEXT:    store i32 [[B]], ptr @g2.body.1, align 4
 ; CHECK-NEXT:    store i32 [[B]], ptr @g2.body.2, align 4
 ; CHECK-NEXT:    store i64 [[A]], ptr @g3.body, align 4
@@ -55,7 +55,7 @@ define void @test_store(i64 %a, i32 %b) {
 define void @test_load() {
 ; CHECK-LABEL: @test_load(
 ; CHECK-NEXT:    [[TMP1:%.*]] = load i64, ptr @g1.body.0, align 8
-; CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr @g2.body.0, align 8
+; CHECK-NEXT:    [[TMP2:%.*]] = load i32, ptr @g2.body.0, align 4
 ; CHECK-NEXT:    [[TMP3:%.*]] = load i32, ptr @g2.body.1, align 4
 ; CHECK-NEXT:    [[TMP4:%.*]] = load i32, ptr @g2.body.2, align 4
 ; CHECK-NEXT:    [[TMP5:%.*]] = load i64, ptr @g3.body, align 4

>From fedfc102889150729b36425f399fa3101bcb6156 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= <omer at osa1.net>
Date: Sat, 3 Oct 2026 12:32:05 +0100
Subject: [PATCH 6/7] Add more cursed tests

---
 .../Transforms/GlobalOpt/global-align-6.ll    | 51 ++++++++++++++++++
 .../Transforms/GlobalOpt/global-align-7.ll    | 54 +++++++++++++++++++
 2 files changed, 105 insertions(+)
 create mode 100644 llvm/test/Transforms/GlobalOpt/global-align-6.ll
 create mode 100644 llvm/test/Transforms/GlobalOpt/global-align-7.ll

diff --git a/llvm/test/Transforms/GlobalOpt/global-align-6.ll b/llvm/test/Transforms/GlobalOpt/global-align-6.ll
new file mode 100644
index 00000000000000..f1b8f29fa9ce6a
--- /dev/null
+++ b/llvm/test/Transforms/GlobalOpt/global-align-6.ll
@@ -0,0 +1,51 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals all --version 6
+; RUN: opt < %s -passes=globalopt -S | FileCheck %s
+
+; An access at a constant offset from the allocation only requires the new
+; global to be aligned to the common alignment of the access and the offset.
+; FIXME: The new global is aligned to 16, which makes the store at offset 8
+; misaligned and UB.
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+ at g = internal global ptr null
+
+;.
+; CHECK: @g.body = internal unnamed_addr global [16 x i8] undef, align 16
+;.
+define void @init() {
+; CHECK-LABEL: define void @init() local_unnamed_addr {
+; CHECK-NEXT:    ret void
+;
+  %m = call noalias ptr @malloc(i64 16)
+  store ptr %m, ptr @g, align 8
+  ret void
+}
+
+define void @store_offset(i64 %v) {
+; CHECK-LABEL: define void @store_offset(
+; CHECK-SAME: i64 [[V:%.*]]) local_unnamed_addr {
+; CHECK-NEXT:    store i64 [[V]], ptr getelementptr inbounds nuw (i8, ptr @g.body, i64 8), align 16
+; CHECK-NEXT:    ret void
+;
+  %p = load ptr, ptr @g, align 8
+  %q = getelementptr i8, ptr %p, i64 8
+  store i64 %v, ptr %q, align 16
+  ret void
+}
+
+define i8 @load(i64 %i) {
+; CHECK-LABEL: define i8 @load(
+; CHECK-SAME: i64 [[I:%.*]]) local_unnamed_addr {
+; CHECK-NEXT:    [[Q:%.*]] = getelementptr i8, ptr @g.body, i64 [[I]]
+; CHECK-NEXT:    [[V:%.*]] = load i8, ptr [[Q]], align 1
+; CHECK-NEXT:    ret i8 [[V]]
+;
+  %p = load ptr, ptr @g, align 8
+  %q = getelementptr i8, ptr %p, i64 %i
+  %v = load i8, ptr %q, align 1
+  ret i8 %v
+}
+
+declare noalias ptr @malloc(i64) allockind("alloc,uninitialized") allocsize(0)
diff --git a/llvm/test/Transforms/GlobalOpt/global-align-7.ll b/llvm/test/Transforms/GlobalOpt/global-align-7.ll
new file mode 100644
index 00000000000000..f038fa52b7a3af
--- /dev/null
+++ b/llvm/test/Transforms/GlobalOpt/global-align-7.ll
@@ -0,0 +1,54 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals all --version 6
+; RUN: opt < %s -passes=globalopt -S | FileCheck %s
+
+; An access through a variable index is only known to be at an offset that is a
+; multiple of the index stride. If the access requires a larger alignment than
+; that, no alignment of the new global can satisfy it, so the allocation should
+; not be replaced with a global.
+; FIXME: The allocation is currently replaced with a global aligned to 16, which
+; makes the store UB when the index is odd.
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+ at g = internal global ptr null
+
+;.
+; CHECK: @g.body = internal unnamed_addr global [16 x i8] undef, align 16
+;.
+define void @init() {
+; CHECK-LABEL: define void @init() local_unnamed_addr {
+; CHECK-NEXT:    ret void
+;
+  %m = call noalias ptr @malloc(i64 16)
+  store ptr %m, ptr @g, align 8
+  ret void
+}
+
+define void @store(i64 %i, i64 %v) {
+; CHECK-LABEL: define void @store(
+; CHECK-SAME: i64 [[I:%.*]], i64 [[V:%.*]]) local_unnamed_addr {
+; CHECK-NEXT:    [[Q:%.*]] = getelementptr i64, ptr @g.body, i64 [[I]]
+; CHECK-NEXT:    store i64 [[V]], ptr [[Q]], align 16
+; CHECK-NEXT:    ret void
+;
+  %p = load ptr, ptr @g, align 8
+  %q = getelementptr i64, ptr %p, i64 %i
+  store i64 %v, ptr %q, align 16
+  ret void
+}
+
+define i8 @load(i64 %i) {
+; CHECK-LABEL: define i8 @load(
+; CHECK-SAME: i64 [[I:%.*]]) local_unnamed_addr {
+; CHECK-NEXT:    [[Q:%.*]] = getelementptr i8, ptr @g.body, i64 [[I]]
+; CHECK-NEXT:    [[V:%.*]] = load i8, ptr [[Q]], align 1
+; CHECK-NEXT:    ret i8 [[V]]
+;
+  %p = load ptr, ptr @g, align 8
+  %q = getelementptr i8, ptr %p, i64 %i
+  %v = load i8, ptr %q, align 1
+  ret i8 %v
+}
+
+declare noalias ptr @malloc(i64) allockind("alloc,uninitialized") allocsize(0)

>From f4bb10f57f48e7f57b1b656a20ae50423cf4c651 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= <omer at osa1.net>
Date: Sat, 3 Oct 2026 12:42:01 +0100
Subject: [PATCH 7/7] Another cursed case

---
 .../Transforms/GlobalOpt/global-align-8.ll    | 84 +++++++++++++++++++
 1 file changed, 84 insertions(+)
 create mode 100644 llvm/test/Transforms/GlobalOpt/global-align-8.ll

diff --git a/llvm/test/Transforms/GlobalOpt/global-align-8.ll b/llvm/test/Transforms/GlobalOpt/global-align-8.ll
new file mode 100644
index 00000000000000..6dc15f727351dc
--- /dev/null
+++ b/llvm/test/Transforms/GlobalOpt/global-align-8.ll
@@ -0,0 +1,84 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals all --version 6
+; RUN: opt < %s -passes=globalopt -S | FileCheck %s
+
+; Accesses through PHIs and address space casts of the pointers loaded from the
+; global should also be considered for the new global's alignment.
+; FIXME: The new globals are not aligned to 16, which makes the stores UB.
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+ at phi = internal global ptr null
+ at ascast = internal global ptr null
+
+;.
+; CHECK: @phi.body = internal unnamed_addr global [16 x i8] undef
+; CHECK: @ascast.body = internal unnamed_addr global [16 x i8] undef
+;.
+define void @init() {
+; CHECK-LABEL: define void @init() local_unnamed_addr {
+; CHECK-NEXT:    ret void
+;
+  %m1 = call noalias ptr @malloc(i64 16)
+  store ptr %m1, ptr @phi, align 8
+  %m2 = call noalias ptr @malloc(i64 16)
+  store ptr %m2, ptr @ascast, align 8
+  ret void
+}
+
+define void @store_phi(<4 x i32> %v, i1 %c) {
+; CHECK-LABEL: define void @store_phi(
+; CHECK-SAME: <4 x i32> [[V:%.*]], i1 [[C:%.*]]) local_unnamed_addr {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    br i1 [[C]], label %[[THEN:.*]], label %[[END:.*]]
+; CHECK:       [[THEN]]:
+; CHECK-NEXT:    br label %[[END]]
+; CHECK:       [[END]]:
+; CHECK-NEXT:    store <4 x i32> [[V]], ptr @phi.body, align 16
+; CHECK-NEXT:    ret void
+;
+entry:
+  %p1 = load ptr, ptr @phi, align 8
+  br i1 %c, label %then, label %end
+then:
+  %p2 = load ptr, ptr @phi, align 8
+  br label %end
+end:
+  %p = phi ptr [ %p1, %entry ], [ %p2, %then ]
+  store <4 x i32> %v, ptr %p, align 16
+  ret void
+}
+
+define void @store_ascast(<4 x i32> %v) {
+; CHECK-LABEL: define void @store_ascast(
+; CHECK-SAME: <4 x i32> [[V:%.*]]) local_unnamed_addr {
+; CHECK-NEXT:    store <4 x i32> [[V]], ptr addrspace(1) addrspacecast (ptr @ascast.body to ptr addrspace(1)), align 16
+; CHECK-NEXT:    ret void
+;
+  %p = load ptr, ptr @ascast, align 8
+  %q = addrspacecast ptr %p to ptr addrspace(1)
+  store <4 x i32> %v, ptr addrspace(1) %q, align 16
+  ret void
+}
+
+define i8 @load(i64 %i) {
+; CHECK-LABEL: define i8 @load(
+; CHECK-SAME: i64 [[I:%.*]]) local_unnamed_addr {
+; CHECK-NEXT:    [[Q1:%.*]] = getelementptr i8, ptr @phi.body, i64 [[I]]
+; CHECK-NEXT:    [[V1:%.*]] = load i8, ptr [[Q1]], align 1
+; CHECK-NEXT:    [[Q2:%.*]] = getelementptr i8, ptr @ascast.body, i64 [[I]]
+; CHECK-NEXT:    [[V2:%.*]] = load i8, ptr [[Q2]], align 1
+; CHECK-NEXT:    [[V:%.*]] = add i8 [[V1]], [[V2]]
+; CHECK-NEXT:    ret i8 [[V]]
+;
+  %p1 = load ptr, ptr @phi, align 8
+  %q1 = getelementptr i8, ptr %p1, i64 %i
+  %v1 = load i8, ptr %q1, align 1
+  %p2 = load ptr, ptr @ascast, align 8
+  %q2 = getelementptr i8, ptr %p2, i64 %i
+  %v2 = load i8, ptr %q2, align 1
+  %v = add i8 %v1, %v2
+  ret i8 %v
+}
+
+declare noalias ptr @malloc(i64) allockind("alloc,uninitialized") allocsize(0)



More information about the llvm-commits mailing list