1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-24 03:33:20 +01:00

ARM64: use GOT for weak symbols & PIC.

Weak symbols cannot use the small code model's usual ADRP sequences since the
instruction simply may not be able to encode a value of 0.

This redirects them to use the GOT, which hopefully linkers are able to cope
with even in the static relocation model.

llvm-svn: 205426
This commit is contained in:
Tim Northover 2014-04-02 14:39:11 +00:00
parent 01192b285d
commit 0688295f2f
3 changed files with 129 additions and 7 deletions

View File

@ -50,16 +50,33 @@ ARM64Subtarget::ClassifyGlobalReference(const GlobalValue *GV,
if (GV->isDeclaration() && !GV->isMaterializable())
isDecl = true;
// If symbol visibility is hidden, the extra load is not needed if
// the symbol is definitely defined in the current translation unit.
if (TM.getRelocationModel() != Reloc::Static && GV->hasDefaultVisibility() &&
(isDecl || GV->isWeakForLinker()))
return ARM64II::MO_GOT;
// MachO large model always goes via a GOT, simply to get a single 8-byte
// absolute relocation on all global addresses.
if (TM.getCodeModel() == CodeModel::Large && isTargetMachO())
return ARM64II::MO_GOT;
// FIXME: this will fail on static ELF for weak symbols.
// The small code mode's direct accesses use ADRP, which cannot necessarily
// produce the value 0 (if the code is above 4GB). Therefore they must use the
// GOT.
if (TM.getCodeModel() == CodeModel::Small && GV->isWeakForLinker() && isDecl)
return ARM64II::MO_GOT;
// If symbol visibility is hidden, the extra load is not needed if
// the symbol is definitely defined in the current translation unit.
// The handling of non-hidden symbols in PIC mode is rather target-dependent:
// + On MachO, if the symbol is defined in this module the GOT can be
// skipped.
// + On ELF, the R_AARCH64_COPY relocation means that even symbols actually
// defined could end up in unexpected places. Use a GOT.
if (TM.getRelocationModel() != Reloc::Static && GV->hasDefaultVisibility()) {
if (isTargetMachO())
return (isDecl || GV->isWeakForLinker()) ? ARM64II::MO_GOT
: ARM64II::MO_NO_FLAG;
else
return ARM64II::MO_GOT;
}
return ARM64II::MO_NO_FLAG;
}

View File

@ -0,0 +1,54 @@
; RUN: llc -mtriple=arm64-none-linux-gnu -verify-machineinstrs -relocation-model=pic %s -o - | FileCheck %s
@var = global i32 0
define i32 @get_globalvar() {
; CHECK-LABEL: get_globalvar:
%val = load i32* @var
; CHECK: adrp x[[GOTHI:[0-9]+]], :got:var
; CHECK: ldr x[[GOTLOC:[0-9]+]], [x[[GOTHI]], :got_lo12:var]
; CHECK: ldr w0, [x[[GOTLOC]]]
ret i32 %val
}
define i32* @get_globalvaraddr() {
; CHECK-LABEL: get_globalvaraddr:
%val = load i32* @var
; CHECK: adrp x[[GOTHI:[0-9]+]], :got:var
; CHECK: ldr x0, [x[[GOTHI]], :got_lo12:var]
ret i32* @var
}
@hiddenvar = hidden global i32 0
define i32 @get_hiddenvar() {
; CHECK-LABEL: get_hiddenvar:
%val = load i32* @hiddenvar
; CHECK: adrp x[[HI:[0-9]+]], hiddenvar
; CHECK: ldr w0, [x[[HI]], :lo12:hiddenvar]
ret i32 %val
}
define i32* @get_hiddenvaraddr() {
; CHECK-LABEL: get_hiddenvaraddr:
%val = load i32* @hiddenvar
; CHECK: adrp [[HI:x[0-9]+]], hiddenvar
; CHECK: add x0, [[HI]], :lo12:hiddenvar
ret i32* @hiddenvar
}
define void()* @get_func() {
; CHECK-LABEL: get_func:
ret void()* bitcast(void()*()* @get_func to void()*)
; CHECK: adrp x[[GOTHI:[0-9]+]], :got:get_func
; CHECK: ldr x0, [x[[GOTHI]], :got_lo12:get_func]
}

View File

@ -0,0 +1,51 @@
; RUN: llc -mtriple=arm64-none-linux-gnu -o - < %s | FileCheck %s
; RUN: llc -mtriple=arm64-none-linux-gnu -code-model=large -o - < %s | FileCheck --check-prefix=CHECK-LARGE %s
declare extern_weak i32 @var()
define i32()* @foo() {
; The usual ADRP/ADD pair can't be used for a weak reference because it must
; evaluate to 0 if the symbol is undefined. We use a litpool entry.
ret i32()* @var
; CHECK: adrp x[[VAR:[0-9]+]], :got:var
; CHECK: ldr x0, [x[[VAR]], :got_lo12:var]
; In the large model, the usual relocations are absolute and can
; materialise 0.
; CHECK-LARGE: movz x0, #:abs_g3:var
; CHECK-LARGE: movk x0, #:abs_g2_nc:var
; CHECK-LARGE: movk x0, #:abs_g1_nc:var
; CHECK-LARGE: movk x0, #:abs_g0_nc:var
}
@arr_var = extern_weak global [10 x i32]
define i32* @bar() {
%addr = getelementptr [10 x i32]* @arr_var, i32 0, i32 5
; CHECK: adrp x[[ARR_VAR_HI:[0-9]+]], :got:arr_var
; CHECK: ldr [[ARR_VAR:x[0-9]+]], [x[[ARR_VAR_HI]], :got_lo12:arr_var]
; CHECK: add x0, [[ARR_VAR]], #20
ret i32* %addr
; In the large model, the usual relocations are absolute and can
; materialise 0.
; CHECK-LARGE: movz [[ARR_VAR:x[0-9]+]], #:abs_g3:arr_var
; CHECK-LARGE: movk [[ARR_VAR]], #:abs_g2_nc:arr_var
; CHECK-LARGE: movk [[ARR_VAR]], #:abs_g1_nc:arr_var
; CHECK-LARGE: movk [[ARR_VAR]], #:abs_g0_nc:arr_var
}
@defined_weak_var = internal unnamed_addr global i32 0
define i32* @wibble() {
ret i32* @defined_weak_var
; CHECK: adrp [[BASE:x[0-9]+]], defined_weak_var
; CHECK: add x0, [[BASE]], :lo12:defined_weak_var
; CHECK-LARGE: movz x0, #:abs_g3:defined_weak_var
; CHECK-LARGE: movk x0, #:abs_g2_nc:defined_weak_var
; CHECK-LARGE: movk x0, #:abs_g1_nc:defined_weak_var
; CHECK-LARGE: movk x0, #:abs_g0_nc:defined_weak_var
}