Index: lib/Target/AArch64/AArch64CallingConvention.td =================================================================== --- lib/Target/AArch64/AArch64CallingConvention.td +++ lib/Target/AArch64/AArch64CallingConvention.td @@ -288,6 +288,14 @@ D8, D9, D10, D11, D12, D13, D14, D15)>; +// Win64 has unwinding codes for an (FP,LR) pair, save_fplr and save_fplr_x. +// We put FP before LR, so that frame lowering logic generates (FP,LR) pairs, +// and not (LR,FP) pairs. +def CSR_Win_AArch64_AAPCS : CalleeSavedRegs<(add FP, LR, X19, X20, X21, X22, + X23, X24, X25, X26, X27, X28, + D8, D9, D10, D11, + D12, D13, D14, D15)>; + // AArch64 PCS for vector functions (VPCS) // must (additionally) preserve full Q8-Q23 registers def CSR_AArch64_AAVPCS : CalleeSavedRegs<(add LR, FP, X19, X20, X21, X22, Index: lib/Target/AArch64/AArch64FrameLowering.cpp =================================================================== --- lib/Target/AArch64/AArch64FrameLowering.cpp +++ lib/Target/AArch64/AArch64FrameLowering.cpp @@ -115,11 +115,13 @@ #include "llvm/CodeGen/TargetInstrInfo.h" #include "llvm/CodeGen/TargetRegisterInfo.h" #include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/CodeGen/WinEHFuncInfo.h" #include "llvm/IR/Attributes.h" #include "llvm/IR/CallingConv.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/DebugLoc.h" #include "llvm/IR/Function.h" +#include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCDwarf.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" @@ -434,12 +436,155 @@ return true; } +// Given a load or a store instruction, generate an appropriate unwinding SEH +// code on Windows. +static MachineBasicBlock::iterator InsertSEH(MachineBasicBlock::iterator MBBI, + const TargetInstrInfo &TII, + MachineInstr::MIFlag Flag) { + unsigned Opc = MBBI->getOpcode(); + MachineBasicBlock *MBB = MBBI->getParent(); + MachineFunction &MF = *MBB->getParent(); + DebugLoc DL = MBBI->getDebugLoc(); + unsigned ImmIdx = MBBI->getNumOperands() - 1; + int Imm = MBBI->getOperand(ImmIdx).getImm(); + MachineInstrBuilder MIB; + const AArch64Subtarget &Subtarget = MF.getSubtarget(); + const AArch64RegisterInfo *RegInfo = Subtarget.getRegisterInfo(); + + switch (Opc) { + default: + assert(false && "No SEH Opcode for this instruction"); + case AArch64::LDPDpost: + Imm = -Imm; + LLVM_FALLTHROUGH; + case AArch64::STPDpre: { + unsigned Reg0 = RegInfo->getSEHRegNum(MBBI->getOperand(1).getReg()); + unsigned Reg1 = RegInfo->getSEHRegNum(MBBI->getOperand(2).getReg()); + MIB = BuildMI(MF, DL, TII.get(AArch64::SEH_SaveFRegP_X)) + .addImm(Reg0) + .addImm(Reg1) + .addImm(Imm * 8) + .setMIFlag(Flag); + break; + } + case AArch64::LDPXpost: + Imm = -Imm; + LLVM_FALLTHROUGH; + case AArch64::STPXpre: { + unsigned Reg0 = MBBI->getOperand(1).getReg(); + unsigned Reg1 = MBBI->getOperand(2).getReg(); + if (Reg0 == AArch64::FP && Reg1 == AArch64::LR) + MIB = BuildMI(MF, DL, TII.get(AArch64::SEH_SaveFPLR_X)) + .addImm(Imm * 8) + .setMIFlag(Flag); + else + MIB = BuildMI(MF, DL, TII.get(AArch64::SEH_SaveRegP_X)) + .addImm(RegInfo->getSEHRegNum(Reg0)) + .addImm(RegInfo->getSEHRegNum(Reg1)) + .addImm(Imm * 8) + .setMIFlag(Flag); + break; + } + case AArch64::LDRDpost: + Imm = -Imm; + LLVM_FALLTHROUGH; + case AArch64::STRDpre: { + unsigned Reg = RegInfo->getSEHRegNum(MBBI->getOperand(1).getReg()); + MIB = BuildMI(MF, DL, TII.get(AArch64::SEH_SaveFReg_X)) + .addImm(Reg) + .addImm(Imm) + .setMIFlag(Flag); + break; + } + case AArch64::LDRXpost: + Imm = -Imm; + LLVM_FALLTHROUGH; + case AArch64::STRXpre: { + unsigned Reg = RegInfo->getSEHRegNum(MBBI->getOperand(1).getReg()); + MIB = BuildMI(MF, DL, TII.get(AArch64::SEH_SaveReg_X)) + .addImm(Reg) + .addImm(Imm) + .setMIFlag(Flag); + break; + } + case AArch64::STPDi: + case AArch64::LDPDi: { + unsigned Reg0 = RegInfo->getSEHRegNum(MBBI->getOperand(0).getReg()); + unsigned Reg1 = RegInfo->getSEHRegNum(MBBI->getOperand(1).getReg()); + MIB = BuildMI(MF, DL, TII.get(AArch64::SEH_SaveFRegP)) + .addImm(Reg0) + .addImm(Reg1) + .addImm(Imm * 8) + .setMIFlag(Flag); + break; + } + case AArch64::STPXi: + case AArch64::LDPXi: { + unsigned Reg0 = MBBI->getOperand(0).getReg(); + unsigned Reg1 = MBBI->getOperand(1).getReg(); + if (Reg0 == AArch64::FP && Reg1 == AArch64::LR) + MIB = BuildMI(MF, DL, TII.get(AArch64::SEH_SaveFPLR)) + .addImm(Imm * 8) + .setMIFlag(Flag); + else + MIB = BuildMI(MF, DL, TII.get(AArch64::SEH_SaveRegP)) + .addImm(RegInfo->getSEHRegNum(Reg0)) + .addImm(RegInfo->getSEHRegNum(Reg1)) + .addImm(Imm * 8) + .setMIFlag(Flag); + break; + } + case AArch64::STRXui: + case AArch64::LDRXui: { + int Reg = RegInfo->getSEHRegNum(MBBI->getOperand(0).getReg()); + MIB = BuildMI(MF, DL, TII.get(AArch64::SEH_SaveReg)) + .addImm(Reg) + .addImm(Imm * 8) + .setMIFlag(Flag); + break; + } + case AArch64::STRDui: + case AArch64::LDRDui: { + unsigned Reg = RegInfo->getSEHRegNum(MBBI->getOperand(0).getReg()); + MIB = BuildMI(MF, DL, TII.get(AArch64::SEH_SaveFReg)) + .addImm(Reg) + .addImm(Imm * 8) + .setMIFlag(Flag); + break; + } + } + auto I = MBB->insertAfter(MBBI, MIB); + return I; +} + +// Fix up the SEH opcode associated with the save/restore instruction. +static void fixupSEHOpcode(MachineBasicBlock::iterator MBBI, + unsigned LocalStackSize) { + MachineOperand *ImmOpnd = nullptr; + unsigned ImmIdx = MBBI->getNumOperands() - 1; + switch (MBBI->getOpcode()) { + default: + assert(false && "Fix the offset in the SEH instruction"); + break; + case AArch64::SEH_SaveFPLR: + case AArch64::SEH_SaveRegP: + case AArch64::SEH_SaveReg: + case AArch64::SEH_SaveFRegP: + case AArch64::SEH_SaveFReg: + ImmOpnd = &MBBI->getOperand(ImmIdx); + break; + } + if (ImmOpnd) + ImmOpnd->setImm(ImmOpnd->getImm() + LocalStackSize); +} + // Convert callee-save register save/restore instruction to do stack pointer // decrement/increment to allocate/deallocate the callee-save stack area by // converting store/load to use pre/post increment version. static MachineBasicBlock::iterator convertCalleeSaveRestoreToSPPrePostIncDec( MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, - const DebugLoc &DL, const TargetInstrInfo *TII, int CSStackSizeInc) { + const DebugLoc &DL, const TargetInstrInfo *TII, int CSStackSizeInc, + bool NeedsWinCFI, bool InProlog = true) { // Ignore instructions that do not operate on SP, i.e. shadow call stack // instructions. while (MBBI->getOpcode() == AArch64::STRXpost || @@ -447,7 +592,6 @@ assert(MBBI->getOperand(0).getReg() != AArch64::SP); ++MBBI; } - unsigned NewOpc; int Scale = 1; switch (MBBI->getOpcode()) { @@ -496,6 +640,12 @@ NewOpc = AArch64::LDRQpost; break; } + // Get rid of the SEH code associated with the old instruction. + if (NeedsWinCFI) { + auto SEH = std::next(MBBI); + if (AArch64InstrInfo::isSEHInstruction(*SEH)) + SEH->eraseFromParent(); + } MachineInstrBuilder MIB = BuildMI(MBB, MBBI, DL, TII->get(NewOpc)); MIB.addReg(AArch64::SP, RegState::Define); @@ -517,13 +667,22 @@ MIB.setMIFlags(MBBI->getFlags()); MIB.setMemRefs(MBBI->memoperands()); + // Generate a new SEH code that corresponds to the new instruction. + if (NeedsWinCFI) + InsertSEH(*MIB, *TII, + InProlog ? MachineInstr::FrameSetup : MachineInstr::FrameDestroy); + return std::prev(MBB.erase(MBBI)); } // Fixup callee-save register save/restore instructions to take into account // combined SP bump by adding the local stack size to the stack offsets. static void fixupCalleeSaveRestoreStackOffset(MachineInstr &MI, - unsigned LocalStackSize) { + unsigned LocalStackSize, + bool NeedsWinCFI) { + if (AArch64InstrInfo::isSEHInstruction(MI)) + return; + unsigned Opc = MI.getOpcode(); // Ignore instructions that do not operate on SP, i.e. shadow call stack @@ -563,6 +722,14 @@ // All generated opcodes have scaled offsets. assert(LocalStackSize % Scale == 0); OffsetOpnd.setImm(OffsetOpnd.getImm() + LocalStackSize / Scale); + + if (NeedsWinCFI) { + auto MBBI = std::next(MachineBasicBlock::iterator(MI)); + assert(MBBI != MI.getParent()->end() && "Expecting a valid instruction"); + assert(AArch64InstrInfo::isSEHInstruction(*MBBI) && + "Expecting a SEH instruction"); + fixupSEHOpcode(MBBI, LocalStackSize); + } } static void adaptForLdStOpt(MachineBasicBlock &MBB, @@ -607,9 +774,12 @@ const TargetInstrInfo *TII = Subtarget.getInstrInfo(); MachineModuleInfo &MMI = MF.getMMI(); AArch64FunctionInfo *AFI = MF.getInfo(); - bool needsFrameMoves = MMI.hasDebugInfo() || F.needsUnwindTableEntry(); + bool needsFrameMoves = (MMI.hasDebugInfo() || F.needsUnwindTableEntry()) && + !MF.getTarget().getMCAsmInfo()->usesWindowsCFI(); bool HasFP = hasFP(MF); - + bool NeedsWinCFI = MF.getTarget().getMCAsmInfo()->usesWindowsCFI() && + F.needsUnwindTableEntry(); + MF.setHasWinCFI(NeedsWinCFI); // At this point, we're going to decide whether or not the function uses a // redzone. In most cases, the function doesn't have a redzone so let's // assume that's false and set it to true in the case that there's a redzone. @@ -632,10 +802,8 @@ int NumBytes = (int)MFI.getStackSize(); if (!AFI->hasStackFrame() && !windowsRequiresStackProbe(MF, NumBytes)) { assert(!HasFP && "unexpected function without stack frame but with FP"); - // All of the stack allocation is for locals. AFI->setLocalStackSize(NumBytes); - if (!NumBytes) return; // REDZONE: If the stack size is less than 128 bytes, we don't need @@ -645,17 +813,23 @@ ++NumRedZoneFunctions; } else { emitFrameOffset(MBB, MBBI, DL, AArch64::SP, AArch64::SP, -NumBytes, TII, - MachineInstr::FrameSetup); - - // Label used to tie together the PROLOG_LABEL and the MachineMoves. - MCSymbol *FrameLabel = MMI.getContext().createTempSymbol(); - // Encode the stack size of the leaf function. - unsigned CFIIndex = MF.addFrameInst( - MCCFIInstruction::createDefCfaOffset(FrameLabel, -NumBytes)); - BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION)) - .addCFIIndex(CFIIndex) - .setMIFlags(MachineInstr::FrameSetup); + MachineInstr::FrameSetup, false, NeedsWinCFI); + if (!NeedsWinCFI) { + // Label used to tie together the PROLOG_LABEL and the MachineMoves. + MCSymbol *FrameLabel = MMI.getContext().createTempSymbol(); + // Encode the stack size of the leaf function. + unsigned CFIIndex = MF.addFrameInst( + MCCFIInstruction::createDefCfaOffset(FrameLabel, -NumBytes)); + BuildMI(MBB, MBBI, DL, TII->get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex) + .setMIFlags(MachineInstr::FrameSetup); + } } + + if (NeedsWinCFI) + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_PrologEnd)) + .setMIFlag(MachineInstr::FrameSetup); + return; } @@ -666,15 +840,14 @@ auto PrologueSaveSize = AFI->getCalleeSavedStackSize() + FixedObject; // All of the remaining stack allocations are for locals. AFI->setLocalStackSize(NumBytes - PrologueSaveSize); - bool CombineSPBump = shouldCombineCSRLocalStackBump(MF, NumBytes); if (CombineSPBump) { emitFrameOffset(MBB, MBBI, DL, AArch64::SP, AArch64::SP, -NumBytes, TII, - MachineInstr::FrameSetup); + MachineInstr::FrameSetup, false, NeedsWinCFI); NumBytes = 0; } else if (PrologueSaveSize != 0) { - MBBI = convertCalleeSaveRestoreToSPPrePostIncDec(MBB, MBBI, DL, TII, - -PrologueSaveSize); + MBBI = convertCalleeSaveRestoreToSPPrePostIncDec( + MBB, MBBI, DL, TII, -PrologueSaveSize, NeedsWinCFI); NumBytes -= PrologueSaveSize; } assert(NumBytes >= 0 && "Negative stack allocation size!?"); @@ -685,9 +858,11 @@ MachineBasicBlock::iterator End = MBB.end(); while (MBBI != End && MBBI->getFlag(MachineInstr::FrameSetup)) { if (CombineSPBump) - fixupCalleeSaveRestoreStackOffset(*MBBI, AFI->getLocalStackSize()); + fixupCalleeSaveRestoreStackOffset(*MBBI, AFI->getLocalStackSize(), + NeedsWinCFI); ++MBBI; } + if (HasFP) { // Only set up FP if we actually need to. Frame pointer is fp = // sp - fixedobject - 16. @@ -700,7 +875,7 @@ // Note: All stores of callee-saved registers are marked as "FrameSetup". // This code marks the instruction(s) that set the FP also. emitFrameOffset(MBB, MBBI, DL, AArch64::FP, AArch64::SP, FPOffset, TII, - MachineInstr::FrameSetup); + MachineInstr::FrameSetup, false, NeedsWinCFI); } if (windowsRequiresStackProbe(MF, NumBytes)) { @@ -709,6 +884,9 @@ BuildMI(MBB, MBBI, DL, TII->get(AArch64::MOVi64imm), AArch64::X15) .addImm(NumWords) .setMIFlags(MachineInstr::FrameSetup); + if (NeedsWinCFI) + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_Nop)) + .setMIFlag(MachineInstr::FrameSetup); switch (MF.getTarget().getCodeModel()) { case CodeModel::Tiny: @@ -719,6 +897,9 @@ .addExternalSymbol("__chkstk") .addReg(AArch64::X15, RegState::Implicit) .setMIFlags(MachineInstr::FrameSetup); + if (NeedsWinCFI) + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_Nop)) + .setMIFlag(MachineInstr::FrameSetup); break; case CodeModel::Large: BuildMI(MBB, MBBI, DL, TII->get(AArch64::MOVaddrEXT)) @@ -726,11 +907,17 @@ .addExternalSymbol("__chkstk") .addExternalSymbol("__chkstk") .setMIFlags(MachineInstr::FrameSetup); + if (NeedsWinCFI) + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_Nop)) + .setMIFlag(MachineInstr::FrameSetup); BuildMI(MBB, MBBI, DL, TII->get(AArch64::BLR)) .addReg(AArch64::X16, RegState::Kill) .addReg(AArch64::X15, RegState::Implicit | RegState::Define) .setMIFlags(MachineInstr::FrameSetup); + if (NeedsWinCFI) + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_Nop)) + .setMIFlag(MachineInstr::FrameSetup); break; } @@ -739,6 +926,10 @@ .addReg(AArch64::X15, RegState::Kill) .addImm(AArch64_AM::getArithExtendImm(AArch64_AM::UXTX, 4)) .setMIFlags(MachineInstr::FrameSetup); + if (NeedsWinCFI) + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_StackAlloc)) + .addImm(NumBytes) + .setMIFlag(MachineInstr::FrameSetup); NumBytes = 0; } @@ -758,7 +949,7 @@ // the correct value here, as NumBytes also includes padding bytes, // which shouldn't be counted here. emitFrameOffset(MBB, MBBI, DL, scratchSPReg, AArch64::SP, -NumBytes, TII, - MachineInstr::FrameSetup); + MachineInstr::FrameSetup, false, NeedsWinCFI); if (NeedsRealignment) { const unsigned Alignment = MFI.getMaxAlignment(); @@ -781,6 +972,10 @@ .addReg(scratchSPReg, RegState::Kill) .addImm(andMaskEncoded); AFI->setStackRealigned(true); + if (NeedsWinCFI) + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_StackAlloc)) + .addImm(NumBytes & andMaskEncoded) + .setMIFlag(MachineInstr::FrameSetup); } } @@ -794,8 +989,17 @@ if (RegInfo->hasBasePointer(MF)) { TII->copyPhysReg(MBB, MBBI, DL, RegInfo->getBaseRegister(), AArch64::SP, false); + if (NeedsWinCFI) + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_Nop)) + .setMIFlag(MachineInstr::FrameSetup); } + // The very last FrameSetup instruction indicates the end of prologue. Emit a + // SEH opcode indicating the prologue end. + if (NeedsWinCFI) + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_PrologEnd)) + .setMIFlag(MachineInstr::FrameSetup); + if (needsFrameMoves) { const DataLayout &TD = MF.getDataLayout(); const int StackGrowth = -TD.getPointerSize(0); @@ -923,6 +1127,9 @@ const TargetInstrInfo *TII = Subtarget.getInstrInfo(); DebugLoc DL; bool IsTailCallReturn = false; + bool NeedsWinCFI = MF.getTarget().getMCAsmInfo()->usesWindowsCFI() && + MF.getFunction().needsUnwindTableEntry(); + if (MBB.end() != MBBI) { DL = MBBI->getDebugLoc(); unsigned RetOpcode = MBBI->getOpcode(); @@ -930,8 +1137,9 @@ RetOpcode == AArch64::TCRETURNri || RetOpcode == AArch64::TCRETURNriBTI; } + int NumBytes = MFI.getStackSize(); - const AArch64FunctionInfo *AFI = MF.getInfo(); + AArch64FunctionInfo *AFI = MF.getInfo(); // All calls are tail calls in GHC calling conv, and functions have no // prologue/epilogue. @@ -996,14 +1204,16 @@ if (!CombineSPBump && PrologueSaveSize != 0) { MachineBasicBlock::iterator Pop = std::prev(MBB.getFirstTerminator()); + while (AArch64InstrInfo::isSEHInstruction(*Pop)) + Pop = std::prev(Pop); // Converting the last ldp to a post-index ldp is valid only if the last // ldp's offset is 0. const MachineOperand &OffsetOp = Pop->getOperand(Pop->getNumOperands() - 1); // If the offset is 0, convert it to a post-index ldp. - if (OffsetOp.getImm() == 0) { - convertCalleeSaveRestoreToSPPrePostIncDec(MBB, Pop, DL, TII, - PrologueSaveSize); - } else { + if (OffsetOp.getImm() == 0) + convertCalleeSaveRestoreToSPPrePostIncDec( + MBB, Pop, DL, TII, PrologueSaveSize, NeedsWinCFI, false); + else { // If not, make sure to emit an add after the last ldp. // We're doing this by transfering the size to be restored from the // adjustment *before* the CSR pops to the adjustment *after* the CSR @@ -1023,14 +1233,23 @@ ++LastPopI; break; } else if (CombineSPBump) - fixupCalleeSaveRestoreStackOffset(*LastPopI, AFI->getLocalStackSize()); + fixupCalleeSaveRestoreStackOffset(*LastPopI, AFI->getLocalStackSize(), + NeedsWinCFI); } + if (NeedsWinCFI) + BuildMI(MBB, LastPopI, DL, TII->get(AArch64::SEH_EpilogStart)) + .setMIFlag(MachineInstr::FrameDestroy); + // If there is a single SP update, insert it before the ret and we're done. if (CombineSPBump) { emitFrameOffset(MBB, MBB.getFirstTerminator(), DL, AArch64::SP, AArch64::SP, - NumBytes + AfterCSRPopSize, TII, - MachineInstr::FrameDestroy); + NumBytes + AfterCSRPopSize, TII, MachineInstr::FrameDestroy, + false, NeedsWinCFI); + if (NeedsWinCFI) + BuildMI(MBB, MBB.getFirstTerminator(), DL, + TII->get(AArch64::SEH_EpilogEnd)) + .setMIFlag(MachineInstr::FrameDestroy); return; } @@ -1058,9 +1277,15 @@ adaptForLdStOpt(MBB, MBB.getFirstTerminator(), LastPopI); emitFrameOffset(MBB, LastPopI, DL, AArch64::SP, AArch64::SP, - StackRestoreBytes, TII, MachineInstr::FrameDestroy); - if (Done) + StackRestoreBytes, TII, MachineInstr::FrameDestroy, false, + NeedsWinCFI); + if (Done) { + if (NeedsWinCFI) + BuildMI(MBB, MBB.getFirstTerminator(), DL, + TII->get(AArch64::SEH_EpilogEnd)) + .setMIFlag(MachineInstr::FrameDestroy); return; + } NumBytes = 0; } @@ -1072,10 +1297,10 @@ if (MFI.hasVarSizedObjects() || AFI->isStackRealigned()) emitFrameOffset(MBB, LastPopI, DL, AArch64::SP, AArch64::FP, -AFI->getCalleeSavedStackSize() + 16, TII, - MachineInstr::FrameDestroy); + MachineInstr::FrameDestroy, false, NeedsWinCFI); else if (NumBytes) emitFrameOffset(MBB, LastPopI, DL, AArch64::SP, AArch64::SP, NumBytes, TII, - MachineInstr::FrameDestroy); + MachineInstr::FrameDestroy, false, NeedsWinCFI); // This must be placed after the callee-save restore code because that code // assumes the SP is at the same location as it was after the callee-save save @@ -1096,8 +1321,12 @@ adaptForLdStOpt(MBB, FirstSPPopI, LastPopI); emitFrameOffset(MBB, FirstSPPopI, DL, AArch64::SP, AArch64::SP, - AfterCSRPopSize, TII, MachineInstr::FrameDestroy); + AfterCSRPopSize, TII, MachineInstr::FrameDestroy, false, + NeedsWinCFI); } + if (NeedsWinCFI) + BuildMI(MBB, MBB.getFirstTerminator(), DL, TII->get(AArch64::SEH_EpilogEnd)) + .setMIFlag(MachineInstr::FrameDestroy); } /// getFrameIndexReference - Provide a base+offset reference to an FI slot for @@ -1222,6 +1451,23 @@ Attrs.hasAttrSomewhere(Attribute::SwiftError)); } +static bool invalidateWindowsRegisterPairing(unsigned Reg1, unsigned Reg2, + bool NeedsWinCFI) { + // If we are generating register pairs for a Windows function that requires + // EH support, then pair consecutive registers only. There are no unwind + // opcodes for saves/restores of non-consectuve register pairs. + // The unwind opcodes are save_regp, save_regp_x, save_fregp, save_frepg_x. + // https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling + + // TODO: LR can be paired with any register. We don't support this yet in + // the MCLayer. We need to add support for the save_lrpair unwind code. + if (!NeedsWinCFI) + return false; + if (Reg2 == Reg1 + 1) + return false; + return true; +} + namespace { struct RegPairInfo { @@ -1246,6 +1492,8 @@ if (CSI.empty()) return; + bool NeedsWinCFI = MF.getTarget().getMCAsmInfo()->usesWindowsCFI() && + MF.getFunction().needsUnwindTableEntry(); AArch64FunctionInfo *AFI = MF.getInfo(); MachineFrameInfo &MFI = MF.getFrameInfo(); CallingConv::ID CC = MF.getFunction().getCallingConv(); @@ -1258,7 +1506,11 @@ (Count & 1) == 0) && "Odd number of callee-saved regs to spill!"); int Offset = AFI->getCalleeSavedStackSize(); - + // On Linux, we will have either one or zero non-paired register. On Windows + // with CFI, we can have multiple unpaired registers in order to utilize the + // available unwind codes. This flag assures that the alignment fixup is done + // only once, as intened. + bool FixupDone = false; for (unsigned i = 0; i < Count; ++i) { RegPairInfo RPI; RPI.Reg1 = CSI[i].getReg(); @@ -1272,16 +1524,17 @@ else llvm_unreachable("Unsupported register class."); - // Add the next reg to the pair if it is in the same register class. if (i + 1 < Count) { unsigned NextReg = CSI[i + 1].getReg(); switch (RPI.Type) { case RegPairInfo::GPR: - if (AArch64::GPR64RegClass.contains(NextReg)) + if (AArch64::GPR64RegClass.contains(NextReg) && + !invalidateWindowsRegisterPairing(RPI.Reg1, NextReg, NeedsWinCFI)) RPI.Reg2 = NextReg; break; case RegPairInfo::FPR64: - if (AArch64::FPR64RegClass.contains(NextReg)) + if (AArch64::FPR64RegClass.contains(NextReg) && + !invalidateWindowsRegisterPairing(RPI.Reg1, NextReg, NeedsWinCFI)) RPI.Reg2 = NextReg; break; case RegPairInfo::FPR128: @@ -1326,8 +1579,9 @@ // Round up size of non-pair to pair size if we need to pad the // callee-save area to ensure 16-byte alignment. - if (AFI->hasCalleeSaveStackFreeSpace() && + if (AFI->hasCalleeSaveStackFreeSpace() && !FixupDone && RPI.Type != RegPairInfo::FPR128 && !RPI.isPaired()) { + FixupDone = NeedsWinCFI; Offset -= 8; assert(Offset % 16 == 0); assert(MFI.getObjectAlignment(RPI.FrameIdx) <= 16); @@ -1351,6 +1605,8 @@ const TargetRegisterInfo *TRI) const { MachineFunction &MF = *MBB.getParent(); const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo(); + bool NeedsWinCFI = MF.getTarget().getMCAsmInfo()->usesWindowsCFI() && + MF.getFunction().needsUnwindTableEntry(); DebugLoc DL; SmallVector RegPairs; @@ -1368,6 +1624,10 @@ .addImm(8) .setMIFlag(MachineInstr::FrameSetup); + if (NeedsWinCFI) + BuildMI(MBB, MI, DL, TII.get(AArch64::SEH_Nop)) + .setMIFlag(MachineInstr::FrameSetup); + // This instruction also makes x18 live-in to the entry block. MBB.addLiveIn(AArch64::X18); } @@ -1413,6 +1673,17 @@ if (RPI.isPaired()) dbgs() << ", " << RPI.FrameIdx + 1; dbgs() << ")\n"); + assert((!NeedsWinCFI || !(Reg1 == AArch64::LR && Reg2 == AArch64::FP)) && + "Windows unwdinding requires a consecutive (FP,LR) pair"); + // Windows unwind codes require consecutive registers if registers are + // paired. Make the switch here, so that the code below will save (x,x+1) + // and not (x+1,x). + unsigned FrameIdxReg1 = RPI.FrameIdx; + unsigned FrameIdxReg2 = RPI.FrameIdx + 1; + if (NeedsWinCFI && RPI.isPaired()) { + std::swap(Reg1, Reg2); + std::swap(FrameIdxReg1, FrameIdxReg2); + } MachineInstrBuilder MIB = BuildMI(MBB, MI, DL, TII.get(StrOpc)); if (!MRI.isReserved(Reg1)) MBB.addLiveIn(Reg1); @@ -1421,7 +1692,7 @@ MBB.addLiveIn(Reg2); MIB.addReg(Reg2, getPrologueDeath(MF, Reg2)); MIB.addMemOperand(MF.getMachineMemOperand( - MachinePointerInfo::getFixedStack(MF, RPI.FrameIdx + 1), + MachinePointerInfo::getFixedStack(MF, FrameIdxReg2), MachineMemOperand::MOStore, Size, Align)); } MIB.addReg(Reg1, getPrologueDeath(MF, Reg1)) @@ -1430,8 +1701,11 @@ // where factor*scale is implicit .setMIFlag(MachineInstr::FrameSetup); MIB.addMemOperand(MF.getMachineMemOperand( - MachinePointerInfo::getFixedStack(MF, RPI.FrameIdx), + MachinePointerInfo::getFixedStack(MF,FrameIdxReg1), MachineMemOperand::MOStore, Size, Align)); + if (NeedsWinCFI) + InsertSEH(MIB, TII, MachineInstr::FrameSetup); + } return true; } @@ -1444,6 +1718,8 @@ const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo(); DebugLoc DL; SmallVector RegPairs; + bool NeedsWinCFI = MF.getTarget().getMCAsmInfo()->usesWindowsCFI() && + MF.getFunction().needsUnwindTableEntry(); if (MI != MBB.end()) DL = MI->getDebugLoc(); @@ -1489,11 +1765,20 @@ if (RPI.isPaired()) dbgs() << ", " << RPI.FrameIdx + 1; dbgs() << ")\n"); + // Windows unwind codes require consecutive registers if registers are + // paired. Make the switch here, so that the code below will save (x,x+1) + // and not (x+1,x). + unsigned FrameIdxReg1 = RPI.FrameIdx; + unsigned FrameIdxReg2 = RPI.FrameIdx + 1; + if (NeedsWinCFI && RPI.isPaired()) { + std::swap(Reg1, Reg2); + std::swap(FrameIdxReg1, FrameIdxReg2); + } MachineInstrBuilder MIB = BuildMI(MBB, MI, DL, TII.get(LdrOpc)); if (RPI.isPaired()) { MIB.addReg(Reg2, getDefRegState(true)); MIB.addMemOperand(MF.getMachineMemOperand( - MachinePointerInfo::getFixedStack(MF, RPI.FrameIdx + 1), + MachinePointerInfo::getFixedStack(MF, FrameIdxReg2), MachineMemOperand::MOLoad, Size, Align)); } MIB.addReg(Reg1, getDefRegState(true)) @@ -1502,10 +1787,11 @@ // where factor*scale is implicit .setMIFlag(MachineInstr::FrameDestroy); MIB.addMemOperand(MF.getMachineMemOperand( - MachinePointerInfo::getFixedStack(MF, RPI.FrameIdx), + MachinePointerInfo::getFixedStack(MF, FrameIdxReg1), MachineMemOperand::MOLoad, Size, Align)); + if (NeedsWinCFI) + InsertSEH(MIB, TII, MachineInstr::FrameDestroy); }; - if (ReverseCSRRestoreSeq) for (const RegPairInfo &RPI : reverse(RegPairs)) EmitMI(RPI); Index: lib/Target/AArch64/AArch64InstrInfo.h =================================================================== --- lib/Target/AArch64/AArch64InstrInfo.h +++ lib/Target/AArch64/AArch64InstrInfo.h @@ -262,6 +262,9 @@ /// Returns true if the instruction has a shift by immediate that can be /// executed in one cycle less. bool isFalkorShiftExtFast(const MachineInstr &MI) const; + /// Return true if the instructions is a SEH instruciton used for unwinding + /// on Windows. + static bool isSEHInstruction(const MachineInstr &MI); private: /// Sets the offsets on outlined instructions in \p MBB which use SP @@ -289,7 +292,7 @@ const DebugLoc &DL, unsigned DestReg, unsigned SrcReg, int Offset, const TargetInstrInfo *TII, MachineInstr::MIFlag = MachineInstr::NoFlags, - bool SetNZCV = false); + bool SetNZCV = false, bool NeedsWinCFI = false); /// rewriteAArch64FrameIndex - Rewrite MI to access 'Offset' bytes from the /// FP. Return false if the offset could not be handled directly in MI, and Index: lib/Target/AArch64/AArch64InstrInfo.cpp =================================================================== --- lib/Target/AArch64/AArch64InstrInfo.cpp +++ lib/Target/AArch64/AArch64InstrInfo.cpp @@ -1085,6 +1085,32 @@ } } +bool AArch64InstrInfo::isSEHInstruction(const MachineInstr &MI) { + unsigned Opc = MI.getOpcode(); + switch (Opc) { + default: + return false; + case AArch64::SEH_StackAlloc: + case AArch64::SEH_SaveFPLR: + case AArch64::SEH_SaveFPLR_X: + case AArch64::SEH_SaveReg: + case AArch64::SEH_SaveReg_X: + case AArch64::SEH_SaveRegP: + case AArch64::SEH_SaveRegP_X: + case AArch64::SEH_SaveFReg: + case AArch64::SEH_SaveFReg_X: + case AArch64::SEH_SaveFRegP: + case AArch64::SEH_SaveFRegP_X: + case AArch64::SEH_SetFP: + case AArch64::SEH_AddFP: + case AArch64::SEH_Nop: + case AArch64::SEH_PrologEnd: + case AArch64::SEH_EpilogStart: + case AArch64::SEH_EpilogEnd: + return true; + } +} + bool AArch64InstrInfo::isCoalescableExtInstr(const MachineInstr &MI, unsigned &SrcReg, unsigned &DstReg, unsigned &SubIdx) const { @@ -3026,7 +3052,8 @@ MachineBasicBlock::iterator MBBI, const DebugLoc &DL, unsigned DestReg, unsigned SrcReg, int Offset, const TargetInstrInfo *TII, - MachineInstr::MIFlag Flag, bool SetNZCV) { + MachineInstr::MIFlag Flag, bool SetNZCV, + bool NeedsWinCFI) { if (DestReg == SrcReg && Offset == 0) return; @@ -3071,6 +3098,11 @@ .addImm(AArch64_AM::getShifterImm(AArch64_AM::LSL, ShiftSize)) .setMIFlag(Flag); + if (NeedsWinCFI && SrcReg == AArch64::SP && DestReg == AArch64::SP) + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_StackAlloc)) + .addImm(ThisVal) + .setMIFlag(Flag); + SrcReg = DestReg; Offset -= ThisVal; if (Offset == 0) @@ -3081,6 +3113,21 @@ .addImm(Offset) .addImm(AArch64_AM::getShifterImm(AArch64_AM::LSL, 0)) .setMIFlag(Flag); + + if (NeedsWinCFI) { + if ((DestReg == AArch64::FP && SrcReg == AArch64::SP) || + (SrcReg == AArch64::FP && DestReg == AArch64::SP)) { + if (Offset == 0) + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_SetFP)). + setMIFlag(Flag); + else + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_AddFP)). + addImm(Offset).setMIFlag(Flag); + } else if (DestReg == AArch64::SP) { + BuildMI(MBB, MBBI, DL, TII->get(AArch64::SEH_StackAlloc)). + addImm(Offset).setMIFlag(Flag); + } + } } MachineInstr *AArch64InstrInfo::foldMemoryOperandImpl( Index: lib/Target/AArch64/AArch64RegisterInfo.h =================================================================== --- lib/Target/AArch64/AArch64RegisterInfo.h +++ lib/Target/AArch64/AArch64RegisterInfo.h @@ -30,6 +30,11 @@ public: AArch64RegisterInfo(const Triple &TT); + // FIXME: This should be tablegen'd like getDwarfRegNum is + int getSEHRegNum(unsigned i) const { + return getEncodingValue(i); + } + bool isReservedReg(const MachineFunction &MF, unsigned Reg) const; bool isAnyArgRegReserved(const MachineFunction &MF) const; void emitReservedArgRegCallError(const MachineFunction &MF) const; Index: lib/Target/AArch64/AArch64RegisterInfo.cpp =================================================================== --- lib/Target/AArch64/AArch64RegisterInfo.cpp +++ lib/Target/AArch64/AArch64RegisterInfo.cpp @@ -43,6 +43,8 @@ const MCPhysReg * AArch64RegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { assert(MF && "Invalid MachineFunction pointer."); + if (MF->getSubtarget().isTargetWindows()) + return CSR_Win_AArch64_AAPCS_SaveList; if (MF->getFunction().getCallingConv() == CallingConv::GHC) // GHC set of callee saved regs is empty as all those regs are // used for passing STG regs around Index: test/CodeGen/AArch64/win64_vararg.ll =================================================================== --- test/CodeGen/AArch64/win64_vararg.ll +++ test/CodeGen/AArch64/win64_vararg.ll @@ -104,7 +104,7 @@ ; CHECK-LABEL: fp ; CHECK: str x21, [sp, #-96]! -; CHECK: stp x20, x19, [sp, #16] +; CHECK: stp x19, x20, [sp, #16] ; CHECK: stp x29, x30, [sp, #32] ; CHECK: add x29, sp, #32 ; CHECK: add x8, x29, #24 @@ -125,7 +125,7 @@ ; CHECK: mov x4, xzr ; CHECK: bl __stdio_common_vsprintf ; CHECK: ldp x29, x30, [sp, #32] -; CHECK: ldp x20, x19, [sp, #16] +; CHECK: ldp x19, x20, [sp, #16] ; CHECK: cmp w0, #0 ; CHECK: csinv w0, w0, wzr, ge ; CHECK: ldr x21, [sp], #96 @@ -151,8 +151,8 @@ ; CHECK-LABEL: vla ; CHECK: str x23, [sp, #-112]! -; CHECK: stp x22, x21, [sp, #16] -; CHECK: stp x20, x19, [sp, #32] +; CHECK: stp x21, x22, [sp, #16] +; CHECK: stp x19, x20, [sp, #32] ; CHECK: stp x29, x30, [sp, #48] ; CHECK: add x29, sp, #48 ; CHECK: add x8, x29, #16 @@ -183,8 +183,8 @@ ; CHECK: mov sp, [[REG2]] ; CHECK: sub sp, x29, #48 ; CHECK: ldp x29, x30, [sp, #48] -; CHECK: ldp x20, x19, [sp, #32] -; CHECK: ldp x22, x21, [sp, #16] +; CHECK: ldp x19, x20, [sp, #32] +; CHECK: ldp x21, x22, [sp, #16] ; CHECK: ldr x23, [sp], #112 ; CHECK: ret define void @vla(i32, i8*, ...) local_unnamed_addr { @@ -211,32 +211,34 @@ declare void @llvm.stackrestore(i8*) ; CHECK-LABEL: snprintf -; CHECK: sub sp, sp, #96 -; CHECK: stp x21, x20, [sp, #16] -; CHECK: stp x19, x30, [sp, #32] -; CHECK: add x8, sp, #56 -; CHECK: mov x19, x2 -; CHECK: mov x20, x1 -; CHECK: mov x21, x0 -; CHECK: stp x6, x7, [sp, #80] -; CHECK: stp x4, x5, [sp, #64] -; CHECK: str x3, [sp, #56] -; CHECK: str x8, [sp, #8] -; CHECK: bl __local_stdio_printf_options -; CHECK: ldr x8, [x0] -; CHECK: add x5, sp, #56 -; CHECK: mov x1, x21 -; CHECK: mov x2, x20 -; CHECK: orr x0, x8, #0x2 -; CHECK: mov x3, x19 -; CHECK: mov x4, xzr -; CHECK: bl __stdio_common_vsprintf -; CHECK: ldp x19, x30, [sp, #32] -; CHECK: ldp x21, x20, [sp, #16] -; CHECK: cmp w0, #0 -; CHECK: csinv w0, w0, wzr, ge -; CHECK: add sp, sp, #96 -; CHECK: ret +; CHECK-DAG: sub sp, sp, #96 +; CHECK-DAG: str x21, [sp, #16] +; CHECK-DAG: stp x19, x20, [sp, #24] +; CHECK-DAG: str x30, [sp, #40] +; CHECK-DAG: add x8, sp, #56 +; CHECK-DAG: mov x19, x2 +; CHECK-DAG: mov x20, x1 +; CHECK-DAG: mov x21, x0 +; CHECK-DAG: stp x6, x7, [sp, #80] +; CHECK-DAG: stp x4, x5, [sp, #64] +; CHECK-DAG: str x3, [sp, #56] +; CHECK-DAG: str x8, [sp, #8] +; CHECK-DAG: bl __local_stdio_printf_options +; CHECK-DAG: ldr x8, [x0] +; CHECK-DAG: add x5, sp, #56 +; CHECK-DAG: mov x1, x21 +; CHECK-DAG: mov x2, x20 +; CHECK-DAG: orr x0, x8, #0x2 +; CHECK-DAG: mov x3, x19 +; CHECK-DAG: mov x4, xzr +; CHECK-DAG: bl __stdio_common_vsprintf +; CHECK-DAG: ldr x30, [sp, #40] +; CHECK-DAG: ldp x19, x20, [sp, #24] +; CHECK-DAG: ldr x21, [sp, #16] +; CHECK-DAG: cmp w0, #0 +; CHECK-DAG: csinv w0, w0, wzr, ge +; CHECK-DAG: add sp, sp, #96 +; CHECK-DAG: ret define i32 @snprintf(i8*, i64, i8*, ...) local_unnamed_addr #5 { %4 = alloca i8*, align 8 %5 = bitcast i8** %4 to i8* Index: test/CodeGen/AArch64/wineh-frame0.mir =================================================================== --- /dev/null +++ test/CodeGen/AArch64/wineh-frame0.mir @@ -0,0 +1,60 @@ +# RUN: llc -o - %s -mtriple=aarch64-windows -start-before=prologepilog \ +# RUN: -stop-after=prologepilog | FileCheck %s +# Check save_regp_x, save_regp + +# CHECK: early-clobber $sp = frame-setup STPXpre killed $x27, killed $x28, $sp, -10 +# CHECK-NEXT: frame-setup SEH_SaveRegP_X 27, 28, -80 +# CHECK-NEXT: frame-setup STPXi killed $x25, killed $x26, $sp, 2 +# CHECK-NEXT: frame-setup SEH_SaveRegP 25, 26, 16 +# CHECK-NEXT: frame-setup STPXi killed $x23, killed $x24, $sp, 4 +# CHECK-NEXT: frame-setup SEH_SaveRegP 23, 24, 32 +# CHECK-NEXT: frame-setup STPXi killed $x21, killed $x22, $sp, 6 +# CHECK-NEXT: frame-setup SEH_SaveRegP 21, 22, 48 +# CHECK-NEXT: frame-setup STPXi killed $x19, killed $x20, $sp, 8 +# CHECK-NEXT: frame-setup SEH_SaveRegP 19, 20, 64 +# CHECK-NEXT: frame-setup SEH_PrologEnd +# CHECK: frame-destroy SEH_EpilogStart +# CHECK-NEXT: $x19, $x20 = frame-destroy LDPXi $sp, 8 +# CHECK-NEXT: frame-destroy SEH_SaveRegP 19, 20, 64 +# CHECK-NEXT: $x21, $x22 = frame-destroy LDPXi $sp, 6 +# CHECK-NEXT: frame-destroy SEH_SaveRegP 21, 22, 48 +# CHECK-NEXT: $x23, $x24 = frame-destroy LDPXi $sp, 4 +# CHECK-NEXT: frame-destroy SEH_SaveRegP 23, 24, 32 +# CHECK-NEXT: $x25, $x26 = frame-destroy LDPXi $sp, 2 +# CHECK-NEXT: frame-destroy SEH_SaveRegP 25, 26, 16 +# CHECK-NEXT: early-clobber $sp, $x27, $x28 = frame-destroy LDPXpost $sp, 10 +# CHECK-NEXT: frame-destroy SEH_SaveRegP_X 27, 28, -80 +# CHECK-NEXT: frame-destroy SEH_EpilogEnd +# CHECK-NEXT: RET_ReallyLR implicit $x0 + +... +--- +name: test +alignment: 2 +tracksRegLiveness: true +hasWinCFI: true +liveins: + - { reg: '$w0' } +frameInfo: + stackSize: 80 + maxAlignment: 8 + maxCallFrameSize: 0 + hasOpaqueSPAdjustment: true +stack: +body: | + bb.0.entry: + liveins: $x0, $x1, $x27, $x28, $x25, $x26, $x23, $x24, $x21, $x22, $x19, $x20 + $x19 = ADDXrr $x0, killed $x1 + $x20 = ADDXrr $x19, killed $x0 + $x21 = ADDXrr $x20, killed $x19 + $x22 = ADDXrr $x21, killed $x20 + $x23 = ADDXrr $x22, killed $x21 + $x24 = ADDXrr $x23, killed $x22 + $x25 = ADDXrr $x24, killed $x23 + $x26 = ADDXrr $x25, killed $x24 + $x27 = ADDXrr $x26, killed $x25 + $x28 = ADDXrr $x27, killed $x26 + $x0 = COPY $x28 + RET_ReallyLR implicit $x0 + +... Index: test/CodeGen/AArch64/wineh-frame1.mir =================================================================== --- /dev/null +++ test/CodeGen/AArch64/wineh-frame1.mir @@ -0,0 +1,94 @@ +# RUN: llc -o - %s -mtriple=aarch64-windows -start-before=prologepilog \ +# RUN: -stop-after=prologepilog | FileCheck %s +# Check save_fregp_x, save_fregp + +# CHECK: early-clobber $sp = frame-setup STPDpre killed $d10, killed $d11, $sp, -14 +# CHECK-NEXT: frame-setup SEH_SaveFRegP_X 10, 11, -112 +# CHECK-NEXT: frame-setup STPDi killed $d8, killed $d9, $sp, 2 +# CHECK-NEXT: frame-setup SEH_SaveFRegP 8, 9, 16 +# CHECK-NEXT: frame-setup STPXi killed $x27, killed $x28, $sp, 4 +# CHECK-NEXT: frame-setup SEH_SaveRegP 27, 28, 32 +# CHECK-NEXT: frame-setup STPXi killed $x25, killed $x26, $sp, 6 +# CHECK-NEXT: frame-setup SEH_SaveRegP 25, 26, 48 +# CHECK-NEXT: frame-setup STPXi killed $x23, killed $x24, $sp, 8 +# CHECK-NEXT: frame-setup SEH_SaveRegP 23, 24, 64 +# CHECK-NEXT: frame-setup STPXi killed $x21, killed $x22, $sp, 10 +# CHECK-NEXT: frame-setup SEH_SaveRegP 21, 22, 80 +# CHECK-NEXT: frame-setup STPXi killed $x19, killed $x20, $sp, 12 +# CHECK-NEXT: frame-setup SEH_SaveRegP 19, 20, 96 +# CHECK-NEXT: frame-setup SEH_PrologEnd +# CHECK: frame-destroy SEH_EpilogStart +# CHECK-NEXT: $x19, $x20 = frame-destroy LDPXi $sp, 12 +# CHECK-NEXT: frame-destroy SEH_SaveRegP 19, 20, 96 +# CHECK-NEXT: $x21, $x22 = frame-destroy LDPXi $sp, 10 +# CHECK-NEXT: frame-destroy SEH_SaveRegP 21, 22, 80 +# CHECK-NEXT: $x23, $x24 = frame-destroy LDPXi $sp, 8 +# CHECK-NEXT: frame-destroy SEH_SaveRegP 23, 24, 64 +# CHECK-NEXT: $x25, $x26 = frame-destroy LDPXi $sp, 6 +# CHECK-NEXT: frame-destroy SEH_SaveRegP 25, 26, 48 +# CHECK-NEXT: $x27, $x28 = frame-destroy LDPXi $sp, 4 +# CHECK-NEXT: frame-destroy SEH_SaveRegP 27, 28, 32 +# CHECK-NEXT: $d8, $d9 = frame-destroy LDPDi $sp, 2 +# CHECK-NEXT: frame-destroy SEH_SaveFRegP 8, 9, 16 +# CHECK-NEXT: early-clobber $sp, $d10, $d11 = frame-destroy LDPDpost $sp, 14 +# CHECK-NEXT: frame-destroy SEH_SaveFRegP_X 10, 11, -112 +# CHECK-NEXT: frame-destroy SEH_EpilogEnd +# CHECK-NEXT: RET_ReallyLR implicit $x0 +... +--- +name: test +alignment: 2 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: true +registers: +liveins: + - { reg: '$w0', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 112 + offsetAdjustment: 0 + maxAlignment: 8 + adjustsStack: false + hasCalls: false + stackProtector: '' + maxCallFrameSize: 0 + hasOpaqueSPAdjustment: true + hasVAStart: false + hasMustTailInVarArgFunc: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: +stack: +constants: +body: | + bb.0.entry: + liveins: $x0, $x1, $d0, $d1, $d10, $d11, $d8, $d9, $x27, $x28, $x25, $x26, $x23, $x24, $x21, $x22, $x19, $x20 + + $x19 = ADDXrr $x0, killed $x1 + $d8 = FADDDrr killed $d0, $d1 + $d9 = FADDDrr $d8, $d1 + $d10 = FADDDrr $d9, $d8 + $d11 = FADDDrr killed $d9, $d10 + $x20 = ADDXrr $x19, killed $x0 + $x21 = ADDXrr $x20, killed $x19 + $x22 = ADDXrr $x21, killed $x20 + $x23 = ADDXrr $x22, killed $x21 + $x24 = ADDXrr $x23, killed $x22 + $x25 = ADDXrr $x24, killed $x23 + $x26 = ADDXrr $x25, killed $x24 + $x27 = ADDXrr $x26, killed $x25 + $x28 = ADDXrr $x27, killed $x26 + $x0 = COPY $d11 + $x0 = ADDXrr $x0, killed $x28 + RET_ReallyLR implicit $x0 + +... Index: test/CodeGen/AArch64/wineh-frame2.mir =================================================================== --- /dev/null +++ test/CodeGen/AArch64/wineh-frame2.mir @@ -0,0 +1,72 @@ +# RUN: llc -o - %s -mtriple=aarch64-windows -start-before=prologepilog \ +# RUN: -stop-after=prologepilog | FileCheck %s +# Check save_freg_x, save_frep, save_reg + +# CHECK: early-clobber $sp = frame-setup STRDpre killed $d12, $sp, -48 +# CHECK-NEXT: frame-setup SEH_SaveFReg_X 12, -48 +# CHECK-NEXT: frame-setup STPDi killed $d10, killed $d11, $sp, 1 +# CHECK-NEXT: frame-setup SEH_SaveFRegP 10, 11, 8 +# CHECK-NEXT: frame-setup STPDi killed $d8, killed $d9, $sp, 3 +# CHECK-NEXT: frame-setup SEH_SaveFRegP 8, 9, 24 +# CHECK-NEXT: frame-setup STRXui killed $x19, $sp, 5 +# CHECK-NEXT: frame-setup SEH_SaveReg 19, 40 +# CHECK-NEXT: frame-setup SEH_PrologEnd +# CHECK: frame-destroy SEH_EpilogStart +# CHECK-NEXT: $x19 = frame-destroy LDRXui $sp, 5 +# CHECK-NEXT: frame-destroy SEH_SaveReg 19, 40 +# CHECK-NEXT: $d8, $d9 = frame-destroy LDPDi $sp, 3 +# CHECK-NEXT: frame-destroy SEH_SaveFRegP 8, 9, 24 +# CHECK-NEXT: $d10, $d11 = frame-destroy LDPDi $sp, 1 +# CHECK-NEXT: frame-destroy SEH_SaveFRegP 10, 11, 8 +# CHECK-NEXT: early-clobber $sp, $d12 = frame-destroy LDRDpost $sp, 48 +# CHECK-NEXT: frame-destroy SEH_SaveFReg_X 12, -48 +# CHECK-NEXT: frame-destroy SEH_EpilogEnd +# CHECK-NEXT: RET_ReallyLR implicit $x0 +... +--- +name: test +alignment: 2 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: true +registers: +liveins: + - { reg: '$w0', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 112 + offsetAdjustment: 0 + maxAlignment: 8 + adjustsStack: false + hasCalls: false + stackProtector: '' + maxCallFrameSize: 0 + hasOpaqueSPAdjustment: true + hasVAStart: false + hasMustTailInVarArgFunc: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: +stack: +constants: +body: | + bb.0.entry: + liveins: $x0, $x1, $d0, $d1, $d10, $d11, $d8, $d9 + $x19 = ADDXrr $x0, killed $x1 + $d8 = FADDDrr killed $d0, $d1 + $d9 = FADDDrr $d8, $d1 + $d10 = FADDDrr $d9, $d8 + $d11 = FADDDrr killed $d9, $d10 + $d12 = FADDDrr $d11, killed $d11 + $x0 = COPY $d12 + RET_ReallyLR implicit $x0 + +... Index: test/CodeGen/AArch64/wineh-frame3.mir =================================================================== --- /dev/null +++ test/CodeGen/AArch64/wineh-frame3.mir @@ -0,0 +1,59 @@ +# RUN: llc -o - %s -mtriple=aarch64-windows -start-before=prologepilog \ +# RUN: -stop-after=prologepilog | FileCheck %s +# Check save_reg_x, save_reg + +# CHECK: early-clobber $sp = frame-setup STRXpre killed $x22, $sp, -16 +# CHECK-NEXT: frame-setup SEH_SaveReg_X 22, -16 +# CHECK-NEXT: frame-setup STRXui killed $x19, $sp, 1 +# CHECK-NEXT: frame-setup SEH_SaveReg 19, 8 +# CHECK-NEXT: frame-setup SEH_PrologEnd +# CHECK: frame-destroy SEH_EpilogStart +# CHECK-NEXT: $x19 = frame-destroy LDRXui $sp, 1 +# CHECK-NEXT: frame-destroy SEH_SaveReg 19, 8 +# CHECK-NEXT: early-clobber $sp, $x22 = frame-destroy LDRXpost $sp, 16 +# CHECK-NEXT: frame-destroy SEH_SaveReg_X 22, -16 +# CHECK-NEXT: frame-destroy SEH_EpilogEnd +# CHECK-NEXT: RET_ReallyLR implicit $x0 +... +--- +name: test +alignment: 2 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: true +registers: +liveins: + - { reg: '$w0', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 112 + offsetAdjustment: 0 + maxAlignment: 8 + adjustsStack: false + hasCalls: false + stackProtector: '' + maxCallFrameSize: 0 + hasOpaqueSPAdjustment: true + hasVAStart: false + hasMustTailInVarArgFunc: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: +stack: +constants: +body: | + bb.0.entry: + liveins: $x0, $x1 + $x19 = ADDXrr $x0, killed $x1 + $x22 = ADDXrr killed $x19, $x0 + $x0 = COPY killed $x22 + RET_ReallyLR implicit $x0 +... Index: test/CodeGen/AArch64/wineh-frame4.mir =================================================================== --- /dev/null +++ test/CodeGen/AArch64/wineh-frame4.mir @@ -0,0 +1,59 @@ +# RUN: llc -o - %s -mtriple=aarch64-windows -start-before=prologepilog \ +# RUN: -stop-after=prologepilog | FileCheck %s +# Check save_freg_x, save_freg + +# CHECK: early-clobber $sp = frame-setup STRDpre killed $d10, $sp, -16 +# CHECK-NEXT: frame-setup SEH_SaveFReg_X 10, -16 +# CHECK-NEXT: frame-setup STRDui killed $d8, $sp, 1 :: (store 8 into %stack.0) +# CHECK-NEXT: frame-setup SEH_SaveFReg 8, 8 +# CHECK-NEXT: frame-setup SEH_PrologEnd +# CHECK: frame-destroy SEH_EpilogStart +# CHECK-NEXT: $d8 = frame-destroy LDRDui $sp, 1 :: (load 8 from %stack.0) +# CHECK-NEXT: frame-destroy SEH_SaveFReg 8, 8 +# CHECK-NEXT: early-clobber $sp, $d10 = frame-destroy LDRDpost $sp, 16 :: (load 8 from %stack.1) +# CHECK-NEXT: frame-destroy SEH_SaveFReg_X 10, -16 +# CHECK-NEXT: frame-destroy SEH_EpilogEnd +# CHECK-NEXT: RET_ReallyLR implicit $x0 +... +--- +name: test +alignment: 2 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: true +registers: +liveins: + - { reg: '$w0', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 112 + offsetAdjustment: 0 + maxAlignment: 8 + adjustsStack: false + hasCalls: false + stackProtector: '' + maxCallFrameSize: 0 + hasOpaqueSPAdjustment: true + hasVAStart: false + hasMustTailInVarArgFunc: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: +stack: +constants: +body: | + bb.0.entry: + liveins: $d0, $d1 + $d8 = FADDDrr $d0, killed $d1 + $d10 = FADDDrr killed $d8, $d0 + $x0 = COPY killed $d10 + RET_ReallyLR implicit $x0 +... Index: test/CodeGen/AArch64/wineh-frame5.mir =================================================================== --- /dev/null +++ test/CodeGen/AArch64/wineh-frame5.mir @@ -0,0 +1,135 @@ +# RUN: llc -o - %s -mtriple=aarch64-windows -start-before=prologepilog \ +# RUN: -stop-after=prologepilog | FileCheck %s +# Check multiple epilogues, save_reg, save_reg_x. + +# CHECK-LABEL: bb.0.entry: +# CHECK: early-clobber $sp = frame-setup STRXpre killed $x28, $sp, -32 +# CHECK-NEXT: frame-setup SEH_SaveReg_X 28, -32 +# CHECK-NEXT: frame-setup STRXui killed $x19, $sp, 1 +# CHECK-NEXT: frame-setup SEH_SaveReg 19, 8 +# CHECK-NEXT: frame-setup STRXui killed $lr, $sp, 2 +# CHECK-NEXT: frame-setup SEH_SaveReg 30, 16 +# CHECK-NEXT: $sp = frame-setup SUBXri $sp, 496, 0 +# CHECK-NEXT: frame-setup SEH_StackAlloc 496 +# CHECK-NEXT: frame-setup SEH_PrologEnd + +# CHECK-LABEL: bb.1.if.then: +# CHECK: frame-destroy SEH_EpilogStart +# CHECK-NEXT: $sp = frame-destroy ADDXri $sp, 496, 0 +# CHECK-NEXT: frame-destroy SEH_StackAlloc 496 +# CHECK-NEXT: $lr = frame-destroy LDRXui $sp, 2 +# CHECK-NEXT: frame-destroy SEH_SaveReg 30, 16 +# CHECK-NEXT: $x19 = frame-destroy LDRXui $sp, 1 +# CHECK-NEXT: frame-destroy SEH_SaveReg 19, 8 +# CHECK-NEXT: early-clobber $sp, $x28 = frame-destroy LDRXpost $sp, 32 +# CHECK-NEXT: frame-destroy SEH_SaveReg_X 28, -32 +# CHECK-NEXT: frame-destroy SEH_EpilogEnd +# CHECK-NEXT: TCRETURNdi @"?func2@@YAHXZ", 0, csr_aarch64_aapcs, implicit $sp + + +--- | + target datalayout = "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128" + target triple = "aarch64-unknown-windows-msvc19.11.0" + + define dso_local i32 @"?func@@YAHH@Z"(i32 %i) local_unnamed_addr #0 { + entry: + %B = alloca [123 x i32], align 4 + %call = tail call i32 @"?func2@@YAHXZ"() + %cmp = icmp sgt i32 %i, 2 + br i1 %cmp, label %if.then, label %if.else + + if.then: ; preds = %entry + %call1 = tail call i32 @"?func2@@YAHXZ"() + ret i32 %call1 + + if.else: ; preds = %entry + %0 = bitcast [123 x i32]* %B to i8* + call void @llvm.lifetime.start.p0i8(i64 492, i8* nonnull %0) #3 + %arraydecay7 = bitcast [123 x i32]* %B to i32* + %call2 = call i32 @"?func3@@YAHPEAH@Z"(i32* nonnull %arraydecay7) + call void @llvm.lifetime.end.p0i8(i64 492, i8* nonnull %0) #3 + ret i32 %call2 + } + + ; Function Attrs: argmemonly nounwind + declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #1 + + declare dso_local i32 @"?func2@@YAHXZ"() local_unnamed_addr #2 + + declare dso_local i32 @"?func3@@YAHPEAH@Z"(i32*) local_unnamed_addr #2 + + ; Function Attrs: argmemonly nounwind + declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) #1 + + ; Function Attrs: nounwind + declare void @llvm.stackprotector(i8*, i8**) #3 + + attributes #0 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+neon" "unsafe-fp-math"="false" "use-soft-float"="false" } + attributes #1 = { argmemonly nounwind } + attributes #2 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+neon" "unsafe-fp-math"="false" "use-soft-float"="false" } + attributes #3 = { nounwind } + +... +--- +name: '?func@@YAHH@Z' +alignment: 2 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +registers: +liveins: + - { reg: '$w0', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 4 + adjustsStack: true + hasCalls: true + stackProtector: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + localFrameSize: 492 + savePoint: '' + restorePoint: '' +fixedStack: +stack: + - { id: 0, name: B, type: default, offset: 0, size: 492, alignment: 4, + stack-id: 0, callee-saved-register: '', callee-saved-restored: true, + local-offset: -492, debug-info-variable: '', debug-info-expression: '', + debug-info-location: '' } +constants: +body: | + bb.0.entry: + successors: %bb.1(0x40000000), %bb.2(0x40000000) + liveins: $w0 + + renamable $w19 = COPY $w0 + ADJCALLSTACKDOWN 0, 0, implicit-def dead $sp, implicit $sp + BL @"?func2@@YAHXZ", csr_aarch64_aapcs, implicit-def dead $lr, implicit $sp, implicit-def $sp, implicit-def dead $w0 + ADJCALLSTACKUP 0, 0, implicit-def dead $sp, implicit $sp + dead $wzr = SUBSWri killed renamable $w19, 3, 0, implicit-def $nzcv + Bcc 11, %bb.2, implicit killed $nzcv + B %bb.1 + + bb.1.if.then: + TCRETURNdi @"?func2@@YAHXZ", 0, csr_aarch64_aapcs, implicit $sp + + bb.2.if.else: + ADJCALLSTACKDOWN 0, 0, implicit-def dead $sp, implicit $sp + $x0 = ADDXri %stack.0.B, 0, 0 + BL @"?func3@@YAHPEAH@Z", csr_aarch64_aapcs, implicit-def dead $lr, implicit $sp, implicit $x0, implicit-def $sp, implicit-def $w0 + ADJCALLSTACKUP 0, 0, implicit-def dead $sp, implicit $sp + RET_ReallyLR implicit $w0 + +... Index: test/CodeGen/AArch64/wineh-frame6.mir =================================================================== --- /dev/null +++ test/CodeGen/AArch64/wineh-frame6.mir @@ -0,0 +1,150 @@ +# RUN: llc -o - %s -mtriple=aarch64-windows -start-before=prologepilog \ +# RUN: -stop-after=prologepilog | FileCheck %s +# Test that stack probe results in Nop unwind codes in the prologue. Test +# save_fplr, save_reg_x and stack_alloc with multiple updates + +# CHECK: early-clobber $sp = frame-setup STPXpre killed $fp, killed $lr, $sp, -2 +# CHECK-NEXT: frame-setup SEH_SaveFPLR_X -16 +# CHECK-NEXT: $fp = frame-setup ADDXri $sp, 0, 0 +# CHECK-NEXT: frame-setup SEH_SetFP +# CHECK-NEXT: $sp = frame-setup SUBXri $sp, 32, 0 +# CHECK-NEXT: frame-setup SEH_StackAlloc 32 +# CHECK-NEXT: frame-setup SEH_PrologEnd +# CHECK: frame-destroy SEH_EpilogStart +# CHECK-NEXT: $sp = frame-destroy ADDXri $fp, 0, 0 +# CHECK-NEXT: frame-destroy SEH_SetFP +# CHECK-NEXT: early-clobber $sp, $fp, $lr = frame-destroy LDPXpost $sp, 2 +# CHECK-NEXT: frame-destroy SEH_SaveFPLR_X -16 +# CHECK-NEXT: frame-destroy SEH_EpilogEnd +# CHECK-NEXT: RET_ReallyLR implicit killed $w0 +--- | + target datalayout = "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128" + target triple = "aarch64-unknown-windows-msvc19.11.0" + + ; Function Attrs: noinline optnone + define dso_local i32 @"?func@@YAHHHHH@Z"(i32 %n, i32 %idx, i32 %b, i32 %c) #0 { + entry: + %c.addr = alloca i32, align 4 + %b.addr = alloca i32, align 4 + %idx.addr = alloca i32, align 4 + %n.addr = alloca i32, align 4 + %a = alloca i32*, align 8 + store i32 %c, i32* %c.addr, align 4 + store i32 %b, i32* %b.addr, align 4 + store i32 %idx, i32* %idx.addr, align 4 + store i32 %n, i32* %n.addr, align 4 + %0 = load i32, i32* %n.addr, align 4 + %conv = sext i32 %0 to i64 + %1 = alloca i8, i64 %conv, align 16 + %2 = bitcast i8* %1 to i32* + store i32* %2, i32** %a, align 8 + %3 = load i32*, i32** %a, align 8 + call void @"?init@@YAXPEAH@Z"(i32* %3) + ret i32 0 + } + + declare dso_local void @"?init@@YAXPEAH@Z"(i32*) #1 + + ; Function Attrs: nounwind + declare void @llvm.stackprotector(i8*, i8**) #2 + + attributes #0 = { noinline optnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+neon" "unsafe-fp-math"="false" "use-soft-float"="false" } + attributes #1 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+neon" "unsafe-fp-math"="false" "use-soft-float"="false" } + attributes #2 = { nounwind } + +... +--- +name: '?func@@YAHHHHH@Z' +alignment: 2 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +registers: +liveins: + - { reg: '$w0', virtual-reg: '' } + - { reg: '$w1', virtual-reg: '' } + - { reg: '$w2', virtual-reg: '' } + - { reg: '$w3', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 8 + adjustsStack: true + hasCalls: true + stackProtector: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + localFrameSize: 24 + savePoint: '' + restorePoint: '' +fixedStack: +stack: + - { id: 0, name: c.addr, type: default, offset: 0, size: 4, alignment: 4, + stack-id: 0, callee-saved-register: '', callee-saved-restored: true, + local-offset: -4, debug-info-variable: '', debug-info-expression: '', + debug-info-location: '' } + - { id: 1, name: b.addr, type: default, offset: 0, size: 4, alignment: 4, + stack-id: 0, callee-saved-register: '', callee-saved-restored: true, + local-offset: -8, debug-info-variable: '', debug-info-expression: '', + debug-info-location: '' } + - { id: 2, name: idx.addr, type: default, offset: 0, size: 4, alignment: 4, + stack-id: 0, callee-saved-register: '', callee-saved-restored: true, + local-offset: -12, debug-info-variable: '', debug-info-expression: '', + debug-info-location: '' } + - { id: 3, name: n.addr, type: default, offset: 0, size: 4, alignment: 4, + stack-id: 0, callee-saved-register: '', callee-saved-restored: true, + local-offset: -16, debug-info-variable: '', debug-info-expression: '', + debug-info-location: '' } + - { id: 4, name: a, type: default, offset: 0, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '', callee-saved-restored: true, + local-offset: -24, debug-info-variable: '', debug-info-expression: '', + debug-info-location: '' } + - { id: 5, name: '', type: variable-sized, offset: 0, + alignment: 1, stack-id: 0, callee-saved-register: '', callee-saved-restored: true, + local-offset: -24, debug-info-variable: '', debug-info-expression: '', + debug-info-location: '' } + - { id: 6, name: '', type: spill-slot, offset: 0, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } +constants: +body: | + bb.0.entry: + liveins: $w0, $w1, $w2, $w3 + + STRWui killed renamable $w3, %stack.0.c.addr, 0 :: (store 4 into %ir.c.addr) + STRWui killed renamable $w2, %stack.1.b.addr, 0 :: (store 4 into %ir.b.addr) + STRWui killed renamable $w1, %stack.2.idx.addr, 0 :: (store 4 into %ir.idx.addr) + STRWui killed renamable $w0, %stack.3.n.addr, 0 :: (store 4 into %ir.n.addr) + renamable $x8 = LDRSWui %stack.3.n.addr, 0 :: (dereferenceable load 4 from %ir.n.addr) + ADJCALLSTACKDOWN 0, 0, implicit-def dead $sp, implicit $sp + renamable $x8 = nuw ADDXri killed renamable $x8, 15, 0 + renamable $x8 = UBFMXri killed renamable $x8, 4, 63 + $x15 = COPY renamable $x8 + STRXui killed $x8, %stack.6, 0 :: (store 8 into %stack.6) + BL &__chkstk, csr_aarch64_stackprobe_windows, implicit-def dead $lr, implicit $sp, implicit killed $x15 + renamable $x8 = COPY $sp + $x15 = LDRXui %stack.6, 0 :: (load 8 from %stack.6) + renamable $x8 = SUBSXrs killed renamable $x8, killed renamable $x15, 4, implicit-def dead $nzcv + $sp = COPY renamable $x8 + ADJCALLSTACKUP 0, 0, implicit-def dead $sp, implicit $sp + STRXui killed renamable $x8, %stack.4.a, 0 :: (store 8 into %ir.a) + renamable $x0 = LDRXui %stack.4.a, 0 :: (dereferenceable load 8 from %ir.a) + ADJCALLSTACKDOWN 0, 0, implicit-def dead $sp, implicit $sp + BL @"?init@@YAXPEAH@Z", csr_aarch64_aapcs, implicit-def dead $lr, implicit $sp, implicit killed $x0, implicit-def $sp + ADJCALLSTACKUP 0, 0, implicit-def dead $sp, implicit $sp + renamable $w1 = COPY $wzr + $w0 = COPY killed renamable $w1 + RET_ReallyLR implicit killed $w0 + +... Index: test/CodeGen/AArch64/wineh-frame7.mir =================================================================== --- /dev/null +++ test/CodeGen/AArch64/wineh-frame7.mir @@ -0,0 +1,187 @@ +# RUN: llc -o - %s -mtriple=aarch64-windows -start-before=prologepilog \ +# RUN: -stop-after=prologepilog | FileCheck %s +# Test that stack probe results in Nop unwind codes in the prologue. Test +# save_fplr, save_reg_x and stack_alloc with multiple updates. + +# CHECK: early-clobber $sp = frame-setup STRXpre killed $x28, $sp, -32 +# CHECK-NEXT: frame-setup SEH_SaveReg_X 28, -32 +# CHECK-NEXT: frame-setup STPXi killed $fp, killed $lr, $sp, 2 +# CHECK-NEXT: frame-setup SEH_SaveFPLR 16 +# CHECK-NEXT: $x15 = frame-setup MOVi64imm 187081 +# CHECK-NEXT: frame-setup SEH_Nop +# CHECK-NEXT: frame-setup BL &__chkstk, implicit-def $lr, implicit $sp, implicit $x15 +# CHECK-NEXT: frame-setup SEH_Nop +# CHECK-NEXT: $sp = frame-setup SUBXrx64 killed $sp, killed $x15, 28 +# CHECK-NEXT: frame-setup SEH_StackAlloc 2993296 +# CHECK-NEXT: frame-setup SEH_PrologEnd +# CHECK: frame-destroy SEH_EpilogStart +# CHECK-NEXT: $sp = frame-destroy ADDXri $sp, 730, 12 +# CHECK-NEXT: frame-destroy SEH_StackAlloc 2990080 +# CHECK-NEXT: $sp = frame-destroy ADDXri $sp, 3216, 0 +# CHECK-NEXT: frame-destroy SEH_StackAlloc 3216 +# CHECK-NEXT: $fp, $lr = frame-destroy LDPXi $sp, 2 +# CHECK-NEXT: frame-destroy SEH_SaveFPLR 16 +# CHECK-NEXT: early-clobber $sp, $x28 = frame-destroy LDRXpost $sp, 32 +# CHECK-NEXT: frame-destroy SEH_SaveReg_X 28, -32 +# CHECK-NEXT: frame-destroy SEH_EpilogEnd +# CHECK-NEXT: RET_ReallyLR implicit killed $w0 +--- | + target datalayout = "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128" + target triple = "aarch64-unknown-windows-msvc19.11.0" + + ; Function Attrs: noinline optnone + define dso_local i32 @"?func@@YAHH@Z"(i32 %i) #0 { + entry: + %retval = alloca i32, align 4 + %i.addr = alloca i32, align 4 + %A = alloca [748193 x i32], align 4 + %a = alloca i32, align 4 + %B = alloca [123 x i32], align 4 + store i32 %i, i32* %i.addr, align 4 + %0 = load i32, i32* %i.addr, align 4 + %add = add nsw i32 %0, 2 + store i32 %add, i32* %a, align 4 + %call = call i32 @"?func2@@YAHXZ"() + %1 = load i32, i32* %i.addr, align 4 + %cmp = icmp sgt i32 %1, 2 + br i1 %cmp, label %if.then, label %if.else + + if.then: ; preds = %entry + %call1 = call i32 @"?func2@@YAHXZ"() + store i32 %call1, i32* %retval, align 4 + br label %return + + if.else: ; preds = %entry + %arraydecay = getelementptr inbounds [123 x i32], [123 x i32]* %B, i32 0, i32 0 + %call2 = call i32 @"?func3@@YAHPEAH@Z"(i32* %arraydecay) + store i32 %call2, i32* %retval, align 4 + br label %return + + return: ; preds = %if.else, %if.then + %2 = load i32, i32* %retval, align 4 + ret i32 %2 + } + + declare dso_local i32 @"?func2@@YAHXZ"() #1 + + declare dso_local i32 @"?func3@@YAHPEAH@Z"(i32*) #1 + + ; Function Attrs: nounwind + declare void @llvm.stackprotector(i8*, i8**) #2 + + attributes #0 = { noinline optnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+neon" "unsafe-fp-math"="false" "use-soft-float"="false" } + attributes #1 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+neon" "unsafe-fp-math"="false" "use-soft-float"="false" } + attributes #2 = { nounwind } + +... +--- +name: '?func@@YAHH@Z' +alignment: 2 +exposesReturnsTwice: false +legalized: true +regBankSelected: true +selected: true +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +registers: +liveins: +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 8 + adjustsStack: true + hasCalls: true + stackProtector: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + localFrameSize: 2993276 + savePoint: '' + restorePoint: '' +fixedStack: +stack: + - { id: 0, name: retval, type: default, offset: 0, size: 4, alignment: 4, + stack-id: 0, callee-saved-register: '', callee-saved-restored: true, + local-offset: -4, debug-info-variable: '', debug-info-expression: '', + debug-info-location: '' } + - { id: 1, name: i.addr, type: default, offset: 0, size: 4, alignment: 4, + stack-id: 0, callee-saved-register: '', callee-saved-restored: true, + local-offset: -8, debug-info-variable: '', debug-info-expression: '', + debug-info-location: '' } + - { id: 2, name: A, type: default, offset: 0, size: 2992772, alignment: 4, + stack-id: 0, callee-saved-register: '', callee-saved-restored: true, + local-offset: -2992780, debug-info-variable: '', debug-info-expression: '', + debug-info-location: '' } + - { id: 3, name: a, type: default, offset: 0, size: 4, alignment: 4, + stack-id: 0, callee-saved-register: '', callee-saved-restored: true, + local-offset: -2992784, debug-info-variable: '', debug-info-expression: '', + debug-info-location: '' } + - { id: 4, name: B, type: default, offset: 0, size: 492, alignment: 4, + stack-id: 0, callee-saved-register: '', callee-saved-restored: true, + local-offset: -2993276, debug-info-variable: '', debug-info-expression: '', + debug-info-location: '' } + - { id: 5, name: '', type: spill-slot, offset: 0, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 6, name: '', type: spill-slot, offset: 0, size: 4, alignment: 4, + stack-id: 0, callee-saved-register: '', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } +constants: +body: | + bb.1.entry: + successors: %bb.2(0x40000000), %bb.3(0x40000000) + liveins: $w0 + + renamable $x8 = ADDXri %stack.1.i.addr, 0, 0 + renamable $w9 = MOVi32imm 2 + STRWui killed renamable $w0, renamable $x8, 0 :: (store 4 into %ir.i.addr) + renamable $w0 = LDRWui renamable $x8, 0 :: (load 4 from %ir.i.addr) + renamable $w0 = ADDWri killed renamable $w0, 2, 0 + STRWui killed renamable $w0, %stack.3.a, 0 :: (store 4 into %ir.a) + ADJCALLSTACKDOWN 0, 0, implicit-def $sp, implicit $sp + STRXui killed $x8, %stack.5, 0 :: (store 8 into %stack.5) + STRWui killed $w9, %stack.6, 0 :: (store 4 into %stack.6) + BL @"?func2@@YAHXZ", csr_aarch64_aapcs, implicit-def $lr, implicit $sp, implicit-def $w0 + ADJCALLSTACKUP 0, 0, implicit-def $sp, implicit $sp + $x8 = LDRXui %stack.5, 0 :: (load 8 from %stack.5) + renamable $w9 = LDRWui killed renamable $x8, 0 :: (load 4 from %ir.i.addr) + $w10 = LDRWui %stack.6, 0 :: (load 4 from %stack.6) + $wzr = SUBSWrr killed renamable $w9, killed renamable $w10, implicit-def $nzcv + renamable $w9 = CSINCWr $wzr, $wzr, 13, implicit $nzcv + TBNZW killed renamable $w9, 0, %bb.2 + B %bb.3 + + bb.2.if.then: + successors: %bb.4(0x80000000) + + ADJCALLSTACKDOWN 0, 0, implicit-def $sp, implicit $sp + BL @"?func2@@YAHXZ", csr_aarch64_aapcs, implicit-def $lr, implicit $sp, implicit-def $w0 + ADJCALLSTACKUP 0, 0, implicit-def $sp, implicit $sp + $x8 = LDRXui %stack.5, 0 :: (load 8 from %stack.5) + STRWui killed renamable $w0, killed renamable $x8, 1 :: (store 4 into %ir.retval) + B %bb.4 + + bb.3.if.else: + successors: %bb.4(0x80000000) + + renamable $x8 = ADDXri %stack.4.B, 0, 0 + ADJCALLSTACKDOWN 0, 0, implicit-def $sp, implicit $sp + $x0 = COPY killed renamable $x8 + BL @"?func3@@YAHPEAH@Z", csr_aarch64_aapcs, implicit-def $lr, implicit $sp, implicit killed $x0, implicit-def $w0 + ADJCALLSTACKUP 0, 0, implicit-def $sp, implicit $sp + $x8 = LDRXui %stack.5, 0 :: (load 8 from %stack.5) + STRWui killed renamable $w0, killed renamable $x8, 1 :: (store 4 into %ir.retval) + + bb.4.return: + $x8 = LDRXui %stack.5, 0 :: (load 8 from %stack.5) + renamable $w0 = LDRWui killed renamable $x8, 1 :: (load 4 from %ir.retval) + RET_ReallyLR implicit killed $w0 + +... Index: test/CodeGen/AArch64/wineh-frame8.mir =================================================================== --- /dev/null +++ test/CodeGen/AArch64/wineh-frame8.mir @@ -0,0 +1,88 @@ +# RUN: llc -o - %s -mtriple=aarch64-windows -start-before=prologepilog \ +# RUN: -stop-after=prologepilog | FileCheck %s +# Test that the frame lowering emits correct SEH updates for the case without +# a stack frame (e.g. no callee saved registers, no frame pointer, just locals) + +# CHECK: $sp = frame-setup SUBXri $sp, 16, 0 +# CHECK-NEXT: frame-setup SEH_StackAlloc 16 +# CHECK-NEXT: frame-setup SEH_PrologEnd +# CHECK: frame-destroy SEH_EpilogStart +# CHECK-NEXT: $sp = frame-destroy ADDXri $sp, 16, 0 +# CHECK-NEXT: frame-destroy SEH_StackAlloc 16 +# CHECK-NEXT: frame-destroy SEH_EpilogEnd +# CHECK-NEXT: RET_ReallyLR implicit killed $w0 + +--- | + target datalayout = "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128" + target triple = "aarch64-unknown-windows-msvc19.11.0" + + ; Function Attrs: noinline nounwind optnone uwtable + define dso_local i32 @"?func@@YAHH@Z"(i32 %a) #0 { + entry: + %a.addr = alloca i32, align 4 + %b = alloca i32, align 4 + store i32 %a, i32* %a.addr, align 4 + store i32 2, i32* %b, align 4 + %0 = load i32, i32* %b, align 4 + %1 = load i32, i32* %a.addr, align 4 + %add = add nsw i32 %0, %1 + ret i32 %add + } + + attributes #0 = { noinline nounwind optnone uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+neon" "unsafe-fp-math"="false" "use-soft-float"="false" } + +... +--- +name: '?func@@YAHH@Z' +alignment: 2 +exposesReturnsTwice: false +legalized: true +regBankSelected: true +selected: true +failedISel: false +tracksRegLiveness: true +registers: +liveins: +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 4 + adjustsStack: false + hasCalls: false + stackProtector: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + localFrameSize: 8 + savePoint: '' + restorePoint: '' +fixedStack: +stack: + - { id: 0, name: a.addr, type: default, offset: 0, size: 4, alignment: 4, + stack-id: 0, callee-saved-register: '', callee-saved-restored: true, + local-offset: -4, debug-info-variable: '', debug-info-expression: '', + debug-info-location: '' } + - { id: 1, name: b, type: default, offset: 0, size: 4, alignment: 4, + stack-id: 0, callee-saved-register: '', callee-saved-restored: true, + local-offset: -8, debug-info-variable: '', debug-info-expression: '', + debug-info-location: '' } +constants: +body: | + bb.1.entry: + liveins: $w0 + + renamable $w8 = MOVi32imm 2 + STRWui killed renamable $w0, %stack.0.a.addr, 0 :: (store 4 into %ir.a.addr) + STRWui killed renamable $w8, %stack.1.b, 0 :: (store 4 into %ir.b) + renamable $w8 = LDRWui %stack.1.b, 0 :: (load 4 from %ir.b) + renamable $w0 = LDRWui %stack.0.a.addr, 0 :: (load 4 from %ir.a.addr) + renamable $w0 = nsw ADDWrr killed renamable $w8, killed renamable $w0 + RET_ReallyLR implicit killed $w0 + +...