[X86][APX] Suppress EGPR/NDD instructions for relocations (#136660)

Suppress EGPR/NDD instructions for relocations to avoid APX relocation
types emitted. This is to keep backward compatibility with old version
of linkers without APX support. The use case is to try APX features with
LLVM + old built-in linker on RHEL9 OS which is expected to be EOL in
2032.
If there are APX relocation types, the old version of linkers would
raise "unsupported relocation type" error. Example:
```
$ llvm-mc -filetype=obj -o got.o -triple=x86_64-unknown-linux got.s
$ ld got.o -o got.exe
ld: got.o: unsupported relocation type 0x2b
...

$ cat got.s
...
movq foo@GOTPCREL(%rip), %r16

$ llvm-objdump -dr got.o
...
1: d5 48 8b 05 00 00 00 00       movq    (%rip), %r16
0000000000000005:  R_X86_64_CODE_4_GOTPCRELX    foo-0x4
```
This commit is contained in:
Feng Zou
2025-04-29 19:12:59 +08:00
committed by GitHub
parent df267d77f6
commit bd6addc032
28 changed files with 696 additions and 18 deletions

View File

@@ -78,6 +78,7 @@ set(sources
X86SpeculativeLoadHardening.cpp
X86SpeculativeExecutionSideEffectSuppression.cpp
X86Subtarget.cpp
X86SuppressAPXForReloc.cpp
X86TargetMachine.cpp
X86TargetObjectFile.cpp
X86TargetTransformInfo.cpp

View File

@@ -169,6 +169,7 @@ FunctionPass *createX86LoadValueInjectionRetHardeningPass();
FunctionPass *createX86SpeculativeLoadHardeningPass();
FunctionPass *createX86SpeculativeExecutionSideEffectSuppression();
FunctionPass *createX86ArgumentStackSlotPass();
FunctionPass *createX86SuppressAPXForRelocationPass();
void initializeCompressEVEXPassPass(PassRegistry &);
void initializeFPSPass(PassRegistry &);
@@ -204,6 +205,7 @@ void initializeX86ReturnThunksPass(PassRegistry &);
void initializeX86SpeculativeExecutionSideEffectSuppressionPass(PassRegistry &);
void initializeX86SpeculativeLoadHardeningPassPass(PassRegistry &);
void initializeX86TileConfigPass(PassRegistry &);
void initializeX86SuppressAPXForRelocationPassPass(PassRegistry &);
namespace X86AS {
enum : unsigned {

View File

@@ -58,6 +58,8 @@ using namespace llvm;
#define DEBUG_TYPE COMP_EVEX_NAME
extern cl::opt<bool> X86EnableAPXForRelocation;
namespace {
// Including the generated EVEX compression tables.
#define GET_X86_COMPRESS_EVEX_TABLE
@@ -252,6 +254,13 @@ static bool CompressEVEXImpl(MachineInstr &MI, const X86Subtarget &ST) {
if (MI.definesRegister(Super, /*TRI=*/nullptr))
IsRedundantNDD = false;
}
// ADDrm/mr instructions with NDD + relocation had been transformed to the
// instructions without NDD in X86SuppressAPXForRelocation pass. That is to
// keep backward compatibility with linkers without APX support.
if (!X86EnableAPXForRelocation)
assert(!isAddMemInstrWithRelocation(MI) &&
"Unexpected NDD instruction with relocation!");
}
// NonNF -> NF only if it's not a compressible NDD instruction and eflags is

View File

@@ -66,6 +66,8 @@ STATISTIC(NumTestsInserted, "Number of test instructions inserted");
STATISTIC(NumAddsInserted, "Number of adds instructions inserted");
STATISTIC(NumNFsConvertedTo, "Number of NF instructions converted to");
extern cl::opt<bool> X86EnableAPXForRelocation;
namespace {
// Convenient array type for storing registers associated with each condition.
@@ -242,7 +244,15 @@ static EFLAGSClobber getClobberType(const MachineInstr &MI) {
MI.findRegisterDefOperand(X86::EFLAGS, /*TRI=*/nullptr);
if (!FlagDef)
return NoClobber;
if (FlagDef->isDead() && X86::getNFVariant(MI.getOpcode()))
// For the instructions are ADDrm/ADDmr with relocation, we'll skip the
// optimization for replacing non-NF with NF. This is to keep backward
// compatiblity with old version of linkers without APX relocation type
// support on Linux OS.
bool IsWithReloc =
X86EnableAPXForRelocation ? false : isAddMemInstrWithRelocation(MI);
if (FlagDef->isDead() && X86::getNFVariant(MI.getOpcode()) && !IsWithReloc)
return EvitableClobber;
return InevitableClobber;

View File

@@ -53,6 +53,8 @@ using namespace llvm;
#define GET_INSTRINFO_CTOR_DTOR
#include "X86GenInstrInfo.inc"
extern cl::opt<bool> X86EnableAPXForRelocation;
static cl::opt<bool>
NoFusing("disable-spill-fusing",
cl::desc("Disable fusing of spill code into instructions"),
@@ -102,22 +104,8 @@ X86InstrInfo::getRegClass(const MCInstrDesc &MCID, unsigned OpNum,
if (X86II::canUseApxExtendedReg(MCID))
return RC;
switch (RC->getID()) {
default:
return RC;
case X86::GR8RegClassID:
return &X86::GR8_NOREX2RegClass;
case X86::GR16RegClassID:
return &X86::GR16_NOREX2RegClass;
case X86::GR32RegClassID:
return &X86::GR32_NOREX2RegClass;
case X86::GR64RegClassID:
return &X86::GR64_NOREX2RegClass;
case X86::GR32_NOSPRegClassID:
return &X86::GR32_NOREX2_NOSPRegClass;
case X86::GR64_NOSPRegClassID:
return &X86::GR64_NOREX2_NOSPRegClass;
}
const X86RegisterInfo *RI = Subtarget.getRegisterInfo();
return RI->constrainRegClassToNonRex2(RC);
}
bool X86InstrInfo::isCoalescableExtInstr(const MachineInstr &MI,
@@ -5464,8 +5452,16 @@ bool X86InstrInfo::optimizeCompareInstr(MachineInstr &CmpInstr, Register SrcReg,
continue;
}
// For the instructions are ADDrm/ADDmr with relocation, we'll skip the
// optimization for replacing non-NF with NF. This is to keep backward
// compatiblity with old version of linkers without APX relocation type
// support on Linux OS.
bool IsWithReloc = X86EnableAPXForRelocation
? false
: isAddMemInstrWithRelocation(Inst);
// Try to replace non-NF with NF instructions.
if (HasNF && Inst.registerDefIsDead(X86::EFLAGS, TRI)) {
if (HasNF && Inst.registerDefIsDead(X86::EFLAGS, TRI) && !IsWithReloc) {
unsigned NewOp = X86::getNFVariant(Inst.getOpcode());
if (!NewOp)
return false;

View File

@@ -174,6 +174,19 @@ inline static bool isMem(const MachineInstr &MI, unsigned Op) {
MI.getOperand(Op + X86::AddrSegmentReg).isReg() && isLeaMem(MI, Op);
}
inline static bool isAddMemInstrWithRelocation(const MachineInstr &MI) {
unsigned Op = MI.getOpcode();
if (Op == X86::ADD64rm || Op == X86::ADD64mr_ND || Op == X86::ADD64rm_ND) {
int MemOpNo = X86II::getMemoryOperandNo(MI.getDesc().TSFlags) +
X86II::getOperandBias(MI.getDesc());
const MachineOperand &MO = MI.getOperand(X86::AddrDisp + MemOpNo);
if (MO.getTargetFlags() == X86II::MO_GOTTPOFF)
return true;
}
return false;
}
class X86InstrInfo final : public X86GenInstrInfo {
X86Subtarget &Subtarget;
const X86RegisterInfo RI;

View File

@@ -1237,3 +1237,23 @@ bool X86RegisterInfo::getRegAllocationHints(Register VirtReg,
return true;
}
const TargetRegisterClass *X86RegisterInfo::constrainRegClassToNonRex2(
const TargetRegisterClass *RC) const {
switch (RC->getID()) {
default:
return RC;
case X86::GR8RegClassID:
return &X86::GR8_NOREX2RegClass;
case X86::GR16RegClassID:
return &X86::GR16_NOREX2RegClass;
case X86::GR32RegClassID:
return &X86::GR32_NOREX2RegClass;
case X86::GR64RegClassID:
return &X86::GR64_NOREX2RegClass;
case X86::GR32_NOSPRegClassID:
return &X86::GR32_NOREX2_NOSPRegClass;
case X86::GR64_NOSPRegClassID:
return &X86::GR64_NOREX2_NOSPRegClass;
}
}

View File

@@ -171,6 +171,9 @@ public:
SmallVectorImpl<MCPhysReg> &Hints,
const MachineFunction &MF, const VirtRegMap *VRM,
const LiveRegMatrix *Matrix) const override;
const TargetRegisterClass *
constrainRegClassToNonRex2(const TargetRegisterClass *RC) const;
};
} // End llvm namespace

View File

@@ -0,0 +1,232 @@
//===- X86SuppressAPXForReloc.cpp - Suppress APX features for relocations -===//
//
// 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
//
//===----------------------------------------------------------------------===//
/// \file
///
/// This pass is added to suppress APX features for relocations. It's used to
/// keep backward compatibility with old version of linker having no APX
/// support. It can be removed after APX support is included in the default
/// linker on OS.
///
//===----------------------------------------------------------------------===//
#include "X86.h"
#include "X86InstrInfo.h"
#include "X86RegisterInfo.h"
#include "X86Subtarget.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineOperand.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
#include "llvm/InitializePasses.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Target/TargetMachine.h"
using namespace llvm;
#define DEBUG_TYPE "x86-suppress-apx-for-relocation"
cl::opt<bool> X86EnableAPXForRelocation(
"x86-enable-apx-for-relocation",
cl::desc("Enable APX features (EGPR, NDD and NF) for instructions with "
"relocations on x86-64 ELF"),
cl::init(false));
namespace {
class X86SuppressAPXForRelocationPass : public MachineFunctionPass {
public:
X86SuppressAPXForRelocationPass() : MachineFunctionPass(ID) {}
StringRef getPassName() const override {
return "X86 Suppress APX features for relocation";
}
bool runOnMachineFunction(MachineFunction &MF) override;
static char ID;
};
} // namespace
char X86SuppressAPXForRelocationPass::ID = 0;
INITIALIZE_PASS_BEGIN(X86SuppressAPXForRelocationPass, DEBUG_TYPE,
"X86 Suppress APX features for relocation", false, false)
INITIALIZE_PASS_END(X86SuppressAPXForRelocationPass, DEBUG_TYPE,
"X86 Suppress APX features for relocation", false, false)
FunctionPass *llvm::createX86SuppressAPXForRelocationPass() {
return new X86SuppressAPXForRelocationPass();
}
static void suppressEGPRRegClass(MachineFunction &MF, MachineInstr &MI,
const X86Subtarget &ST, unsigned int OpNum) {
MachineRegisterInfo *MRI = &MF.getRegInfo();
Register Reg = MI.getOperand(OpNum).getReg();
if (!Reg.isVirtual()) {
assert(!X86II::isApxExtendedReg(Reg) && "APX EGPR is used unexpectedly.");
return;
}
const TargetRegisterClass *RC = MRI->getRegClass(Reg);
const X86RegisterInfo *RI = ST.getRegisterInfo();
const TargetRegisterClass *NewRC = RI->constrainRegClassToNonRex2(RC);
MRI->setRegClass(Reg, NewRC);
}
static bool handleInstructionWithEGPR(MachineFunction &MF,
const X86Subtarget &ST) {
if (!ST.hasEGPR())
return false;
auto suppressEGPRInInstrWithReloc = [&](MachineInstr &MI,
ArrayRef<unsigned> OpNoArray) {
int MemOpNo = X86II::getMemoryOperandNo(MI.getDesc().TSFlags) +
X86II::getOperandBias(MI.getDesc());
const MachineOperand &MO = MI.getOperand(X86::AddrDisp + MemOpNo);
if (MO.getTargetFlags() == X86II::MO_GOTTPOFF ||
MO.getTargetFlags() == X86II::MO_GOTPCREL) {
LLVM_DEBUG(dbgs() << "Transform instruction with relocation type:\n "
<< MI);
for (unsigned OpNo : OpNoArray)
suppressEGPRRegClass(MF, MI, ST, OpNo);
LLVM_DEBUG(dbgs() << "to:\n " << MI << "\n");
}
};
for (MachineBasicBlock &MBB : MF) {
for (MachineInstr &MI : MBB) {
unsigned Opcode = MI.getOpcode();
switch (Opcode) {
// For GOTPC32_TLSDESC, it's emitted with physical register (EAX/RAX) in
// X86AsmPrinter::LowerTlsAddr, and there is no corresponding target
// flag for it, so we don't need to handle LEA64r with TLSDESC and EGPR
// in this pass (before emitting assembly).
case X86::TEST32mr:
case X86::TEST64mr: {
suppressEGPRInInstrWithReloc(MI, {5});
break;
}
case X86::CMP32rm:
case X86::CMP64rm:
case X86::MOV32rm:
case X86::MOV64rm: {
suppressEGPRInInstrWithReloc(MI, {0});
break;
}
case X86::ADC32rm:
case X86::ADD32rm:
case X86::AND32rm:
case X86::OR32rm:
case X86::SBB32rm:
case X86::SUB32rm:
case X86::XOR32rm:
case X86::ADC64rm:
case X86::ADD64rm:
case X86::AND64rm:
case X86::OR64rm:
case X86::SBB64rm:
case X86::SUB64rm:
case X86::XOR64rm: {
suppressEGPRInInstrWithReloc(MI, {0, 1});
break;
}
}
}
}
return true;
}
static bool handleNDDOrNFInstructions(MachineFunction &MF,
const X86Subtarget &ST) {
if (!ST.hasNDD() && !ST.hasNF())
return false;
const X86InstrInfo *TII = ST.getInstrInfo();
MachineRegisterInfo *MRI = &MF.getRegInfo();
for (MachineBasicBlock &MBB : MF) {
for (MachineInstr &MI : llvm::make_early_inc_range(MBB)) {
unsigned Opcode = MI.getOpcode();
switch (Opcode) {
case X86::ADD64rm_NF:
case X86::ADD64mr_NF_ND:
case X86::ADD64rm_NF_ND:
llvm_unreachable("Unexpected NF instruction!");
case X86::ADD64rm_ND: {
int MemOpNo = X86II::getMemoryOperandNo(MI.getDesc().TSFlags) +
X86II::getOperandBias(MI.getDesc());
const MachineOperand &MO = MI.getOperand(X86::AddrDisp + MemOpNo);
if (MO.getTargetFlags() == X86II::MO_GOTTPOFF) {
LLVM_DEBUG(dbgs() << "Transform instruction with relocation type:\n "
<< MI);
Register Reg = MRI->createVirtualRegister(&X86::GR64_NOREX2RegClass);
[[maybe_unused]] MachineInstrBuilder CopyMIB =
BuildMI(MBB, MI, MI.getDebugLoc(), TII->get(TargetOpcode::COPY),
Reg)
.addReg(MI.getOperand(1).getReg());
MI.getOperand(1).setReg(Reg);
const MCInstrDesc &NewDesc = TII->get(X86::ADD64rm);
MI.setDesc(NewDesc);
suppressEGPRRegClass(MF, MI, ST, 0);
MI.tieOperands(0, 1);
LLVM_DEBUG(dbgs() << "to:\n " << *CopyMIB << "\n");
LLVM_DEBUG(dbgs() << " " << MI << "\n");
}
break;
}
case X86::ADD64mr_ND: {
int MemRefBegin = X86II::getMemoryOperandNo(MI.getDesc().TSFlags);
const MachineOperand &MO = MI.getOperand(MemRefBegin + X86::AddrDisp);
if (MO.getTargetFlags() == X86II::MO_GOTTPOFF) {
LLVM_DEBUG(dbgs() << "Transform instruction with relocation type:\n "
<< MI);
suppressEGPRRegClass(MF, MI, ST, 0);
Register Reg = MRI->createVirtualRegister(&X86::GR64_NOREX2RegClass);
[[maybe_unused]] MachineInstrBuilder CopyMIB =
BuildMI(MBB, MI, MI.getDebugLoc(), TII->get(TargetOpcode::COPY),
Reg)
.addReg(MI.getOperand(6).getReg());
MachineInstrBuilder NewMIB =
BuildMI(MBB, MI, MI.getDebugLoc(), TII->get(X86::ADD64rm),
MI.getOperand(0).getReg())
.addReg(Reg)
.addReg(MI.getOperand(1).getReg())
.addImm(MI.getOperand(2).getImm())
.addReg(MI.getOperand(3).getReg())
.add(MI.getOperand(4))
.addReg(MI.getOperand(5).getReg());
MachineOperand *FlagDef =
MI.findRegisterDefOperand(X86::EFLAGS, /*TRI=*/nullptr);
if (FlagDef && FlagDef->isDead()) {
MachineOperand *NewFlagDef =
NewMIB->findRegisterDefOperand(X86::EFLAGS, /*TRI=*/nullptr);
if (NewFlagDef)
NewFlagDef->setIsDead();
}
MI.eraseFromParent();
LLVM_DEBUG(dbgs() << "to:\n " << *CopyMIB << "\n");
LLVM_DEBUG(dbgs() << " " << *NewMIB << "\n");
}
break;
}
}
}
}
return true;
}
bool X86SuppressAPXForRelocationPass::runOnMachineFunction(
MachineFunction &MF) {
if (X86EnableAPXForRelocation)
return false;
const X86Subtarget &ST = MF.getSubtarget<X86Subtarget>();
bool Changed = handleInstructionWithEGPR(MF, ST);
Changed |= handleNDDOrNFInstructions(MF, ST);
return Changed;
}

View File

@@ -105,6 +105,7 @@ extern "C" LLVM_C_ABI void LLVMInitializeX86Target() {
initializeX86FixupInstTuningPassPass(PR);
initializeX86FixupVectorConstantsPassPass(PR);
initializeX86DynAllocaExpanderPass(PR);
initializeX86SuppressAPXForRelocationPassPass(PR);
}
static std::unique_ptr<TargetLoweringObjectFile> createTLOF(const Triple &TT) {
@@ -559,6 +560,8 @@ void X86PassConfig::addPreRegAlloc() {
addPass(createX86AvoidStoreForwardingBlocks());
}
addPass(createX86SuppressAPXForRelocationPass());
addPass(createX86SpeculativeLoadHardeningPass());
addPass(createX86FlagsCopyLoweringPass());
addPass(createX86DynAllocaExpander());

View File

@@ -41,6 +41,7 @@
; CHECK-NEXT: Argument Stack Rebase
; CHECK-NEXT: Finalize ISel and expand pseudo-instructions
; CHECK-NEXT: Local Stack Slot Allocation
; CHECK-NEXT: X86 Suppress APX features for relocation
; CHECK-NEXT: X86 speculative load hardening
; CHECK-NEXT: X86 EFLAGS copy lowering
; CHECK-NEXT: X86 DynAlloca Expander

View File

@@ -1,6 +1,7 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd -verify-machineinstrs --show-mc-encoding | FileCheck %s
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd,nf -verify-machineinstrs --show-mc-encoding | FileCheck --check-prefix=NF %s
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd,nf -x86-enable-apx-for-relocation=true -verify-machineinstrs --show-mc-encoding | FileCheck --check-prefix=NF %s
define i8 @add8rr(i8 noundef %a, i8 noundef %b) {
; CHECK-LABEL: add8rr:

View File

@@ -1,6 +1,7 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd -verify-machineinstrs --show-mc-encoding | FileCheck %s
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd,nf -verify-machineinstrs --show-mc-encoding | FileCheck --check-prefix=NF %s
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd,nf -x86-enable-apx-for-relocation=true -verify-machineinstrs --show-mc-encoding | FileCheck --check-prefix=NF %s
define i8 @and8rr(i8 noundef %a, i8 noundef %b) {
; CHECK-LABEL: and8rr:

View File

@@ -1,5 +1,6 @@
# RUN: llc %s -mtriple=x86_64-unknown -mattr=+ndd,+egpr -start-before=x86-compress-evex -show-mc-encoding -o - | FileCheck --check-prefixes=CHECK,NDD %s
# RUN: llc %s -mtriple=x86_64-unknown -mattr=+ndd,+egpr,+nf -start-before=x86-compress-evex -show-mc-encoding -o - | FileCheck --check-prefixes=CHECK,NDD-NF %s
# RUN: llc %s -mtriple=x86_64-unknown -mattr=+ndd,+egpr,+nf -x86-enable-apx-for-relocation=true -start-before=x86-compress-evex -show-mc-encoding -o - | FileCheck --check-prefixes=CHECK,NDD-NF %s
...
---

View File

@@ -1,6 +1,7 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd -verify-machineinstrs | FileCheck %s
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd,nf -verify-machineinstrs | FileCheck --check-prefix=NF %s
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd,nf -x86-enable-apx-for-relocation=true -verify-machineinstrs | FileCheck --check-prefix=NF %s
define i8 @dec8r(i8 noundef %a) {
; CHECK-LABEL: dec8r:

View File

@@ -1,5 +1,6 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
; RUN: llc < %s -mtriple=x86_64-unknown-unknown -mattr=+nf | FileCheck %s
; RUN: llc < %s -mtriple=x86_64-unknown-unknown -mattr=+nf -x86-enable-apx-for-relocation=true | FileCheck %s
define i32 @flag_copy_1(i32 %x, i32 %y, ptr %pz) nounwind {
; CHECK-LABEL: flag_copy_1:

View File

@@ -1,6 +1,8 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5
# RUN: llc -mtriple=x86_64 -run-pass x86-flags-copy-lowering -mattr=+ndd -verify-machineinstrs -o - %s | FileCheck --check-prefixes=CHECK,NDD %s
# RUN: llc -mtriple=x86_64 -run-pass x86-flags-copy-lowering -mattr=+ndd,+nf -verify-machineinstrs -o - %s | FileCheck --check-prefixes=CHECK,NDD-NF %s
# RUN: llc -mtriple=x86_64 -run-pass x86-flags-copy-lowering -mattr=+ndd,+nf -x86-enable-apx-for-relocation=true -verify-machineinstrs -o - %s | FileCheck --check-prefixes=CHECK,NDD-NF %s
# Lower various interesting copy patterns of EFLAGS without using LAHF/SAHF.
...

View File

@@ -1,6 +1,7 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd -verify-machineinstrs | FileCheck %s
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd,nf -verify-machineinstrs | FileCheck --check-prefix=NF %s
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd,nf -x86-enable-apx-for-relocation=true -verify-machineinstrs | FileCheck --check-prefix=NF %s
define i16 @mul16rr(i16 noundef %a, i16 noundef %b) {
; CHECK-LABEL: mul16rr:

View File

@@ -1,6 +1,7 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd -verify-machineinstrs | FileCheck %s
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd,nf -verify-machineinstrs | FileCheck --check-prefix=NF %s
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd,nf -x86-enable-apx-for-relocation=true -verify-machineinstrs | FileCheck --check-prefix=NF %s
define i8 @inc8r(i8 noundef %a) {
; CHECK-LABEL: inc8r:

View File

@@ -1,6 +1,7 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd -verify-machineinstrs | FileCheck %s
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd,nf -verify-machineinstrs | FileCheck --check-prefix=NF %s
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd,nf -x86-enable-apx-for-relocation=true -verify-machineinstrs --show-mc-encoding | FileCheck --check-prefix=NF %s
define i8 @neg8r(i8 noundef %a) {
; CHECK-LABEL: neg8r:

View File

@@ -1,6 +1,7 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd -verify-machineinstrs --show-mc-encoding | FileCheck %s
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd,nf -verify-machineinstrs --show-mc-encoding | FileCheck --check-prefix=NF %s
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd,nf -x86-enable-apx-for-relocation=true -verify-machineinstrs --show-mc-encoding | FileCheck --check-prefix=NF %s
define i8 @or8rr(i8 noundef %a, i8 noundef %b) {
; CHECK-LABEL: or8rr:

View File

@@ -0,0 +1,296 @@
# RUN: llc -mattr=+egpr,+ndd -x mir < %s -x86-enable-apx-for-relocation=true -run-pass x86-suppress-apx-for-relocation -verify-machineinstrs -o - | FileCheck %s --check-prefixes=CHECK,APXREL
# RUN: llc -mattr=+egpr,+ndd -x mir < %s -run-pass x86-suppress-apx-for-relocation -verify-machineinstrs -o - | FileCheck %s --check-prefixes=CHECK,NOAPXREL
--- |
target triple = "x86_64-unknown-linux-gnu"
@x = external global i64
@i = external thread_local global i32
define i32 @mov() {
entry:
ret i32 undef
}
define i8 @test() {
entry:
ret i8 undef
}
define i32 @adc() {
entry:
ret i32 undef
}
define i32 @add() {
entry:
ret i32 undef
}
define i32 @and() {
entry:
ret i32 undef
}
define i8 @cmp() {
entry:
ret i8 undef
}
define i32 @or() {
entry:
ret i32 undef
}
define i32 @sbb() {
entry:
ret i32 undef
}
define i32 @sub() {
entry:
ret i32 undef
}
define i32 @xor() {
entry:
ret i32 undef
}
define i32 @add64rm_nd() {
entry:
ret i32 undef
}
define i32 @add64mr_nd() {
entry:
ret i32 undef
}
...
---
name: mov
alignment: 16
tracksRegLiveness: true
registers:
- { id: 0, class: gr64 }
- { id: 1, class: gr32 }
body: |
bb.0.entry:
%0:gr64 = MOV64rm $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg :: (load (s64))
%1:gr32 = MOV32rm killed %0, 1, $noreg, 0, $fs :: (load (s32))
$eax = COPY %1
RET 0, $eax
# CHECK: name: mov
# APXREL: %0:gr64 = MOV64rm $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg :: (load (s64))
# NOAPXREL: %0:gr64_norex2 = MOV64rm $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg :: (load (s64))
...
---
name: test
alignment: 16
tracksRegLiveness: true
registers:
- { id: 0, class: gr64 }
- { id: 1, class: gr8 }
body: |
bb.0.entry:
%0:gr64 = MOV64rm $rip, 1, $noreg, @x, $noreg :: (load (s64))
TEST64mr $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg, %0:gr64, implicit-def $eflags :: (load (s64))
%1:gr8 = SETCCr 5, implicit $eflags
$al = COPY %1
RET 0, $al
# CHECK: name: test
# APXREL: %0:gr64 = MOV64rm $rip, 1, $noreg, @x, $noreg :: (load (s64))
# APXREL-NEXT: TEST64mr $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg, %0, implicit-def $eflags :: (load (s64))
# NOAPXREL: %0:gr64_norex2 = MOV64rm $rip, 1, $noreg, @x, $noreg :: (load (s64))
# NOAPXREL-NEXT: TEST64mr $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg, %0, implicit-def $eflags :: (load (s64))
...
---
name: adc
alignment: 16
tracksRegLiveness: true
registers:
- { id: 0, class: gr64 }
- { id: 1, class: gr64 }
- { id: 2, class: gr64 }
- { id: 3, class: gr32 }
body: |
bb.0.entry:
%0:gr64 = MOV64rm $rip, 1, $noreg, @x, $noreg :: (load (s64))
%1:gr64 = OR64ri8 %0, 7, implicit-def $eflags
%2:gr64 = ADC64rm %1, $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg, implicit-def $eflags, implicit $eflags :: (load (s64))
%3:gr32 = MOV32rm killed %2, 1, $noreg, 0, $fs :: (load (s32))
$eax = COPY %3
RET 0, $eax
# CHECK: name: adc
# APXREL: %2:gr64 = ADC64rm %1, $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg, implicit-def $eflags, implicit $eflags :: (load (s64))
# NOAPXREL: %2:gr64_norex2 = ADC64rm %1, $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg, implicit-def $eflags, implicit $eflags :: (load (s64))
...
---
name: add
alignment: 16
tracksRegLiveness: true
registers:
- { id: 0, class: gr64 }
- { id: 1, class: gr64 }
- { id: 2, class: gr32 }
body: |
bb.0.entry:
%0:gr64 = MOV64rm $rip, 1, $noreg, @x, $noreg :: (load (s64))
%1:gr64 = ADD64rm %0, $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg, implicit-def $eflags :: (load (s64))
%2:gr32 = MOV32rm killed %1, 1, $noreg, 0, $fs :: (load (s32))
$eax = COPY %2
RET 0, $eax
# CHECK: name: add
# APXREL: %1:gr64 = ADD64rm %0, $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg, implicit-def $eflags :: (load (s64))
# NOAPXREL: %1:gr64_norex2 = ADD64rm %0, $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg, implicit-def $eflags :: (load (s64))
...
---
name: and
alignment: 16
tracksRegLiveness: true
registers:
- { id: 0, class: gr64 }
- { id: 1, class: gr64 }
- { id: 2, class: gr32 }
body: |
bb.0.entry:
%0:gr64 = MOV64rm $rip, 1, $noreg, @x, $noreg :: (load (s64))
%1:gr64 = AND64rm %0, $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg, implicit-def $eflags :: (load (s64))
%2:gr32 = MOV32rm killed %1, 1, $noreg, 0, $fs :: (load (s32))
$eax = COPY %2
RET 0, $eax
# CHECK: name: and
# APXREL: %1:gr64 = AND64rm %0, $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg, implicit-def $eflags :: (load (s64))
# NOAPXREL: %1:gr64_norex2 = AND64rm %0, $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg, implicit-def $eflags :: (load (s64))
...
---
name: or
alignment: 16
tracksRegLiveness: true
registers:
- { id: 0, class: gr64 }
- { id: 1, class: gr64 }
- { id: 2, class: gr32 }
body: |
bb.0.entry:
%0:gr64 = MOV64rm $rip, 1, $noreg, @x, $noreg :: (load (s64))
%1:gr64 = OR64rm %0, $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg, implicit-def $eflags :: (load (s64))
%2:gr32 = MOV32rm killed %1, 1, $noreg, 0, $fs :: (load (s32))
$eax = COPY %2
RET 0, $eax
# CHECK: name: or
# APXREL: %1:gr64 = OR64rm %0, $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg, implicit-def $eflags :: (load (s64))
# NOAPXREL: %1:gr64_norex2 = OR64rm %0, $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg, implicit-def $eflags :: (load (s64))
...
---
name: sbb
alignment: 16
tracksRegLiveness: true
registers:
- { id: 0, class: gr64 }
- { id: 1, class: gr64 }
- { id: 2, class: gr64 }
- { id: 3, class: gr32 }
body: |
bb.0.entry:
%0:gr64 = MOV64rm $rip, 1, $noreg, @x, $noreg :: (load (s64))
%1:gr64 = OR64ri8 %0, 7, implicit-def $eflags
%2:gr64 = SBB64rm %1, $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg, implicit-def $eflags, implicit $eflags :: (load (s64))
%3:gr32 = MOV32rm killed %2, 1, $noreg, 0, $fs :: (load (s32))
$eax = COPY %3
RET 0, $eax
# CHECK: name: sbb
# APXREL: %1:gr64 = OR64ri8 %0, 7, implicit-def $eflags
# APXREL-NEXT: %2:gr64 = SBB64rm %1, $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg, implicit-def $eflags, implicit $eflags :: (load (s64))
# NOAPXREL: %1:gr64_norex2 = OR64ri8 %0, 7, implicit-def $eflags
# NOAPXREL-NEXT: %2:gr64_norex2 = SBB64rm %1, $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg, implicit-def $eflags, implicit $eflags :: (load (s64))
...
---
name: sub
alignment: 16
tracksRegLiveness: true
registers:
- { id: 0, class: gr64 }
- { id: 1, class: gr64 }
- { id: 2, class: gr32 }
body: |
bb.0.entry:
%0:gr64 = MOV64rm $rip, 1, $noreg, @x, $noreg :: (load (s64))
%1:gr64 = SUB64rm %0, $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg, implicit-def $eflags :: (load (s64))
%2:gr32 = MOV32rm killed %1, 1, $noreg, 0, $fs :: (load (s32))
$eax = COPY %2
RET 0, $eax
# CHECK: name: sub
# APXREL: %1:gr64 = SUB64rm %0, $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg, implicit-def $eflags :: (load (s64))
# NOAPXREL: %1:gr64_norex2 = SUB64rm %0, $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg, implicit-def $eflags :: (load (s64))
...
---
name: xor
alignment: 16
tracksRegLiveness: true
registers:
- { id: 0, class: gr64 }
- { id: 1, class: gr64 }
- { id: 2, class: gr32 }
body: |
bb.0.entry:
%0:gr64 = MOV64rm $rip, 1, $noreg, @x, $noreg :: (load (s64))
%1:gr64 = XOR64rm %0, $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg, implicit-def $eflags :: (load (s64))
%2:gr32 = MOV32rm killed %1, 1, $noreg, 0, $fs :: (load (s32))
$eax = COPY %2
RET 0, $eax
# CHECK: name: xor
# APXREL: %1:gr64 = XOR64rm %0, $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg, implicit-def $eflags :: (load (s64))
# NOAPXREL: %1:gr64_norex2 = XOR64rm %0, $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg, implicit-def $eflags :: (load (s64))
...
---
name: add64rm_nd
alignment: 16
tracksRegLiveness: true
registers:
- { id: 0, class: gr64 }
- { id: 1, class: gr64 }
- { id: 2, class: gr32 }
body: |
bb.0.entry:
%0:gr64 = MOV64rm $rip, 1, $noreg, @x, $noreg :: (load (s64))
%1:gr64 = ADD64rm_ND %0, $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg, implicit-def dead $eflags :: (load (s64) from got)
%2:gr32 = MOV32rm killed %1, 1, $noreg, 0, $fs :: (load (s32))
$eax = COPY %2
RET 0, $eax
# CHECK: name: add64rm_nd
# APXREL: %1:gr64 = ADD64rm_ND %0, $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg, implicit-def dead $eflags :: (load (s64) from got)
# NOAPXREL: %3:gr64_norex2 = COPY %0
# NOAPXREL: %1:gr64_norex2 = ADD64rm %3, $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg, implicit-def dead $eflags
...
---
name: add64mr_nd
alignment: 16
tracksRegLiveness: true
registers:
- { id: 0, class: gr64 }
- { id: 1, class: gr64 }
- { id: 2, class: gr32 }
body: |
bb.0.entry:
%0:gr64 = MOV64rm $rip, 1, $noreg, @x, $noreg :: (load (s64))
%1:gr64 = ADD64mr_ND $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg, %0, implicit-def dead $eflags :: (load (s64) from got)
%2:gr32 = MOV32rm killed %1, 1, $noreg, 0, $fs :: (load (s32))
$eax = COPY %2
RET 0, $eax
# CHECK: name: add64mr_nd
# APXREL: %1:gr64 = ADD64mr_ND $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg, %0, implicit-def dead $eflags :: (load (s64) from got)
# NOAPXREL: %3:gr64_norex2 = COPY %0
# NOAPXREL: %1:gr64_norex2 = ADD64rm %3, $rip, 1, $noreg, target-flags(x86-gottpoff) @i, $noreg, implicit-def dead $eflags
...

View File

@@ -1,6 +1,7 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd -verify-machineinstrs --show-mc-encoding | FileCheck %s
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd,nf -verify-machineinstrs --show-mc-encoding | FileCheck --check-prefix=NF %s
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd,nf -x86-enable-apx-for-relocation=true -verify-machineinstrs --show-mc-encoding | FileCheck --check-prefix=NF %s
define i8 @shl8ri(i8 noundef %a) {
; CHECK-LABEL: shl8ri:

View File

@@ -1,6 +1,7 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd -verify-machineinstrs --show-mc-encoding | FileCheck %s
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd,nf -verify-machineinstrs --show-mc-encoding | FileCheck --check-prefix=NF %s
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd,nf -x86-enable-apx-for-relocation=true -verify-machineinstrs --show-mc-encoding | FileCheck --check-prefix=NF %s
define i8 @shr8m1(ptr %ptr) {
; CHECK-LABEL: shr8m1:

View File

@@ -1,6 +1,7 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd -verify-machineinstrs --show-mc-encoding | FileCheck %s
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd,nf -verify-machineinstrs --show-mc-encoding | FileCheck --check-prefix=NF %s
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd,nf -x86-enable-apx-for-relocation=true -verify-machineinstrs --show-mc-encoding | FileCheck --check-prefix=NF %s
define i8 @sub8rr(i8 noundef %a, i8 noundef %b) {
; CHECK-LABEL: sub8rr:

View File

@@ -0,0 +1,76 @@
; RUN: llc -mattr=+egpr %s -mtriple=x86_64 --relocation-model=pic -enable-tlsdesc -filetype=obj -o %t.o
; RUN: llvm-objdump --no-print-imm-hex -dr %t.o | FileCheck %s --check-prefix=TLSDESC
; RUN: echo '.tbss; .globl b,c,d,e,f,g,h,i,j; b: .zero 4;c: .zero 4;d: .zero 4;e: .zero 4;f: .zero 4;g: .zero 4;h: .zero 4;i: .zero 4;j: .zero 4' | llvm-mc -filetype=obj -triple=x86_64 - -o %t1.o
; RUN: llc -mattr=+egpr %s -mtriple=x86_64 -filetype=obj -o %t.o -x86-enable-apx-for-relocation=true
; RUN: llvm-objdump --no-print-imm-hex -dr %t.o | FileCheck %s --check-prefix=GOTTPOFF_APXRELAX
; RUN: echo '.tbss; .globl b,c,d,e,f,g,h,i,j; b: .zero 4;c: .zero 4;d: .zero 4;e: .zero 4;f: .zero 4;g: .zero 4;h: .zero 4;i: .zero 4;j: .zero 4' | llvm-mc -filetype=obj -triple=x86_64 - -o %t1.o
; RUN: ld.lld %t.o %t1.o -o %t.so
; RUN: llvm-objdump --no-print-imm-hex -dr %t.so | FileCheck %s --check-prefix=GOTTPOFF_LD_APXRELAX
; RUN: llc -mattr=+egpr %s -mtriple=x86_64 -filetype=obj -o %t.o
; RUN: llvm-objdump --no-print-imm-hex -dr %t.o | FileCheck %s --check-prefix=GOTTPOFF_NOAPXRELAX
; RUN: echo '.tbss; .globl b,c,d,e,f,g,h,i,j; b: .zero 4;c: .zero 4;d: .zero 4;e: .zero 4;f: .zero 4;g: .zero 4;h: .zero 4;i: .zero 4;j: .zero 4' | llvm-mc -filetype=obj -triple=x86_64 - -o %t1.o
; RUN: ld.lld %t.o %t1.o -o %t.so
; RUN: llvm-objdump --no-print-imm-hex -dr %t.so | FileCheck %s --check-prefix=GOTTPOFF_LD_NOAPXRELAX
; TLSDESC: d5 18 89 c0 movq %rax, %r16
; TLSDESC-NEXT: 48 8d 05 00 00 00 00 leaq (%rip), %rax
; TLSDESC-NEXT: R_X86_64_GOTPC32_TLSDESC j-0x4
; GOTTPOFF_APXRELAX: d5 48 8b 05 00 00 00 00 movq (%rip), %r16
; GOTTPOFF_APXRELAX-NEXT: R_X86_64_CODE_4_GOTTPOFF j-0x4
; GOTTPOFF_LD_APXRELAX: d5 18 c7 c0 fc ff ff ff movq $-4, %r16
; GOTTPOFF_NOAPXRELAX: 48 8b 1d 00 00 00 00 movq (%rip), %rbx
; GOTTPOFF_NOAPXRELAX-NEXT: R_X86_64_GOTTPOFF j-0x4
; GOTTPOFF_LD_NOAPXRELAX: 48 c7 c3 fc ff ff ff movq $-4, %rbx
@a = thread_local global i32 0, align 4
@b = external thread_local global i32, align 4
@c = external thread_local global i32, align 4
@d = external thread_local global i32, align 4
@e = external thread_local global i32, align 4
@f = external thread_local global i32, align 4
@g = external thread_local global i32, align 4
@h = external thread_local global i32, align 4
@i = external thread_local global i32, align 4
@j = external thread_local global i32, align 4
define i32 @f2() nounwind {
%1 = tail call ptr @llvm.threadlocal.address.p0(ptr @a)
%2 = tail call ptr @llvm.threadlocal.address.p0(ptr @b)
%3 = tail call ptr @llvm.threadlocal.address.p0(ptr @c)
%4 = tail call ptr @llvm.threadlocal.address.p0(ptr @d)
%5 = tail call ptr @llvm.threadlocal.address.p0(ptr @e)
%6 = tail call ptr @llvm.threadlocal.address.p0(ptr @f)
%7 = tail call ptr @llvm.threadlocal.address.p0(ptr @g)
%8 = tail call ptr @llvm.threadlocal.address.p0(ptr @h)
%9 = tail call ptr @llvm.threadlocal.address.p0(ptr @i)
%10 = tail call ptr @llvm.threadlocal.address.p0(ptr @j)
%11 = load i32, ptr %1
%12 = load i32, ptr %2
%13 = load i32, ptr %3
%14 = load i32, ptr %4
%15 = load i32, ptr %5
%16 = load i32, ptr %6
%17 = load i32, ptr %7
%18 = load i32, ptr %8
%19 = load i32, ptr %9
%20 = load i32, ptr %10
%21 = add i32 %11, %12
%22 = add i32 %13, %14
%23 = add i32 %15, %16
%24 = add i32 %17, %18
%25 = add i32 %19, %20
%26 = add i32 %21, %22
%27 = add i32 %23, %24
%28 = add i32 %26, %27
%29 = add i32 %25, %28
ret i32 %29
}

View File

@@ -1,6 +1,7 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd -verify-machineinstrs --show-mc-encoding | FileCheck %s
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd,nf -verify-machineinstrs --show-mc-encoding | FileCheck --check-prefix=NF %s
; RUN: llc < %s -mtriple=x86_64-unknown -mattr=+ndd,nf -x86-enable-apx-for-relocation=true -verify-machineinstrs --show-mc-encoding | FileCheck --check-prefix=NF %s
define i8 @xor8rr(i8 noundef %a, i8 noundef %b) {
; CHECK-LABEL: xor8rr:

View File

@@ -127,6 +127,7 @@
; CHECK-NEXT: X86 LEA Optimize
; CHECK-NEXT: X86 Optimize Call Frame
; CHECK-NEXT: X86 Avoid Store Forwarding Block
; CHECK-NEXT: X86 Suppress APX features for relocation
; CHECK-NEXT: X86 speculative load hardening
; CHECK-NEXT: X86 EFLAGS copy lowering
; CHECK-NEXT: X86 DynAlloca Expander