Index: include/llvm/ADT/APInt.h =================================================================== --- include/llvm/ADT/APInt.h +++ include/llvm/ADT/APInt.h @@ -1180,6 +1180,13 @@ /// that is greater than or equal to the current width. APInt LLVM_ATTRIBUTE_UNUSED_RESULT trunc(unsigned width) const; + /// Truncate to a width. + /// + /// Truncate the APInt to \p width, or leave it leave it alone if its current + /// width is \p width. It is an error to specify a \p width that is greater + /// than its current width. + APInt LLVM_ATTRIBUTE_UNUSED_RESULT truncOrSelf(unsigned width) const; + /// \brief Sign extend to a new width. /// /// This operation sign extends the APInt to a new width. If the high order Index: include/llvm/ADT/STLExtras.h =================================================================== --- include/llvm/ADT/STLExtras.h +++ include/llvm/ADT/STLExtras.h @@ -419,6 +419,22 @@ return std::find(Range.begin(), Range.end(), Element) != Range.end(); } +/// Wrapper function around std::count_if to count the number of times an +/// element satisfying a given predicate occurs in a range. +template +auto count_if(R &&Range, UnaryPredicate &&P) + -> typename std::iterator_traits::difference_type { + return std::count_if(Range.begin(), Range.end(), P); +} + +/// Wrapper function around std::copy_if to copy the elements in \p Range +/// satisfying \p P to another range beginning at \p DestBegin. +template +OutputItrTy copy_if(RangeTy &&Range, OutputItrTy &&DestBegin, + UnaryPredicate &&P) { + return std::copy_if(Range.begin(), Range.end(), DestBegin, P); +} + //===----------------------------------------------------------------------===// // Extra additions to //===----------------------------------------------------------------------===// Index: include/llvm/CodeGen/Passes.h =================================================================== --- include/llvm/CodeGen/Passes.h +++ include/llvm/CodeGen/Passes.h @@ -263,6 +263,10 @@ /// \brief This pass lays out funclets contiguously. extern char &FuncletLayoutID; + /// This pass instruments generated code to verify that call targets honour + /// their callee saved registers specification. + extern char &VerifyCalleeSavedRegsID; + /// \brief This pass implements the "patchable-function" attribute. extern char &PatchableFunctionID; Index: include/llvm/InitializePasses.h =================================================================== --- include/llvm/InitializePasses.h +++ include/llvm/InitializePasses.h @@ -327,6 +327,7 @@ void initializeUnreachableBlockElimPass(PassRegistry&); void initializeUnreachableMachineBlockElimPass(PassRegistry&); void initializeVerifierLegacyPassPass(PassRegistry&); +void initializeVerifyCalleeSavedRegsPass(PassRegistry &); void initializeVirtRegMapPass(PassRegistry&); void initializeVirtRegRewriterPass(PassRegistry&); void initializeWholeProgramDevirtPass(PassRegistry &); Index: include/llvm/Target/TargetInstrInfo.h =================================================================== --- include/llvm/Target/TargetInstrInfo.h +++ include/llvm/Target/TargetInstrInfo.h @@ -526,11 +526,10 @@ llvm_unreachable("Target didn't implement TargetInstrInfo::RemoveBranch!"); } - /// Insert branch code into the end of the specified MachineBasicBlock. - /// The operands to this method are the same as those - /// returned by AnalyzeBranch. This is only invoked in cases where - /// AnalyzeBranch returns success. It returns the number of instructions - /// inserted. + /// Insert branch code into the end of the specified MachineBasicBlock. The + /// operands to this method are the same as those returned by AnalyzeBranch or + /// InsertCompareImm. This is only invoked in cases where AnalyzeBranch + /// returns success. It returns the number of instructions inserted. /// /// It is also invoked by tail merging to add unconditional branches in /// cases where AnalyzeBranch doesn't apply because there was no original @@ -758,6 +757,17 @@ llvm_unreachable("Target didn't implement TargetInstrInfo::copyPhysReg!"); } + /// Emit instructions to copy the constant \p Imm into \p DestReg (which has + /// to be a physical register). If \p AllowConstantPool is true, use a + /// constant pool value if profitable or required. Return true on success. + virtual bool insertLoadImmediate(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, DebugLoc DL, + const TargetRegisterClass *RC, + unsigned DestReg, Constant *Imm, + bool AllowConstantPool = true) const { + return false; + } + /// Store the specified register of the given register class to the specified /// stack frame index. The store instruction is to be added to the given /// machine basic block before the specified machine instruction. If isKill @@ -1167,6 +1177,23 @@ return false; } + /// Append code to the end of MBB to compare \p Reg and \p Imm according to \p + /// CmpCond. On success append a series of condition codes to \p Cond that + /// can be passed to \c InsertBranch, and return true. On failure, return + /// false. + virtual bool InsertCompareImm( + MachineBasicBlock &MBB, DebugLoc DL, const TargetRegisterClass *RC, + MachineBranchPredicate::ComparePredicate CmpCond, unsigned Reg, + Constant *Imm, SmallVectorImpl &Cond) const { + return false; + } + + /// Append a breakpoint instruction to the end of \p MBB. A breakpoint + /// instruction raises a SIGTRAP signal when executed. + virtual bool EmitBreakpoint(MachineBasicBlock &MBB, DebugLoc DL) const { + return false; + } + /// Return the number of u-operations the given machine /// instruction will be decoded to on the target cpu. The itinerary's /// IssueWidth is the number of microops that can be dispatched each Index: lib/CodeGen/CMakeLists.txt =================================================================== --- lib/CodeGen/CMakeLists.txt +++ lib/CodeGen/CMakeLists.txt @@ -129,6 +129,7 @@ TargetSchedule.cpp TwoAddressInstructionPass.cpp UnreachableBlockElim.cpp + VerifyCalleeSavedRegs.cpp VirtRegMap.cpp WinEHPrepare.cpp Index: lib/CodeGen/CodeGen.cpp =================================================================== --- lib/CodeGen/CodeGen.cpp +++ lib/CodeGen/CodeGen.cpp @@ -83,6 +83,7 @@ initializeUnpackMachineBundlesPass(Registry); initializeUnreachableBlockElimPass(Registry); initializeUnreachableMachineBlockElimPass(Registry); + initializeVerifyCalleeSavedRegsPass(Registry); initializeVirtRegMapPass(Registry); initializeVirtRegRewriterPass(Registry); initializeWinEHPreparePass(Registry); Index: lib/CodeGen/TargetPassConfig.cpp =================================================================== --- lib/CodeGen/TargetPassConfig.cpp +++ lib/CodeGen/TargetPassConfig.cpp @@ -96,6 +96,12 @@ cl::desc("Print machine instrs"), cl::value_desc("pass-name"), cl::init("option-unspecified")); +static cl::opt EnableCSRVerification( + "verify-callee-saved-registers", cl::Hidden, + cl::desc( + "Instrument code to check that call targets honour their CSR list"), + cl::init(false)); + // Temporary option to allow experimenting with MachineScheduler as a post-RA // scheduler. Targets can "properly" enable this with // substitutePass(&PostRASchedulerID, &PostMachineSchedulerID). @@ -555,6 +561,9 @@ addPass(&LocalStackSlotAllocationID, false); } + if (EnableCSRVerification) + addPass(&VerifyCalleeSavedRegsID, true); + // Run pre-ra passes. addPreRegAlloc(); Index: lib/CodeGen/VerifyCalleeSavedRegs.cpp =================================================================== --- /dev/null +++ lib/CodeGen/VerifyCalleeSavedRegs.cpp @@ -0,0 +1,324 @@ +//===-- VerifyCalleeSavedRegs.cpp - Instrument code to verify CSR spec ----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This pass instruments generated code to check that call targets preserve the +// registers they were supposed to preserve. This is a best-effort pass, but it +// does get many interesting cases. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/Sequence.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/CodeGen/Analysis.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/IR/Constants.h" +#include "llvm/Target/TargetInstrInfo.h" +using namespace llvm; + +#define DEBUG_TYPE "verify-calling-convs" + +namespace { +struct VerifyCalleeSavedRegs : public MachineFunctionPass { + static char ID; // Pass identification, replacement for typeid + VerifyCalleeSavedRegs() : MachineFunctionPass(ID) { + initializeVerifyCalleeSavedRegsPass(*PassRegistry::getPassRegistry()); + } + + unsigned FrameSetupOp; + unsigned FrameDestroyOp; + + bool runOnMachineFunction(MachineFunction &F) override; + + /// Gather all of the callee saved registers specified in \p Regs that are: + /// + /// - Maximal (i.e. do not also have a super register specified in \p Regs). + /// - Not used elsewhere in the function. Since this is running before + /// register allocation, such cases should be rare. + void gatherMaximalPreservedRegisters(const TargetRegisterInfo &TRI, + const MachineRegisterInfo &MRI, + MachineOperand &RegMask, + SmallVectorImpl &Regs); + + /// Rewrites all of the physical registers live over the edge from FromMBB to + /// ToMBB to use virtual registers instead, since in SSA physical registers + /// cannot be live across basic blocks. + /// + /// \pre This inserts COPY instructions at the end of \p FromMBB, so it is + /// expected that \p FromMBB does not yet have a terminator. + void updatePhysRegUses(MachineRegisterInfo &MRI, + const TargetRegisterInfo &TRI, + const TargetInstrInfo &TII, const DebugLoc &DL, + MachineBasicBlock &FromMBB, + MachineBasicBlock &ToMBB) const; + + /// Instrument the call instruction at \p MI to verify at runtime that the + /// callee saved registers specified in \p CSRs are preserved across the call. + bool instrumentCall(LLVMContext &, const TargetInstrInfo &, + const TargetRegisterInfo &, MachineRegisterInfo &, + MachineFunction &, MachineInstr *MI, + ArrayRef CSRs) const; +}; +} + +char VerifyCalleeSavedRegs::ID = 0; +char &llvm::VerifyCalleeSavedRegsID = VerifyCalleeSavedRegs::ID; +INITIALIZE_PASS(VerifyCalleeSavedRegs, "verify-calling-convs", + "Add code to verify callee saved registers", false, false) + +static MachineOperand &getRegMask(MachineInstr &MI) { + assert(MI.isCall() && "Must be!"); + auto IsRegMask = [&](const MachineOperand &MO) { return MO.isRegMask(); }; + assert(count_if(MI.operands(), IsRegMask) == 1 && + "Expected exactly one regmask operand!"); + return *find_if(MI.operands(), IsRegMask); +} + +void VerifyCalleeSavedRegs::gatherMaximalPreservedRegisters( + const TargetRegisterInfo &TRI, const MachineRegisterInfo &MRI, + MachineOperand &RegMask, SmallVectorImpl &Regs) { + auto ShouldVerifyReg = [&](const unsigned Reg) { + if (RegMask.clobbersPhysReg(Reg)) + return false; // Not an otherwise unused preserved register + + // None of the super or sub registers should be in use either. We don't + // want to instrument callee saved registers registers that are already + // used, for instance, for passing parameters, since that will make the + // instrumentation code more complex. + for (MCSuperRegIterator SR(Reg, &TRI, /*IncludeSelf=*/true); SR.isValid(); + ++SR) + if (MRI.isPhysRegUsed(Reg)) + return false; + for (MCSubRegIterator SR(Reg, &TRI); SR.isValid(); ++SR) + if (MRI.isPhysRegUsed(Reg)) + return false; + + // Check for maximality -- there should be no super register that is also + // preserved. If there is, we will instrument that super-register instead. + for (MCSuperRegIterator SR(Reg, &TRI, /*IncludeSelf=*/false); SR.isValid(); + ++SR) + if (!RegMask.clobbersPhysReg(*SR)) + return false; + + return true; + }; + + copy_if(seq(0u, TRI.getNumRegs()), std::back_inserter(Regs), ShouldVerifyReg); +} + +bool VerifyCalleeSavedRegs::runOnMachineFunction(MachineFunction &MF) { + bool Changed = false; + auto &MRI = MF.getRegInfo(); + auto &TRI = *MRI.getTargetRegisterInfo(); + auto &TII = *MF.getSubtarget().getInstrInfo(); + auto &Ctx = MF.getFunction()->getContext(); + + SmallVector Calls; + std::vector> CSRsAtCall; + + for (auto &BB : MF) + for (auto &MI : BB) { + if (!MI.isCall()) + continue; + + Calls.push_back(&MI); + CSRsAtCall.emplace_back(); + gatherMaximalPreservedRegisters(TRI, MRI, getRegMask(MI), + CSRsAtCall.back()); + } + + FrameSetupOp = TII.getCallFrameSetupOpcode(); + FrameDestroyOp = TII.getCallFrameDestroyOpcode(); + + for (unsigned CallIdx : seq(0ul, Calls.size())) + Changed |= instrumentCall(Ctx, TII, TRI, MRI, MF, Calls[CallIdx], + CSRsAtCall[CallIdx]); + + return Changed; +} + +bool VerifyCalleeSavedRegs::instrumentCall( + LLVMContext &Ctx, const TargetInstrInfo &TII, const TargetRegisterInfo &TRI, + MachineRegisterInfo &MRI, MachineFunction &MF, MachineInstr *CallMI, + ArrayRef CSRsAtCall) const { + const auto &DL = CallMI->getDebugLoc(); + auto *CallMBB = CallMI->getParent(); + + // Compute the locations before and after the call instruction. + auto BeforeCallIt = std::next(MachineBasicBlock::iterator(CallMI)); + auto AfterCallIt = std::next(MachineBasicBlock::iterator(CallMI)); + + while (BeforeCallIt != CallMBB->begin() && + BeforeCallIt->getOpcode() != FrameSetupOp) + BeforeCallIt--; + + assert(BeforeCallIt->getOpcode() == FrameSetupOp && + "Could not find beginning of call sequence!"); + + while (AfterCallIt != CallMBB->end() && + AfterCallIt->getOpcode() != FrameDestroyOp) + AfterCallIt++; + + assert(AfterCallIt->getOpcode() == FrameDestroyOp && + "Could not find end of call sequence!"); + AfterCallIt++; + + // We don't handle invokes yet. + if (AfterCallIt->isEHLabel()) + return false; + + struct CSRToCheck { + unsigned Reg; + const TargetRegisterClass *RC; + ConstantInt *Cookie; + + CSRToCheck(unsigned Reg, const TargetRegisterClass *RC, ConstantInt *Cookie) + : Reg(Reg), RC(RC), Cookie(Cookie) {} + }; + + SmallVector CSRsToCheck; + for (unsigned PhysReg : CSRsAtCall) { + auto *RC = TRI.getMinimalPhysRegClass(PhysReg); + unsigned RegSize = RC->getSize(); + if (RegSize > 8) { + // To work with wider types, we'll need to update the callbacks into TII + // to have more coverage. + continue; + } + + auto *Cookie = ConstantInt::get( + Ctx, APInt(64, 0xCA5FCA5FCA5FCA5F).truncOrSelf(RegSize * 8)); + + if (!TII.insertLoadImmediate(*CallMBB, BeforeCallIt, DL, RC, PhysReg, + Cookie)) + continue; + + unsigned VirtReg = MRI.createVirtualRegister(RC); + BuildMI(*CallMBB, AfterCallIt, DL, TII.get(TargetOpcode::COPY), VirtReg) + .addReg(PhysReg, RegState::Kill); + + CSRsToCheck.emplace_back(VirtReg, RC, Cookie); + } + + if (CSRsToCheck.empty()) + return false; + + auto *ContinueMBB = MF.CreateMachineBasicBlock(); + + // AfterCopiesIt points to the location in CallMBB after all the physical + // registers have been copied to virtual registers. This includes the COPY + // out of the return value, if any. + auto AfterCopiesIt = AfterCallIt; + while (AfterCopiesIt != CallMBB->end() && + AfterCopiesIt->getOpcode() == TargetOpcode::COPY) + AfterCopiesIt++; + + MF.insert(std::next(MachineFunction::iterator(CallMBB)), ContinueMBB); + ContinueMBB->splice(ContinueMBB->begin(), CallMBB, AfterCopiesIt, + CallMBB->end()); + ContinueMBB->transferSuccessorsAndUpdatePHIs(CallMBB); + + updatePhysRegUses(MRI, TRI, TII, DL, *CallMBB, *ContinueMBB); + + // FailingMBB is the block we branch to to indicate that something went wrong. + auto *FailingMBB = MF.CreateMachineBasicBlock(); + MF.insert(MF.end(), FailingMBB); + TII.EmitBreakpoint(*FailingMBB, DL); + + MachineBasicBlock *CheckMBB = CallMBB; + + for (auto &RegToCheck : CSRsToCheck) { + unsigned Reg = RegToCheck.Reg; + ConstantInt *Cookie = RegToCheck.Cookie; + const TargetRegisterClass *RC = RegToCheck.RC; + + // The control flow we want is: + // + // CALL + // CMP64rm %csr0, + // JNE + // JMP + // + // check_next_0: + // CMP64rm %csr1, + // JNE + // JMP + // + // check_next_1: + // ... + // ... + // + // check_next_: + // br + + SmallVector Cond; + if (!TII.InsertCompareImm(*CheckMBB, DL, RC, + TargetInstrInfo::MachineBranchPredicate::PRED_NE, + Reg, Cookie, Cond)) + continue; + + auto *NextCheckMBB = MF.CreateMachineBasicBlock(); + MF.insert(std::next(MachineFunction::iterator(CheckMBB)), NextCheckMBB); + TII.InsertBranch(*CheckMBB, FailingMBB, NextCheckMBB, Cond, DL); + + CheckMBB->addSuccessor(NextCheckMBB); + CheckMBB->addSuccessor(FailingMBB); + + CheckMBB = NextCheckMBB; + } + + TII.InsertBranch(*CheckMBB, ContinueMBB, nullptr, None, DL); + CheckMBB->addSuccessor(ContinueMBB); + + return true; +} + +void VerifyCalleeSavedRegs::updatePhysRegUses(MachineRegisterInfo &MRI, + const TargetRegisterInfo &TRI, + const TargetInstrInfo &TII, + const DebugLoc &DL, + MachineBasicBlock &FromMBB, + MachineBasicBlock &ToMBB) const { + DenseMap ExportedPhysRegs; + SmallSet PhysRegDefs; + + for (auto &MI : ToMBB) { + // Look at every physical register use in ToMBB, and rewrite them to use a + // virtual register exported from FromMBB. + for (auto &MO : MI.operands()) { + if (!MO.isReg() || !TargetRegisterInfo::isPhysicalRegister(MO.getReg())) + continue; + + if (MO.isDef()) { + PhysRegDefs.insert(MO.getReg()); + continue; + } + + if (PhysRegDefs.count(MO.getReg())) + continue; + + auto Itr = ExportedPhysRegs.find(MO.getReg()); + if (Itr == ExportedPhysRegs.end()) { + // Add copies only if we've not already copies out the physical + // register. + unsigned VirtReg = + MRI.createVirtualRegister(TRI.getMinimalPhysRegClass(MO.getReg())); + BuildMI(FromMBB, FromMBB.end(), DL, TII.get(TargetOpcode::COPY), + VirtReg).addReg(MO.getReg(), RegState::Kill); + Itr = ExportedPhysRegs.insert({MO.getReg(), VirtReg}).first; + } + + // Change to use a virtual register. + MO.setReg(Itr->second); + } + } +} Index: lib/Support/APInt.cpp =================================================================== --- lib/Support/APInt.cpp +++ lib/Support/APInt.cpp @@ -957,6 +957,13 @@ return Result; } +APInt APInt::truncOrSelf(unsigned width) const { + assert(width <= BitWidth && "Invalid APInt Truncate request"); + assert(width && "Can't truncate to 0 bits"); + + return width == BitWidth ? *this : trunc(width); +} + // Sign extend to a new width. APInt APInt::sext(unsigned width) const { assert(width > BitWidth && "Invalid APInt SignExtend request"); Index: lib/Target/X86/X86InstrInfo.h =================================================================== --- lib/Target/X86/X86InstrInfo.h +++ lib/Target/X86/X86InstrInfo.h @@ -525,6 +525,20 @@ unsigned &FoldAsLoadDefReg, MachineInstr *&DefMI) const override; + bool InsertCompareImm(MachineBasicBlock &MBB, DebugLoc DL, + const TargetRegisterClass *RC, + MachineBranchPredicate::ComparePredicate CmpCond, + unsigned Reg, Constant *Imm, + SmallVectorImpl &Cond) const; + + bool EmitBreakpoint(MachineBasicBlock &MBB, DebugLoc DL) const override; + + bool insertLoadImmediate(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, DebugLoc DL, + const TargetRegisterClass *RC, unsigned DestReg, + Constant *Imm, + bool AllowConstantPool) const override; + std::pair decomposeMachineOperandsTargetFlags(unsigned TF) const override; Index: lib/Target/X86/X86InstrInfo.cpp =================================================================== --- lib/Target/X86/X86InstrInfo.cpp +++ lib/Target/X86/X86InstrInfo.cpp @@ -4207,6 +4207,11 @@ return Count; } +bool X86InstrInfo::EmitBreakpoint(MachineBasicBlock &MBB, DebugLoc DL) const { + BuildMI(MBB, MBB.end(), DL, get(X86::INT3)); + return true; +} + unsigned X86InstrInfo::InsertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB, MachineBasicBlock *FBB, ArrayRef Cond, @@ -4580,6 +4585,11 @@ llvm_unreachable("Cannot emit physreg copy instruction"); } +static unsigned getLoadStoreRegOpcode(unsigned Reg, + const TargetRegisterClass *RC, + bool isStackAligned, + const X86Subtarget &STI, bool load); + static unsigned getLoadStoreMaskRegOpcode(const TargetRegisterClass *RC, bool load) { switch (RC->getSize()) { @@ -4742,6 +4752,71 @@ return getLoadStoreRegOpcode(DestReg, RC, isStackAligned, STI, true); } +bool X86InstrInfo::insertLoadImmediate(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + DebugLoc DL, + const TargetRegisterClass *RC, + unsigned DestReg, Constant *Imm, + bool AllowConstantPool) const { + unsigned Opcode; + if (X86::GR8RegClass.contains(DestReg)) + Opcode = X86::MOV8ri; + else if (X86::GR16RegClass.contains(DestReg)) + Opcode = X86::MOV16ri; + else if (X86::GR32RegClass.contains(DestReg)) + Opcode = X86::MOV32ri; + else if (X86::GR64RegClass.contains(DestReg)) { + MachineFunction &MF = *MBB.getParent(); + if (!AllowConstantPool || + MF.getTarget().getRelocationModel() == Reloc::PIC_) + Opcode = X86::MOV64ri; + else { + MachineConstantPool &MCP = *MF.getConstantPool(); + unsigned CPI = MCP.getConstantPoolIndex(Imm, RC->getSize()); + BuildMI(MBB, MI, DL, get(X86::MOV64rm), DestReg) + .addReg(0) + .addImm(1) + .addReg(0) + .addConstantPoolIndex(CPI) + .addReg(0); + return true; + } + } else + return false; + + BuildMI(MBB, MI, DL, get(Opcode), DestReg) + .addImm(cast(Imm)->getZExtValue()); + return true; +} + +bool X86InstrInfo::InsertCompareImm( + MachineBasicBlock &MBB, DebugLoc DL, const TargetRegisterClass *RC, + MachineBranchPredicate::ComparePredicate CmpCond, unsigned Reg, + Constant *Imm, SmallVectorImpl &Cond) const { + // Lifting the following two restrictions should be easy, but for now we only + // support the configurations that are tested. + if (CmpCond != MachineBranchPredicate::PRED_NE || + !X86::GR64RegClass.hasSubClassEq(RC)) + return false; + + MachineFunction &MF = *MBB.getParent(); + if (MF.getTarget().getRelocationModel() == Reloc::PIC_) + return false; + + MachineConstantPool &MCP = *MF.getConstantPool(); + unsigned CPI = MCP.getConstantPoolIndex(Imm, RC->getSize()); + BuildMI(MBB, MBB.end(), DL, get(X86::CMP64rm)) + .addReg(Reg) + .addReg(0) + .addImm(1) + .addReg(0) + .addConstantPoolIndex(CPI) + .addReg(0); + + Cond.push_back(MachineOperand::CreateImm(X86::COND_NE)); + return true; +} + void X86InstrInfo::storeRegToStackSlot(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, unsigned SrcReg, bool isKill, int FrameIdx, Index: test/CodeGen/X86/verify-csrs.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/verify-csrs.ll @@ -0,0 +1,165 @@ +; RUN: llc -O3 -verify-machineinstrs -verify-callee-saved-registers < %s | FileCheck %s + +target triple = "x86_64-unknown-linux-gnu" + +declare void @foo() +declare double @foo_dbl() +declare i32 @bar(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32) + +define void @f_0(i32* %ptr) { +; CHECK-LABEL: f_0: +; CHECK: movl $10, (%rdi) +; CHECK: movq .LCPI0_0, %rbp +; CHECK: movq .LCPI0_0, %rbx +; CHECK: movq .LCPI0_0, %r12 +; CHECK: movq .LCPI0_0, %r13 +; CHECK: movq .LCPI0_0, %r14 +; CHECK: movq .LCPI0_0, %r15 +; CHECK: callq foo +; CHECK: cmpq .LCPI0_0, %rbp +; CHECK: jne [[BREAK_LBL_F_0:.LBB[0-9_]+]] +; CHECK: cmpq .LCPI0_0, %rbx +; CHECK: jne [[BREAK_LBL_F_0]] +; CHECK: cmpq .LCPI0_0, %r12 +; CHECK: jne [[BREAK_LBL_F_0]] +; CHECK: cmpq .LCPI0_0, %r13 +; CHECK: jne [[BREAK_LBL_F_0]] +; CHECK: cmpq .LCPI0_0, %r14 +; CHECK: jne [[BREAK_LBL_F_0]] +; CHECK: cmpq .LCPI0_0, %r15 +; CHECK: jne [[BREAK_LBL_F_0]] +; CHECK: movl $20, (%rax) +; CHECK: retq +; CHECK: [[BREAK_LBL_F_0]]: +; CHECK: int3 + +entry: + store i32 10, i32* %ptr + call void @foo() + store i32 20, i32* %ptr + ret void +} + +define void @f_1(i32* %ptr) personality i8 3 { +; Invokes are not supported yet + +; CHECK-LABEL: f_1: +; CHECK-NOT: .LCPI +; CHECK: .cfi_endproc +entry: + store i32 10, i32* %ptr + invoke void @foo() to label %ok unwind label %not_ok + +ok: + store i32 20, i32* %ptr + ret void + +not_ok: + %e = landingpad { i8*, i32 } cleanup + store i32 30, i32* %ptr + ret void +} + +define void @f_2(i32* %ptr) { +; CHECK-LABEL: f_2: +; CHECK: movl $10, (%rdi) +; CHECK: movq .LCPI2_0, %rbp +; CHECK: movq .LCPI2_0, %rbx +; CHECK: movq .LCPI2_0, %r12 +; CHECK: movq .LCPI2_0, %r13 +; CHECK: movq .LCPI2_0, %r14 +; CHECK: movq .LCPI2_0, %r15 +; CHECK: movl $100, %edi +; CHECK: movl $200, %esi +; CHECK: movl $300, %edx # imm = 0x12C +; CHECK: movl $400, %ecx # imm = 0x190 +; CHECK: movl $500, %r8d # imm = 0x1F4 +; CHECK: movl $600, %r9d # imm = 0x258 +; CHECK: pushq $1200 # imm = 0x4B0 +; CHECK: pushq $1100 # imm = 0x44C +; CHECK: pushq $1000 # imm = 0x3E8 +; CHECK: pushq $900 # imm = 0x384 +; CHECK: pushq $800 # imm = 0x320 +; CHECK: pushq $700 # imm = 0x2BC +; CHECK: callq bar +; CHECK: addq $48, %rsp +; CHECK: cmpq .LCPI2_0, %rbp +; CHECK: jne [[BREAK_LBL_F_2:.LBB[0-9_]+]] +; CHECK: cmpq .LCPI2_0, %rbx +; CHECK: jne [[BREAK_LBL_F_2]] +; CHECK: cmpq .LCPI2_0, %r12 +; CHECK: jne [[BREAK_LBL_F_2]] +; CHECK: cmpq .LCPI2_0, %r13 +; CHECK: jne [[BREAK_LBL_F_2]] +; CHECK: cmpq .LCPI2_0, %r14 +; CHECK: jne [[BREAK_LBL_F_2]] +; CHECK: cmpq .LCPI2_0, %r15 +; CHECK: jne [[BREAK_LBL_F_2]] +; CHECK: movl %eax, (%rcx) +; CHECK: [[BREAK_LBL_F_2]]: +; CHECK: int3 + +entry: + store i32 10, i32* %ptr + %val = call i32 @bar(i32 100, i32 200, i32 300, i32 400, i32 500, i32 600, i32 700, i32 800, i32 900, i32 1000, i32 1100, i32 1200) + store i32 %val, i32* %ptr + ret void +} + +define double @f_3(i32 %cond) { +; CHECK-LABEL: f_3: +; CHECK: movq .LCPI3_0, %rbp +; CHECK: movq .LCPI3_0, %rbx +; CHECK: movq .LCPI3_0, %r12 +; CHECK: movq .LCPI3_0, %r13 +; CHECK: movq .LCPI3_0, %r14 +; CHECK: movq .LCPI3_0, %r15 +; CHECK: callq foo_dbl +; CHECK: cmpq .LCPI3_0, %rbp +; CHECK: jne [[BREAK_LBL_F_3:.LBB[0-9_]+]] +; CHECK: cmpq .LCPI3_0, %rbx +; CHECK: jne [[BREAK_LBL_F_3]] +; CHECK: cmpq .LCPI3_0, %r12 +; CHECK: jne [[BREAK_LBL_F_3]] +; CHECK: cmpq .LCPI3_0, %r13 +; CHECK: jne [[BREAK_LBL_F_3]] +; CHECK: cmpq .LCPI3_0, %r14 +; CHECK: jne [[BREAK_LBL_F_3]] +; CHECK: cmpq .LCPI3_0, %r15 +; CHECK: jne [[BREAK_LBL_F_3]] +; CHECK: retq +; CHECK: [[BREAK_LBL_F_3]]: +; CHECK: int3 + +entry: + %rval = call double @foo_dbl() + ret double %rval +} + + +define void @f_4(i32 %cond, i32* %ptr) { +; FileCheck for only one CSR to avoid verbosity. + +; CHECK-LABEL: f_4: +; CHECK: movq .LCPI4_0, %r15 +; CHECK: callq foo +; CHECK: cmpq .LCPI4_0, %rbp +; CHECK: jne [[BREAK_LBL_F_4:.LBB[0-9_]+]] +; CHECK: movq .LCPI4_0, %r15 +; CHECK: callq foo +; CHECK: cmpq .LCPI4_0, %rbp +; CHECK: jne [[BREAK_LBL_F_4]] + +entry: + %cmp = icmp eq i32 %cond, 0 + br i1 %cmp, label %leave, label %stay + +leave: + store i32 10, i32* %ptr + call void @foo() + call void @foo() + unreachable + +stay: + ret void +}