From 6584d946102f0e52e7365fa8d05922b6dee29e70 Mon Sep 17 00:00:00 2001 From: Tim Northover Date: Wed, 2 Apr 2014 14:39:11 +0000 Subject: [PATCH] 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. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@205426 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Target/ARM64/ARM64Subtarget.cpp | 31 +++++++++++++---- test/CodeGen/ARM64/basic-pic.ll | 54 +++++++++++++++++++++++++++++ test/CodeGen/ARM64/extern-weak.ll | 51 +++++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 7 deletions(-) create mode 100644 test/CodeGen/ARM64/basic-pic.ll create mode 100644 test/CodeGen/ARM64/extern-weak.ll diff --git a/lib/Target/ARM64/ARM64Subtarget.cpp b/lib/Target/ARM64/ARM64Subtarget.cpp index c28c26b0eaf..14b54443dc9 100644 --- a/lib/Target/ARM64/ARM64Subtarget.cpp +++ b/lib/Target/ARM64/ARM64Subtarget.cpp @@ -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; } diff --git a/test/CodeGen/ARM64/basic-pic.ll b/test/CodeGen/ARM64/basic-pic.ll new file mode 100644 index 00000000000..9fdb1e91385 --- /dev/null +++ b/test/CodeGen/ARM64/basic-pic.ll @@ -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] +} diff --git a/test/CodeGen/ARM64/extern-weak.ll b/test/CodeGen/ARM64/extern-weak.ll new file mode 100644 index 00000000000..a239403befa --- /dev/null +++ b/test/CodeGen/ARM64/extern-weak.ll @@ -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 +}