if (DstSize == 128 && !Query.Types[0].isVector())
return false; // Extending to a scalar s128 needs narrowing.
-
+
// Make sure that we have something that will fit in a register, and
// make sure it's a power of 2.
if (DstSize < 8 || DstSize > 128 || !isPowerOf2_32(DstSize))
// Set the regclass on the dest reg too.
MRI.setRegClass(ADRP.getReg(0), &AArch64::GPR64RegClass);
+ // MO_TAGGED on the page indicates a tagged address. Set the tag now. We do so
+ // by creating a MOVK that sets bits 48-63 of the register to (global address
+ // + 0x100000000 - PC) >> 48. The additional 0x100000000 offset here is to
+ // prevent an incorrect tag being generated during relocation when the the
+ // global appears before the code section. Without the offset, a global at
+ // `0x0f00'0000'0000'1000` (i.e. at `0x1000` with tag `0xf`) that's referenced
+ // by code at `0x2000` would result in `0x0f00'0000'0000'1000 - 0x2000 =
+ // 0x0eff'ffff'ffff'f000`, meaning the tag would be incorrectly set to `0xe`
+ // instead of `0xf`.
+ // This assumes that we're in the small code model so we can assume a binary
+ // size of <= 4GB, which makes the untagged PC relative offset positive. The
+ // binary must also be loaded into address range [0, 2^48). Both of these
+ // properties need to be ensured at runtime when using tagged addresses.
+ if (OpFlags & AArch64II::MO_TAGGED) {
+ ADRP = MIRBuilder.buildInstr(AArch64::MOVKXi, {LLT::pointer(0, 64)}, {ADRP})
+ .addGlobalAddress(GV, 0x100000000,
+ AArch64II::MO_PREL | AArch64II::MO_G3)
+ .addImm(48);
+ MRI.setRegClass(ADRP.getReg(0), &AArch64::GPR64RegClass);
+ }
+
MIRBuilder.buildInstr(AArch64::G_ADD_LOW, {DstReg}, {ADRP})
.addGlobalAddress(GV, 0,
OpFlags | AArch64II::MO_PAGEOFF | AArch64II::MO_NC);
-; RUN: llc < %s | FileCheck %s
+; RUN: llc --relocation-model=static < %s \
+; RUN: | FileCheck %s --check-prefixes=CHECK-STATIC,CHECK-SELECTIONDAGISEL
+; RUN: llc --relocation-model=pic < %s \
+; RUN: | FileCheck %s --check-prefix=CHECK-PIC
+
+; Ensure that GlobalISel lowers correctly. GlobalISel is the default ISel for
+; -O0 on aarch64. GlobalISel lowers the instruction sequence in the static
+; relocation model different to SelectionDAGISel. GlobalISel does the lowering
+; of AddLow *after* legalization, and thus doesn't differentiate between
+; address-taken-only vs. address-taken-for-loadstore. Hence, we generate a movk
+; instruction for load/store instructions as well with GlobalISel. GlobalISel
+; also doesn't have the scaffolding to correctly check the bounds of the global
+; offset, and cannot fold the lo12 bits into the load/store. Neither of these
+; things are a problem as GlobalISel is only used by default at -O0, so we don't
+; mind the code size and performance increase.
+
+; RUN: llc --aarch64-enable-global-isel-at-O=0 -O0 < %s \
+; RUN: | FileCheck %s --check-prefixes=CHECK-STATIC,CHECK-GLOBALISEL
+; RUN: llc --aarch64-enable-global-isel-at-O=0 -O0 --relocation-model=pic < %s \
+; RUN: | FileCheck %s --check-prefix=CHECK-PIC
target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
target triple = "aarch64-unknown-linux-android"
-@global = external hidden global i32
+@global = external global i32
declare void @func()
define i32* @global_addr() #0 {
- ; CHECK: global_addr:
- ; CHECK: adrp x0, :pg_hi21_nc:global
- ; CHECK: movk x0, #:prel_g3:global+4294967296
- ; CHECK: add x0, x0, :lo12:global
+ ; Static relocation model has common codegen between SelectionDAGISel and
+ ; GlobalISel when the address-taken of a global isn't folded into a load or
+ ; store instruction.
+ ; CHECK-STATIC: global_addr:
+ ; CHECK-STATIC: adrp [[REG:x[0-9]+]], :pg_hi21_nc:global
+ ; CHECK-STATIC: movk [[REG]], #:prel_g3:global+4294967296
+ ; CHECK-STATIC: add x0, [[REG]], :lo12:global
+ ; CHECK-STATIC: ret
+
+ ; CHECK-PIC: global_addr:
+ ; CHECK-PIC: adrp [[REG:x[0-9]+]], :got:global
+ ; CHECK-PIC: ldr x0, {{\[}}[[REG]], :got_lo12:global]
+ ; CHECK-PIC: ret
+
ret i32* @global
}
define i32 @global_load() #0 {
- ; CHECK: global_load:
- ; CHECK: adrp x8, :pg_hi21_nc:global
- ; CHECK: ldr w0, [x8, :lo12:global]
+ ; CHECK-SELECTIONDAGISEL: global_load:
+ ; CHECK-SELECTIONDAGISEL: adrp [[REG:x[0-9]+]], :pg_hi21_nc:global
+ ; CHECK-SELECTIONDAGISEL: ldr w0, {{\[}}[[REG]], :lo12:global{{\]}}
+ ; CHECK-SELECTIONDAGISEL: ret
+
+ ; CHECK-GLOBALISEL: global_load:
+ ; CHECK-GLOBALISEL: adrp [[REG:x[0-9]+]], :pg_hi21_nc:global
+ ; CHECK-GLOBALISEL: movk [[REG]], #:prel_g3:global+4294967296
+ ; CHECK-GLOBALISEL: add [[REG]], [[REG]], :lo12:global
+ ; CHECK-GLOBALISEL: ldr w0, {{\[}}[[REG]]{{\]}}
+ ; CHECK-GLOBALISEL: ret
+
+ ; CHECK-PIC: global_load:
+ ; CHECK-PIC: adrp [[REG:x[0-9]+]], :got:global
+ ; CHECK-PIC: ldr [[REG]], {{\[}}[[REG]], :got_lo12:global]
+ ; CHECK-PIC: ldr w0, {{\[}}[[REG]]{{\]}}
+ ; CHECK-PIC: ret
+
%load = load i32, i32* @global
ret i32 %load
}
+define void @global_store() #0 {
+ ; CHECK-SELECTIONDAGISEL: global_store:
+ ; CHECK-SELECTIONDAGISEL: adrp [[REG:x[0-9]+]], :pg_hi21_nc:global
+ ; CHECK-SELECTIONDAGISEL: str wzr, {{\[}}[[REG]], :lo12:global{{\]}}
+ ; CHECK-SELECTIONDAGISEL: ret
+
+ ; CHECK-GLOBALISEL: global_store:
+ ; CHECK-GLOBALISEL: adrp [[REG:x[0-9]+]], :pg_hi21_nc:global
+ ; CHECK-GLOBALISEL: movk [[REG]], #:prel_g3:global+4294967296
+ ; CHECK-GLOBALISEL: add [[REG]], [[REG]], :lo12:global
+ ; CHECK-GLOBALISEL: str wzr, {{\[}}[[REG]]{{\]}}
+ ; CHECK-GLOBALISEL: ret
+
+ ; CHECK-PIC: global_store:
+ ; CHECK-PIC: adrp [[REG:x[0-9]+]], :got:global
+ ; CHECK-PIC: ldr [[REG]], {{\[}}[[REG]], :got_lo12:global]
+ ; CHECK-PIC: str wzr, {{\[}}[[REG]]{{\]}}
+ ; CHECK-PIC: ret
+
+ store i32 0, i32* @global
+ ret void
+}
+
define void ()* @func_addr() #0 {
- ; CHECK: func_addr:
- ; CHECK: adrp x0, func
- ; CHECK: add x0, x0, :lo12:func
+ ; CHECK-STATIC: func_addr:
+ ; CHECK-STATIC: adrp [[REG:x[0-9]+]], func
+ ; CHECK-STATIC: add x0, [[REG]], :lo12:func
+ ; CHECK-STATIC: ret
+
+ ; CHECK-PIC: func_addr:
+ ; CHECK-PIC: adrp [[REG:x[0-9]+]], :got:func
+ ; CHECK-PIC: ldr x0, {{\[}}[[REG]], :got_lo12:func]
+ ; CHECK-PIC: ret
+
ret void ()* @func
}