Skip to content

Fix issues with GlobalMerge on Mach-O. #110046

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 28 additions & 6 deletions llvm/lib/CodeGen/GlobalMerge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -578,12 +578,18 @@ bool GlobalMergeImpl::doMerge(const SmallVectorImpl<GlobalVariable *> &Globals,
Globals[k]->replaceAllUsesWith(GEP);
Globals[k]->eraseFromParent();

// When the linkage is not internal we must emit an alias for the original
// variable name as it may be accessed from another object. On non-Mach-O
// we can also emit an alias for internal linkage as it's safe to do so.
// It's not safe on Mach-O as the alias (and thus the portion of the
// MergedGlobals variable) may be dead stripped at link time.
if (Linkage != GlobalValue::InternalLinkage || !IsMachO) {
// Emit an alias for the original variable name. This is necessary for an
// external symbol, as it may be accessed from another object. For
// internal symbols, it's not strictly required, but it's useful.
//
// This _should_ also work on Mach-O ever since '.alt_entry' support was
// added in 2016. Unfortunately, there's a bug in ld-prime (present at
// least from Xcode 15.0 through Xcode 16.0), in which -dead_strip doesn't
// always honor alt_entry. To workaround this issue, we don't emit aliases
// on Mach-O. Except, we _must_ do so for external symbols. That means
// MergeExternal is broken with that linker. (That option is currently off
// by default on MachO).
if (!IsMachO || Linkage == GlobalValue::ExternalLinkage) {
GlobalAlias *GA = GlobalAlias::create(Tys[StructIdxs[idx]], AddrSpace,
Linkage, Name, GEP, &M);
GA->setVisibility(Visibility);
Expand Down Expand Up @@ -640,6 +646,18 @@ void GlobalMergeImpl::setMustKeepGlobalVariables(Module &M) {
}
}

// This function returns true if the given data Section name has custom
// subsection-splitting semantics in Mach-O (such as splitting by a fixed size)
//
// See also ObjFile::parseSections and getRecordSize in lld/MachO/InputFiles.cpp
static bool isSpecialMachOSection(StringRef Section) {
// Uses starts_with, since section attributes can appear at the end of the
// name.
return Section.starts_with("__DATA,__cfstring") ||
Section.starts_with("__DATA,__objc_classrefs") ||
Section.starts_with("__DATA,__objc_selrefs");
}

bool GlobalMergeImpl::run(Module &M) {
if (!EnableGlobalMerge)
return false;
Expand Down Expand Up @@ -678,6 +696,10 @@ bool GlobalMergeImpl::run(Module &M) {
unsigned AddressSpace = PT->getAddressSpace();
StringRef Section = GV.getSection();

// On Mach-O, some section names have special semantics. Don't merge these.
if (IsMachO && isSpecialMachOSection(Section))
continue;

// Ignore all 'special' globals.
if (GV.getName().starts_with("llvm.") || GV.getName().starts_with(".llvm."))
continue;
Expand Down
31 changes: 31 additions & 0 deletions llvm/test/Transforms/GlobalMerge/macho-sections.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
; RUN: opt -global-merge -global-merge-max-offset=100 -S -o - %s | FileCheck %s
; RUN: opt -passes='global-merge<max-offset=100>' -S -o - %s | FileCheck %s

;; Check that we do _not_ merge globals which are in certain special
;; sections under Mach-O.

target datalayout = "e-p:64:64"
target triple = "x86_64-apple-macos11"

; CHECK: @cfstring1 = private global i32 1, section "__DATA,__cfstring"
@cfstring1 = private global i32 1, section "__DATA,__cfstring"
; CHECK: @cfstring2 = private global i32 2, section "__DATA,__cfstring"
@cfstring2 = private global i32 2, section "__DATA,__cfstring"
; CHECK: @objcclassrefs1 = private global i32 3, section "__DATA,__objc_classrefs,regular,no_dead_strip"
@objcclassrefs1 = private global i32 3, section "__DATA,__objc_classrefs,regular,no_dead_strip"
; CHECK: @objcclassrefs2 = private global i32 4, section "__DATA,__objc_classrefs,regular,no_dead_strip"
@objcclassrefs2 = private global i32 4, section "__DATA,__objc_classrefs,regular,no_dead_strip"
; CHECK: @objcselrefs1 = private global i32 5, section "__DATA,__objc_selrefs,literal_pointers,no_dead_strip"
@objcselrefs1 = private global i32 5, section "__DATA,__objc_selrefs,literal_pointers,no_dead_strip"
; CHECK: @objcselrefs2 = private global i32 6, section "__DATA,__objc_selrefs,literal_pointers,no_dead_strip"
@objcselrefs2 = private global i32 6, section "__DATA,__objc_selrefs,literal_pointers,no_dead_strip"

define void @use() {
load ptr, ptr @cfstring1
load ptr, ptr @cfstring2
load ptr, ptr @objcclassrefs1
load ptr, ptr @objcclassrefs2
load ptr, ptr @objcselrefs1
load ptr, ptr @objcselrefs2
ret void
}
28 changes: 28 additions & 0 deletions llvm/test/Transforms/GlobalMerge/macho-symbols.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
; RUN: opt -global-merge -global-merge-max-offset=100 -S -o - %s | FileCheck %s
; RUN: opt -passes='global-merge<max-offset=100>' -S -o - %s | FileCheck %s

;; For Mach-O, we do not expect any alias symbols to be created for
;; internal/private symbols by GlobalMerge.

target datalayout = "e-p:64:64"
target triple = "x86_64-apple-macos11"

@a = private global i32 1
@b = private global i32 2
@c = internal global i32 3
@d = internal global i32 4

; CHECK: @_MergedGlobals = internal global <{ i32, i32, i32, i32 }> <{ i32 1, i32 2, i32 3, i32 4 }>, align 4
; CHECK-NOT: alias

define void @use() {
; CHECK: load i32, ptr @_MergedGlobals,
%x = load i32, ptr @a
; CHECK: load i32, ptr getelementptr inbounds (<{ i32, i32, i32, i32 }>, ptr @_MergedGlobals, i32 0, i32 1)
%y = load i32, ptr @b
; CHECK: load i32, ptr getelementptr inbounds (<{ i32, i32, i32, i32 }>, ptr @_MergedGlobals, i32 0, i32 2)
%z1 = load i32, ptr @c
; CHECK: load i32, ptr getelementptr inbounds (<{ i32, i32, i32, i32 }>, ptr @_MergedGlobals, i32 0, i32 3)
%z2 = load i32, ptr @d
ret void
}
Loading