Index: llvm/trunk/include/llvm/MC/MCStreamer.h =================================================================== --- llvm/trunk/include/llvm/MC/MCStreamer.h +++ llvm/trunk/include/llvm/MC/MCStreamer.h @@ -198,10 +198,6 @@ WinEH::FrameInfo *CurrentWinFrameInfo; - /// Retreive the current frame info if one is available and it is not yet - /// closed. Otherwise, issue an error and return null. - WinEH::FrameInfo *EnsureValidWinFrameInfo(SMLoc Loc); - /// Tracks an index to represent the order a symbol was emitted in. /// Zero means we did not emit that symbol. DenseMap SymbolOrdering; @@ -224,10 +220,6 @@ virtual void EmitCFIStartProcImpl(MCDwarfFrameInfo &Frame); virtual void EmitCFIEndProcImpl(MCDwarfFrameInfo &CurFrame); - /// When emitting an object file, create and emit a real label. When emitting - /// textual assembly, this should do nothing to avoid polluting our output. - virtual MCSymbol *EmitCFILabel(); - WinEH::FrameInfo *getCurrentWinFrameInfo() { return CurrentWinFrameInfo; } @@ -266,6 +258,14 @@ return TargetStreamer.get(); } + /// When emitting an object file, create and emit a real label. When emitting + /// textual assembly, this should do nothing to avoid polluting our output. + virtual MCSymbol *EmitCFILabel(); + + /// Retreive the current frame info if one is available and it is not yet + /// closed. Otherwise, issue an error and return null. + WinEH::FrameInfo *EnsureValidWinFrameInfo(SMLoc Loc); + unsigned getNumFrameInfos() { return DwarfFrameInfos.size(); } ArrayRef getDwarfFrameInfos() const { return DwarfFrameInfos; @@ -899,6 +899,11 @@ virtual void EmitWinCFIStartProc(const MCSymbol *Symbol, SMLoc Loc = SMLoc()); virtual void EmitWinCFIEndProc(SMLoc Loc = SMLoc()); + /// This is used on platforms, such as Windows on ARM64, that require function + /// or funclet sizes to be emitted in .xdata before the End marker is emitted + /// for the frame. We cannot use the End marker, as it is not set at the + /// point of emitting .xdata, in order to indicate that the frame is active. + virtual void EmitWinCFIFuncletOrFuncEnd(SMLoc Loc = SMLoc()); virtual void EmitWinCFIStartChained(SMLoc Loc = SMLoc()); virtual void EmitWinCFIEndChained(SMLoc Loc = SMLoc()); virtual void EmitWinCFIPushReg(unsigned Register, SMLoc Loc = SMLoc()); Index: llvm/trunk/include/llvm/MC/MCWin64EH.h =================================================================== --- llvm/trunk/include/llvm/MC/MCWin64EH.h +++ llvm/trunk/include/llvm/MC/MCWin64EH.h @@ -56,6 +56,14 @@ void Emit(MCStreamer &Streamer) const override; void EmitUnwindInfo(MCStreamer &Streamer, WinEH::FrameInfo *FI) const override; }; + +class ARM64UnwindEmitter : public WinEH::UnwindEmitter { +public: + void Emit(MCStreamer &Streamer) const override; + void EmitUnwindInfo(MCStreamer &Streamer, + WinEH::FrameInfo *FI) const override; +}; + } } // end namespace llvm Index: llvm/trunk/include/llvm/MC/MCWinEH.h =================================================================== --- llvm/trunk/include/llvm/MC/MCWinEH.h +++ llvm/trunk/include/llvm/MC/MCWinEH.h @@ -10,6 +10,7 @@ #ifndef LLVM_MC_MCWINEH_H #define LLVM_MC_MCWINEH_H +#include "llvm/ADT/MapVector.h" #include namespace llvm { @@ -20,9 +21,9 @@ namespace WinEH { struct Instruction { const MCSymbol *Label; - const unsigned Offset; - const unsigned Register; - const unsigned Operation; + unsigned Offset; + unsigned Register; + unsigned Operation; Instruction(unsigned Op, MCSymbol *L, unsigned Reg, unsigned Off) : Label(L), Offset(Off), Register(Reg), Operation(Op) {} @@ -31,6 +32,7 @@ struct FrameInfo { const MCSymbol *Begin = nullptr; const MCSymbol *End = nullptr; + const MCSymbol *FuncletOrFuncEnd = nullptr; const MCSymbol *ExceptionHandler = nullptr; const MCSymbol *Function = nullptr; const MCSymbol *PrologEnd = nullptr; @@ -43,6 +45,7 @@ int LastFrameInst = -1; const FrameInfo *ChainedParent = nullptr; std::vector Instructions; + MapVector> EpilogMap; FrameInfo() = default; FrameInfo(const MCSymbol *Function, const MCSymbol *BeginFuncEHLabel) Index: llvm/trunk/include/llvm/Support/Win64EH.h =================================================================== --- llvm/trunk/include/llvm/Support/Win64EH.h +++ llvm/trunk/include/llvm/Support/Win64EH.h @@ -33,7 +33,24 @@ UOP_SaveNonVolBig, UOP_SaveXMM128 = 8, UOP_SaveXMM128Big, - UOP_PushMachFrame + UOP_PushMachFrame, + // The following set of unwind opcodes is for ARM64. They are documented at + // https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling + UOP_AllocMedium, + UOP_SaveFPLRX, + UOP_SaveFPLR, + UOP_SaveReg, + UOP_SaveRegX, + UOP_SaveRegP, + UOP_SaveRegPX, + UOP_SaveFReg, + UOP_SaveFRegX, + UOP_SaveFRegP, + UOP_SaveFRegPX, + UOP_SetFP, + UOP_AddFP, + UOP_Nop, + UOP_End }; /// UnwindCode - This union describes a single operation in a function prolog, Index: llvm/trunk/lib/CodeGen/AsmPrinter/WinException.h =================================================================== --- llvm/trunk/lib/CodeGen/AsmPrinter/WinException.h +++ llvm/trunk/lib/CodeGen/AsmPrinter/WinException.h @@ -38,6 +38,9 @@ /// True if this is a 64-bit target and we should use image relative offsets. bool useImageRel32 = false; + /// True if we are generating exception handling on Windows for ARM64. + bool isAArch64 = false; + /// Pointer to the current funclet entry BB. const MachineBasicBlock *CurrentFuncletEntry = nullptr; @@ -72,7 +75,7 @@ const MCExpr *create32bitRef(const MCSymbol *Value); const MCExpr *create32bitRef(const GlobalValue *GV); - const MCExpr *getLabelPlusOne(const MCSymbol *Label); + const MCExpr *getLabel(const MCSymbol *Label); const MCExpr *getOffset(const MCSymbol *OffsetOf, const MCSymbol *OffsetFrom); const MCExpr *getOffsetPlusOne(const MCSymbol *OffsetOf, const MCSymbol *OffsetFrom); Index: llvm/trunk/lib/CodeGen/AsmPrinter/WinException.cpp =================================================================== --- llvm/trunk/lib/CodeGen/AsmPrinter/WinException.cpp +++ llvm/trunk/lib/CodeGen/AsmPrinter/WinException.cpp @@ -42,6 +42,7 @@ // MSVC's EH tables are always composed of 32-bit words. All known 64-bit // platforms use an imagerel32 relocation to refer to symbols. useImageRel32 = (A->getDataLayout().getPointerSizeInBits() == 64); + isAArch64 = Asm->TM.getTargetTriple().isAArch64(); } WinException::~WinException() {} @@ -242,6 +243,17 @@ if (F.hasPersonalityFn()) Per = classifyEHPersonality(F.getPersonalityFn()->stripPointerCasts()); + // On funclet exit, we emit a fake "function" end marker, so that the call + // to EmitWinEHHandlerData below can calculate the size of the funclet or + // function. + if (isAArch64) { + Asm->OutStreamer->SwitchSection(CurrentFuncletTextSection); + Asm->OutStreamer->EmitWinCFIFuncletOrFuncEnd(); + MCSection *XData = Asm->OutStreamer->getAssociatedXDataSection( + Asm->OutStreamer->getCurrentSectionOnly()); + Asm->OutStreamer->SwitchSection(XData); + } + // Emit an UNWIND_INFO struct describing the prologue. Asm->OutStreamer->EmitWinEHHandlerData(); @@ -286,7 +298,10 @@ return create32bitRef(Asm->getSymbol(GV)); } -const MCExpr *WinException::getLabelPlusOne(const MCSymbol *Label) { +const MCExpr *WinException::getLabel(const MCSymbol *Label) { + if (isAArch64) + return MCSymbolRefExpr::create(Label, MCSymbolRefExpr::VK_COFF_IMGREL32, + Asm->OutContext); return MCBinaryExpr::createAdd(create32bitRef(Label), MCConstantExpr::create(1, Asm->OutContext), Asm->OutContext); @@ -588,7 +603,6 @@ const MCSymbol *EndLabel, int State) { auto &OS = *Asm->OutStreamer; MCContext &Ctx = Asm->OutContext; - bool VerboseAsm = OS.isVerboseAsm(); auto AddComment = [&](const Twine &Comment) { if (VerboseAsm) @@ -613,9 +627,9 @@ } AddComment("LabelStart"); - OS.EmitValue(getLabelPlusOne(BeginLabel), 4); + OS.EmitValue(getLabel(BeginLabel), 4); AddComment("LabelEnd"); - OS.EmitValue(getLabelPlusOne(EndLabel), 4); + OS.EmitValue(getLabel(EndLabel), 4); AddComment(UME.IsFinally ? "FinallyFunclet" : UME.Filter ? "FilterFunction" : "CatchAll"); OS.EmitValue(FilterOrFinally, 4); @@ -799,7 +813,7 @@ // TypeDescriptor *Type; // int32_t CatchObjOffset; // void (*Handler)(); - // int32_t ParentFrameOffset; // x64 only + // int32_t ParentFrameOffset; // x64 and AArch64 only // }; OS.EmitLabel(HandlerMapXData); for (const WinEHHandlerType &HT : TBME.HandlerArray) { @@ -901,7 +915,7 @@ ChangeLabel = StateChange.PreviousEndLabel; // Emit an entry indicating that PCs after 'Label' have this EH state. IPToStateTable.push_back( - std::make_pair(getLabelPlusOne(ChangeLabel), StateChange.NewState)); + std::make_pair(getLabel(ChangeLabel), StateChange.NewState)); // FIXME: assert that NewState is between CatchLow and CatchHigh. } } Index: llvm/trunk/lib/MC/MCAsmStreamer.cpp =================================================================== --- llvm/trunk/lib/MC/MCAsmStreamer.cpp +++ llvm/trunk/lib/MC/MCAsmStreamer.cpp @@ -289,6 +289,7 @@ void EmitWinCFIStartProc(const MCSymbol *Symbol, SMLoc Loc) override; void EmitWinCFIEndProc(SMLoc Loc) override; + void EmitWinCFIFuncletOrFuncEnd(SMLoc Loc) override; void EmitWinCFIStartChained(SMLoc Loc) override; void EmitWinCFIEndChained(SMLoc Loc) override; void EmitWinCFIPushReg(unsigned Register, SMLoc Loc) override; @@ -1589,6 +1590,10 @@ EmitEOL(); } +// TODO: Implement +void MCAsmStreamer::EmitWinCFIFuncletOrFuncEnd(SMLoc Loc) { +} + void MCAsmStreamer::EmitWinCFIStartChained(SMLoc Loc) { MCStreamer::EmitWinCFIStartChained(Loc); Index: llvm/trunk/lib/MC/MCStreamer.cpp =================================================================== --- llvm/trunk/lib/MC/MCStreamer.cpp +++ llvm/trunk/lib/MC/MCStreamer.cpp @@ -627,6 +627,17 @@ CurFrame->End = Label; } +void MCStreamer::EmitWinCFIFuncletOrFuncEnd(SMLoc Loc) { + WinEH::FrameInfo *CurFrame = EnsureValidWinFrameInfo(Loc); + if (!CurFrame) + return; + if (CurFrame->ChainedParent) + getContext().reportError(Loc, "Not all chained regions terminated!"); + + MCSymbol *Label = EmitCFILabel(); + CurFrame->FuncletOrFuncEnd = Label; +} + void MCStreamer::EmitWinCFIStartChained(SMLoc Loc) { WinEH::FrameInfo *CurFrame = EnsureValidWinFrameInfo(Loc); if (!CurFrame) Index: llvm/trunk/lib/MC/MCWin64EH.cpp =================================================================== --- llvm/trunk/lib/MC/MCWin64EH.cpp +++ llvm/trunk/lib/MC/MCWin64EH.cpp @@ -11,6 +11,9 @@ #include "llvm/ADT/Twine.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCObjectStreamer.h" +#include "llvm/MC/MCSectionCOFF.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSymbol.h" #include "llvm/Support/Win64EH.h" @@ -23,6 +26,8 @@ uint8_t Count = 0; for (const auto &I : Insns) { switch (static_cast(I.Operation)) { + default: + llvm_unreachable("Unsupported unwind code"); case Win64EH::UOP_PushNonVol: case Win64EH::UOP_AllocSmall: case Win64EH::UOP_SetFPReg: @@ -60,6 +65,8 @@ uint16_t w; b2 = (inst.Operation & 0x0F); switch (static_cast(inst.Operation)) { + default: + llvm_unreachable("Unsupported unwind code"); case Win64EH::UOP_PushNonVol: EmitAbsDifference(streamer, inst.Label, begin); b2 |= (inst.Register & 0x0F) << 4; @@ -242,3 +249,343 @@ ::EmitUnwindInfo(Streamer, info); } +static int64_t GetAbsDifference(MCStreamer &Streamer, const MCSymbol *LHS, + const MCSymbol *RHS) { + MCContext &Context = Streamer.getContext(); + const MCExpr *Diff = + MCBinaryExpr::createSub(MCSymbolRefExpr::create(LHS, Context), + MCSymbolRefExpr::create(RHS, Context), Context); + MCObjectStreamer *OS = (MCObjectStreamer *)(&Streamer); + int64_t value; + Diff->evaluateAsAbsolute(value, OS->getAssembler()); + return value; +} + +static uint32_t +ARM64CountOfUnwindCodes(const std::vector &Insns) { + uint32_t Count = 0; + for (const auto &I : Insns) { + switch (static_cast(I.Operation)) { + default: + llvm_unreachable("Unsupported ARM64 unwind code"); + case Win64EH::UOP_AllocSmall: + Count += 1; + break; + case Win64EH::UOP_AllocMedium: + Count += 2; + break; + case Win64EH::UOP_AllocLarge: + Count += 4; + break; + case Win64EH::UOP_SaveFPLRX: + Count += 1; + break; + case Win64EH::UOP_SaveFPLR: + Count += 1; + break; + case Win64EH::UOP_SaveReg: + Count += 2; + break; + case Win64EH::UOP_SaveRegP: + Count += 2; + break; + case Win64EH::UOP_SaveRegPX: + Count += 2; + break; + case Win64EH::UOP_SaveRegX: + Count += 2; + break; + case Win64EH::UOP_SaveFReg: + Count += 2; + break; + case Win64EH::UOP_SaveFRegP: + Count += 2; + break; + case Win64EH::UOP_SaveFRegX: + Count += 2; + break; + case Win64EH::UOP_SaveFRegPX: + Count += 2; + break; + case Win64EH::UOP_SetFP: + Count += 1; + break; + case Win64EH::UOP_AddFP: + Count += 2; + break; + case Win64EH::UOP_Nop: + Count += 1; + break; + case Win64EH::UOP_End: + Count += 1; + break; + } + } + return Count; +} + +// Unwind opcode encodings and restrictions are documented at +// https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling +static void ARM64EmitUnwindCode(MCStreamer &streamer, const MCSymbol *begin, + WinEH::Instruction &inst) { + uint8_t b, reg; + switch (static_cast(inst.Operation)) { + default: + llvm_unreachable("Unsupported ARM64 unwind code"); + case Win64EH::UOP_AllocSmall: + b = (inst.Offset >> 4) & 0x1F; + streamer.EmitIntValue(b, 1); + break; + case Win64EH::UOP_AllocMedium: { + uint16_t hw = (inst.Offset >> 4) & 0x7FF; + b = 0xC0; + b |= (hw >> 8); + streamer.EmitIntValue(b, 1); + b = hw & 0xFF; + streamer.EmitIntValue(b, 1); + break; + } + case Win64EH::UOP_AllocLarge: { + uint32_t w; + b = 0xE0; + streamer.EmitIntValue(b, 1); + w = inst.Offset >> 4; + b = (w & 0x00FF0000) >> 16; + streamer.EmitIntValue(b, 1); + b = (w & 0x0000FF00) >> 8; + streamer.EmitIntValue(b, 1); + b = w & 0x000000FF; + streamer.EmitIntValue(b, 1); + break; + } + case Win64EH::UOP_SetFP: + b = 0xE1; + streamer.EmitIntValue(b, 1); + break; + case Win64EH::UOP_AddFP: + b = 0xE2; + streamer.EmitIntValue(b, 1); + b = (inst.Offset >> 3); + streamer.EmitIntValue(b, 1); + break; + case Win64EH::UOP_Nop: + b = 0xE3; + streamer.EmitIntValue(b, 1); + break; + case Win64EH::UOP_SaveFPLRX: + b = 0x80; + b |= ((inst.Offset - 1) >> 3) & 0x3F; + streamer.EmitIntValue(b, 1); + break; + case Win64EH::UOP_SaveFPLR: + b = 0x40; + b |= (inst.Offset >> 3) & 0x3F; + streamer.EmitIntValue(b, 1); + break; + case Win64EH::UOP_SaveReg: + assert(inst.Register >= 19 && "Saved reg must be >= 19"); + reg = inst.Register - 19; + b = 0xD0 | ((reg & 0xC) >> 2); + streamer.EmitIntValue(b, 1); + b = ((reg & 0x3) << 6) | (inst.Offset >> 3); + streamer.EmitIntValue(b, 1); + break; + case Win64EH::UOP_SaveRegX: + assert(inst.Register >= 19 && "Saved reg must be >= 19"); + reg = inst.Register - 19; + b = 0xD4 | ((reg & 0x8) >> 3); + streamer.EmitIntValue(b, 1); + b = ((reg & 0x7) << 5) | ((inst.Offset >> 3) - 1); + streamer.EmitIntValue(b, 1); + break; + case Win64EH::UOP_SaveRegP: + assert(inst.Register >= 19 && "Saved registers must be >= 19"); + reg = inst.Register - 19; + b = 0xC8 | ((reg & 0xC) >> 2); + streamer.EmitIntValue(b, 1); + b = ((reg & 0x3) << 6) | (inst.Offset >> 3); + streamer.EmitIntValue(b, 1); + break; + case Win64EH::UOP_SaveRegPX: + assert(inst.Register >= 19 && "Saved registers must be >= 19"); + reg = inst.Register - 19; + b = 0xCC | ((reg & 0xC) >> 2); + streamer.EmitIntValue(b, 1); + b = ((reg & 0x3) << 6) | ((inst.Offset >> 3) - 1); + streamer.EmitIntValue(b, 1); + break; + case Win64EH::UOP_SaveFReg: + assert(inst.Register >= 8 && "Saved dreg must be >= 8"); + reg = inst.Register - 8; + b = 0xDC | ((reg & 0x4) >> 2); + streamer.EmitIntValue(b, 1); + b = ((reg & 0x3) << 6) | (inst.Offset >> 3); + streamer.EmitIntValue(b, 1); + break; + case Win64EH::UOP_SaveFRegX: + assert(inst.Register >= 8 && "Saved dreg must be >= 8"); + reg = inst.Register - 8; + b = 0xDE; + streamer.EmitIntValue(b, 1); + b = ((reg & 0x7) << 5) | ((inst.Offset >> 3) - 1); + streamer.EmitIntValue(b, 1); + break; + case Win64EH::UOP_SaveFRegP: + assert(inst.Register >= 8 && "Saved dregs must be >= 8"); + reg = inst.Register - 8; + b = 0xD8 | ((reg & 0x4) >> 2); + streamer.EmitIntValue(b, 1); + b = ((reg & 0x3) << 6) | (inst.Offset >> 3); + streamer.EmitIntValue(b, 1); + break; + case Win64EH::UOP_SaveFRegPX: + assert(inst.Register >= 8 && "Saved dregs must be >= 8"); + reg = inst.Register - 8; + b = 0xDA | ((reg & 0x4) >> 2); + streamer.EmitIntValue(b, 1); + b = ((reg & 0x3) << 6) | ((inst.Offset >> 3) - 1); + streamer.EmitIntValue(b, 1); + break; + case Win64EH::UOP_End: + b = 0xE4; + streamer.EmitIntValue(b, 1); + break; + } +} + +// Populate the .xdata section. The format of .xdata on ARM64 is documented at +// https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling +static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info) { + // If this UNWIND_INFO already has a symbol, it's already been emitted. + if (info->Symbol) + return; + + MCContext &context = streamer.getContext(); + MCSymbol *Label = context.createTempSymbol(); + + streamer.EmitValueToAlignment(4); + streamer.EmitLabel(Label); + info->Symbol = Label; + + uint32_t FuncLength = 0x0; + FuncLength = (uint32_t)GetAbsDifference(streamer, info->FuncletOrFuncEnd, + info->Begin); + if (FuncLength) + FuncLength /= 4; + uint32_t PrologCodeBytes = ARM64CountOfUnwindCodes(info->Instructions); + uint32_t TotalCodeBytes = PrologCodeBytes; + + // Process epilogs. + MapVector EpilogInfo; + for (auto &I : info->EpilogMap) { + MCSymbol *EpilogStart = I.first; + auto &EpilogInstrs = I.second; + uint32_t CodeBytes = ARM64CountOfUnwindCodes(EpilogInstrs); + EpilogInfo[EpilogStart] = TotalCodeBytes; + TotalCodeBytes += CodeBytes; + } + + // Code Words, Epilog count, E, X, Vers, Function Length + uint32_t row1 = 0x0; + uint8_t CodeWords = TotalCodeBytes / 4; + uint8_t CodeWordsMod = TotalCodeBytes % 4; + if (CodeWordsMod) + CodeWords++; + uint32_t EpilogCount = info->EpilogMap.size(); + bool ExtensionWord = EpilogCount > 31 || TotalCodeBytes > 124; + if (!ExtensionWord) { + row1 |= (EpilogCount & 0x1F) << 22; + row1 |= (CodeWords & 0x1F) << 27; + } + // E is always 0 right now, TODO: packed epilog setup + if (info->HandlesExceptions) // X + row1 |= 1 << 20; + row1 |= FuncLength & 0x3FFFF; + streamer.EmitIntValue(row1, 4); + + // Extended Code Words, Extended Epilog Count + if (ExtensionWord) { + uint32_t row2 = 0x0; + row2 |= (CodeWords & 0xFF) << 16; + row2 |= (EpilogCount & 0xFFFF); + streamer.EmitIntValue(row2, 4); + } + + // Epilog Start Index, Epilog Start Offset + for (auto &I : EpilogInfo) { + MCSymbol *EpilogStart = I.first; + uint32_t EpilogIndex = I.second; + uint32_t EpilogOffset = + (uint32_t)GetAbsDifference(streamer, EpilogStart, info->Begin); + if (EpilogOffset) + EpilogOffset /= 4; + uint32_t row3 = EpilogOffset; + row3 |= (EpilogIndex & 0x3FF) << 22; + streamer.EmitIntValue(row3, 4); + } + + // Emit prolog unwind instructions (in reverse order). + uint8_t numInst = info->Instructions.size(); + for (uint8_t c = 0; c < numInst; ++c) { + WinEH::Instruction inst = info->Instructions.back(); + info->Instructions.pop_back(); + ARM64EmitUnwindCode(streamer, info->Begin, inst); + } + + // Emit epilog unwind instructions + for (auto &I : info->EpilogMap) { + auto &EpilogInstrs = I.second; + for (uint32_t i = 0; i < EpilogInstrs.size(); i++) { + WinEH::Instruction inst = EpilogInstrs[i]; + ARM64EmitUnwindCode(streamer, info->Begin, inst); + } + } + + int32_t BytesMod = CodeWords * 4 - TotalCodeBytes; + assert(BytesMod >= 0); + for (int i = 0; i < BytesMod; i++) + streamer.EmitIntValue(0xE3, 1); + + if (info->HandlesExceptions) + streamer.EmitValue( + MCSymbolRefExpr::create(info->ExceptionHandler, + MCSymbolRefExpr::VK_COFF_IMGREL32, context), + 4); +} + +static void ARM64EmitRuntimeFunction(MCStreamer &streamer, + const WinEH::FrameInfo *info) { + MCContext &context = streamer.getContext(); + + streamer.EmitValueToAlignment(4); + EmitSymbolRefWithOfs(streamer, info->Function, info->Begin); + streamer.EmitValue(MCSymbolRefExpr::create(info->Symbol, + MCSymbolRefExpr::VK_COFF_IMGREL32, + context), + 4); +} + +void llvm::Win64EH::ARM64UnwindEmitter::Emit(MCStreamer &Streamer) const { + // Emit the unwind info structs first. + for (const auto &CFI : Streamer.getWinFrameInfos()) { + MCSection *XData = Streamer.getAssociatedXDataSection(CFI->TextSection); + Streamer.SwitchSection(XData); + ARM64EmitUnwindInfo(Streamer, CFI.get()); + } + + // Now emit RUNTIME_FUNCTION entries. + for (const auto &CFI : Streamer.getWinFrameInfos()) { + MCSection *PData = Streamer.getAssociatedPDataSection(CFI->TextSection); + Streamer.SwitchSection(PData); + ARM64EmitRuntimeFunction(Streamer, CFI.get()); + } +} + +void llvm::Win64EH::ARM64UnwindEmitter::EmitUnwindInfo( + MCStreamer &Streamer, WinEH::FrameInfo *info) const { + // Switch sections (the static function above is meant to be called from + // here and from Emit(). + MCSection *XData = Streamer.getAssociatedXDataSection(info->TextSection); + Streamer.SwitchSection(XData); + ARM64EmitUnwindInfo(Streamer, info); +} Index: llvm/trunk/lib/Target/AArch64/AArch64AsmPrinter.cpp =================================================================== --- llvm/trunk/lib/Target/AArch64/AArch64AsmPrinter.cpp +++ llvm/trunk/lib/Target/AArch64/AArch64AsmPrinter.cpp @@ -21,6 +21,7 @@ #include "InstPrinter/AArch64InstPrinter.h" #include "MCTargetDesc/AArch64AddressingModes.h" #include "MCTargetDesc/AArch64MCTargetDesc.h" +#include "MCTargetDesc/AArch64TargetStreamer.h" #include "Utils/AArch64BaseInfo.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" @@ -665,6 +666,8 @@ OutStreamer->EmitLabel(LOHLabel); } + AArch64TargetStreamer *TS = + static_cast(OutStreamer->getTargetStreamer()); // Do any manual lowerings. switch (MI->getOpcode()) { default: @@ -817,6 +820,100 @@ case TargetOpcode::PATCHABLE_TAIL_CALL: LowerPATCHABLE_TAIL_CALL(*MI); return; + + case AArch64::SEH_StackAlloc: + TS->EmitARM64WinCFIAllocStack(MI->getOperand(0).getImm()); + return; + + case AArch64::SEH_SaveFPLR: + TS->EmitARM64WinCFISaveFPLR(MI->getOperand(0).getImm()); + return; + + case AArch64::SEH_SaveFPLR_X: + assert(MI->getOperand(0).getImm() < 0 && + "Pre increment SEH opcode must have a negative offset"); + TS->EmitARM64WinCFISaveFPLRX(-MI->getOperand(0).getImm()); + return; + + case AArch64::SEH_SaveReg: + TS->EmitARM64WinCFISaveReg(MI->getOperand(0).getImm(), + MI->getOperand(1).getImm()); + return; + + case AArch64::SEH_SaveReg_X: + assert(MI->getOperand(1).getImm() < 0 && + "Pre increment SEH opcode must have a negative offset"); + TS->EmitARM64WinCFISaveRegX(MI->getOperand(0).getImm(), + -MI->getOperand(1).getImm()); + return; + + case AArch64::SEH_SaveRegP: + assert((MI->getOperand(1).getImm() - MI->getOperand(0).getImm() == 1) && + "Non-consecutive registers not allowed for save_regp"); + TS->EmitARM64WinCFISaveRegP(MI->getOperand(0).getImm(), + MI->getOperand(2).getImm()); + return; + + case AArch64::SEH_SaveRegP_X: + assert((MI->getOperand(1).getImm() - MI->getOperand(0).getImm() == 1) && + "Non-consecutive registers not allowed for save_regp_x"); + assert(MI->getOperand(2).getImm() < 0 && + "Pre increment SEH opcode must have a negative offset"); + TS->EmitARM64WinCFISaveRegPX(MI->getOperand(0).getImm(), + -MI->getOperand(2).getImm()); + return; + + case AArch64::SEH_SaveFReg: + TS->EmitARM64WinCFISaveFReg(MI->getOperand(0).getImm(), + MI->getOperand(1).getImm()); + return; + + case AArch64::SEH_SaveFReg_X: + assert(MI->getOperand(1).getImm() < 0 && + "Pre increment SEH opcode must have a negative offset"); + TS->EmitARM64WinCFISaveFRegX(MI->getOperand(0).getImm(), + -MI->getOperand(1).getImm()); + return; + + case AArch64::SEH_SaveFRegP: + assert((MI->getOperand(1).getImm() - MI->getOperand(0).getImm() == 1) && + "Non-consecutive registers not allowed for save_regp"); + TS->EmitARM64WinCFISaveFRegP(MI->getOperand(0).getImm(), + MI->getOperand(2).getImm()); + return; + + case AArch64::SEH_SaveFRegP_X: + assert((MI->getOperand(1).getImm() - MI->getOperand(0).getImm() == 1) && + "Non-consecutive registers not allowed for save_regp_x"); + assert(MI->getOperand(2).getImm() < 0 && + "Pre increment SEH opcode must have a negative offset"); + TS->EmitARM64WinCFISaveFRegPX(MI->getOperand(0).getImm(), + -MI->getOperand(2).getImm()); + return; + + case AArch64::SEH_SetFP: + TS->EmitARM64WinCFISetFP(); + return; + + case AArch64::SEH_AddFP: + TS->EmitARM64WinCFIAddFP(MI->getOperand(0).getImm()); + return; + + case AArch64::SEH_Nop: + TS->EmitARM64WinCFINop(); + return; + + case AArch64::SEH_PrologEnd: + TS->EmitARM64WinCFIPrologEnd(); + return; + + case AArch64::SEH_EpilogStart: + TS->EmitARM64WinCFIEpilogStart(); + return; + + case AArch64::SEH_EpilogEnd: + TS->EmitARM64WinCFIEpilogEnd(); + return; } // Finally, do the automated lowerings for everything else. Index: llvm/trunk/lib/Target/AArch64/AArch64InstrInfo.td =================================================================== --- llvm/trunk/lib/Target/AArch64/AArch64InstrInfo.td +++ llvm/trunk/lib/Target/AArch64/AArch64InstrInfo.td @@ -3138,6 +3138,28 @@ let hasNoSchedulingInfo = 1; } +//===----------------------------------------------------------------------===// +// Instructions used for emitting unwind opcodes on ARM64 Windows. +//===----------------------------------------------------------------------===// +let isPseudo = 1 in { + def SEH_StackAlloc : Pseudo<(outs), (ins i32imm:$size), []>, Sched<[]>; + def SEH_SaveFPLR : Pseudo<(outs), (ins i32imm:$offs), []>, Sched<[]>; + def SEH_SaveFPLR_X : Pseudo<(outs), (ins i32imm:$offs), []>, Sched<[]>; + def SEH_SaveReg : Pseudo<(outs), (ins i32imm:$reg, i32imm:$offs), []>, Sched<[]>; + def SEH_SaveReg_X : Pseudo<(outs), (ins i32imm:$reg, i32imm:$offs), []>, Sched<[]>; + def SEH_SaveRegP : Pseudo<(outs), (ins i32imm:$reg0, i32imm:$reg1, i32imm:$offs), []>, Sched<[]>; + def SEH_SaveRegP_X : Pseudo<(outs), (ins i32imm:$reg0, i32imm:$reg1, i32imm:$offs), []>, Sched<[]>; + def SEH_SaveFReg : Pseudo<(outs), (ins i32imm:$reg, i32imm:$offs), []>, Sched<[]>; + def SEH_SaveFReg_X : Pseudo<(outs), (ins i32imm:$reg, i32imm:$offs), []>, Sched<[]>; + def SEH_SaveFRegP : Pseudo<(outs), (ins i32imm:$reg0, i32imm:$reg1, i32imm:$offs), []>, Sched<[]>; + def SEH_SaveFRegP_X : Pseudo<(outs), (ins i32imm:$reg0, i32imm:$reg1, i32imm:$offs), []>, Sched<[]>; + def SEH_SetFP : Pseudo<(outs), (ins), []>, Sched<[]>; + def SEH_AddFP : Pseudo<(outs), (ins i32imm:$offs), []>, Sched<[]>; + def SEH_Nop : Pseudo<(outs), (ins), []>, Sched<[]>; + def SEH_PrologEnd : Pseudo<(outs), (ins), []>, Sched<[]>; + def SEH_EpilogStart : Pseudo<(outs), (ins), []>, Sched<[]>; + def SEH_EpilogEnd : Pseudo<(outs), (ins), []>, Sched<[]>; +} //===----------------------------------------------------------------------===// // Floating point immediate move. Index: llvm/trunk/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp =================================================================== --- llvm/trunk/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp +++ llvm/trunk/lib/Target/AArch64/MCTargetDesc/AArch64ELFStreamer.cpp @@ -60,16 +60,6 @@ OS << "\t.inst\t0x" << Twine::utohexstr(Inst) << "\n"; } -class AArch64TargetELFStreamer : public AArch64TargetStreamer { -private: - AArch64ELFStreamer &getStreamer(); - - void emitInst(uint32_t Inst) override; - -public: - AArch64TargetELFStreamer(MCStreamer &S) : AArch64TargetStreamer(S) {} -}; - /// Extend the generic ELFStreamer class so that it can emit mapping symbols at /// the appropriate points in the object files. These symbols are defined in the /// AArch64 ELF ABI: @@ -197,6 +187,8 @@ } // end anonymous namespace +namespace llvm { + AArch64ELFStreamer &AArch64TargetELFStreamer::getStreamer() { return static_cast(Streamer); } @@ -205,8 +197,6 @@ getStreamer().emitInst(Inst); } -namespace llvm { - MCTargetStreamer *createAArch64AsmTargetStreamer(MCStreamer &S, formatted_raw_ostream &OS, MCInstPrinter *InstPrint, @@ -226,14 +216,4 @@ return S; } -MCTargetStreamer * -createAArch64ObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI) { - const Triple &TT = STI.getTargetTriple(); - if (TT.isOSBinFormatELF()) - return new AArch64TargetELFStreamer(S); - if (TT.isOSBinFormatCOFF()) - return new AArch64TargetWinCOFFStreamer(S); - return nullptr; -} - } // end namespace llvm Index: llvm/trunk/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp =================================================================== --- llvm/trunk/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp +++ llvm/trunk/lib/Target/AArch64/MCTargetDesc/AArch64MCAsmInfo.cpp @@ -115,6 +115,7 @@ CommentString = ";"; ExceptionsType = ExceptionHandling::WinEH; + WinEHEncodingType = WinEH::EncodingType::Itanium; } AArch64MCAsmInfoGNUCOFF::AArch64MCAsmInfoGNUCOFF() { Index: llvm/trunk/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h =================================================================== --- llvm/trunk/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h +++ llvm/trunk/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.h @@ -12,6 +12,10 @@ #include "llvm/MC/MCStreamer.h" +namespace { +class AArch64ELFStreamer; +} + namespace llvm { class AArch64TargetStreamer : public MCTargetStreamer { @@ -33,10 +37,75 @@ /// Callback used to implement the .inst directive. virtual void emitInst(uint32_t Inst); + virtual void EmitARM64WinCFIAllocStack(unsigned Size) {} + virtual void EmitARM64WinCFISaveFPLR(int Offset) {} + virtual void EmitARM64WinCFISaveFPLRX(int Offset) {} + virtual void EmitARM64WinCFISaveReg(unsigned Reg, int Offset) {} + virtual void EmitARM64WinCFISaveRegX(unsigned Reg, int Offset) {} + virtual void EmitARM64WinCFISaveRegP(unsigned Reg, int Offset) {} + virtual void EmitARM64WinCFISaveRegPX(unsigned Reg, int Offset) {} + virtual void EmitARM64WinCFISaveFReg(unsigned Reg, int Offset) {} + virtual void EmitARM64WinCFISaveFRegX(unsigned Reg, int Offset) {} + virtual void EmitARM64WinCFISaveFRegP(unsigned Reg, int Offset) {} + virtual void EmitARM64WinCFISaveFRegPX(unsigned Reg, int Offset) {} + virtual void EmitARM64WinCFISetFP() {} + virtual void EmitARM64WinCFIAddFP(unsigned Size) {} + virtual void EmitARM64WinCFINop() {} + virtual void EmitARM64WinCFIPrologEnd() {} + virtual void EmitARM64WinCFIEpilogStart() {} + virtual void EmitARM64WinCFIEpilogEnd() {} + private: std::unique_ptr ConstantPools; }; +class AArch64TargetELFStreamer : public AArch64TargetStreamer { +private: + AArch64ELFStreamer &getStreamer(); + + void emitInst(uint32_t Inst) override; + +public: + AArch64TargetELFStreamer(MCStreamer &S) : AArch64TargetStreamer(S) {} +}; + +class AArch64TargetWinCOFFStreamer : public llvm::AArch64TargetStreamer { +private: + // True if we are processing SEH directives in an epilogue. + bool InEpilogCFI = false; + + // Symbol of the current epilog for which we are processing SEH directives. + MCSymbol *CurrentEpilog = nullptr; +public: + AArch64TargetWinCOFFStreamer(llvm::MCStreamer &S) + : AArch64TargetStreamer(S) {} + + // The unwind codes on ARM64 Windows are documented at + // https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling + void EmitARM64WinCFIAllocStack(unsigned Size) override; + void EmitARM64WinCFISaveFPLR(int Offset) override; + void EmitARM64WinCFISaveFPLRX(int Offset) override; + void EmitARM64WinCFISaveReg(unsigned Reg, int Offset) override; + void EmitARM64WinCFISaveRegX(unsigned Reg, int Offset) override; + void EmitARM64WinCFISaveRegP(unsigned Reg, int Offset) override; + void EmitARM64WinCFISaveRegPX(unsigned Reg, int Offset) override; + void EmitARM64WinCFISaveFReg(unsigned Reg, int Offset) override; + void EmitARM64WinCFISaveFRegX(unsigned Reg, int Offset) override; + void EmitARM64WinCFISaveFRegP(unsigned Reg, int Offset) override; + void EmitARM64WinCFISaveFRegPX(unsigned Reg, int Offset) override; + void EmitARM64WinCFISetFP() override; + void EmitARM64WinCFIAddFP(unsigned Size) override; + void EmitARM64WinCFINop() override; + void EmitARM64WinCFIPrologEnd() override; + void EmitARM64WinCFIEpilogStart() override; + void EmitARM64WinCFIEpilogEnd() override; +private: + void EmitARM64WinUnwindCode(unsigned UnwindCode, int Reg, int Offset); +}; + +MCTargetStreamer * +createAArch64ObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI); + } // end namespace llvm #endif Index: llvm/trunk/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.cpp =================================================================== --- llvm/trunk/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.cpp +++ llvm/trunk/lib/Target/AArch64/MCTargetDesc/AArch64TargetStreamer.cpp @@ -13,6 +13,7 @@ #include "AArch64TargetStreamer.h" #include "llvm/MC/ConstantPools.h" +#include "llvm/MC/MCSubtargetInfo.h" using namespace llvm; @@ -52,3 +53,17 @@ getStreamer().EmitBytes(StringRef(Buffer, 4)); } + +namespace llvm { + +MCTargetStreamer * +createAArch64ObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI) { + const Triple &TT = STI.getTargetTriple(); + if (TT.isOSBinFormatELF()) + return new AArch64TargetELFStreamer(S); + if (TT.isOSBinFormatCOFF()) + return new AArch64TargetWinCOFFStreamer(S); + return nullptr; +} + +} // end namespace llvm Index: llvm/trunk/lib/Target/AArch64/MCTargetDesc/AArch64WinCOFFStreamer.h =================================================================== --- llvm/trunk/lib/Target/AArch64/MCTargetDesc/AArch64WinCOFFStreamer.h +++ llvm/trunk/lib/Target/AArch64/MCTargetDesc/AArch64WinCOFFStreamer.h @@ -17,20 +17,6 @@ #include "AArch64TargetStreamer.h" #include "llvm/MC/MCWinCOFFStreamer.h" -namespace { -class AArch64WinCOFFStreamer; - -class AArch64TargetWinCOFFStreamer : public llvm::AArch64TargetStreamer { -private: - AArch64WinCOFFStreamer &getStreamer(); - -public: - AArch64TargetWinCOFFStreamer(llvm::MCStreamer &S) - : AArch64TargetStreamer(S) {} -}; - -} // end anonymous namespace - namespace llvm { MCWinCOFFStreamer *createAArch64WinCOFFStreamer( Index: llvm/trunk/lib/Target/AArch64/MCTargetDesc/AArch64WinCOFFStreamer.cpp =================================================================== --- llvm/trunk/lib/Target/AArch64/MCTargetDesc/AArch64WinCOFFStreamer.cpp +++ llvm/trunk/lib/Target/AArch64/MCTargetDesc/AArch64WinCOFFStreamer.cpp @@ -11,12 +11,16 @@ #include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCCodeEmitter.h" #include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCWin64EH.h" +#include "llvm/MC/MCWinCOFFStreamer.h" using namespace llvm; namespace { class AArch64WinCOFFStreamer : public MCWinCOFFStreamer { + Win64EH::ARM64UnwindEmitter EHStreamer; + public: friend class AArch64TargetWinCOFFStreamer; @@ -25,17 +29,168 @@ std::unique_ptr OW) : MCWinCOFFStreamer(C, std::move(AB), std::move(CE), std::move(OW)) {} + void EmitWinEHHandlerData(SMLoc Loc) override; + void EmitWindowsUnwindTables() override; void FinishImpl() override; }; +void AArch64WinCOFFStreamer::EmitWinEHHandlerData(SMLoc Loc) { + MCStreamer::EmitWinEHHandlerData(Loc); + + // We have to emit the unwind info now, because this directive + // actually switches to the .xdata section! + EHStreamer.EmitUnwindInfo(*this, getCurrentWinFrameInfo()); +} + +void AArch64WinCOFFStreamer::EmitWindowsUnwindTables() { + if (!getNumWinFrameInfos()) + return; + EHStreamer.Emit(*this); +} + void AArch64WinCOFFStreamer::FinishImpl() { EmitFrames(nullptr); + EmitWindowsUnwindTables(); MCWinCOFFStreamer::FinishImpl(); } } // end anonymous namespace namespace llvm { + +// Helper function to common out unwind code setup for those codes that can +// belong to both prolog and epilog. +// There are three types of Windows ARM64 SEH codes. They can +// 1) take no operands: SEH_Nop, SEH_PrologEnd, SEH_EpilogStart, SEH_EpilogEnd +// 2) take an offset: SEH_StackAlloc, SEH_SaveFPLR, SEH_SaveFPLR_X +// 3) take a register and an offset/size: all others +void AArch64TargetWinCOFFStreamer::EmitARM64WinUnwindCode(unsigned UnwindCode, + int Reg, + int Offset) { + auto &S = getStreamer(); + WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); + if (!CurFrame) + return; + MCSymbol *Label = S.EmitCFILabel(); + auto Inst = WinEH::Instruction(UnwindCode, Label, Reg, Offset); + if (InEpilogCFI) + CurFrame->EpilogMap[CurrentEpilog].push_back(Inst); + else + CurFrame->Instructions.push_back(Inst); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIAllocStack(unsigned Size) { + unsigned Op = Win64EH::UOP_AllocSmall; + if (Size >= 16384) + Op = Win64EH::UOP_AllocLarge; + else if (Size >= 512) + Op = Win64EH::UOP_AllocMedium; + EmitARM64WinUnwindCode(Op, -1, Size); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFPLR(int Offset) { + EmitARM64WinUnwindCode(Win64EH::UOP_SaveFPLR, -1, Offset); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFPLRX(int Offset) { + EmitARM64WinUnwindCode(Win64EH::UOP_SaveFPLRX, -1, Offset); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveReg(unsigned Reg, + int Offset) { + assert(Offset >= 0 && Offset <= 504 && + "Offset for save reg should be >= 0 && <= 504"); + EmitARM64WinUnwindCode(Win64EH::UOP_SaveReg, Reg, Offset); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegX(unsigned Reg, + int Offset) { + EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegX, Reg, Offset); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegP(unsigned Reg, + int Offset) { + EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegP, Reg, Offset); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveRegPX(unsigned Reg, + int Offset) { + EmitARM64WinUnwindCode(Win64EH::UOP_SaveRegPX, Reg, Offset); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFReg(unsigned Reg, + int Offset) { + assert(Offset >= 0 && Offset <= 504 && + "Offset for save reg should be >= 0 && <= 504"); + EmitARM64WinUnwindCode(Win64EH::UOP_SaveFReg, Reg, Offset); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegX(unsigned Reg, + int Offset) { + EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegX, Reg, Offset); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegP(unsigned Reg, + int Offset) { + EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegP, Reg, Offset); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISaveFRegPX(unsigned Reg, + int Offset) { + EmitARM64WinUnwindCode(Win64EH::UOP_SaveFRegPX, Reg, Offset); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFISetFP() { + EmitARM64WinUnwindCode(Win64EH::UOP_SetFP, -1, 0); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIAddFP(unsigned Offset) { + assert(Offset <= 2040 && "UOP_AddFP must have offset <= 2040"); + EmitARM64WinUnwindCode(Win64EH::UOP_AddFP, -1, Offset); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFINop() { + EmitARM64WinUnwindCode(Win64EH::UOP_Nop, -1, 0); +} + +// The functions below handle opcodes that can end up in either a prolog or +// an epilog, but not both. +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIPrologEnd() { + auto &S = getStreamer(); + WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); + if (!CurFrame) + return; + + MCSymbol *Label = S.EmitCFILabel(); + CurFrame->PrologEnd = Label; + WinEH::Instruction Inst = WinEH::Instruction(Win64EH::UOP_End, Label, -1, 0); + auto it = CurFrame->Instructions.begin(); + CurFrame->Instructions.insert(it, Inst); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIEpilogStart() { + auto &S = getStreamer(); + WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); + if (!CurFrame) + return; + + InEpilogCFI = true; + CurrentEpilog = S.EmitCFILabel(); +} + +void AArch64TargetWinCOFFStreamer::EmitARM64WinCFIEpilogEnd() { + auto &S = getStreamer(); + WinEH::FrameInfo *CurFrame = S.EnsureValidWinFrameInfo(SMLoc()); + if (!CurFrame) + return; + + InEpilogCFI = false; + MCSymbol *Label = S.EmitCFILabel(); + WinEH::Instruction Inst = WinEH::Instruction(Win64EH::UOP_End, Label, -1, 0); + CurFrame->EpilogMap[CurrentEpilog].push_back(Inst); + CurrentEpilog = nullptr; +} + MCWinCOFFStreamer *createAArch64WinCOFFStreamer( MCContext &Context, std::unique_ptr MAB, std::unique_ptr OW, std::unique_ptr Emitter, Index: llvm/trunk/test/CodeGen/AArch64/wineh1.mir =================================================================== --- llvm/trunk/test/CodeGen/AArch64/wineh1.mir +++ llvm/trunk/test/CodeGen/AArch64/wineh1.mir @@ -0,0 +1,120 @@ +# RUN: llc -o - %s -mtriple=aarch64-windows -start-after=prologepilog -filetype=obj -disable-post-ra \ +# RUN: | llvm-readobj -unwind | FileCheck %s +# This test case checks the basic validity of the .xdata section. It's +# documented at: +# https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling + +# We expect to see the following in the .xdata section: + +# CHECK: ExceptionData { +# CHECK-NEXT: FunctionLength: 92 +# CHECK-NEXT: Version: 0 +# CHECK-NEXT: ExceptionData: No +# CHECK-NEXT: EpiloguePacked: No +# CHECK-NEXT: EpilogueScopes: 1 +# CHECK-NEXT: ByteCodeLength: 28 +# CHECK-NEXT: Prologue [ +# CHECK-NEXT: 0xc808 ; stp x19, x20, [sp, #64] +# CHECK-NEXT: 0xd0c7 ; str x22, [sp, #56] +# CHECK-NEXT: 0xd086 ; str x21, [sp, #48] +# CHECK-NEXT: 0xc904 ; stp x23, x24, [sp, #32] +# CHECK-NEXT: 0xc982 ; stp x25, x26, [sp, #16] +# CHECK-NEXT: 0xce09 ; stp x27, x28, [sp, #-80]! +# CHECK-NEXT: 0xe4 ; end +# CHECK-NEXT: ] +# CHECK-NEXT: EpilogueScopes [ +# CHECK-NEXT: EpilogueScope { +# CHECK-NEXT: StartOffset: 15 +# CHECK-NEXT: EpilogueStartIndex: 13 +# CHECK-NEXT: Opcodes [ +# CHECK-NEXT: 0xc808 ; ldp x19, x20, [sp, #64] +# CHECK-NEXT: 0xd086 ; ldr x21, [sp, #48] +# CHECK-NEXT: 0xe3 ; nop +# CHECK-NEXT: 0xd0c7 ; ldr x22, [sp, #56] +# CHECK-NEXT: 0xc904 ; ldp x23, x24, [sp, #32] +# CHECK-NEXT: 0xc982 ; ldp x25, x26, [sp, #16] +# CHECK-NEXT: 0xce09 ; ldp x27, x28, [sp], #80 +# CHECK-NEXT: 0xe4 ; end +# CHECK-NEXT: ] +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: } +... +--- +name: test +alignment: 2 +tracksRegLiveness: true +hasWinCFI: true +liveins: + - { reg: '$w0' } +frameInfo: + stackSize: 80 + maxAlignment: 8 + maxCallFrameSize: 0 + hasOpaqueSPAdjustment: true +stack: + - { id: 0, type: spill-slot, offset: -8, size: 8, alignment: 8, stack-id: 0, + callee-saved-register: '$x19' } + - { id: 1, type: spill-slot, offset: -16, size: 8, alignment: 8, stack-id: 0, + callee-saved-register: '$x20' } + - { id: 2, type: spill-slot, offset: -24, size: 8, alignment: 8, stack-id: 0, + callee-saved-register: '$x21' } + - { id: 3, type: spill-slot, offset: -32, size: 8, alignment: 8, stack-id: 0, + callee-saved-register: '$x22' } + - { id: 4, type: spill-slot, offset: -40, size: 8, alignment: 8, stack-id: 0, + callee-saved-register: '$x23' } + - { id: 5, type: spill-slot, offset: -48, size: 8, alignment: 8, stack-id: 0, + callee-saved-register: '$x24' } + - { id: 6, type: spill-slot, offset: -56, size: 8, alignment: 8, stack-id: 0, + callee-saved-register: '$x25' } + - { id: 7, type: spill-slot, offset: -64, size: 8, alignment: 8, stack-id: 0, + callee-saved-register: '$x26' } + - { id: 8, type: spill-slot, offset: -72, size: 8, alignment: 8, stack-id: 0, + callee-saved-register: '$x27' } + - { id: 9, type: spill-slot, offset: -80, size: 8, alignment: 8, stack-id: 0, + callee-saved-register: '$x28' } +body: | + bb.0.entry: + liveins: $x0, $x1, $x27, $x28, $x25, $x26, $x23, $x24, $x21, $x22, $x19, $x20 + early-clobber $sp = frame-setup STPXpre killed $x27, killed $x28, $sp, -10 :: (store 8 into %stack.8), (store 8 into %stack.9) + frame-setup SEH_SaveRegP_X 27, 28, -80 + frame-setup STPXi killed $x25, killed $x26, $sp, 2 :: (store 8 into %stack.6), (store 8 into %stack.7) + frame-setup SEH_SaveRegP 25, 26, 16 + frame-setup STPXi killed $x23, killed $x24, $sp, 4 :: (store 8 into %stack.4), (store 8 into %stack.5) + frame-setup SEH_SaveRegP 23, 24, 32 + frame-setup STRXui killed $x21, $sp, 6 :: (store 8 into %stack.2) + frame-setup SEH_SaveReg 21, 48 + frame-setup STRXui killed $x22, $sp, 7 :: (store 8 into %stack.3) + frame-setup SEH_SaveReg 22, 56 + frame-setup STPXi killed $x19, killed $x20, $sp, 8 :: (store 8 into %stack.0), (store 8 into %stack.1) + frame-setup SEH_SaveRegP 19, 20, 64 + frame-setup SEH_PrologEnd + $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 + frame-destroy SEH_EpilogStart + $x19, $x20 = frame-destroy LDPXi $sp, 8 :: (load 8 from %stack.0), (load 8 from %stack.1) + frame-destroy SEH_SaveRegP 19, 20, 64 + $x21 = frame-destroy LDRXui $sp, 6 :: (load 8 from %stack.2) + frame-destroy SEH_SaveReg 21, 48 + $x0 = COPY $x28 + frame-destroy SEH_Nop + $x21 = frame-destroy LDRXui $sp, 6 :: (load 8 from %stack.2) + frame-destroy SEH_SaveReg 22, 56 + $x23, $x24 = frame-destroy LDPXi $sp, 4 :: (load 8 from %stack.4), (load 8 from %stack.5) + frame-destroy SEH_SaveRegP 23, 24, 32 + $x25, $x26 = frame-destroy LDPXi $sp, 2 :: (load 8 from %stack.6), (load 8 from %stack.7) + frame-destroy SEH_SaveRegP 25, 26, 16 + early-clobber $sp, $x27, $x28 = frame-destroy LDPXpost $sp, 10 :: (load 8 from %stack.8), (load 8 from %stack.9) + frame-destroy SEH_SaveRegP_X 27, 28, -80 + frame-destroy SEH_EpilogEnd + RET_ReallyLR implicit $x0 + +... Index: llvm/trunk/test/CodeGen/AArch64/wineh2.mir =================================================================== --- llvm/trunk/test/CodeGen/AArch64/wineh2.mir +++ llvm/trunk/test/CodeGen/AArch64/wineh2.mir @@ -0,0 +1,185 @@ +# RUN: llc -o - %s -mtriple=aarch64-windows -start-after=prologepilog \ +# RUN: -disable-post-ra -filetype=obj | llvm-readobj -unwind | FileCheck %s +# Test that the pre/post increment save of a flating point register is correct. + +# CHECK: ExceptionData { +# CHECK-NEXT: FunctionLength: 136 +# CHECK-NEXT: Version: 0 +# CHECK-NEXT: ExceptionData: No +# CHECK-NEXT: EpiloguePacked: No +# CHECK-NEXT: EpilogueScopes: 1 +# CHECK-NEXT: ByteCodeLength: 40 +# CHECK-NEXT: Prologue [ +# CHECK-NEXT: 0xc80e ; stp x19, x20, [sp, #112] +# CHECK-NEXT: 0xc88c ; stp x21, x22, [sp, #96] +# CHECK-NEXT: 0xc90a ; stp x23, x24, [sp, #80] +# CHECK-NEXT: 0xc988 ; stp x25, x26, [sp, #64] +# CHECK-NEXT: 0xca06 ; stp x27, x28, [sp, #48] +# CHECK-NEXT: 0xdc45 ; str d9, [sp, #40] +# CHECK-NEXT: 0xdc04 ; str d8, [sp, #32] +# CHECK-NEXT: 0xd882 ; stp d10, d11, [sp, #16] +# CHECK-NEXT: 0xde8f ; str d12, [sp, #-128]! +# CHECK-NEXT: 0xe4 ; end +# CHECK-NEXT: ] +# CHECK-NEXT: EpilogueScopes [ +# CHECK-NEXT: EpilogueScope { +# CHECK-NEXT: StartOffset: 25 +# CHECK-NEXT: EpilogueStartIndex: 19 +# CHECK-NEXT: Opcodes [ +# CHECK-NEXT: 0xc80e ; ldp x19, x20, [sp, #112] +# CHECK-NEXT: 0xc88c ; ldp x21, x22, [sp, #96] +# CHECK-NEXT: 0xc90a ; ldp x23, x24, [sp, #80] +# CHECK-NEXT: 0xc988 ; ldp x25, x26, [sp, #64] +# CHECK-NEXT: 0xca06 ; ldp x27, x28, [sp, #48] +# CHECK-NEXT: 0xdc04 ; ldr d8, [sp, #32] +# CHECK-NEXT: 0xdc45 ; ldr d9, [sp, #40] +# CHECK-NEXT: 0xd882 ; ldp d10, d11, [sp, #16] +# CHECK-NEXT: 0xde8f ; ldr d12, [sp], #128 +# CHECK-NEXT: 0xe4 ; end +# CHECK-NEXT: ] +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: } +... +--- +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: 128 + offsetAdjustment: 0 + maxAlignment: 16 + adjustsStack: false + hasCalls: false + stackProtector: '' + maxCallFrameSize: 0 + hasOpaqueSPAdjustment: true + hasVAStart: false + hasMustTailInVarArgFunc: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: +stack: + - { id: 0, name: '', type: spill-slot, offset: -8, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x19', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 1, name: '', type: spill-slot, offset: -16, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x20', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 2, name: '', type: spill-slot, offset: -24, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x21', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 3, name: '', type: spill-slot, offset: -32, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x22', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 4, name: '', type: spill-slot, offset: -40, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x23', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 5, name: '', type: spill-slot, offset: -48, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x24', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 6, name: '', type: spill-slot, offset: -56, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x25', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 7, name: '', type: spill-slot, offset: -64, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x26', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 8, name: '', type: spill-slot, offset: -72, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x27', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 9, name: '', type: spill-slot, offset: -80, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x28', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 10, name: '', type: spill-slot, offset: -88, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$d8', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 11, name: '', type: spill-slot, offset: -96, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$d9', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 12, name: '', type: spill-slot, offset: -104, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$d10', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 13, name: '', type: spill-slot, offset: -112, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$d11', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 14, name: '', type: spill-slot, offset: -128, size: 8, alignment: 16, + stack-id: 0, callee-saved-register: '$d12', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } +constants: +body: | + bb.0.entry: + liveins: $x0, $x1, $d0, $d1, $d8, $d9, $d10, $d11, $d12, $x19, $x20, $x21, $x22, $x23, $x24, $x25, $x26, $x27, $x28 + + early-clobber $sp = frame-setup STRDpre killed $d12, $sp, -128 :: (store 8 into %stack.14) + frame-setup SEH_SaveFReg_X 12, -128 + frame-setup STPDi killed $d10, killed $d11, $sp, 2 :: (store 8 into %stack.12), (store 8 into %stack.13) + frame-setup SEH_SaveFRegP 10, 11, 16 + frame-setup STRDui killed $d8, $sp, 4 :: (store 8 into %stack.10) + frame-setup SEH_SaveFReg 8, 32 + frame-setup STRDui killed $d9, $sp, 5 :: (store 8 into %stack.11) + frame-setup SEH_SaveFReg 9, 40 + frame-setup STPXi killed $x27, killed $x28, $sp, 6 :: (store 8 into %stack.8), (store 8 into %stack.9) + frame-setup SEH_SaveRegP 27, 28, 48 + frame-setup STPXi killed $x25, killed $x26, $sp, 8 :: (store 8 into %stack.6), (store 8 into %stack.7) + frame-setup SEH_SaveRegP 25, 26, 64 + frame-setup STPXi killed $x23, killed $x24, $sp, 10 :: (store 8 into %stack.4), (store 8 into %stack.5) + frame-setup SEH_SaveRegP 23, 24, 80 + frame-setup STPXi killed $x21, killed $x22, $sp, 12 :: (store 8 into %stack.2), (store 8 into %stack.3) + frame-setup SEH_SaveRegP 21, 22, 96 + frame-setup STPXi killed $x19, killed $x20, $sp, 14 :: (store 8 into %stack.0), (store 8 into %stack.1) + frame-setup SEH_SaveRegP 19, 20, 112 + frame-setup SEH_PrologEnd + $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 killed $d10, killed $d11 + $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 $d12 + $x0 = ADDXrr $x0, killed $x28 + frame-destroy SEH_EpilogStart + $x19, $x20 = frame-destroy LDPXi $sp, 14 :: (load 8 from %stack.0), (load 8 from %stack.1) + frame-destroy SEH_SaveRegP 19, 20, 112 + $x21, $x22 = frame-destroy LDPXi $sp, 12 :: (load 8 from %stack.2), (load 8 from %stack.3) + frame-destroy SEH_SaveRegP 21, 22, 96 + $x23, $x24 = frame-destroy LDPXi $sp, 10 :: (load 8 from %stack.4), (load 8 from %stack.5) + frame-destroy SEH_SaveRegP 23, 24, 80 + $x25, $x26 = frame-destroy LDPXi $sp, 8 :: (load 8 from %stack.6), (load 8 from %stack.7) + frame-destroy SEH_SaveRegP 25, 26, 64 + $x27, $x28 = frame-destroy LDPXi $sp, 6 :: (load 8 from %stack.8), (load 8 from %stack.9) + frame-destroy SEH_SaveRegP 27, 28, 48 + $d8 = frame-destroy LDRDui $sp, 4 :: (load 8 from %stack.10) + frame-destroy SEH_SaveFReg 8, 32 + $d9 = frame-destroy LDRDui $sp, 5 :: (load 8 from %stack.11) + frame-destroy SEH_SaveFReg 9, 40 + $d10, $d11 = frame-destroy LDPDi $sp, 2 :: (load 8 from %stack.12), (load 8 from %stack.13) + frame-destroy SEH_SaveFRegP 10, 11, 16 + early-clobber $sp, $d12 = frame-destroy LDRDpost $sp, 128 :: (load 8 from %stack.14) + frame-destroy SEH_SaveFReg_X 12, -128 + frame-destroy SEH_EpilogEnd + RET_ReallyLR implicit $x0 + +... Index: llvm/trunk/test/CodeGen/AArch64/wineh3.mir =================================================================== --- llvm/trunk/test/CodeGen/AArch64/wineh3.mir +++ llvm/trunk/test/CodeGen/AArch64/wineh3.mir @@ -0,0 +1,171 @@ +# RUN: llc -o - %s -mtriple=aarch64-windows -start-after=prologepilog \ +# RUN: -disable-post-ra -filetype=obj | llvm-readobj -unwind | FileCheck %s +# Test that the register pairing of both general purpose and floating point +# registers is correctly saved in the .xdata section, as well as the pre/post +# increment of floating point register pairs. + +# CHECK: ExceptionData { +# CHECK-NEXT: FunctionLength: 124 +# CHECK-NEXT: Version: 0 +# CHECK-NEXT: ExceptionData: No +# CHECK-NEXT: EpiloguePacked: No +# CHECK-NEXT: EpilogueScopes: 1 +# CHECK-NEXT: ByteCodeLength: 32 +# CHECK-NEXT: Prologue [ +# CHECK-NEXT: 0xc80c ; stp x19, x20, [sp, #96] +# CHECK-NEXT: 0xc88a ; stp x21, x22, [sp, #80] +# CHECK-NEXT: 0xc908 ; stp x23, x24, [sp, #64] +# CHECK-NEXT: 0xc986 ; stp x25, x26, [sp, #48] +# CHECK-NEXT: 0xca04 ; stp x27, x28, [sp, #32] +# CHECK-NEXT: 0xd802 ; stp d8, d9, [sp, #16] +# CHECK-NEXT: 0xda8d ; stp d10, d11, [sp, #-112]! +# CHECK-NEXT: 0xe4 ; end +# CHECK-NEXT: ] +# CHECK-NEXT: EpilogueScopes [ +# CHECK-NEXT: EpilogueScope { +# CHECK-NEXT: StartOffset: 23 +# CHECK-NEXT: EpilogueStartIndex: 15 +# CHECK-NEXT: Opcodes [ +# CHECK-NEXT: 0xc80c ; ldp x19, x20, [sp, #96] +# CHECK-NEXT: 0xc88a ; ldp x21, x22, [sp, #80] +# CHECK-NEXT: 0xc908 ; ldp x23, x24, [sp, #64] +# CHECK-NEXT: 0xc986 ; ldp x25, x26, [sp, #48] +# CHECK-NEXT: 0xca04 ; ldp x27, x28, [sp, #32] +# CHECK-NEXT: 0xd802 ; ldp d8, d9, [sp, #16] +# CHECK-NEXT: 0xda8d ; ldp d10, d11, [sp], #112 +# CHECK-NEXT: 0xe4 ; end +# CHECK-NEXT: ] +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: } +... +--- +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: + - { id: 0, name: '', type: spill-slot, offset: -8, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x19', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 1, name: '', type: spill-slot, offset: -16, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x20', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 2, name: '', type: spill-slot, offset: -24, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x21', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 3, name: '', type: spill-slot, offset: -32, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x22', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 4, name: '', type: spill-slot, offset: -40, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x23', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 5, name: '', type: spill-slot, offset: -48, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x24', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 6, name: '', type: spill-slot, offset: -56, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x25', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 7, name: '', type: spill-slot, offset: -64, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x26', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 8, name: '', type: spill-slot, offset: -72, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x27', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 9, name: '', type: spill-slot, offset: -80, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x28', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 10, name: '', type: spill-slot, offset: -88, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$d8', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 11, name: '', type: spill-slot, offset: -96, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$d9', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 12, name: '', type: spill-slot, offset: -104, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$d10', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 13, name: '', type: spill-slot, offset: -112, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$d11', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } +constants: +body: | + bb.0.entry: + liveins: $x0, $x1, $d0, $d1, $d10, $d11, $d8, $d9, $x27, $x28, $x25, $x26, $x23, $x24, $x21, $x22, $x19, $x20 + + early-clobber $sp = frame-setup STPDpre killed $d10, killed $d11, $sp, -14 :: (store 8 into %stack.12), (store 8 into %stack.13) + frame-setup SEH_SaveFRegP_X 10, 11, -112 + frame-setup STPDi killed $d8, killed $d9, $sp, 2 :: (store 8 into %stack.10), (store 8 into %stack.11) + frame-setup SEH_SaveFRegP 8, 9, 16 + frame-setup STPXi killed $x27, killed $x28, $sp, 4 :: (store 8 into %stack.8), (store 8 into %stack.9) + frame-setup SEH_SaveRegP 27, 28, 32 + frame-setup STPXi killed $x25, killed $x26, $sp, 6 :: (store 8 into %stack.6), (store 8 into %stack.7) + frame-setup SEH_SaveRegP 25, 26, 48 + frame-setup STPXi killed $x23, killed $x24, $sp, 8 :: (store 8 into %stack.4), (store 8 into %stack.5) + frame-setup SEH_SaveRegP 23, 24, 64 + frame-setup STPXi killed $x21, killed $x22, $sp, 10 :: (store 8 into %stack.2), (store 8 into %stack.3) + frame-setup SEH_SaveRegP 21, 22, 80 + frame-setup STPXi killed $x19, killed $x20, $sp, 12 :: (store 8 into %stack.0), (store 8 into %stack.1) + frame-setup SEH_SaveRegP 19, 20, 96 + frame-setup SEH_PrologEnd + $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 + frame-destroy SEH_EpilogStart + $x19, $x20 = frame-destroy LDPXi $sp, 12 :: (load 8 from %stack.0), (load 8 from %stack.1) + frame-destroy SEH_SaveRegP 19, 20, 96 + $x21, $x22 = frame-destroy LDPXi $sp, 10 :: (load 8 from %stack.2), (load 8 from %stack.3) + frame-destroy SEH_SaveRegP 21, 22, 80 + $x23, $x24 = frame-destroy LDPXi $sp, 8 :: (load 8 from %stack.4), (load 8 from %stack.5) + frame-destroy SEH_SaveRegP 23, 24, 64 + $x25, $x26 = frame-destroy LDPXi $sp, 6 :: (load 8 from %stack.6), (load 8 from %stack.7) + frame-destroy SEH_SaveRegP 25, 26, 48 + $x27, $x28 = frame-destroy LDPXi $sp, 4 :: (load 8 from %stack.8), (load 8 from %stack.9) + frame-destroy SEH_SaveRegP 27, 28, 32 + $d8, $d9 = frame-destroy LDPDi $sp, 2 :: (load 8 from %stack.10), (load 8 from %stack.11) + frame-destroy SEH_SaveFRegP 8, 9, 16 + early-clobber $sp, $d10, $d11 = frame-destroy LDPDpost $sp, 14 :: (load 8 from %stack.12), (load 8 from %stack.13) + frame-destroy SEH_SaveFRegP_X 10, 11, -112 + frame-destroy SEH_EpilogEnd + RET_ReallyLR implicit $x0 + +... Index: llvm/trunk/test/CodeGen/AArch64/wineh4.mir =================================================================== --- llvm/trunk/test/CodeGen/AArch64/wineh4.mir +++ llvm/trunk/test/CodeGen/AArch64/wineh4.mir @@ -0,0 +1,228 @@ +# RUN: llc -o - %s -mtriple=aarch64-windows -start-after=prologepilog \ +# RUN: -disable-branch-fold -disable-post-ra -filetype=obj \ +# RUN: | llvm-readobj -unwind | FileCheck %s +# Check that multiple epilgoues are correctly placed in .xdata. + +# CHECK: ExceptionData { +# CHECK-NEXT: FunctionLength: 164 +# CHECK-NEXT: Version: 0 +# CHECK-NEXT: ExceptionData: No +# CHECK-NEXT: EpiloguePacked: No +# CHECK-NEXT: EpilogueScopes: 2 +# CHECK-NEXT: ByteCodeLength: 48 +# CHECK-NEXT: Prologue [ +# CHECK-NEXT: 0xc80c ; stp x19, x20, [sp, #96] +# CHECK-NEXT: 0xc88a ; stp x21, x22, [sp, #80] +# CHECK-NEXT: 0xc908 ; stp x23, x24, [sp, #64] +# CHECK-NEXT: 0xc986 ; stp x25, x26, [sp, #48] +# CHECK-NEXT: 0xca04 ; stp x27, x28, [sp, #32] +# CHECK-NEXT: 0xd802 ; stp d8, d9, [sp, #16] +# CHECK-NEXT: 0xda8d ; stp d10, d11, [sp, #-112]! +# CHECK-NEXT: 0xe4 ; end +# CHECK-NEXT: ] +# CHECK-NEXT: EpilogueScopes [ +# CHECK-NEXT: EpilogueScope { +# CHECK-NEXT: StartOffset: 16 +# CHECK-NEXT: EpilogueStartIndex: 15 +# CHECK-NEXT: Opcodes [ +# CHECK-NEXT: 0xc80c ; ldp x19, x20, [sp, #96] +# CHECK-NEXT: 0xc88a ; ldp x21, x22, [sp, #80] +# CHECK-NEXT: 0xc908 ; ldp x23, x24, [sp, #64] +# CHECK-NEXT: 0xc986 ; ldp x25, x26, [sp, #48] +# CHECK-NEXT: 0xca04 ; ldp x27, x28, [sp, #32] +# CHECK-NEXT: 0xd802 ; ldp d8, d9, [sp, #16] +# CHECK-NEXT: 0xda8d ; ldp d10, d11, [sp], #112 +# CHECK-NEXT: 0xe4 ; end +# CHECK-NEXT: ] +# CHECK-NEXT: } +# CHECK-NEXT: EpilogueScope { +# CHECK-NEXT: StartOffset: 33 +# CHECK-NEXT: EpilogueStartIndex: 30 +# CHECK-NEXT: Opcodes [ +# CHECK-NEXT: 0xc80c ; ldp x19, x20, [sp, #96] +# CHECK-NEXT: 0xc88a ; ldp x21, x22, [sp, #80] +# CHECK-NEXT: 0xc908 ; ldp x23, x24, [sp, #64] +# CHECK-NEXT: 0xc986 ; ldp x25, x26, [sp, #48] +# CHECK-NEXT: 0xca04 ; ldp x27, x28, [sp, #32] +# CHECK-NEXT: 0xd802 ; ldp d8, d9, [sp, #16] +# CHECK-NEXT: 0xda8d ; ldp d10, d11, [sp], #112 +# CHECK-NEXT: 0xe4 ; end +# CHECK-NEXT: ] +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: } +... +--- +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: + - { id: 0, name: '', type: spill-slot, offset: -8, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x19', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 1, name: '', type: spill-slot, offset: -16, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x20', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 2, name: '', type: spill-slot, offset: -24, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x21', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 3, name: '', type: spill-slot, offset: -32, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x22', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 4, name: '', type: spill-slot, offset: -40, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x23', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 5, name: '', type: spill-slot, offset: -48, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x24', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 6, name: '', type: spill-slot, offset: -56, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x25', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 7, name: '', type: spill-slot, offset: -64, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x26', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 8, name: '', type: spill-slot, offset: -72, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x27', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 9, name: '', type: spill-slot, offset: -80, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x28', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 10, name: '', type: spill-slot, offset: -88, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$d8', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 11, name: '', type: spill-slot, offset: -96, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$d9', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 12, name: '', type: spill-slot, offset: -104, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$d10', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 13, name: '', type: spill-slot, offset: -112, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$d11', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } +constants: +body: | + bb.0.entry: + successors: %bb.2(0x40000000), %bb.1(0x40000000) + liveins: $x0, $x1, $d0, $d1, $d10, $d11, $d8, $d9, $x27, $x28, $x25, $x26, $x23, $x24, $x21, $x22, $x19, $x20 + + early-clobber $sp = frame-setup STPDpre killed $d10, killed $d11, $sp, -14 :: (store 8 into %stack.12), (store 8 into %stack.13) + frame-setup SEH_SaveFRegP_X 10, 11, -112 + frame-setup STPDi killed $d8, killed $d9, $sp, 2 :: (store 8 into %stack.10), (store 8 into %stack.11) + frame-setup SEH_SaveFRegP 8, 9, 16 + frame-setup STPXi killed $x27, killed $x28, $sp, 4 :: (store 8 into %stack.8), (store 8 into %stack.9) + frame-setup SEH_SaveRegP 27, 28, 32 + frame-setup STPXi killed $x25, killed $x26, $sp, 6 :: (store 8 into %stack.6), (store 8 into %stack.7) + frame-setup SEH_SaveRegP 25, 26, 48 + frame-setup STPXi killed $x23, killed $x24, $sp, 8 :: (store 8 into %stack.4), (store 8 into %stack.5) + frame-setup SEH_SaveRegP 23, 24, 64 + frame-setup STPXi killed $x21, killed $x22, $sp, 10 :: (store 8 into %stack.2), (store 8 into %stack.3) + frame-setup SEH_SaveRegP 21, 22, 80 + frame-setup STPXi killed $x19, killed $x20, $sp, 12 :: (store 8 into %stack.0), (store 8 into %stack.1) + frame-setup SEH_SaveRegP 19, 20, 96 + frame-setup SEH_PrologEnd + frame-setup CFI_INSTRUCTION def_cfa_offset 112 + frame-setup CFI_INSTRUCTION offset $w19, -8 + frame-setup CFI_INSTRUCTION offset $w20, -16 + frame-setup CFI_INSTRUCTION offset $w21, -24 + frame-setup CFI_INSTRUCTION offset $w22, -32 + frame-setup CFI_INSTRUCTION offset $w23, -40 + frame-setup CFI_INSTRUCTION offset $w24, -48 + frame-setup CFI_INSTRUCTION offset $w25, -56 + frame-setup CFI_INSTRUCTION offset $w26, -64 + frame-setup CFI_INSTRUCTION offset $w27, -72 + frame-setup CFI_INSTRUCTION offset $w28, -80 + frame-setup CFI_INSTRUCTION offset $b8, -88 + frame-setup CFI_INSTRUCTION offset $b9, -96 + frame-setup CFI_INSTRUCTION offset $b10, -104 + frame-setup CFI_INSTRUCTION offset $b11, -112 + $x19 = ADDXrr $x0, killed $x1 + $d8 = FADDDrr killed $d0, $d1 + $d9 = FADDDrr $d8, $d1 + $d10 = FADDDrr $d9, $d8 + $d11 = FADDDrr killed $d9, $d10 + $x20 = SUBSXrr $x19, killed $x0, implicit-def $nzcv + Bcc 1, %bb.2, implicit killed $nzcv + B %bb.1 + + bb.1: + liveins: $x19, $x20 + + $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 + frame-destroy SEH_EpilogStart + $x19, $x20 = frame-destroy LDPXi $sp, 12 :: (load 8 from %stack.0), (load 8 from %stack.1) + frame-destroy SEH_SaveRegP 19, 20, 96 + $x21, $x22 = frame-destroy LDPXi $sp, 10 :: (load 8 from %stack.2), (load 8 from %stack.3) + frame-destroy SEH_SaveRegP 21, 22, 80 + $x23, $x24 = frame-destroy LDPXi $sp, 8 :: (load 8 from %stack.4), (load 8 from %stack.5) + frame-destroy SEH_SaveRegP 23, 24, 64 + $x25, $x26 = frame-destroy LDPXi $sp, 6 :: (load 8 from %stack.6), (load 8 from %stack.7) + frame-destroy SEH_SaveRegP 25, 26, 48 + $x27, $x28 = frame-destroy LDPXi $sp, 4 :: (load 8 from %stack.8), (load 8 from %stack.9) + frame-destroy SEH_SaveRegP 27, 28, 32 + $d8, $d9 = frame-destroy LDPDi $sp, 2 :: (load 8 from %stack.10), (load 8 from %stack.11) + frame-destroy SEH_SaveFRegP 8, 9, 16 + early-clobber $sp, $d10, $d11 = frame-destroy LDPDpost $sp, 14 :: (load 8 from %stack.12), (load 8 from %stack.13) + frame-destroy SEH_SaveFRegP_X 10, 11, -112 + frame-destroy SEH_EpilogEnd + RET_ReallyLR implicit $x0 + + bb.2: + liveins: $x28, $d11 + + $x0 = COPY $d11 + $x0 = ADDXrr $x0, killed $x28 + frame-destroy SEH_EpilogStart + $x19, $x20 = frame-destroy LDPXi $sp, 12 :: (load 8 from %stack.0), (load 8 from %stack.1) + frame-destroy SEH_SaveRegP 19, 20, 96 + $x21, $x22 = frame-destroy LDPXi $sp, 10 :: (load 8 from %stack.2), (load 8 from %stack.3) + frame-destroy SEH_SaveRegP 21, 22, 80 + $x23, $x24 = frame-destroy LDPXi $sp, 8 :: (load 8 from %stack.4), (load 8 from %stack.5) + frame-destroy SEH_SaveRegP 23, 24, 64 + $x25, $x26 = frame-destroy LDPXi $sp, 6 :: (load 8 from %stack.6), (load 8 from %stack.7) + frame-destroy SEH_SaveRegP 25, 26, 48 + $x27, $x28 = frame-destroy LDPXi $sp, 4 :: (load 8 from %stack.8), (load 8 from %stack.9) + frame-destroy SEH_SaveRegP 27, 28, 32 + $d8, $d9 = frame-destroy LDPDi $sp, 2 :: (load 8 from %stack.10), (load 8 from %stack.11) + frame-destroy SEH_SaveFRegP 8, 9, 16 + early-clobber $sp, $d10, $d11 = frame-destroy LDPDpost $sp, 14 :: (load 8 from %stack.12), (load 8 from %stack.13) + frame-destroy SEH_SaveFRegP_X 10, 11, -112 + frame-destroy SEH_EpilogEnd + RET_ReallyLR implicit $x0 + +... Index: llvm/trunk/test/CodeGen/AArch64/wineh5.mir =================================================================== --- llvm/trunk/test/CodeGen/AArch64/wineh5.mir +++ llvm/trunk/test/CodeGen/AArch64/wineh5.mir @@ -0,0 +1,224 @@ +# RUN: llc -o - %s -mtriple=aarch64-windows -start-after=prologepilog \ +# RUN: -disable-post-ra -filetype=obj | llvm-readobj -unwind | FileCheck %s + +# Check that that the large stack allocation is correctly represented in .xdata. + +# CHECK: ExceptionData { +# CHECK-NEXT: FunctionLength: 156 +# CHECK-NEXT: Version: 0 +# CHECK-NEXT: ExceptionData: No +# CHECK-NEXT: EpiloguePacked: No +# CHECK-NEXT: EpilogueScopes: 1 +# CHECK-NEXT: ByteCodeLength: 20 +# CHECK-NEXT: Prologue [ +# CHECK-NEXT: 0xe002dac9 ; sub sp, #2993296 +# CHECK-NEXT: 0xe3 ; nop +# CHECK-NEXT: 0xe3 ; nop +# CHECK-NEXT: 0x42 ; stp x29, x30, [sp, #16] +# CHECK-NEXT: 0xd53f ; str x28, [sp, #256]! +# CHECK-NEXT: 0xe4 ; end +# CHECK-NEXT: ] +# CHECK-NEXT: EpilogueScopes [ +# CHECK-NEXT: EpilogueScope { +# CHECK-NEXT: StartOffset: 34 +# CHECK-NEXT: EpilogueStartIndex: 10 +# CHECK-NEXT: Opcodes [ +# CHECK-NEXT: 0xe002da00 ; add sp, #2990080 +# CHECK-NEXT: 0xc0c9 ; add sp, #3216 +# CHECK-NEXT: 0x42 ; ldp x29, x30, [sp, #16] +# CHECK-NEXT: 0xd53f ; ldr x28, [sp], #256 +# CHECK-NEXT: 0xe4 ; end +# CHECK-NEXT: ] +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: } + + +--- | + 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" "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 } + + !llvm.module.flags = !{!0} + + !0 = !{i32 1, !"wchar_size", i32 2} + +... +--- +name: '?func@@YAHH@Z' +alignment: 2 +exposesReturnsTwice: false +legalized: true +regBankSelected: true +selected: true +failedISel: false +tracksRegLiveness: true +hasWinCFI: true +registers: +liveins: +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 2993328 + offsetAdjustment: 0 + maxAlignment: 16 + adjustsStack: true + hasCalls: true + stackProtector: '' + maxCallFrameSize: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + localFrameSize: 2993276 + savePoint: '' + restorePoint: '' +fixedStack: +stack: + - { id: 0, name: retval, type: default, offset: -36, 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: -40, 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: -2992812, 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: -2992816, 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: -2993308, 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: -2993320, 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: -2993324, size: 4, alignment: 4, + stack-id: 0, callee-saved-register: '', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 7, name: '', type: spill-slot, offset: -8, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$fp', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 8, name: '', type: spill-slot, offset: -16, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$lr', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 9, name: '', type: spill-slot, offset: -32, size: 8, alignment: 16, + stack-id: 0, callee-saved-register: '$x28', 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, $x28, $fp, $lr + + early-clobber $sp = frame-setup STRXpre killed $x28, $sp, -32 :: (store 8 into %stack.9) + frame-setup SEH_SaveReg_X 28, -256 + frame-setup STPXi killed $fp, killed $lr, $sp, 2 :: (store 8 into %stack.7), (store 8 into %stack.8) + frame-setup SEH_SaveFPLR 16 + $x15 = frame-setup MOVi64imm 187081 + frame-setup SEH_Nop + frame-setup BL &__chkstk, implicit-def $lr, implicit $sp, implicit $x15 + frame-setup SEH_Nop + $sp = frame-setup SUBXrx64 killed $sp, killed $x15, 28 + frame-setup SEH_StackAlloc 2993296 + frame-setup SEH_PrologEnd + $x8 = ADDXri $sp, 730, 12 + $x8 = ADDXri $x8, 3208, 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, $sp, 128 :: (store 4 into %ir.a) + STRXui killed $x8, $sp, 1 :: (store 8 into %stack.5) + STRWui killed $w9, $sp, 1 :: (store 4 into %stack.6) + BL @"?func2@@YAHXZ", csr_aarch64_aapcs, implicit-def $lr, implicit $sp, implicit-def $w0 + $x8 = LDRXui $sp, 1 :: (load 8 from %stack.5) + renamable $w9 = LDRWui killed renamable $x8, 0 :: (load 4 from %ir.i.addr) + $w10 = LDRWui $sp, 1 :: (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) + + BL @"?func2@@YAHXZ", csr_aarch64_aapcs, implicit-def $lr, implicit $sp, implicit-def $w0 + $x8 = LDRXui $sp, 1 :: (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) + + $x8 = ADDXri $sp, 20, 0 + $x0 = COPY killed renamable $x8 + BL @"?func3@@YAHPEAH@Z", csr_aarch64_aapcs, implicit-def $lr, implicit $sp, implicit killed $x0, implicit-def $w0 + $x8 = LDRXui $sp, 1 :: (load 8 from %stack.5) + STRWui killed renamable $w0, killed renamable $x8, 1 :: (store 4 into %ir.retval) + + bb.4.return: + $x8 = LDRXui $sp, 1 :: (load 8 from %stack.5) + renamable $w0 = LDRWui killed renamable $x8, 1 :: (load 4 from %ir.retval) + frame-destroy SEH_EpilogStart + $sp = frame-destroy ADDXri $sp, 730, 12 + frame-destroy SEH_StackAlloc 2990080 + $sp = frame-destroy ADDXri $sp, 3216, 0 + frame-destroy SEH_StackAlloc 3216 + $fp, $lr = frame-destroy LDPXi $sp, 2 :: (load 8 from %stack.7), (load 8 from %stack.8) + frame-destroy SEH_SaveFPLR 16 + early-clobber $sp, $x28 = frame-destroy LDRXpost $sp, 32 :: (load 8 from %stack.9) + frame-destroy SEH_SaveReg_X 28, -256 + frame-destroy SEH_EpilogEnd + RET_ReallyLR implicit killed $w0 + +... Index: llvm/trunk/test/CodeGen/AArch64/wineh6.mir =================================================================== --- llvm/trunk/test/CodeGen/AArch64/wineh6.mir +++ llvm/trunk/test/CodeGen/AArch64/wineh6.mir @@ -0,0 +1,138 @@ +# RUN: llc -o - %s -mtriple=aarch64-windows -start-after=prologepilog \ +# RUN: -disable-post-ra -filetype=obj | llvm-readobj -unwind | FileCheck %s +# Check save_fplr_x, set_fp, alloc_s + +# CHECK: ExceptionData { +# CHECK-NEXT: FunctionLength: 92 +# CHECK-NEXT: Version: 0 +# CHECK-NEXT: ExceptionData: No +# CHECK-NEXT: EpiloguePacked: No +# CHECK-NEXT: EpilogueScopes: 1 +# CHECK-NEXT: ByteCodeLength: 8 +# CHECK-NEXT: Prologue [ +# CHECK-NEXT: 0x02 ; sub sp, #32 +# CHECK-NEXT: 0xe1 ; mov fp, sp +# CHECK-NEXT: 0x81 ; stp x29, x30, [sp, #-16]! +# CHECK-NEXT: 0xe4 ; end +# CHECK-NEXT: ] +# CHECK-NEXT: EpilogueScopes [ +# CHECK-NEXT: EpilogueScope { +# CHECK-NEXT: StartOffset: 20 +# CHECK-NEXT: EpilogueStartIndex: 4 +# CHECK-NEXT: Opcodes [ +# CHECK-NEXT: 0xe1 ; mov fp, sp +# CHECK-NEXT: 0x81 ; ldp x29, x30, [sp], #16 +# CHECK-NEXT: 0xe4 ; end +# CHECK-NEXT: ] +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: } +... +--- +name: '?func@@YAHHHHH@Z' +alignment: 3 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: true +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: 48 + offsetAdjustment: 0 + maxAlignment: 8 + adjustsStack: true + hasCalls: true + stackProtector: '' + maxCallFrameSize: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + localFrameSize: 24 + savePoint: '' + restorePoint: '' +fixedStack: +stack: + - { id: 0, name: '', type: default, offset: -20, 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: '', type: default, offset: -24, 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: '', type: default, offset: -28, 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: '', type: default, offset: -32, 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: '', type: default, offset: -40, 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: -40, + 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: -48, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 7, name: '', type: spill-slot, offset: -8, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$fp', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 8, name: '', type: spill-slot, offset: -16, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$lr', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } +constants: +body: | + bb.0.entry: + liveins: $w0, $w1, $w2, $w3, $lr + + early-clobber $sp = frame-setup STPXpre killed $fp, killed $lr, $sp, -2 :: (store 8 into %stack.7), (store 8 into %stack.8) + frame-setup SEH_SaveFPLR_X -16 + $fp = frame-setup ADDXri $sp, 0, 0 + frame-setup SEH_SetFP + $sp = frame-setup SUBXri $sp, 32, 0 + frame-setup SEH_StackAlloc 32 + frame-setup SEH_PrologEnd + STURWi killed renamable $w3, $fp, -4 + STURWi killed renamable $w2, $fp, -8 + STURWi killed renamable $w1, $fp, -12 + STURWi killed renamable $w0, $fp, -16 + renamable $x8 = LDURSWi $fp, -16 + renamable $x8 = ADDXri killed renamable $x8, 15, 0 + renamable $x8 = UBFMXri killed renamable $x8, 4, 63 + $x15 = COPY renamable $x8 + STURXi killed $x8, $fp, -32 :: (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 = LDURXi $fp, -32 :: (load 8 from %stack.6) + renamable $x8 = SUBSXrs killed renamable $x8, killed renamable $x15, 4, implicit-def dead $nzcv + $sp = COPY renamable $x8 + STURXi killed renamable $x8, $fp, -24 + renamable $x0 = LDURXi $fp, -24 + renamable $w1 = COPY $wzr + $w0 = COPY killed renamable $w1 + frame-destroy SEH_EpilogStart + $sp = frame-destroy ADDXri $fp, 0, 0 + frame-destroy SEH_SetFP + early-clobber $sp, $fp, $lr = frame-destroy LDPXpost $sp, 2 + frame-destroy SEH_SaveFPLR_X -16 + frame-destroy SEH_EpilogEnd + RET_ReallyLR implicit killed $w0 + +... Index: llvm/trunk/test/CodeGen/AArch64/wineh7.mir =================================================================== --- llvm/trunk/test/CodeGen/AArch64/wineh7.mir +++ llvm/trunk/test/CodeGen/AArch64/wineh7.mir @@ -0,0 +1,134 @@ +# RUN: llc -o - %s -mtriple=aarch64-windows -start-after=prologepilog \ +# RUN: -filetype=obj -disable-post-ra | llvm-readobj -unwind | FileCheck %s +# Check AddFP + +# CHECK: ExceptionData { +# CHECK-NEXT: FunctionLength: 72 +# CHECK-NEXT: Version: 0 +# CHECK-NEXT: ExceptionData: No +# CHECK-NEXT: EpiloguePacked: No +# CHECK-NEXT: EpilogueScopes: 1 +# CHECK-NEXT: ByteCodeLength: 16 +# CHECK-NEXT: Prologue [ +# CHECK-NEXT: 0xe204 ; add fp, sp, #32 +# CHECK-NEXT: 0x44 ; stp x29, x30, [sp, #32] +# CHECK-NEXT: 0xc802 ; stp x19, x20, [sp, #16] +# CHECK-NEXT: 0xcc85 ; stp x21, x22, [sp, #-48]! +# CHECK-NEXT: 0xe4 ; end +# CHECK-NEXT: ] +# CHECK-NEXT: EpilogueScopes [ +# CHECK-NEXT: EpilogueScope { +# CHECK-NEXT: StartOffset: 13 +# CHECK-NEXT: EpilogueStartIndex: 8 +# CHECK-NEXT: Opcodes [ +# CHECK-NEXT: 0xe204 ; add fp, sp, #32 +# CHECK-NEXT: 0x44 ; ldp x29, x30, [sp, #32] +# CHECK-NEXT: 0xc802 ; ldp x19, x20, [sp, #16] +# CHECK-NEXT: 0xcc85 ; ldp x21, x22, [sp], #48 +# CHECK-NEXT: 0xe4 ; end +# CHECK-NEXT: ] +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: } +# CHECK-NEXT: } + +... +--- +name: '?func@@YAHHHHH@Z' +alignment: 3 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: true +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: 48 + offsetAdjustment: 0 + maxAlignment: 8 + adjustsStack: true + hasCalls: true + stackProtector: '' + maxCallFrameSize: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: +stack: + - { id: 0, name: '', type: variable-sized, offset: -48, + alignment: 1, stack-id: 0, callee-saved-register: '', callee-saved-restored: true, + local-offset: 0, debug-info-variable: '', debug-info-expression: '', + debug-info-location: '' } + - { id: 1, name: '', type: spill-slot, offset: -8, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$fp', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 2, name: '', type: spill-slot, offset: -16, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$lr', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 3, name: '', type: spill-slot, offset: -24, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x19', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 4, name: '', type: spill-slot, offset: -32, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x20', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 5, name: '', type: spill-slot, offset: -40, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x21', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 6, name: '', type: spill-slot, offset: -48, size: 8, alignment: 8, + stack-id: 0, callee-saved-register: '$x22', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } +constants: +body: | + bb.0.entry: + liveins: $w0, $w1, $w2, $w3, $x21, $x22, $x19, $x20, $lr + + early-clobber $sp = frame-setup STPXpre killed $x21, killed $x22, $sp, -6 :: (store 8 into %stack.5), (store 8 into %stack.6) + frame-setup SEH_SaveRegP_X 21, 22, -48 + frame-setup STPXi killed $x19, killed $x20, $sp, 2 :: (store 8 into %stack.3), (store 8 into %stack.4) + frame-setup SEH_SaveRegP 19, 20, 16 + frame-setup STPXi killed $fp, killed $lr, $sp, 4 :: (store 8 into %stack.1), (store 8 into %stack.2) + frame-setup SEH_SaveFPLR 32 + $fp = frame-setup ADDXri $sp, 32, 0 + frame-setup SEH_AddFP 32 + frame-setup SEH_PrologEnd + renamable $w19 = COPY $w3 + renamable $w0 = KILL $w0, implicit-def $x0 + renamable $w20 = COPY $w2 + renamable $w21 = COPY $w1 + renamable $x8 = SBFMXri killed renamable $x0, 0, 31 + renamable $x9 = ADDXri killed renamable $x8, 15, 0 + renamable $x15 = UBFMXri killed renamable $x9, 4, 63 + renamable $x8 = COPY $sp + renamable $x22 = SUBXrs killed renamable $x8, killed renamable $x15, 4 + $sp = COPY renamable $x22 + $x0 = COPY renamable $x22 + renamable $w8 = LDRWroW killed renamable $x22, killed renamable $w21, 1, 1 + renamable $w9 = ADDWrr killed renamable $w19, killed renamable $w20 + renamable $w0 = ADDWrr killed renamable $w9, killed renamable $w8 + frame-destroy SEH_EpilogStart + $sp = frame-destroy SUBXri $fp, 32, 0 + frame-destroy SEH_AddFP 32 + $fp, $lr = frame-destroy LDPXi $sp, 4 :: (load 8 from %stack.1), (load 8 from %stack.2) + frame-destroy SEH_SaveFPLR 32 + $x19, $x20 = frame-destroy LDPXi $sp, 2 :: (load 8 from %stack.3), (load 8 from %stack.4) + frame-destroy SEH_SaveRegP 19, 20, 16 + early-clobber $sp, $x21, $x22 = frame-destroy LDPXpost $sp, 6 :: (load 8 from %stack.5), (load 8 from %stack.6) + frame-destroy SEH_SaveRegP_X 21, 22, -48 + frame-destroy SEH_EpilogEnd + RET_ReallyLR implicit $w0 + +... Index: llvm/trunk/test/CodeGen/AArch64/wineh_shrinkwrap.mir =================================================================== --- llvm/trunk/test/CodeGen/AArch64/wineh_shrinkwrap.mir +++ llvm/trunk/test/CodeGen/AArch64/wineh_shrinkwrap.mir @@ -0,0 +1,146 @@ +# RUN: llc -O2 -o - %s -mtriple=aarch64-windows -start-before=shrink-wrap \ +# RUN: -stop-after=prologepilog | FileCheck %s --check-prefix=WIN64 +# RUN: llc -O2 -o - %s -mtriple=aarch64-linux -start-before=shrink-wrap \ +# RUN: -stop-after=prologepilog | FileCheck %s --check-prefix=LINUX + +# This tests checks that shrink wrapping bails out on Windows AMR64 due to the +# use of Windows CFI. We don't currently support fragments for WIndows EH on +# ARM64. +# The same test gets shrink wrapped on Linux ARM64. + +# WIN64-LABEL: bb.0.entry: +# WIN64: early-clobber $sp = frame-setup STRXpre killed $x28, $sp, -32 +# WIN64-LABEL: bb.1: +# WIN64-LABEL: bb.2.if.then: + +# LINUX-LABEL: bb.0.entry: +# LINUX-LABEL: bb.1: +# LINUX-LABEL: bb.2.if.then: +# LINUX: early-clobber $sp = frame-setup STRXpre killed $x28, $sp, -32 +--- | + ; ModuleID = 'shrink.cpp' + 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@@YAHHH@Z"(i32 %a, i32 %b) local_unnamed_addr #0 { + entry: + %A = alloca [1000 x i32], align 4 + %cmp = icmp sgt i32 %a, 1 + br i1 %cmp, label %if.then, label %return + + if.then: ; preds = %entry + %0 = bitcast [1000 x i32]* %A to i8* + call void @llvm.lifetime.start.p0i8(i64 4000, i8* nonnull %0) #3 + %arraydecay2 = bitcast [1000 x i32]* %A to i32* + call void @"?init@@YAXPEAH@Z"(i32* nonnull %arraydecay2) + %arrayidx = getelementptr inbounds [1000 x i32], [1000 x i32]* %A, i64 0, i64 100 + %1 = load i32, i32* %arrayidx, align 4, !tbaa !2 + %add = add i32 %b, 1 + %add1 = add i32 %add, %1 + call void @llvm.lifetime.end.p0i8(i64 4000, i8* nonnull %0) #3 + br label %return + + return: ; preds = %entry, %if.then + %retval.0 = phi i32 [ %add1, %if.then ], [ 0, %entry ] + ret i32 %retval.0 + } + + ; Function Attrs: argmemonly nounwind + declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #1 + + declare dso_local void @"?init@@YAXPEAH@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 } + + !llvm.module.flags = !{!0} + !llvm.ident = !{!1} + + !0 = !{i32 1, !"wchar_size", i32 2} + !1 = !{!"clang version 8.0.0"} + !2 = !{!3, !3, i64 0} + !3 = !{!"int", !4, i64 0} + !4 = !{!"omnipotent char", !5, i64 0} + !5 = !{!"Simple C++ TBAA"} + +... +--- +name: '?func@@YAHHH@Z' +alignment: 2 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +registers: +liveins: + - { reg: '$w0', virtual-reg: '' } + - { reg: '$w1', 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: 4000 + savePoint: '' + restorePoint: '' +fixedStack: +stack: + - { id: 0, name: A, type: default, offset: 0, size: 4000, alignment: 4, + stack-id: 0, callee-saved-register: '', callee-saved-restored: true, + local-offset: -4000, debug-info-variable: '', debug-info-expression: '', + debug-info-location: '' } +constants: +body: | + bb.0.entry: + successors: %bb.2(0x40000000), %bb.1(0x40000000) + liveins: $w0, $w1 + + dead $wzr = SUBSWri killed renamable $w0, 2, 0, implicit-def $nzcv + Bcc 10, %bb.2, implicit killed $nzcv + + bb.1: + successors: %bb.3(0x80000000) + + renamable $w0 = COPY $wzr + B %bb.3 + + bb.2.if.then: + successors: %bb.3(0x80000000) + liveins: $w1 + + renamable $w19 = COPY $w1 + ADJCALLSTACKDOWN 0, 0, implicit-def dead $sp, implicit $sp + $x0 = ADDXri %stack.0.A, 0, 0 + BL @"?init@@YAXPEAH@Z", csr_aarch64_aapcs, implicit-def dead $lr, implicit $sp, implicit $x0, implicit-def $sp + ADJCALLSTACKUP 0, 0, implicit-def dead $sp, implicit $sp + renamable $w8 = LDRWui %stack.0.A, 100 :: (dereferenceable load 4 from %ir.arrayidx, !tbaa !2) + renamable $w8 = ADDWrr killed renamable $w19, killed renamable $w8 + renamable $w0 = ADDWri killed renamable $w8, 1, 0 + + bb.3.return: + liveins: $w0 + + RET_ReallyLR implicit $w0 + +...