aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib
diff options
context:
space:
mode:
authorBill Wendling <isanbard@gmail.com>2022-02-08 17:40:59 -0800
committerBill Wendling <isanbard@gmail.com>2022-02-08 17:42:54 -0800
commitdeaf22bc0e306bc44c70d2503e9364b5ed312c49 (patch)
tree180a857a884a7ac508577fc0b2bcd69f078c328b /llvm/lib
parentdc8f4e118d92982ff5f6316fe7d541d120ebabd1 (diff)
downloadllvm-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.cpp15
-rw-r--r--llvm/lib/CodeGen/PrologEpilogInserter.cpp93
-rw-r--r--llvm/lib/Target/X86/X86FrameLowering.cpp94
-rw-r--r--llvm/lib/Target/X86/X86FrameLowering.h4
-rw-r--r--llvm/lib/Target/X86/X86RegisterInfo.cpp56
-rw-r--r--llvm/lib/Target/X86/X86RegisterInfo.h9
-rw-r--r--llvm/lib/Target/X86/X86RegisterInfo.td11
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]>;