diff options
author | Bill Wendling <isanbard@gmail.com> | 2022-02-08 17:40:59 -0800 |
---|---|---|
committer | Bill Wendling <isanbard@gmail.com> | 2022-02-08 17:42:54 -0800 |
commit | deaf22bc0e306bc44c70d2503e9364b5ed312c49 (patch) | |
tree | 180a857a884a7ac508577fc0b2bcd69f078c328b /llvm/lib | |
parent | dc8f4e118d92982ff5f6316fe7d541d120ebabd1 (diff) | |
download | llvm-deaf22bc0e306bc44c70d2503e9364b5ed312c49.zip llvm-deaf22bc0e306bc44c70d2503e9364b5ed312c49.tar.gz llvm-deaf22bc0e306bc44c70d2503e9364b5ed312c49.tar.bz2 |
[X86] Implement -fzero-call-used-regs option
The "-fzero-call-used-regs" option tells the compiler to zero out
certain registers before the function returns. It's also available as a
function attribute: zero_call_used_regs.
The two upper categories are:
- "used": Zero out used registers.
- "all": Zero out all registers, whether used or not.
The individual options are:
- "skip": Don't zero out any registers. This is the default.
- "used": Zero out all used registers.
- "used-arg": Zero out used registers that are used for arguments.
- "used-gpr": Zero out used registers that are GPRs.
- "used-gpr-arg": Zero out used GPRs that are used as arguments.
- "all": Zero out all registers.
- "all-arg": Zero out all registers used for arguments.
- "all-gpr": Zero out all GPRs.
- "all-gpr-arg": Zero out all GPRs used for arguments.
This is used to help mitigate Return-Oriented Programming exploits.
Reviewed By: nickdesaulniers
Differential Revision: https://reviews.llvm.org/D110869
Diffstat (limited to 'llvm/lib')
-rw-r--r-- | llvm/lib/CodeGen/MachineRegisterInfo.cpp | 15 | ||||
-rw-r--r-- | llvm/lib/CodeGen/PrologEpilogInserter.cpp | 93 | ||||
-rw-r--r-- | llvm/lib/Target/X86/X86FrameLowering.cpp | 94 | ||||
-rw-r--r-- | llvm/lib/Target/X86/X86FrameLowering.h | 4 | ||||
-rw-r--r-- | llvm/lib/Target/X86/X86RegisterInfo.cpp | 56 | ||||
-rw-r--r-- | llvm/lib/Target/X86/X86RegisterInfo.h | 9 | ||||
-rw-r--r-- | llvm/lib/Target/X86/X86RegisterInfo.td | 11 |
7 files changed, 282 insertions, 0 deletions
diff --git a/llvm/lib/CodeGen/MachineRegisterInfo.cpp b/llvm/lib/CodeGen/MachineRegisterInfo.cpp index 1a4ad53..bf37358 100644 --- a/llvm/lib/CodeGen/MachineRegisterInfo.cpp +++ b/llvm/lib/CodeGen/MachineRegisterInfo.cpp @@ -651,3 +651,18 @@ bool MachineRegisterInfo::isReservedRegUnit(unsigned Unit) const { } return false; } + +bool MachineRegisterInfo::isArgumentRegister(const MachineFunction &MF, + MCRegister Reg) const { + return getTargetRegisterInfo()->isArgumentRegister(MF, Reg); +} + +bool MachineRegisterInfo::isFixedRegister(const MachineFunction &MF, + MCRegister Reg) const { + return getTargetRegisterInfo()->isFixedRegister(MF, Reg); +} + +bool MachineRegisterInfo::isGeneralPurposeRegister(const MachineFunction &MF, + MCRegister Reg) const { + return getTargetRegisterInfo()->isGeneralPurposeRegister(MF, Reg); +} diff --git a/llvm/lib/CodeGen/PrologEpilogInserter.cpp b/llvm/lib/CodeGen/PrologEpilogInserter.cpp index 8d8a612..46ae48b 100644 --- a/llvm/lib/CodeGen/PrologEpilogInserter.cpp +++ b/llvm/lib/CodeGen/PrologEpilogInserter.cpp @@ -130,6 +130,7 @@ private: void replaceFrameIndices(MachineBasicBlock *BB, MachineFunction &MF, int &SPAdj); void insertPrologEpilogCode(MachineFunction &MF); + void insertZeroCallUsedRegs(MachineFunction &MF); }; } // end anonymous namespace @@ -1145,6 +1146,9 @@ void PEI::insertPrologEpilogCode(MachineFunction &MF) { for (MachineBasicBlock *RestoreBlock : RestoreBlocks) TFI.emitEpilogue(MF, *RestoreBlock); + // Zero call used registers before restoring callee-saved registers. + insertZeroCallUsedRegs(MF); + for (MachineBasicBlock *SaveBlock : SaveBlocks) TFI.inlineStackProbe(MF, *SaveBlock); @@ -1171,6 +1175,95 @@ void PEI::insertPrologEpilogCode(MachineFunction &MF) { TFI.adjustForHiPEPrologue(MF, *SaveBlock); } +/// insertZeroCallUsedRegs - Zero out call used registers. +void PEI::insertZeroCallUsedRegs(MachineFunction &MF) { + const Function &F = MF.getFunction(); + + if (!F.hasFnAttribute("zero-call-used-regs")) + return; + + using namespace ZeroCallUsedRegs; + + ZeroCallUsedRegsKind ZeroRegsKind = + StringSwitch<ZeroCallUsedRegsKind>( + F.getFnAttribute("zero-call-used-regs").getValueAsString()) + .Case("skip", ZeroCallUsedRegsKind::Skip) + .Case("used-gpr-arg", ZeroCallUsedRegsKind::UsedGPRArg) + .Case("used-gpr", ZeroCallUsedRegsKind::UsedGPR) + .Case("used-arg", ZeroCallUsedRegsKind::UsedArg) + .Case("used", ZeroCallUsedRegsKind::Used) + .Case("all-gpr-arg", ZeroCallUsedRegsKind::AllGPRArg) + .Case("all-gpr", ZeroCallUsedRegsKind::AllGPR) + .Case("all-arg", ZeroCallUsedRegsKind::AllArg) + .Case("all", ZeroCallUsedRegsKind::All); + + if (ZeroRegsKind == ZeroCallUsedRegsKind::Skip) + return; + + const bool OnlyGPR = static_cast<unsigned>(ZeroRegsKind) & ONLY_GPR; + const bool OnlyUsed = static_cast<unsigned>(ZeroRegsKind) & ONLY_USED; + const bool OnlyArg = static_cast<unsigned>(ZeroRegsKind) & ONLY_ARG; + + const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo(); + const BitVector AllocatableSet(TRI.getAllocatableSet(MF)); + + // Mark all used registers. + BitVector UsedRegs(TRI.getNumRegs()); + if (OnlyUsed) + for (const MachineBasicBlock &MBB : MF) + for (const MachineInstr &MI : MBB) + for (const MachineOperand &MO : MI.operands()) { + if (!MO.isReg()) + continue; + + MCRegister Reg = MO.getReg(); + if (AllocatableSet[Reg] && !MO.isImplicit() && + (MO.isDef() || MO.isUse())) + UsedRegs.set(Reg); + } + + BitVector RegsToZero(TRI.getNumRegs()); + for (MCRegister Reg : AllocatableSet.set_bits()) { + // Skip over fixed registers. + if (TRI.isFixedRegister(MF, Reg)) + continue; + + // Want only general purpose registers. + if (OnlyGPR && !TRI.isGeneralPurposeRegister(MF, Reg)) + continue; + + // Want only used registers. + if (OnlyUsed && !UsedRegs[Reg]) + continue; + + // Want only registers used for arguments. + if (OnlyArg && !TRI.isArgumentRegister(MF, Reg)) + continue; + + RegsToZero.set(Reg); + } + + // Remove registers that are live when leaving the function. + for (const MachineBasicBlock &MBB : MF) + for (const MachineInstr &MI : MBB.terminators()) { + if (!MI.isReturn()) + continue; + + for (const auto &MO : MI.operands()) { + if (!MO.isReg()) + continue; + + for (MCPhysReg SReg : TRI.sub_and_superregs_inclusive(MO.getReg())) + RegsToZero.reset(SReg); + } + } + + const TargetFrameLowering &TFI = *MF.getSubtarget().getFrameLowering(); + for (MachineBasicBlock &MBB : MF) + if (MBB.isReturnBlock()) + TFI.emitZeroCallUsedRegs(RegsToZero, MBB); +} + /// replaceFrameIndices - Replace all MO_FrameIndex operands with physical /// register references and actual offsets. void PEI::replaceFrameIndices(MachineFunction &MF) { diff --git a/llvm/lib/Target/X86/X86FrameLowering.cpp b/llvm/lib/Target/X86/X86FrameLowering.cpp index 51f2ced..d0e98e5 100644 --- a/llvm/lib/Target/X86/X86FrameLowering.cpp +++ b/llvm/lib/Target/X86/X86FrameLowering.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "X86FrameLowering.h" +#include "MCTargetDesc/X86MCTargetDesc.h" #include "X86InstrBuilder.h" #include "X86InstrInfo.h" #include "X86MachineFunctionInfo.h" @@ -492,6 +493,99 @@ void X86FrameLowering::emitCalleeSavedFrameMoves( } } +void X86FrameLowering::emitZeroCallUsedRegs(BitVector RegsToZero, + MachineBasicBlock &MBB) const { + const MachineFunction &MF = *MBB.getParent(); + + // Don't clear registers that will just be reset before exiting. + for (const CalleeSavedInfo &CSI : MF.getFrameInfo().getCalleeSavedInfo()) + for (MCRegister Reg : TRI->sub_and_superregs_inclusive(CSI.getReg())) + RegsToZero.reset(Reg); + + // Insertion point. + MachineBasicBlock::iterator MBBI = MBB.getFirstTerminator(); + + // We don't need to zero out registers that are clobbered by "pop" + // instructions. + for (MachineBasicBlock::iterator I = MBBI, E = MBB.end(); I != E; ++I) + for (const MachineOperand &MO : I->operands()) { + if (!MO.isReg()) + continue; + + for (const MCPhysReg &Reg : TRI->sub_and_superregs_inclusive(MO.getReg())) + RegsToZero.reset(Reg); + } + + // Fake a debug loc. + DebugLoc DL; + if (MBBI != MBB.end()) + DL = MBBI->getDebugLoc(); + + // Zero out FP stack if referenced. Do this outside of the loop below so that + // it's done only once. + const X86Subtarget &ST = MF.getSubtarget<X86Subtarget>(); + for (MCRegister Reg : RegsToZero.set_bits()) { + if (!X86::RFP80RegClass.contains(Reg)) + continue; + + unsigned NumFPRegs = ST.is64Bit() ? 8 : 7; + for (unsigned i = 0; i != NumFPRegs; ++i) + BuildMI(MBB, MBBI, DL, TII.get(X86::LD_F0)); + + for (unsigned i = 0; i != NumFPRegs; ++i) + BuildMI(MBB, MBBI, DL, TII.get(X86::ST_FPrr)).addReg(X86::ST0); + break; + } + + // For GPRs, we only care to clear out the 32-bit register. + for (MCRegister Reg : RegsToZero.set_bits()) + if (TRI->isGeneralPurposeRegister(MF, Reg)) { + Reg = getX86SubSuperRegisterOrZero(Reg, 32); + for (const MCPhysReg &Reg : TRI->sub_and_superregs_inclusive(Reg)) + RegsToZero.reset(Reg); + RegsToZero.set(Reg); + } + + // Zero out registers. + for (MCRegister Reg : RegsToZero.set_bits()) { + if (ST.hasMMX() && X86::VR64RegClass.contains(Reg)) + // FIXME: Ignore MMX registers? + continue; + + unsigned XorOp; + if (TRI->isGeneralPurposeRegister(MF, Reg)) { + XorOp = X86::XOR32rr; + } else if (X86::VR128RegClass.contains(Reg)) { + // XMM# + if (!ST.hasSSE1()) + continue; + XorOp = X86::PXORrr; + } else if (X86::VR256RegClass.contains(Reg)) { + // YMM# + if (!ST.hasAVX()) + continue; + XorOp = X86::VPXORrr; + } else if (X86::VR512RegClass.contains(Reg)) { + // ZMM# + if (!ST.hasAVX512()) + continue; + XorOp = X86::VPXORYrr; + } else if (X86::VK1RegClass.contains(Reg) || + X86::VK2RegClass.contains(Reg) || + X86::VK4RegClass.contains(Reg) || + X86::VK8RegClass.contains(Reg) || + X86::VK16RegClass.contains(Reg)) { + if (!ST.hasVLX()) + continue; + XorOp = ST.hasBWI() ? X86::KXORQrr : X86::KXORWrr; + } else { + continue; + } + + BuildMI(MBB, MBBI, DL, TII.get(XorOp), Reg).addReg(Reg).addReg(Reg); + } +} + void X86FrameLowering::emitStackProbe( MachineFunction &MF, MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, const DebugLoc &DL, bool InProlog, diff --git a/llvm/lib/Target/X86/X86FrameLowering.h b/llvm/lib/Target/X86/X86FrameLowering.h index 987facb..43651ac 100644 --- a/llvm/lib/Target/X86/X86FrameLowering.h +++ b/llvm/lib/Target/X86/X86FrameLowering.h @@ -233,6 +233,10 @@ private: const DebugLoc &DL, uint64_t Offset, uint64_t Align) const; + /// Emit target zero call-used regs. + void emitZeroCallUsedRegs(BitVector RegsToZero, + MachineBasicBlock &MBB) const override; + void adjustFrameForMsvcCxxEh(MachineFunction &MF) const; /// Aligns the stack pointer by ANDing it with -MaxAlign. diff --git a/llvm/lib/Target/X86/X86RegisterInfo.cpp b/llvm/lib/Target/X86/X86RegisterInfo.cpp index 130cb61..9436a10 100644 --- a/llvm/lib/Target/X86/X86RegisterInfo.cpp +++ b/llvm/lib/Target/X86/X86RegisterInfo.cpp @@ -618,6 +618,62 @@ BitVector X86RegisterInfo::getReservedRegs(const MachineFunction &MF) const { return Reserved; } +bool X86RegisterInfo::isArgumentRegister(const MachineFunction &MF, + MCRegister Reg) const { + const X86Subtarget &ST = MF.getSubtarget<X86Subtarget>(); + const TargetRegisterInfo &TRI = *ST.getRegisterInfo(); + auto IsSubReg = [&](MCRegister RegA, MCRegister RegB) { + return TRI.isSuperOrSubRegisterEq(RegA, RegB); + }; + + if (!ST.is64Bit()) + return llvm::any_of( + SmallVector<MCRegister>{X86::EAX, X86::ECX, X86::EDX}, + [&](MCRegister &RegA) { return IsSubReg(RegA, Reg); }) || + (ST.hasMMX() && X86::VR64RegClass.contains(Reg)); + + CallingConv::ID CC = MF.getFunction().getCallingConv(); + + if (CC == CallingConv::X86_64_SysV && IsSubReg(X86::RAX, Reg)) + return true; + + if (llvm::any_of( + SmallVector<MCRegister>{X86::RDX, X86::RCX, X86::R8, X86::R9}, + [&](MCRegister &RegA) { return IsSubReg(RegA, Reg); })) + return true; + + if (CC != CallingConv::Win64 && + llvm::any_of(SmallVector<MCRegister>{X86::RDI, X86::RSI}, + [&](MCRegister &RegA) { return IsSubReg(RegA, Reg); })) + return true; + + if (ST.hasSSE1() && + llvm::any_of(SmallVector<MCRegister>{X86::XMM0, X86::XMM1, X86::XMM2, + X86::XMM3, X86::XMM4, X86::XMM5, + X86::XMM6, X86::XMM7}, + [&](MCRegister &RegA) { return IsSubReg(RegA, Reg); })) + return true; + + return false; +} + +bool X86RegisterInfo::isFixedRegister(const MachineFunction &MF, + MCRegister PhysReg) const { + const X86Subtarget &ST = MF.getSubtarget<X86Subtarget>(); + const TargetRegisterInfo &TRI = *ST.getRegisterInfo(); + + // Stack pointer. + if (TRI.isSuperOrSubRegisterEq(X86::RSP, PhysReg)) + return true; + + // Don't use the frame pointer if it's being used. + const X86FrameLowering &TFI = *getFrameLowering(MF); + if (TFI.hasFP(MF) && TRI.isSuperOrSubRegisterEq(X86::RBP, PhysReg)) + return true; + + return X86GenRegisterInfo::isFixedRegister(MF, PhysReg); +} + void X86RegisterInfo::adjustStackMapLiveOutMask(uint32_t *Mask) const { // Check if the EFLAGS register is marked as live-out. This shouldn't happen, // because the calling convention defines the EFLAGS register as NOT diff --git a/llvm/lib/Target/X86/X86RegisterInfo.h b/llvm/lib/Target/X86/X86RegisterInfo.h index 7fd10dd..4db0ea6 100644 --- a/llvm/lib/Target/X86/X86RegisterInfo.h +++ b/llvm/lib/Target/X86/X86RegisterInfo.h @@ -115,6 +115,15 @@ public: /// register scavenger to determine what registers are free. BitVector getReservedRegs(const MachineFunction &MF) const override; + /// isArgumentReg - Returns true if Reg can be used as an argument to a + /// function. + bool isArgumentRegister(const MachineFunction &MF, + MCRegister Reg) const override; + + /// Returns true if PhysReg is a fixed register. + bool isFixedRegister(const MachineFunction &MF, + MCRegister PhysReg) const override; + void adjustStackMapLiveOutMask(uint32_t *Mask) const override; bool hasBasePointer(const MachineFunction &MF) const; diff --git a/llvm/lib/Target/X86/X86RegisterInfo.td b/llvm/lib/Target/X86/X86RegisterInfo.td index 1b704bc..6362386 100644 --- a/llvm/lib/Target/X86/X86RegisterInfo.td +++ b/llvm/lib/Target/X86/X86RegisterInfo.td @@ -638,3 +638,14 @@ def VK64WM : RegisterClass<"X86", [v64i1], 64, (add VK32WM)> {let Size = 64;} let CopyCost = -1 in // Don't allow copying of tile registers def TILE : RegisterClass<"X86", [x86amx], 8192, (sequence "TMM%u", 0, 7)> {let Size = 8192;} + +//===----------------------------------------------------------------------===// +// Register categories. +// + +// The TILE and VK*PAIR registers may not be "fixed", but we don't want them +// anyway. +def FixedRegisters : RegisterCategory<[DEBUG_REG, CONTROL_REG, CCR, FPCCR, + DFCCR, TILE, VK1PAIR, VK2PAIR, VK4PAIR, + VK8PAIR, VK16PAIR]>; +def GeneralPurposeRegisters : RegisterCategory<[GR64, GR32, GR16, GR8]>; |