diff --git a/llvm/lib/Target/AArch64/AArch64ExpandPseudoInsts.cpp b/llvm/lib/Target/AArch64/AArch64ExpandPseudoInsts.cpp --- a/llvm/lib/Target/AArch64/AArch64ExpandPseudoInsts.cpp +++ b/llvm/lib/Target/AArch64/AArch64ExpandPseudoInsts.cpp @@ -17,6 +17,7 @@ #include "AArch64InstrInfo.h" #include "AArch64MachineFunctionInfo.h" #include "AArch64Subtarget.h" +#include "AArch64Tagging.h" #include "MCTargetDesc/AArch64AddressingModes.h" #include "Utils/AArch64BaseInfo.h" #include "llvm/ADT/DenseMap.h" @@ -1260,13 +1261,9 @@ // 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. - auto Tag = MI.getOperand(1); - Tag.setTargetFlags(AArch64II::MO_PREL | AArch64II::MO_G3); - Tag.setOffset(0x100000000); - BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(AArch64::MOVKXi), DstReg) - .addReg(DstReg) - .add(Tag) - .addImm(48); + PropagateTag(BuildMI(MBB, MBBI, MI.getDebugLoc(), + TII->get(AArch64::MOVKXi), DstReg), + DstReg, MI.getOperand(1)); } MachineInstrBuilder MIB2 = diff --git a/llvm/lib/Target/AArch64/AArch64FastISel.cpp b/llvm/lib/Target/AArch64/AArch64FastISel.cpp --- a/llvm/lib/Target/AArch64/AArch64FastISel.cpp +++ b/llvm/lib/Target/AArch64/AArch64FastISel.cpp @@ -17,6 +17,7 @@ #include "AArch64MachineFunctionInfo.h" #include "AArch64RegisterInfo.h" #include "AArch64Subtarget.h" +#include "AArch64Tagging.h" #include "MCTargetDesc/AArch64AddressingModes.h" #include "Utils/AArch64BaseInfo.h" #include "llvm/ADT/APFloat.h" @@ -496,6 +497,21 @@ ADRPReg) .addGlobalAddress(GV, 0, AArch64II::MO_PAGE | OpFlags); + if (OpFlags & AArch64II::MO_TAGGED) { + // 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. 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. + unsigned DstReg = createResultReg(&AArch64::GPR64spRegClass); + PropagateTag(BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, + TII.get(AArch64::MOVKXi), DstReg), + ADRPReg, GV); + ADRPReg = DstReg; + } + ResultReg = createResultReg(&AArch64::GPR64spRegClass); BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, MIMD, TII.get(AArch64::ADDXri), ResultReg) diff --git a/llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.h b/llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.h --- a/llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.h +++ b/llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.h @@ -21,6 +21,7 @@ #include "llvm/CodeGen/MIRYamlMapping.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/IR/Function.h" #include "llvm/MC/MCLinkerOptimizationHint.h" #include diff --git a/llvm/lib/Target/AArch64/AArch64Tagging.h b/llvm/lib/Target/AArch64/AArch64Tagging.h new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/AArch64/AArch64Tagging.h @@ -0,0 +1,46 @@ +//=- AArch64Tagging.h - AArch64 tag utility functions -------------*- C++ -*-=// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares AArch64-specific helper functions related to top-byte +// tags. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_AARCH64_AARCH64TAGGING_H_ +#define LLVM_LIB_TARGET_AARCH64_AARCH64TAGGING_H_ + +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/IR/GlobalValue.h" + +namespace llvm { + +// 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. 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. +static inline void PropagateTag(const MachineInstrBuilder &MI, Register DstReg, + MachineOperand &Tag) { + Tag.setTargetFlags(AArch64II::MO_PREL | AArch64II::MO_G3); + Tag.setOffset(0x100000000); + MI.addReg(DstReg).add(Tag).addImm(48); +} + +static inline void PropagateTag(const MachineInstrBuilder &MI, Register DstReg, + const GlobalValue *GV) { + MI.addReg(DstReg) + .addGlobalAddress(GV, /*Offset=*/0x100000000, + AArch64II::MO_PREL | AArch64II::MO_G3) + .addImm(48); +} + +} // namespace llvm + +#endif // LLVM_LIB_TARGET_AARCH64_AARCH64TAGGING_H_ diff --git a/llvm/test/CodeGen/AArch64/arm64-fast-isel-tag.ll b/llvm/test/CodeGen/AArch64/arm64-fast-isel-tag.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/arm64-fast-isel-tag.ll @@ -0,0 +1,23 @@ +; RUN: llc < %s -fast-isel -relocation-model=pic | FileCheck %s + +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "aarch64-unknown-linux-gnu" + +@glob.hwasan = private constant i64 0 + +;; The constant here is 0x2F << 56. This effectively makes the alias a tagged version of the original global. +@glob = private alias i64, inttoptr (i64 add (i64 ptrtoint (ptr @glob.hwasan to i64), i64 3386706919782612992) to ptr) + +; CHECK-LABEL: func +define void @func() #0 { +entry: + ; CHECK: adrp [[REG:x[0-9]+]], :pg_hi21_nc:.Lglob + ; CHECK: movk [[REG]], #:prel_g3:.Lglob+4294967296 + ; CHECK: add x0, [[REG]], :lo12:.Lglob + call void @extern_func(ptr @glob) + ret void +} + +declare void @extern_func(ptr) + +attributes #0 = { "target-features"="+tagged-globals" }