Index: llvm/include/llvm/MC/MCCodeView.h =================================================================== --- llvm/include/llvm/MC/MCCodeView.h +++ llvm/include/llvm/MC/MCCodeView.h @@ -276,6 +276,10 @@ /// Emits the offset into the checksum table of the given file number. void emitFileChecksumOffset(MCObjectStreamer &OS, unsigned FileNo); + /// Add something to the string table. Returns the final string as well as + /// offset into the string table. + std::pair addToStringTable(StringRef S); + private: /// The current CodeView line information from the last .cv_loc directive. MCCVLoc CurrentCVLoc = MCCVLoc(0, 0, 0, 0, false, true); @@ -290,10 +294,6 @@ MCDataFragment *getStringTableFragment(); - /// Add something to the string table. Returns the final string as well as - /// offset into the string table. - std::pair addToStringTable(StringRef S); - /// Get a string table offset. unsigned getStringTableOffset(StringRef S); Index: llvm/include/llvm/MC/MCStreamer.h =================================================================== --- llvm/include/llvm/MC/MCStreamer.h +++ llvm/include/llvm/MC/MCStreamer.h @@ -790,6 +790,9 @@ /// directive. virtual void EmitCVFileChecksumOffsetDirective(unsigned FileNo) {} + /// This implements the CodeView '.cv_fpo_data' assembler directive. + virtual void EmitCVFPOData(const MCSymbol *ProcSym, SMLoc Loc = {}) {} + /// Emit the absolute difference between two symbols. /// /// \pre Offset of \c Hi is greater than the offset \c Lo. Index: llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp +++ llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp @@ -808,6 +808,10 @@ if (FuncName.empty()) FuncName = GlobalValue::dropLLVMManglingEscape(GV->getName()); + // Emit FPO data, but only on 32-bit x86. No other platforms use it. + if (Triple(MMI->getModule()->getTargetTriple()).getArch() == Triple::x86) + OS.EmitCVFPOData(Fn); + // Emit a symbol subsection, required by VS2012+ to find function boundaries. OS.AddComment("Symbol subsection for " + Twine(FuncName)); MCSymbol *SymbolsEnd = beginCVSubsection(DebugSubsectionKind::Symbols); Index: llvm/lib/MC/MCAsmStreamer.cpp =================================================================== --- llvm/lib/MC/MCAsmStreamer.cpp +++ llvm/lib/MC/MCAsmStreamer.cpp @@ -248,6 +248,7 @@ void EmitCVStringTableDirective() override; void EmitCVFileChecksumsDirective() override; void EmitCVFileChecksumOffsetDirective(unsigned FileNo) override; + void EmitCVFPOData(const MCSymbol *ProcSym, SMLoc L) override; void EmitIdent(StringRef IdentString) override; void EmitCFISections(bool EH, bool Debug) override; @@ -1252,6 +1253,12 @@ EmitEOL(); } +void MCAsmStreamer::EmitCVFPOData(const MCSymbol *ProcSym, SMLoc L) { + OS << "\t.cv_fpo_data\t"; + ProcSym->print(OS, MAI); + EmitEOL(); +} + void MCAsmStreamer::EmitIdent(StringRef IdentString) { assert(MAI->hasIdentDirective() && ".ident directive not supported"); OS << "\t.ident\t"; Index: llvm/lib/MC/MCParser/AsmParser.cpp =================================================================== --- llvm/lib/MC/MCParser/AsmParser.cpp +++ llvm/lib/MC/MCParser/AsmParser.cpp @@ -503,6 +503,7 @@ DK_CV_STRINGTABLE, DK_CV_FILECHECKSUMS, DK_CV_FILECHECKSUM_OFFSET, + DK_CV_FPO_DATA, DK_CFI_SECTIONS, DK_CFI_STARTPROC, DK_CFI_ENDPROC, @@ -580,6 +581,7 @@ bool parseDirectiveCVStringTable(); bool parseDirectiveCVFileChecksums(); bool parseDirectiveCVFileChecksumOffset(); + bool parseDirectiveCVFPOData(); // .cfi directives bool parseDirectiveCFIRegister(SMLoc DirectiveLoc); @@ -2039,6 +2041,8 @@ return parseDirectiveCVFileChecksums(); case DK_CV_FILECHECKSUM_OFFSET: return parseDirectiveCVFileChecksumOffset(); + case DK_CV_FPO_DATA: + return parseDirectiveCVFPOData(); case DK_CFI_SECTIONS: return parseDirectiveCFISections(); case DK_CFI_STARTPROC: @@ -3791,6 +3795,20 @@ return false; } +/// parseDirectiveCVFPOData +/// ::= .cv_fpo_data procsym +bool AsmParser::parseDirectiveCVFPOData() { + SMLoc DirLoc = getLexer().getLoc(); + StringRef ProcName; + if (parseIdentifier(ProcName)) + return TokError("expected symbol name"); + if (parseEOL("unexpected tokens")) + return addErrorSuffix(" in '.cv_fpo_data' directive"); + MCSymbol *ProcSym = getContext().getOrCreateSymbol(ProcName); + getStreamer().EmitCVFPOData(ProcSym, DirLoc); + return false; +} + /// parseDirectiveCFISections /// ::= .cfi_sections section [, section] bool AsmParser::parseDirectiveCFISections() { @@ -5174,6 +5192,7 @@ DirectiveKindMap[".cv_stringtable"] = DK_CV_STRINGTABLE; DirectiveKindMap[".cv_filechecksums"] = DK_CV_FILECHECKSUMS; DirectiveKindMap[".cv_filechecksumoffset"] = DK_CV_FILECHECKSUM_OFFSET; + DirectiveKindMap[".cv_fpo_data"] = DK_CV_FPO_DATA; DirectiveKindMap[".sleb128"] = DK_SLEB128; DirectiveKindMap[".uleb128"] = DK_ULEB128; DirectiveKindMap[".cfi_sections"] = DK_CFI_SECTIONS; Index: llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp =================================================================== --- llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp +++ llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp @@ -7,11 +7,12 @@ // //===----------------------------------------------------------------------===// +#include "InstPrinter/X86IntelInstPrinter.h" #include "MCTargetDesc/X86BaseInfo.h" +#include "MCTargetDesc/X86TargetStreamer.h" #include "X86AsmInstrumentation.h" #include "X86AsmParserCommon.h" #include "X86Operand.h" -#include "InstPrinter/X86IntelInstPrinter.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" @@ -80,6 +81,13 @@ return Result; } + X86TargetStreamer &getTargetStreamer() { + assert(getParser().getStreamer().getTargetStreamer() && + "do not have a target streamer"); + MCTargetStreamer &TS = *getParser().getStreamer().getTargetStreamer(); + return static_cast(TS); + } + unsigned MatchInstruction(const OperandVector &Operands, MCInst &Inst, uint64_t &ErrorInfo, bool matchingInlineAsm, unsigned VariantID = 0) { @@ -839,6 +847,15 @@ bool ParseDirectiveWord(unsigned Size, SMLoc L); bool ParseDirectiveCode(StringRef IDVal, SMLoc L); + /// CodeView FPO data directives. + bool parseDirectiveFPOProc(SMLoc L); + bool parseDirectiveFPOSetFrame(SMLoc L); + bool parseDirectiveFPOPushReg(SMLoc L); + bool parseDirectiveFPOStackAlloc(SMLoc L); + bool parseDirectiveFPOEndPrologue(SMLoc L); + bool parseDirectiveFPOEndProc(SMLoc L); + bool parseDirectiveFPOData(SMLoc L); + bool processInstruction(MCInst &Inst, const OperandVector &Ops); /// Wrapper around MCStreamer::EmitInstruction(). Possibly adds @@ -3027,6 +3044,19 @@ return false; } else if (IDVal == ".even") return parseDirectiveEven(DirectiveID.getLoc()); + else if (IDVal == ".cv_fpo_proc") + return parseDirectiveFPOProc(DirectiveID.getLoc()); + else if (IDVal == ".cv_fpo_setframe") + return parseDirectiveFPOSetFrame(DirectiveID.getLoc()); + else if (IDVal == ".cv_fpo_pushreg") + return parseDirectiveFPOPushReg(DirectiveID.getLoc()); + else if (IDVal == ".cv_fpo_stackalloc") + return parseDirectiveFPOStackAlloc(DirectiveID.getLoc()); + else if (IDVal == ".cv_fpo_endprologue") + return parseDirectiveFPOEndPrologue(DirectiveID.getLoc()); + else if (IDVal == ".cv_fpo_endproc") + return parseDirectiveFPOEndProc(DirectiveID.getLoc()); + return true; } @@ -3124,6 +3154,71 @@ return false; } +// .cv_fpo_proc foo +bool X86AsmParser::parseDirectiveFPOProc(SMLoc L) { + MCAsmParser &Parser = getParser(); + StringRef ProcName; + int64_t ParamsSize; + if (Parser.parseIdentifier(ProcName)) + return Parser.TokError("expected symbol name"); + if (Parser.parseIntToken(ParamsSize, "expected parameter byte count")) + return true; + if (!isUIntN(32, ParamsSize)) + return Parser.TokError("parameters size out of range"); + if (Parser.parseEOL("unexpected tokens")) + return addErrorSuffix(" in '.cv_fpo_proc' directive"); + MCSymbol *ProcSym = getContext().getOrCreateSymbol(ProcName); + return getTargetStreamer().emitFPOProc(ProcSym, ParamsSize, L); +} + +// .cv_fpo_setframe ebp +bool X86AsmParser::parseDirectiveFPOSetFrame(SMLoc L) { + MCAsmParser &Parser = getParser(); + unsigned Reg; + SMLoc DummyLoc; + if (ParseRegister(Reg, DummyLoc, DummyLoc) || + Parser.parseEOL("unexpected tokens")) + return addErrorSuffix(" in '.cv_fpo_setframe' directive"); + return getTargetStreamer().emitFPOSetFrame(Reg, L); +} + +// .cv_fpo_pushreg ebx +bool X86AsmParser::parseDirectiveFPOPushReg(SMLoc L) { + MCAsmParser &Parser = getParser(); + unsigned Reg; + SMLoc DummyLoc; + if (ParseRegister(Reg, DummyLoc, DummyLoc) || + Parser.parseEOL("unexpected tokens")) + return addErrorSuffix(" in '.cv_fpo_pushreg' directive"); + return getTargetStreamer().emitFPOPushReg(Reg, L); +} + +// .cv_fpo_stackalloc 20 +bool X86AsmParser::parseDirectiveFPOStackAlloc(SMLoc L) { + MCAsmParser &Parser = getParser(); + int64_t Offset; + if (Parser.parseIntToken(Offset, "expected offset") || + Parser.parseEOL("unexpected tokens")) + return addErrorSuffix(" in '.cv_fpo_stackalloc' directive"); + return getTargetStreamer().emitFPOStackAlloc(Offset, L); +} + +// .cv_fpo_endprologue +bool X86AsmParser::parseDirectiveFPOEndPrologue(SMLoc L) { + MCAsmParser &Parser = getParser(); + if (Parser.parseEOL("unexpected tokens")) + return addErrorSuffix(" in '.cv_fpo_endprologue' directive"); + return getTargetStreamer().emitFPOEndPrologue(L); +} + +// .cv_fpo_endproc +bool X86AsmParser::parseDirectiveFPOEndProc(SMLoc L) { + MCAsmParser &Parser = getParser(); + if (Parser.parseEOL("unexpected tokens")) + return addErrorSuffix(" in '.cv_fpo_endproc' directive"); + return getTargetStreamer().emitFPOEndProc(L); +} + // Force static initialization. extern "C" void LLVMInitializeX86AsmParser() { RegisterMCAsmParser X(getTheX86_32Target()); Index: llvm/lib/Target/X86/MCTargetDesc/CMakeLists.txt =================================================================== --- llvm/lib/Target/X86/MCTargetDesc/CMakeLists.txt +++ llvm/lib/Target/X86/MCTargetDesc/CMakeLists.txt @@ -5,6 +5,7 @@ X86MCCodeEmitter.cpp X86MachObjectWriter.cpp X86ELFObjectWriter.cpp - X86WinCOFFStreamer.cpp X86WinCOFFObjectWriter.cpp + X86WinCOFFStreamer.cpp + X86WinCOFFTargetStreamer.cpp ) Index: llvm/lib/Target/X86/MCTargetDesc/X86MCTargetDesc.h =================================================================== --- llvm/lib/Target/X86/MCTargetDesc/X86MCTargetDesc.h +++ llvm/lib/Target/X86/MCTargetDesc/X86MCTargetDesc.h @@ -77,6 +77,16 @@ const Triple &TT, StringRef CPU, const MCTargetOptions &Options); +/// Implements X86-only directives for assembly emission. +MCTargetStreamer *createX86AsmTargetStreamer(MCStreamer &S, + formatted_raw_ostream &OS, + MCInstPrinter *InstPrint, + bool isVerboseAsm); + +/// Implements X86-only directives for object files. +MCTargetStreamer *createX86ObjectTargetStreamer(MCStreamer &OS, + const MCSubtargetInfo &STI); + /// Construct an X86 Windows COFF machine code streamer which will generate /// PE/COFF format object files. /// Index: llvm/lib/Target/X86/MCTargetDesc/X86MCTargetDesc.cpp =================================================================== --- llvm/lib/Target/X86/MCTargetDesc/X86MCTargetDesc.cpp +++ llvm/lib/Target/X86/MCTargetDesc/X86MCTargetDesc.cpp @@ -319,7 +319,13 @@ // Register the code emitter. TargetRegistry::RegisterMCCodeEmitter(*T, createX86MCCodeEmitter); - // Register the object streamer. + // Register the obj target streamer. + TargetRegistry::RegisterObjectTargetStreamer(*T, + createX86ObjectTargetStreamer); + + // Register the asm target streamer. + TargetRegistry::RegisterAsmTargetStreamer(*T, createX86AsmTargetStreamer); + TargetRegistry::RegisterCOFFStreamer(*T, createX86WinCOFFStreamer); // Register the MCInstPrinter. Index: llvm/lib/Target/X86/MCTargetDesc/X86TargetStreamer.h =================================================================== --- /dev/null +++ llvm/lib/Target/X86/MCTargetDesc/X86TargetStreamer.h @@ -0,0 +1,35 @@ +//===- X86TargetStreamer.h ------------------------------*- C++ -*---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// + +#ifndef LLVM_LIB_TARGET_X86_MCTARGETDESC_X86TARGETSTREAMER_H +#define LLVM_LIB_TARGET_X86_MCTARGETDESC_X86TARGETSTREAMER_H + +#include "llvm/MC/MCStreamer.h" + +namespace llvm { + +/// X86 target streamer implementing x86-only assembly directives. +class X86TargetStreamer : public MCTargetStreamer { +public: + X86TargetStreamer(MCStreamer &S) : MCTargetStreamer(S) {} + + virtual bool emitFPOProc(const MCSymbol *ProcSym, unsigned ParamsSize, + SMLoc L = {}) = 0; + virtual bool emitFPOEndPrologue(SMLoc L = {}) = 0; + virtual bool emitFPOEndProc(SMLoc L = {}) = 0; + virtual bool emitFPOData(const MCSymbol *ProcSym, SMLoc L = {}) = 0; + virtual bool emitFPOPushReg(unsigned Reg, SMLoc L = {}) = 0; + virtual bool emitFPOStackAlloc(unsigned StackAlloc, SMLoc L = {}) = 0; + virtual bool emitFPOSetFrame(unsigned Reg, SMLoc L = {}) = 0; +}; + +} // end namespace llvm + +#endif Index: llvm/lib/Target/X86/MCTargetDesc/X86WinCOFFStreamer.cpp =================================================================== --- llvm/lib/Target/X86/MCTargetDesc/X86WinCOFFStreamer.cpp +++ llvm/lib/Target/X86/MCTargetDesc/X86WinCOFFStreamer.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "X86MCTargetDesc.h" +#include "X86TargetStreamer.h" #include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCWin64EH.h" #include "llvm/MC/MCWinCOFFStreamer.h" @@ -24,6 +25,7 @@ void EmitWinEHHandlerData(SMLoc Loc) override; void EmitWindowsUnwindTables() override; + void EmitCVFPOData(const MCSymbol *ProcSym, SMLoc Loc) override; void FinishImpl() override; }; @@ -41,6 +43,12 @@ EHStreamer.Emit(*this); } +void X86WinCOFFStreamer::EmitCVFPOData(const MCSymbol *ProcSym, SMLoc Loc) { + X86TargetStreamer *XTS = + static_cast(getTargetStreamer()); + XTS->emitFPOData(ProcSym, Loc); +} + void X86WinCOFFStreamer::FinishImpl() { EmitFrames(nullptr); EmitWindowsUnwindTables(); Index: llvm/lib/Target/X86/MCTargetDesc/X86WinCOFFTargetStreamer.cpp =================================================================== --- /dev/null +++ llvm/lib/Target/X86/MCTargetDesc/X86WinCOFFTargetStreamer.cpp @@ -0,0 +1,410 @@ +//===-- X86WinCOFFTargetStreamer.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "X86MCTargetDesc.h" +#include "X86TargetStreamer.h" +#include "llvm/DebugInfo/CodeView/CodeView.h" +#include "llvm/MC/MCCodeView.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCInstPrinter.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/FormattedStream.h" + +using namespace llvm; +using namespace llvm::codeview; + +namespace { +/// Implements Windows x86-only directives for assembly emission. +class X86WinCOFFAsmTargetStreamer : public X86TargetStreamer { + formatted_raw_ostream &OS; + MCInstPrinter &InstPrinter; + +public: + X86WinCOFFAsmTargetStreamer(MCStreamer &S, formatted_raw_ostream &OS, + MCInstPrinter &InstPrinter) + : X86TargetStreamer(S), OS(OS), InstPrinter(InstPrinter) {} + + bool emitFPOProc(const MCSymbol *ProcSym, unsigned ParamsSize, + SMLoc L) override; + bool emitFPOEndPrologue(SMLoc L) override; + bool emitFPOEndProc(SMLoc L) override; + bool emitFPOData(const MCSymbol *ProcSym, SMLoc L) override; + bool emitFPOPushReg(unsigned Reg, SMLoc L) override; + bool emitFPOStackAlloc(unsigned StackAlloc, SMLoc L) override; + bool emitFPOSetFrame(unsigned Reg, SMLoc L) override; +}; + +/// Represents a single FPO directive. +struct FPOInstruction { + MCSymbol *Label; + enum Operation { + PushReg, + StackAlloc, + SetFrame, + } Op; + unsigned RegOrOffset; +}; + +struct FPOData { + const MCSymbol *Function = nullptr; + MCSymbol *Begin = nullptr; + MCSymbol *PrologueEnd = nullptr; + MCSymbol *End = nullptr; + unsigned ParamsSize = 0; + + SmallVector Instructions; +}; + +/// Implements Windows x86-only directives for object emission. +class X86WinCOFFTargetStreamer : public X86TargetStreamer { + /// Map from function symbol to its FPO data. + DenseMap> AllFPOData; + + /// Current FPO data created by .cv_fpo_proc. + std::unique_ptr CurFPOData; + + bool haveOpenFPOData() { return !!CurFPOData; } + + /// Diagnoses an error at L if we are not in an FPO prologue. Return true on + /// error. + bool checkInFPOPrologue(SMLoc L); + + MCSymbol *emitFPOLabel(); + + MCContext &getContext() { return getStreamer().getContext(); } + +public: + X86WinCOFFTargetStreamer(MCStreamer &S) : X86TargetStreamer(S) {} + + bool emitFPOProc(const MCSymbol *ProcSym, unsigned ParamsSize, + SMLoc L) override; + bool emitFPOEndPrologue(SMLoc L) override; + bool emitFPOEndProc(SMLoc L) override; + bool emitFPOData(const MCSymbol *ProcSym, SMLoc L) override; + bool emitFPOPushReg(unsigned Reg, SMLoc L) override; + bool emitFPOStackAlloc(unsigned StackAlloc, SMLoc L) override; + bool emitFPOSetFrame(unsigned Reg, SMLoc L) override; +}; +} // end namespace + +bool X86WinCOFFAsmTargetStreamer::emitFPOProc(const MCSymbol *ProcSym, + unsigned ParamsSize, SMLoc L) { + OS << "\t.cv_fpo_proc\t"; + ProcSym->print(OS, getStreamer().getContext().getAsmInfo()); + OS << ' ' << ParamsSize << '\n'; + return false; +} + +bool X86WinCOFFAsmTargetStreamer::emitFPOEndPrologue(SMLoc L) { + OS << "\t.cv_fpo_endprologue\n"; + return false; +} + +bool X86WinCOFFAsmTargetStreamer::emitFPOEndProc(SMLoc L) { + OS << "\t.cv_fpo_endproc\n"; + return false; +} + +bool X86WinCOFFAsmTargetStreamer::emitFPOData(const MCSymbol *ProcSym, + SMLoc L) { + OS << "\t.cv_fpo_data\t"; + ProcSym->print(OS, getStreamer().getContext().getAsmInfo()); + OS << '\n'; + return false; +} + +bool X86WinCOFFAsmTargetStreamer::emitFPOPushReg(unsigned Reg, SMLoc L) { + OS << "\t.cv_fpo_pushreg\t"; + InstPrinter.printRegName(OS, Reg); + OS << '\n'; + return false; +} + +bool X86WinCOFFAsmTargetStreamer::emitFPOStackAlloc(unsigned StackAlloc, + SMLoc L) { + OS << "\t.cv_fpo_stackalloc\t" << StackAlloc << '\n'; + return false; +} + +bool X86WinCOFFAsmTargetStreamer::emitFPOSetFrame(unsigned Reg, SMLoc L) { + OS << "\t.cv_fpo_setframe\t"; + InstPrinter.printRegName(OS, Reg); + OS << '\n'; + return false; +} + +bool X86WinCOFFTargetStreamer::checkInFPOPrologue(SMLoc L) { + if (!haveOpenFPOData() || CurFPOData->PrologueEnd) { + getContext().reportError( + L, + "directive must appear between .cv_fpo_proc and .cv_fpo_endprologue"); + return true; + } + return false; +} + +MCSymbol *X86WinCOFFTargetStreamer::emitFPOLabel() { + MCSymbol *Label = getContext().createTempSymbol("cfi", true); + getStreamer().EmitLabel(Label); + return Label; +} + +bool X86WinCOFFTargetStreamer::emitFPOProc(const MCSymbol *ProcSym, + unsigned ParamsSize, SMLoc L) { + if (haveOpenFPOData()) { + getContext().reportError( + L, "opening new .cv_fpo_proc before closing previous frame"); + return true; + } + CurFPOData = llvm::make_unique(); + CurFPOData->Function = ProcSym; + CurFPOData->Begin = emitFPOLabel(); + CurFPOData->ParamsSize = ParamsSize; + return false; +} + +bool X86WinCOFFTargetStreamer::emitFPOEndProc(SMLoc L) { + if (!haveOpenFPOData()) { + getContext().reportError(L, ".cv_fpo_endproc must appear after .cv_proc"); + return true; + } + if (!CurFPOData->PrologueEnd) { + // Complain if there were prologue setup instructions but no end prologue. + if (!CurFPOData->Instructions.empty()) { + getContext().reportError(L, "missing .cv_fpo_endprologue"); + CurFPOData->Instructions.clear(); + } + + // Claim there is a zero-length prologue to make the label math work out + // later. + CurFPOData->PrologueEnd = CurFPOData->Begin; + } + + CurFPOData->End = emitFPOLabel(); + const MCSymbol *Fn = CurFPOData->Function; + AllFPOData.insert({Fn, std::move(CurFPOData)}); + return false; +} + +bool X86WinCOFFTargetStreamer::emitFPOSetFrame(unsigned Reg, SMLoc L) { + if (checkInFPOPrologue(L)) + return true; + FPOInstruction Inst; + Inst.Label = emitFPOLabel(); + Inst.Op = FPOInstruction::SetFrame; + Inst.RegOrOffset = Reg; + CurFPOData->Instructions.push_back(Inst); + return false; +} + +bool X86WinCOFFTargetStreamer::emitFPOPushReg(unsigned Reg, SMLoc L) { + if (checkInFPOPrologue(L)) + return true; + FPOInstruction Inst; + Inst.Label = emitFPOLabel(); + Inst.Op = FPOInstruction::PushReg; + Inst.RegOrOffset = Reg; + CurFPOData->Instructions.push_back(Inst); + return false; +} + +bool X86WinCOFFTargetStreamer::emitFPOStackAlloc(unsigned StackAlloc, SMLoc L) { + if (checkInFPOPrologue(L)) + return true; + FPOInstruction Inst; + Inst.Label = emitFPOLabel(); + Inst.Op = FPOInstruction::StackAlloc; + Inst.RegOrOffset = StackAlloc; + CurFPOData->Instructions.push_back(Inst); + return false; +} + +bool X86WinCOFFTargetStreamer::emitFPOEndPrologue(SMLoc L) { + if (checkInFPOPrologue(L)) + return true; + CurFPOData->PrologueEnd = emitFPOLabel(); + return false; +} + +namespace { +struct RegSaveOffset { + unsigned Reg = 0; + unsigned Offset = 0; +}; + +struct FPOStateMachine { + explicit FPOStateMachine(const FPOData *FPO) : FPO(FPO) {} + + const FPOData *FPO = nullptr; + unsigned FrameReg = 0; + unsigned FrameRegOff = 0; + unsigned CurOffset = 0; + unsigned LocalSize = 0; + unsigned SavedRegSize = 0; + unsigned Flags = 0; // FIXME: Set HasSEH / HasEH. + + SmallString<128> FrameFunc; + + SmallVector RegSaveOffsets; + + void emitFrameDataRecord(MCStreamer &OS, MCSymbol *Label); +}; +} // end namespace + +static Printable printFPOReg(const MCRegisterInfo *MRI, unsigned LLVMReg) { + return Printable([MRI, LLVMReg](raw_ostream &OS) { + switch (LLVMReg) { + // MSVC only seems to emit symbolic register names for EIP, EBP, and ESP, + // but the format seems to support more than that, so we emit them. + case X86::EAX: OS << "$eax"; break; + case X86::EBX: OS << "$ebx"; break; + case X86::ECX: OS << "$ecx"; break; + case X86::EDX: OS << "$edx"; break; + case X86::EDI: OS << "$edi"; break; + case X86::ESI: OS << "$esi"; break; + case X86::ESP: OS << "$esp"; break; + case X86::EBP: OS << "$ebp"; break; + case X86::EIP: OS << "$eip"; break; + // Otherwise, get the codeview register number and print $N. + default: + OS << '$' << MRI->getCodeViewRegNum(LLVMReg); + break; + } + }); +} + +void FPOStateMachine::emitFrameDataRecord(MCStreamer &OS, MCSymbol *Label) { + unsigned CurFlags = Flags; + if (Label == FPO->Begin) + CurFlags |= FrameData::IsFunctionStart; + + // Compute the new FrameFunc string. + FrameFunc.clear(); + raw_svector_ostream FuncOS(FrameFunc); + const MCRegisterInfo *MRI = OS.getContext().getRegisterInfo(); + if (FrameReg) { + // CFA is FrameReg + FrameRegOff. + FuncOS << "$T0 " << printFPOReg(MRI, FrameReg) << " " << FrameRegOff + << " + = "; + } else { + // The address of return address is ESP + CurOffset, but we use .raSearch to + // match MSVC. This seems to ask the debugger to subtract some combination + // of LocalSize and SavedRegSize from ESP and grovel around in that memory + // to find the address of a plausible return address. + FuncOS << "$T0 .raSearch = "; + } + + // Caller's $eip should be dereferenced CFA, and $esp should be CFA plus 4. + FuncOS << "$eip $T0 ^ = $esp $T0 4 + = "; + + // Each saved register is stored at an unchanging negative CFA offset. + for (RegSaveOffset RO : RegSaveOffsets) + FuncOS << printFPOReg(MRI, RO.Reg) << " $T0 " << RO.Offset << " - ^ = "; + + // Add it to the CV string table. + CodeViewContext &CVCtx = OS.getContext().getCVContext(); + unsigned FrameFuncStrTabOff = CVCtx.addToStringTable(FuncOS.str()).second; + + // The FrameData record format is: + // ulittle32_t RvaStart; + // ulittle32_t CodeSize; + // ulittle32_t LocalSize; + // ulittle32_t ParamsSize; + // ulittle32_t MaxStackSize; + // ulittle32_t FrameFunc; // String table offset + // ulittle16_t PrologSize; + // ulittle16_t SavedRegsSize; + // ulittle32_t Flags; + + OS.emitAbsoluteSymbolDiff(Label, FPO->Begin, 4); // RvaStart + OS.emitAbsoluteSymbolDiff(FPO->End, Label, 4); // CodeSize + OS.EmitIntValue(LocalSize, 4); + OS.EmitIntValue(FPO->ParamsSize, 4); + OS.EmitIntValue(0, 4); // MaxStackSize + OS.EmitIntValue(FrameFuncStrTabOff, 4); // FrameFunc + OS.emitAbsoluteSymbolDiff(FPO->PrologueEnd, Label, 2); + OS.EmitIntValue(SavedRegSize, 2); + OS.EmitIntValue(CurFlags, 4); +} + +/// Compute and emit the real CodeView FrameData subsection. +bool X86WinCOFFTargetStreamer::emitFPOData(const MCSymbol *ProcSym, SMLoc L) { + MCStreamer &OS = getStreamer(); + MCContext &Ctx = OS.getContext(); + + auto I = AllFPOData.find(ProcSym); + if (I == AllFPOData.end()) { + Ctx.reportError(L, Twine("no FPO data found for symbol ") + + ProcSym->getName()); + return true; + } + const FPOData *FPO = I->second.get(); + assert(FPO->Begin && FPO->End && FPO->PrologueEnd && "missing FPO label"); + + MCSymbol *FrameBegin = Ctx.createTempSymbol(), + *FrameEnd = Ctx.createTempSymbol(); + + OS.EmitIntValue(unsigned(DebugSubsectionKind::FrameData), 4); + OS.emitAbsoluteSymbolDiff(FrameEnd, FrameBegin, 4); + OS.EmitLabel(FrameBegin); + + // Start with the RVA of the function in question. + OS.EmitValue(MCSymbolRefExpr::create(FPO->Function, + MCSymbolRefExpr::VK_COFF_IMGREL32, Ctx), + 4); + + // Emit a sequence of FrameData records. + FPOStateMachine FSM(FPO); + + FSM.emitFrameDataRecord(OS, FPO->Begin); + for (const FPOInstruction &Inst : FPO->Instructions) { + switch (Inst.Op) { + case FPOInstruction::PushReg: + FSM.CurOffset += 4; + FSM.SavedRegSize += 4; + FSM.RegSaveOffsets.push_back({Inst.RegOrOffset, FSM.CurOffset}); + break; + case FPOInstruction::SetFrame: + FSM.FrameReg = Inst.RegOrOffset; + FSM.FrameRegOff = FSM.CurOffset; + break; + case FPOInstruction::StackAlloc: + FSM.CurOffset += Inst.RegOrOffset; + FSM.LocalSize += Inst.RegOrOffset; + // No need to emit FrameData for stack allocations with a frame pointer. + if (FSM.FrameReg) + continue; + break; + } + FSM.emitFrameDataRecord(OS, Inst.Label); + } + + OS.EmitValueToAlignment(4, 0); + OS.EmitLabel(FrameEnd); + return false; +} + +MCTargetStreamer *llvm::createX86AsmTargetStreamer(MCStreamer &S, + formatted_raw_ostream &OS, + MCInstPrinter *InstPrinter, + bool IsVerboseAsm) { + // FIXME: This makes it so we textually assemble COFF directives on ELF. + // That's kind of nonsensical. + return new X86WinCOFFAsmTargetStreamer(S, OS, *InstPrinter); +} + +MCTargetStreamer * +llvm::createX86ObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI) { + // No need to register a target streamer. + if (!STI.getTargetTriple().isOSBinFormatCOFF()) + return nullptr; + // Registers itself to the MCStreamer. + return new X86WinCOFFTargetStreamer(S); +} Index: llvm/lib/Target/X86/X86AsmPrinter.h =================================================================== --- llvm/lib/Target/X86/X86AsmPrinter.h +++ llvm/lib/Target/X86/X86AsmPrinter.h @@ -30,6 +30,7 @@ StackMaps SM; FaultMaps FM; std::unique_ptr CodeEmitter; + bool EmitFPOData = false; // This utility class tracks the length of a stackmap instruction's 'shadow'. // It is used by the X86AsmPrinter to ensure that the stackmap shadow @@ -99,6 +100,9 @@ // function. void EmitXRayTable(); + // Choose between emitting .seh_ directives and .cv_fpo_ directives. + void EmitSEHInstruction(const MachineInstr *MI); + public: explicit X86AsmPrinter(TargetMachine &TM, std::unique_ptr Streamer) @@ -137,6 +141,8 @@ } bool runOnMachineFunction(MachineFunction &F) override; + void EmitFunctionBodyStart() override; + void EmitFunctionBodyEnd() override; }; } // end namespace llvm Index: llvm/lib/Target/X86/X86AsmPrinter.cpp =================================================================== --- llvm/lib/Target/X86/X86AsmPrinter.cpp +++ llvm/lib/Target/X86/X86AsmPrinter.cpp @@ -15,6 +15,7 @@ #include "X86AsmPrinter.h" #include "InstPrinter/X86ATTInstPrinter.h" #include "MCTargetDesc/X86BaseInfo.h" +#include "MCTargetDesc/X86TargetStreamer.h" #include "X86InstrInfo.h" #include "X86MachineFunctionInfo.h" #include "llvm/BinaryFormat/COFF.h" @@ -51,9 +52,12 @@ SMShadowTracker.startFunction(MF); CodeEmitter.reset(TM.getTarget().createMCCodeEmitter( - *MF.getSubtarget().getInstrInfo(), *MF.getSubtarget().getRegisterInfo(), + *Subtarget->getInstrInfo(), *Subtarget->getRegisterInfo(), MF.getContext())); + EmitFPOData = + Subtarget->isTargetWin32() && MF.getMMI().getModule()->getCodeViewFlag(); + SetupMachineFunction(MF); if (Subtarget->isTargetCOFF()) { @@ -72,10 +76,30 @@ // Emit the XRay table for this function. emitXRayTable(); + EmitFPOData = false; + // We didn't modify anything. return false; } +void X86AsmPrinter::EmitFunctionBodyStart() { + if (EmitFPOData) { + X86TargetStreamer *XTS = + static_cast(OutStreamer->getTargetStreamer()); + unsigned ParamsSize = + MF->getInfo()->getArgumentStackSize(); + XTS->emitFPOProc(CurrentFnSym, ParamsSize); + } +} + +void X86AsmPrinter::EmitFunctionBodyEnd() { + if (EmitFPOData) { + X86TargetStreamer *XTS = + static_cast(OutStreamer->getTargetStreamer()); + XTS->emitFPOEndProc(); + } +} + /// printSymbolOperand - Print a raw symbol reference operand. This handles /// jump tables, constant pools, global address and external symbols, all of /// which print to a label with various suffixes for relocation types etc. Index: llvm/lib/Target/X86/X86FrameLowering.cpp =================================================================== --- llvm/lib/Target/X86/X86FrameLowering.cpp +++ llvm/lib/Target/X86/X86FrameLowering.cpp @@ -924,6 +924,7 @@ Notes: - .seh directives are emitted only for Windows 64 ABI + - .cv_fpo directives are emitted on win32 when emitting CodeView - .cfi directives are emitted for all other ABIs - for 32-bit code, substitute %e?? registers for %r?? */ @@ -949,7 +950,9 @@ bool HasFP = hasFP(MF); bool IsWin64CC = STI.isCallingConvWin64(Fn->getCallingConv()); bool IsWin64Prologue = MF.getTarget().getMCAsmInfo()->usesWindowsCFI(); - bool NeedsWinCFI = IsWin64Prologue && Fn->needsUnwindTableEntry(); + bool NeedsWin64CFI = IsWin64Prologue && Fn->needsUnwindTableEntry(); + bool NeedsWinFPO = STI.isTargetWin32() && MMI.getModule()->getCodeViewFlag(); + bool NeedsWinCFI = NeedsWin64CFI || NeedsWinFPO; bool NeedsDwarfCFI = !IsWin64Prologue && (MMI.hasDebugInfo() || Fn->needsUnwindTableEntry()); unsigned FramePtr = TRI->getFrameRegister(MF); @@ -958,7 +961,7 @@ ? getX86SubSuperRegister(FramePtr, 64) : FramePtr; unsigned BasePtr = TRI->getBaseRegister(); bool HasWinCFI = false; - + // Debug location must be unknown since the first debug location is used // to determine the end of the prologue. DebugLoc DL; @@ -1120,6 +1123,15 @@ BuildCFI(MBB, MBBI, DL, MCCFIInstruction::createDefCfaRegister( nullptr, DwarfFramePtr)); } + + if (NeedsWinFPO) { + // .cv_fpo_setframe $FramePtr + HasWinCFI = true; + BuildMI(MBB, MBBI, DL, TII.get(X86::SEH_SetFrame)) + .addImm(FramePtr) + .addImm(0) + .setMIFlag(MachineInstr::FrameSetup); + } } } else { assert(!IsFunclet && "funclets without FPs not yet implemented"); @@ -1155,8 +1167,9 @@ if (NeedsWinCFI) { HasWinCFI = true; - BuildMI(MBB, MBBI, DL, TII.get(X86::SEH_PushReg)).addImm(Reg).setMIFlag( - MachineInstr::FrameSetup); + BuildMI(MBB, MBBI, DL, TII.get(X86::SEH_PushReg)) + .addImm(Reg) + .setMIFlag(MachineInstr::FrameSetup); } } @@ -1295,6 +1308,7 @@ // If this is not a funclet, emit the CFI describing our frame pointer. if (NeedsWinCFI && !IsFunclet) { + assert(!NeedsWinFPO && "this setframe incompatible with FPO data"); HasWinCFI = true; BuildMI(MBB, MBBI, DL, TII.get(X86::SEH_SetFrame)) .addImm(FramePtr) @@ -1333,6 +1347,7 @@ Offset += SEHFrameOffset; HasWinCFI = true; + assert(!NeedsWinFPO && "SEH_SaveXMM incompatible with FPO data"); BuildMI(MBB, MBBI, DL, TII.get(X86::SEH_SaveXMM)) .addImm(Reg) .addImm(Offset) @@ -1534,7 +1549,7 @@ Is64BitILP32 ? getX86SubSuperRegister(FramePtr, 64) : FramePtr; bool IsWin64Prologue = MF.getTarget().getMCAsmInfo()->usesWindowsCFI(); - bool NeedsWinCFI = + bool NeedsWin64CFI = IsWin64Prologue && MF.getFunction()->needsUnwindTableEntry(); bool IsFunclet = MBBI == MBB.end() ? false : isFuncletReturnInstr(*MBBI); @@ -1639,7 +1654,7 @@ // into the epilogue. To cope with that, we insert an epilogue marker here, // then replace it with a 'nop' if it ends up immediately after a CALL in the // final emitted code. - if (NeedsWinCFI && MF.hasWinCFI()) + if (NeedsWin64CFI && MF.hasWinCFI()) BuildMI(MBB, MBBI, DL, TII.get(X86::SEH_Epilogue)); if (Terminator == MBB.end() || !isTailCallOpcode(Terminator->getOpcode())) { Index: llvm/lib/Target/X86/X86MCInstLower.cpp =================================================================== --- llvm/lib/Target/X86/X86MCInstLower.cpp +++ llvm/lib/Target/X86/X86MCInstLower.cpp @@ -15,6 +15,7 @@ #include "InstPrinter/X86ATTInstPrinter.h" #include "InstPrinter/X86InstComments.h" #include "MCTargetDesc/X86BaseInfo.h" +#include "MCTargetDesc/X86TargetStreamer.h" #include "Utils/X86ShuffleDecode.h" #include "X86AsmPrinter.h" #include "X86RegisterInfo.h" @@ -1363,6 +1364,82 @@ } } +void X86AsmPrinter::EmitSEHInstruction(const MachineInstr *MI) { + assert(MF->hasWinCFI() && "SEH_ instruction in function without WinCFI?"); + assert(getSubtarget().isOSWindows() && "SEH_ instruction Windows only"); + const X86RegisterInfo *RI = + MF->getSubtarget().getRegisterInfo(); + + // Use the .cv_fpo directives if we're emitting CodeView on 32-bit x86. + if (EmitFPOData) { + X86TargetStreamer *XTS = + static_cast(OutStreamer->getTargetStreamer()); + switch (MI->getOpcode()) { + case X86::SEH_PushReg: + XTS->emitFPOPushReg(MI->getOperand(0).getImm()); + break; + case X86::SEH_StackAlloc: + XTS->emitFPOStackAlloc(MI->getOperand(0).getImm()); + break; + case X86::SEH_SetFrame: + assert(MI->getOperand(1).getImm() == 0 && + ".cv_fpo_setframe takes no offset"); + XTS->emitFPOSetFrame(MI->getOperand(0).getImm()); + break; + case X86::SEH_EndPrologue: + XTS->emitFPOEndPrologue(); + break; + case X86::SEH_SaveReg: + case X86::SEH_SaveXMM: + case X86::SEH_PushFrame: + llvm_unreachable("SEH_ directive incompatible with FPO"); + break; + default: + llvm_unreachable("expected SEH_ instruction"); + } + return; + } + + // Otherwise, use the .seh_ directives for all other Windows platforms. + switch (MI->getOpcode()) { + case X86::SEH_PushReg: + OutStreamer->EmitWinCFIPushReg( + RI->getSEHRegNum(MI->getOperand(0).getImm())); + break; + + case X86::SEH_SaveReg: + OutStreamer->EmitWinCFISaveReg(RI->getSEHRegNum(MI->getOperand(0).getImm()), + MI->getOperand(1).getImm()); + break; + + case X86::SEH_SaveXMM: + OutStreamer->EmitWinCFISaveXMM(RI->getSEHRegNum(MI->getOperand(0).getImm()), + MI->getOperand(1).getImm()); + break; + + case X86::SEH_StackAlloc: + OutStreamer->EmitWinCFIAllocStack(MI->getOperand(0).getImm()); + break; + + case X86::SEH_SetFrame: + OutStreamer->EmitWinCFISetFrame( + RI->getSEHRegNum(MI->getOperand(0).getImm()), + MI->getOperand(1).getImm()); + break; + + case X86::SEH_PushFrame: + OutStreamer->EmitWinCFIPushFrame(MI->getOperand(0).getImm()); + break; + + case X86::SEH_EndPrologue: + OutStreamer->EmitWinCFIEndProlog(); + break; + + default: + llvm_unreachable("expected SEH_ instruction"); + } +} + void X86AsmPrinter::EmitInstruction(const MachineInstr *MI) { X86MCInstLower MCInstLowering(*MF, *this); const X86RegisterInfo *RI = MF->getSubtarget().getRegisterInfo(); @@ -1540,41 +1617,13 @@ return; case X86::SEH_PushReg: - assert(MF->hasWinCFI() && "SEH_ instruction in function without WinCFI?"); - OutStreamer->EmitWinCFIPushReg(RI->getSEHRegNum(MI->getOperand(0).getImm())); - return; - case X86::SEH_SaveReg: - assert(MF->hasWinCFI() && "SEH_ instruction in function without WinCFI?"); - OutStreamer->EmitWinCFISaveReg(RI->getSEHRegNum(MI->getOperand(0).getImm()), - MI->getOperand(1).getImm()); - return; - case X86::SEH_SaveXMM: - assert(MF->hasWinCFI() && "SEH_ instruction in function without WinCFI?"); - OutStreamer->EmitWinCFISaveXMM(RI->getSEHRegNum(MI->getOperand(0).getImm()), - MI->getOperand(1).getImm()); - return; - case X86::SEH_StackAlloc: - assert(MF->hasWinCFI() && "SEH_ instruction in function without WinCFI?"); - OutStreamer->EmitWinCFIAllocStack(MI->getOperand(0).getImm()); - return; - case X86::SEH_SetFrame: - assert(MF->hasWinCFI() && "SEH_ instruction in function without WinCFI?"); - OutStreamer->EmitWinCFISetFrame(RI->getSEHRegNum(MI->getOperand(0).getImm()), - MI->getOperand(1).getImm()); - return; - case X86::SEH_PushFrame: - assert(MF->hasWinCFI() && "SEH_ instruction in function without WinCFI?"); - OutStreamer->EmitWinCFIPushFrame(MI->getOperand(0).getImm()); - return; - case X86::SEH_EndPrologue: - assert(MF->hasWinCFI() && "SEH_ instruction in function without WinCFI?"); - OutStreamer->EmitWinCFIEndProlog(); + EmitSEHInstruction(MI); return; case X86::SEH_Epilogue: { Index: llvm/lib/Target/X86/X86Subtarget.h =================================================================== --- llvm/lib/Target/X86/X86Subtarget.h +++ llvm/lib/Target/X86/X86Subtarget.h @@ -592,13 +592,9 @@ bool isOSWindows() const { return TargetTriple.isOSWindows(); } - bool isTargetWin64() const { - return In64BitMode && TargetTriple.isOSWindows(); - } + bool isTargetWin64() const { return In64BitMode && isOSWindows(); } - bool isTargetWin32() const { - return !In64BitMode && (isTargetCygMing() || isTargetKnownWindowsMSVC()); - } + bool isTargetWin32() const { return !In64BitMode && isOSWindows(); } bool isPICStyleGOT() const { return PICStyle == PICStyles::GOT; } bool isPICStyleRIPRel() const { return PICStyle == PICStyles::RIPRel; } Index: llvm/test/DebugInfo/COFF/asan-module-ctor.ll =================================================================== --- llvm/test/DebugInfo/COFF/asan-module-ctor.ll +++ llvm/test/DebugInfo/COFF/asan-module-ctor.ll @@ -10,7 +10,7 @@ ; The module ctor has no debug info. All we have to do is don't crash. ; X86: _asan.module_ctor: ; X86-NEXT: L{{.*}}: -; X86-NEXT: # BB +; X86: # BB ; X86-NEXT: calll ___asan_init_v3 ; X86-NEXT: retl Index: llvm/test/DebugInfo/COFF/fpo-argsize.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/COFF/fpo-argsize.ll @@ -0,0 +1,454 @@ +; RUN: llc < %s | grep cv_fpo_proc | FileCheck %s + +; C++ source: +; extern "C" { +; extern int g; +; +; void cdecl1(int a) { g += a; } +; void cdecl2(int a, int b) { g += a + b; } +; void cdecl3(int a, int b, int c) { g += a + b + c; } +; +; void __fastcall fastcall1(int a) { g += a; } +; void __fastcall fastcall2(int a, int b) { g += a + b; } +; void __fastcall fastcall3(int a, int b, int c) { g += a + b + c; } +; +; void __stdcall stdcall1(int a) { g += a; } +; void __stdcall stdcall2(int a, int b) { g += a + b; } +; void __stdcall stdcall3(int a, int b, int c) { g += a + b + c; } +; } +; +; struct Foo { +; void thiscall1(int a); +; void thiscall2(int a, int b); +; void thiscall3(int a, int b, int c); +; }; +; +; void Foo::thiscall1(int a) { g += a; } +; void Foo::thiscall2(int a, int b) { g += a + b; } +; void Foo::thiscall3(int a, int b, int c) { g += a + b + c; } + +; CHECK: .cv_fpo_proc _cdecl1 4 +; CHECK: .cv_fpo_proc _cdecl2 8 +; CHECK: .cv_fpo_proc _cdecl3 12 + +; First two args are in registers and don't count. +; CHECK: .cv_fpo_proc @fastcall1@4 0 +; CHECK: .cv_fpo_proc @fastcall2@8 0 +; CHECK: .cv_fpo_proc @fastcall3@12 4 + +; CHECK: .cv_fpo_proc _stdcall1@4 4 +; CHECK: .cv_fpo_proc _stdcall2@8 8 +; CHECK: .cv_fpo_proc _stdcall3@12 12 + +; 'this' is in ecx and doesn't count. +; CHECK: .cv_fpo_proc "?thiscall1@Foo@@QAEXH@Z" 4 +; CHECK: .cv_fpo_proc "?thiscall2@Foo@@QAEXHH@Z" 8 +; CHECK: .cv_fpo_proc "?thiscall3@Foo@@QAEXHHH@Z" 12 + +; ModuleID = 't.c' +source_filename = "t.c" +target datalayout = "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32" +target triple = "i386-pc-windows-msvc19.11.25508" + +%struct.Foo = type { i8 } + +@g = external global i32, align 4 + +; Function Attrs: noinline nounwind optnone +define void @cdecl1(i32 %a) #0 !dbg !8 { +entry: + %a.addr = alloca i32, align 4 + store i32 %a, i32* %a.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %a.addr, metadata !12, metadata !DIExpression()), !dbg !13 + %0 = load i32, i32* %a.addr, align 4, !dbg !14 + %1 = load i32, i32* @g, align 4, !dbg !15 + %add = add nsw i32 %1, %0, !dbg !15 + store i32 %add, i32* @g, align 4, !dbg !15 + ret void, !dbg !16 +} + +; Function Attrs: nounwind readnone speculatable +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +; Function Attrs: noinline nounwind optnone +define void @cdecl2(i32 %a, i32 %b) #0 !dbg !17 { +entry: + %b.addr = alloca i32, align 4 + %a.addr = alloca i32, align 4 + store i32 %b, i32* %b.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %b.addr, metadata !20, metadata !DIExpression()), !dbg !21 + store i32 %a, i32* %a.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %a.addr, metadata !22, metadata !DIExpression()), !dbg !23 + %0 = load i32, i32* %a.addr, align 4, !dbg !24 + %1 = load i32, i32* %b.addr, align 4, !dbg !25 + %add = add nsw i32 %0, %1, !dbg !26 + %2 = load i32, i32* @g, align 4, !dbg !27 + %add1 = add nsw i32 %2, %add, !dbg !27 + store i32 %add1, i32* @g, align 4, !dbg !27 + ret void, !dbg !28 +} + +; Function Attrs: noinline nounwind optnone +define void @cdecl3(i32 %a, i32 %b, i32 %c) #0 !dbg !29 { +entry: + %c.addr = alloca i32, align 4 + %b.addr = alloca i32, align 4 + %a.addr = alloca i32, align 4 + store i32 %c, i32* %c.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %c.addr, metadata !32, metadata !DIExpression()), !dbg !33 + store i32 %b, i32* %b.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %b.addr, metadata !34, metadata !DIExpression()), !dbg !35 + store i32 %a, i32* %a.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %a.addr, metadata !36, metadata !DIExpression()), !dbg !37 + %0 = load i32, i32* %a.addr, align 4, !dbg !38 + %1 = load i32, i32* %b.addr, align 4, !dbg !39 + %add = add nsw i32 %0, %1, !dbg !40 + %2 = load i32, i32* %c.addr, align 4, !dbg !41 + %add1 = add nsw i32 %add, %2, !dbg !42 + %3 = load i32, i32* @g, align 4, !dbg !43 + %add2 = add nsw i32 %3, %add1, !dbg !43 + store i32 %add2, i32* @g, align 4, !dbg !43 + ret void, !dbg !44 +} + +; Function Attrs: noinline nounwind optnone +define x86_fastcallcc void @"\01@fastcall1@4"(i32 inreg %a) #0 !dbg !45 { +entry: + %a.addr = alloca i32, align 4 + store i32 %a, i32* %a.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %a.addr, metadata !47, metadata !DIExpression()), !dbg !48 + %0 = load i32, i32* %a.addr, align 4, !dbg !49 + %1 = load i32, i32* @g, align 4, !dbg !50 + %add = add nsw i32 %1, %0, !dbg !50 + store i32 %add, i32* @g, align 4, !dbg !50 + ret void, !dbg !51 +} + +; Function Attrs: noinline nounwind optnone +define x86_fastcallcc void @"\01@fastcall2@8"(i32 inreg %a, i32 inreg %b) #0 !dbg !52 { +entry: + %b.addr = alloca i32, align 4 + %a.addr = alloca i32, align 4 + store i32 %b, i32* %b.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %b.addr, metadata !54, metadata !DIExpression()), !dbg !55 + store i32 %a, i32* %a.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %a.addr, metadata !56, metadata !DIExpression()), !dbg !57 + %0 = load i32, i32* %a.addr, align 4, !dbg !58 + %1 = load i32, i32* %b.addr, align 4, !dbg !59 + %add = add nsw i32 %0, %1, !dbg !60 + %2 = load i32, i32* @g, align 4, !dbg !61 + %add1 = add nsw i32 %2, %add, !dbg !61 + store i32 %add1, i32* @g, align 4, !dbg !61 + ret void, !dbg !62 +} + +; Function Attrs: noinline nounwind optnone +define x86_fastcallcc void @"\01@fastcall3@12"(i32 inreg %a, i32 inreg %b, i32 %c) #0 !dbg !63 { +entry: + %c.addr = alloca i32, align 4 + %b.addr = alloca i32, align 4 + %a.addr = alloca i32, align 4 + store i32 %c, i32* %c.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %c.addr, metadata !65, metadata !DIExpression()), !dbg !66 + store i32 %b, i32* %b.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %b.addr, metadata !67, metadata !DIExpression()), !dbg !68 + store i32 %a, i32* %a.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %a.addr, metadata !69, metadata !DIExpression()), !dbg !70 + %0 = load i32, i32* %a.addr, align 4, !dbg !71 + %1 = load i32, i32* %b.addr, align 4, !dbg !72 + %add = add nsw i32 %0, %1, !dbg !73 + %2 = load i32, i32* %c.addr, align 4, !dbg !74 + %add1 = add nsw i32 %add, %2, !dbg !75 + %3 = load i32, i32* @g, align 4, !dbg !76 + %add2 = add nsw i32 %3, %add1, !dbg !76 + store i32 %add2, i32* @g, align 4, !dbg !76 + ret void, !dbg !77 +} + +; Function Attrs: noinline nounwind optnone +define x86_stdcallcc void @"\01_stdcall1@4"(i32 %a) #0 !dbg !78 { +entry: + %a.addr = alloca i32, align 4 + store i32 %a, i32* %a.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %a.addr, metadata !80, metadata !DIExpression()), !dbg !81 + %0 = load i32, i32* %a.addr, align 4, !dbg !82 + %1 = load i32, i32* @g, align 4, !dbg !83 + %add = add nsw i32 %1, %0, !dbg !83 + store i32 %add, i32* @g, align 4, !dbg !83 + ret void, !dbg !84 +} + +; Function Attrs: noinline nounwind optnone +define x86_stdcallcc void @"\01_stdcall2@8"(i32 %a, i32 %b) #0 !dbg !85 { +entry: + %b.addr = alloca i32, align 4 + %a.addr = alloca i32, align 4 + store i32 %b, i32* %b.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %b.addr, metadata !87, metadata !DIExpression()), !dbg !88 + store i32 %a, i32* %a.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %a.addr, metadata !89, metadata !DIExpression()), !dbg !90 + %0 = load i32, i32* %a.addr, align 4, !dbg !91 + %1 = load i32, i32* %b.addr, align 4, !dbg !92 + %add = add nsw i32 %0, %1, !dbg !93 + %2 = load i32, i32* @g, align 4, !dbg !94 + %add1 = add nsw i32 %2, %add, !dbg !94 + store i32 %add1, i32* @g, align 4, !dbg !94 + ret void, !dbg !95 +} + +; Function Attrs: noinline nounwind optnone +define x86_stdcallcc void @"\01_stdcall3@12"(i32 %a, i32 %b, i32 %c) #0 !dbg !96 { +entry: + %c.addr = alloca i32, align 4 + %b.addr = alloca i32, align 4 + %a.addr = alloca i32, align 4 + store i32 %c, i32* %c.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %c.addr, metadata !98, metadata !DIExpression()), !dbg !99 + store i32 %b, i32* %b.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %b.addr, metadata !100, metadata !DIExpression()), !dbg !101 + store i32 %a, i32* %a.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %a.addr, metadata !102, metadata !DIExpression()), !dbg !103 + %0 = load i32, i32* %a.addr, align 4, !dbg !104 + %1 = load i32, i32* %b.addr, align 4, !dbg !105 + %add = add nsw i32 %0, %1, !dbg !106 + %2 = load i32, i32* %c.addr, align 4, !dbg !107 + %add1 = add nsw i32 %add, %2, !dbg !108 + %3 = load i32, i32* @g, align 4, !dbg !109 + %add2 = add nsw i32 %3, %add1, !dbg !109 + store i32 %add2, i32* @g, align 4, !dbg !109 + ret void, !dbg !110 +} + +; Function Attrs: noinline nounwind optnone +define x86_thiscallcc void @"\01?thiscall1@Foo@@QAEXH@Z"(%struct.Foo* %this, i32 %a) #0 align 2 !dbg !111 { +entry: + %a.addr = alloca i32, align 4 + %this.addr = alloca %struct.Foo*, align 4 + store i32 %a, i32* %a.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %a.addr, metadata !124, metadata !DIExpression()), !dbg !125 + store %struct.Foo* %this, %struct.Foo** %this.addr, align 4 + call void @llvm.dbg.declare(metadata %struct.Foo** %this.addr, metadata !126, metadata !DIExpression()), !dbg !128 + %this1 = load %struct.Foo*, %struct.Foo** %this.addr, align 4 + %0 = load i32, i32* %a.addr, align 4, !dbg !129 + %1 = load i32, i32* @g, align 4, !dbg !130 + %add = add nsw i32 %1, %0, !dbg !130 + store i32 %add, i32* @g, align 4, !dbg !130 + ret void, !dbg !131 +} + +; Function Attrs: noinline nounwind optnone +define x86_thiscallcc void @"\01?thiscall2@Foo@@QAEXHH@Z"(%struct.Foo* %this, i32 %a, i32 %b) #0 align 2 !dbg !132 { +entry: + %b.addr = alloca i32, align 4 + %a.addr = alloca i32, align 4 + %this.addr = alloca %struct.Foo*, align 4 + store i32 %b, i32* %b.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %b.addr, metadata !133, metadata !DIExpression()), !dbg !134 + store i32 %a, i32* %a.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %a.addr, metadata !135, metadata !DIExpression()), !dbg !136 + store %struct.Foo* %this, %struct.Foo** %this.addr, align 4 + call void @llvm.dbg.declare(metadata %struct.Foo** %this.addr, metadata !137, metadata !DIExpression()), !dbg !138 + %this1 = load %struct.Foo*, %struct.Foo** %this.addr, align 4 + %0 = load i32, i32* %a.addr, align 4, !dbg !139 + %1 = load i32, i32* %b.addr, align 4, !dbg !140 + %add = add nsw i32 %0, %1, !dbg !141 + %2 = load i32, i32* @g, align 4, !dbg !142 + %add2 = add nsw i32 %2, %add, !dbg !142 + store i32 %add2, i32* @g, align 4, !dbg !142 + ret void, !dbg !143 +} + +; Function Attrs: noinline nounwind optnone +define x86_thiscallcc void @"\01?thiscall3@Foo@@QAEXHHH@Z"(%struct.Foo* %this, i32 %a, i32 %b, i32 %c) #0 align 2 !dbg !144 { +entry: + %c.addr = alloca i32, align 4 + %b.addr = alloca i32, align 4 + %a.addr = alloca i32, align 4 + %this.addr = alloca %struct.Foo*, align 4 + store i32 %c, i32* %c.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %c.addr, metadata !145, metadata !DIExpression()), !dbg !146 + store i32 %b, i32* %b.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %b.addr, metadata !147, metadata !DIExpression()), !dbg !148 + store i32 %a, i32* %a.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %a.addr, metadata !149, metadata !DIExpression()), !dbg !150 + store %struct.Foo* %this, %struct.Foo** %this.addr, align 4 + call void @llvm.dbg.declare(metadata %struct.Foo** %this.addr, metadata !151, metadata !DIExpression()), !dbg !152 + %this1 = load %struct.Foo*, %struct.Foo** %this.addr, align 4 + %0 = load i32, i32* %a.addr, align 4, !dbg !153 + %1 = load i32, i32* %b.addr, align 4, !dbg !154 + %add = add nsw i32 %0, %1, !dbg !155 + %2 = load i32, i32* %c.addr, align 4, !dbg !156 + %add2 = add nsw i32 %add, %2, !dbg !157 + %3 = load i32, i32* @g, align 4, !dbg !158 + %add3 = add nsw i32 %3, %add2, !dbg !158 + store i32 %add3, i32* @g, align 4, !dbg !158 + ret void, !dbg !159 +} + +attributes #0 = { noinline nounwind optnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "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"="pentium4" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind readnone speculatable } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5, !6} +!llvm.ident = !{!7} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 6.0.0 ", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) +!1 = !DIFile(filename: "t.c", directory: "C:\5Csrc\5Cllvm-project\5Cbuild", checksumkind: CSK_MD5, checksum: "0ce3e4edcf2f8511157da4edb99fcdf4") +!2 = !{} +!3 = !{i32 1, !"NumRegisterParameters", i32 0} +!4 = !{i32 2, !"CodeView", i32 1} +!5 = !{i32 2, !"Debug Info Version", i32 3} +!6 = !{i32 1, !"wchar_size", i32 2} +!7 = !{!"clang version 6.0.0 "} +!8 = distinct !DISubprogram(name: "cdecl1", scope: !1, file: !1, line: 4, type: !9, isLocal: false, isDefinition: true, scopeLine: 4, flags: DIFlagPrototyped, isOptimized: false, unit: !0, variables: !2) +!9 = !DISubroutineType(types: !10) +!10 = !{null, !11} +!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!12 = !DILocalVariable(name: "a", arg: 1, scope: !8, file: !1, line: 4, type: !11) +!13 = !DILocation(line: 4, column: 17, scope: !8) +!14 = !DILocation(line: 4, column: 27, scope: !8) +!15 = !DILocation(line: 4, column: 24, scope: !8) +!16 = !DILocation(line: 4, column: 30, scope: !8) +!17 = distinct !DISubprogram(name: "cdecl2", scope: !1, file: !1, line: 5, type: !18, isLocal: false, isDefinition: true, scopeLine: 5, flags: DIFlagPrototyped, isOptimized: false, unit: !0, variables: !2) +!18 = !DISubroutineType(types: !19) +!19 = !{null, !11, !11} +!20 = !DILocalVariable(name: "b", arg: 2, scope: !17, file: !1, line: 5, type: !11) +!21 = !DILocation(line: 5, column: 24, scope: !17) +!22 = !DILocalVariable(name: "a", arg: 1, scope: !17, file: !1, line: 5, type: !11) +!23 = !DILocation(line: 5, column: 17, scope: !17) +!24 = !DILocation(line: 5, column: 34, scope: !17) +!25 = !DILocation(line: 5, column: 38, scope: !17) +!26 = !DILocation(line: 5, column: 36, scope: !17) +!27 = !DILocation(line: 5, column: 31, scope: !17) +!28 = !DILocation(line: 5, column: 41, scope: !17) +!29 = distinct !DISubprogram(name: "cdecl3", scope: !1, file: !1, line: 6, type: !30, isLocal: false, isDefinition: true, scopeLine: 6, flags: DIFlagPrototyped, isOptimized: false, unit: !0, variables: !2) +!30 = !DISubroutineType(types: !31) +!31 = !{null, !11, !11, !11} +!32 = !DILocalVariable(name: "c", arg: 3, scope: !29, file: !1, line: 6, type: !11) +!33 = !DILocation(line: 6, column: 31, scope: !29) +!34 = !DILocalVariable(name: "b", arg: 2, scope: !29, file: !1, line: 6, type: !11) +!35 = !DILocation(line: 6, column: 24, scope: !29) +!36 = !DILocalVariable(name: "a", arg: 1, scope: !29, file: !1, line: 6, type: !11) +!37 = !DILocation(line: 6, column: 17, scope: !29) +!38 = !DILocation(line: 6, column: 41, scope: !29) +!39 = !DILocation(line: 6, column: 45, scope: !29) +!40 = !DILocation(line: 6, column: 43, scope: !29) +!41 = !DILocation(line: 6, column: 49, scope: !29) +!42 = !DILocation(line: 6, column: 47, scope: !29) +!43 = !DILocation(line: 6, column: 38, scope: !29) +!44 = !DILocation(line: 6, column: 52, scope: !29) +!45 = distinct !DISubprogram(name: "fastcall1", linkageName: "\01@fastcall1@4", scope: !1, file: !1, line: 8, type: !46, isLocal: false, isDefinition: true, scopeLine: 8, flags: DIFlagPrototyped, isOptimized: false, unit: !0, variables: !2) +!46 = !DISubroutineType(cc: DW_CC_BORLAND_msfastcall, types: !10) +!47 = !DILocalVariable(name: "a", arg: 1, scope: !45, file: !1, line: 8, type: !11) +!48 = !DILocation(line: 8, column: 31, scope: !45) +!49 = !DILocation(line: 8, column: 41, scope: !45) +!50 = !DILocation(line: 8, column: 38, scope: !45) +!51 = !DILocation(line: 8, column: 44, scope: !45) +!52 = distinct !DISubprogram(name: "fastcall2", linkageName: "\01@fastcall2@8", scope: !1, file: !1, line: 9, type: !53, isLocal: false, isDefinition: true, scopeLine: 9, flags: DIFlagPrototyped, isOptimized: false, unit: !0, variables: !2) +!53 = !DISubroutineType(cc: DW_CC_BORLAND_msfastcall, types: !19) +!54 = !DILocalVariable(name: "b", arg: 2, scope: !52, file: !1, line: 9, type: !11) +!55 = !DILocation(line: 9, column: 38, scope: !52) +!56 = !DILocalVariable(name: "a", arg: 1, scope: !52, file: !1, line: 9, type: !11) +!57 = !DILocation(line: 9, column: 31, scope: !52) +!58 = !DILocation(line: 9, column: 48, scope: !52) +!59 = !DILocation(line: 9, column: 52, scope: !52) +!60 = !DILocation(line: 9, column: 50, scope: !52) +!61 = !DILocation(line: 9, column: 45, scope: !52) +!62 = !DILocation(line: 9, column: 55, scope: !52) +!63 = distinct !DISubprogram(name: "fastcall3", linkageName: "\01@fastcall3@12", scope: !1, file: !1, line: 10, type: !64, isLocal: false, isDefinition: true, scopeLine: 10, flags: DIFlagPrototyped, isOptimized: false, unit: !0, variables: !2) +!64 = !DISubroutineType(cc: DW_CC_BORLAND_msfastcall, types: !31) +!65 = !DILocalVariable(name: "c", arg: 3, scope: !63, file: !1, line: 10, type: !11) +!66 = !DILocation(line: 10, column: 45, scope: !63) +!67 = !DILocalVariable(name: "b", arg: 2, scope: !63, file: !1, line: 10, type: !11) +!68 = !DILocation(line: 10, column: 38, scope: !63) +!69 = !DILocalVariable(name: "a", arg: 1, scope: !63, file: !1, line: 10, type: !11) +!70 = !DILocation(line: 10, column: 31, scope: !63) +!71 = !DILocation(line: 10, column: 55, scope: !63) +!72 = !DILocation(line: 10, column: 59, scope: !63) +!73 = !DILocation(line: 10, column: 57, scope: !63) +!74 = !DILocation(line: 10, column: 63, scope: !63) +!75 = !DILocation(line: 10, column: 61, scope: !63) +!76 = !DILocation(line: 10, column: 52, scope: !63) +!77 = !DILocation(line: 10, column: 66, scope: !63) +!78 = distinct !DISubprogram(name: "stdcall1", linkageName: "\01_stdcall1@4", scope: !1, file: !1, line: 12, type: !79, isLocal: false, isDefinition: true, scopeLine: 12, flags: DIFlagPrototyped, isOptimized: false, unit: !0, variables: !2) +!79 = !DISubroutineType(cc: DW_CC_BORLAND_stdcall, types: !10) +!80 = !DILocalVariable(name: "a", arg: 1, scope: !78, file: !1, line: 12, type: !11) +!81 = !DILocation(line: 12, column: 29, scope: !78) +!82 = !DILocation(line: 12, column: 39, scope: !78) +!83 = !DILocation(line: 12, column: 36, scope: !78) +!84 = !DILocation(line: 12, column: 42, scope: !78) +!85 = distinct !DISubprogram(name: "stdcall2", linkageName: "\01_stdcall2@8", scope: !1, file: !1, line: 13, type: !86, isLocal: false, isDefinition: true, scopeLine: 13, flags: DIFlagPrototyped, isOptimized: false, unit: !0, variables: !2) +!86 = !DISubroutineType(cc: DW_CC_BORLAND_stdcall, types: !19) +!87 = !DILocalVariable(name: "b", arg: 2, scope: !85, file: !1, line: 13, type: !11) +!88 = !DILocation(line: 13, column: 36, scope: !85) +!89 = !DILocalVariable(name: "a", arg: 1, scope: !85, file: !1, line: 13, type: !11) +!90 = !DILocation(line: 13, column: 29, scope: !85) +!91 = !DILocation(line: 13, column: 46, scope: !85) +!92 = !DILocation(line: 13, column: 50, scope: !85) +!93 = !DILocation(line: 13, column: 48, scope: !85) +!94 = !DILocation(line: 13, column: 43, scope: !85) +!95 = !DILocation(line: 13, column: 53, scope: !85) +!96 = distinct !DISubprogram(name: "stdcall3", linkageName: "\01_stdcall3@12", scope: !1, file: !1, line: 14, type: !97, isLocal: false, isDefinition: true, scopeLine: 14, flags: DIFlagPrototyped, isOptimized: false, unit: !0, variables: !2) +!97 = !DISubroutineType(cc: DW_CC_BORLAND_stdcall, types: !31) +!98 = !DILocalVariable(name: "c", arg: 3, scope: !96, file: !1, line: 14, type: !11) +!99 = !DILocation(line: 14, column: 43, scope: !96) +!100 = !DILocalVariable(name: "b", arg: 2, scope: !96, file: !1, line: 14, type: !11) +!101 = !DILocation(line: 14, column: 36, scope: !96) +!102 = !DILocalVariable(name: "a", arg: 1, scope: !96, file: !1, line: 14, type: !11) +!103 = !DILocation(line: 14, column: 29, scope: !96) +!104 = !DILocation(line: 14, column: 53, scope: !96) +!105 = !DILocation(line: 14, column: 57, scope: !96) +!106 = !DILocation(line: 14, column: 55, scope: !96) +!107 = !DILocation(line: 14, column: 61, scope: !96) +!108 = !DILocation(line: 14, column: 59, scope: !96) +!109 = !DILocation(line: 14, column: 50, scope: !96) +!110 = !DILocation(line: 14, column: 64, scope: !96) +!111 = distinct !DISubprogram(name: "thiscall1", linkageName: "\01?thiscall1@Foo@@QAEXH@Z", scope: !112, file: !1, line: 23, type: !115, isLocal: false, isDefinition: true, scopeLine: 23, flags: DIFlagPrototyped, isOptimized: false, unit: !0, declaration: !114, variables: !2) +!112 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Foo", file: !1, line: 17, size: 8, elements: !113, identifier: ".?AUFoo@@") +!113 = !{!114, !118, !121} +!114 = !DISubprogram(name: "thiscall1", linkageName: "\01?thiscall1@Foo@@QAEXH@Z", scope: !112, file: !1, line: 18, type: !115, isLocal: false, isDefinition: false, scopeLine: 18, flags: DIFlagPrototyped, isOptimized: false) +!115 = !DISubroutineType(cc: DW_CC_BORLAND_thiscall, types: !116) +!116 = !{null, !117, !11} +!117 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !112, size: 32, flags: DIFlagArtificial | DIFlagObjectPointer) +!118 = !DISubprogram(name: "thiscall2", linkageName: "\01?thiscall2@Foo@@QAEXHH@Z", scope: !112, file: !1, line: 19, type: !119, isLocal: false, isDefinition: false, scopeLine: 19, flags: DIFlagPrototyped, isOptimized: false) +!119 = !DISubroutineType(cc: DW_CC_BORLAND_thiscall, types: !120) +!120 = !{null, !117, !11, !11} +!121 = !DISubprogram(name: "thiscall3", linkageName: "\01?thiscall3@Foo@@QAEXHHH@Z", scope: !112, file: !1, line: 20, type: !122, isLocal: false, isDefinition: false, scopeLine: 20, flags: DIFlagPrototyped, isOptimized: false) +!122 = !DISubroutineType(cc: DW_CC_BORLAND_thiscall, types: !123) +!123 = !{null, !117, !11, !11, !11} +!124 = !DILocalVariable(name: "a", arg: 2, scope: !111, file: !1, line: 23, type: !11) +!125 = !DILocation(line: 23, column: 25, scope: !111) +!126 = !DILocalVariable(name: "this", arg: 1, scope: !111, type: !127, flags: DIFlagArtificial | DIFlagObjectPointer) +!127 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !112, size: 32) +!128 = !DILocation(line: 0, scope: !111) +!129 = !DILocation(line: 23, column: 35, scope: !111) +!130 = !DILocation(line: 23, column: 32, scope: !111) +!131 = !DILocation(line: 23, column: 38, scope: !111) +!132 = distinct !DISubprogram(name: "thiscall2", linkageName: "\01?thiscall2@Foo@@QAEXHH@Z", scope: !112, file: !1, line: 24, type: !119, isLocal: false, isDefinition: true, scopeLine: 24, flags: DIFlagPrototyped, isOptimized: false, unit: !0, declaration: !118, variables: !2) +!133 = !DILocalVariable(name: "b", arg: 3, scope: !132, file: !1, line: 24, type: !11) +!134 = !DILocation(line: 24, column: 32, scope: !132) +!135 = !DILocalVariable(name: "a", arg: 2, scope: !132, file: !1, line: 24, type: !11) +!136 = !DILocation(line: 24, column: 25, scope: !132) +!137 = !DILocalVariable(name: "this", arg: 1, scope: !132, type: !127, flags: DIFlagArtificial | DIFlagObjectPointer) +!138 = !DILocation(line: 0, scope: !132) +!139 = !DILocation(line: 24, column: 42, scope: !132) +!140 = !DILocation(line: 24, column: 46, scope: !132) +!141 = !DILocation(line: 24, column: 44, scope: !132) +!142 = !DILocation(line: 24, column: 39, scope: !132) +!143 = !DILocation(line: 24, column: 49, scope: !132) +!144 = distinct !DISubprogram(name: "thiscall3", linkageName: "\01?thiscall3@Foo@@QAEXHHH@Z", scope: !112, file: !1, line: 25, type: !122, isLocal: false, isDefinition: true, scopeLine: 25, flags: DIFlagPrototyped, isOptimized: false, unit: !0, declaration: !121, variables: !2) +!145 = !DILocalVariable(name: "c", arg: 4, scope: !144, file: !1, line: 25, type: !11) +!146 = !DILocation(line: 25, column: 39, scope: !144) +!147 = !DILocalVariable(name: "b", arg: 3, scope: !144, file: !1, line: 25, type: !11) +!148 = !DILocation(line: 25, column: 32, scope: !144) +!149 = !DILocalVariable(name: "a", arg: 2, scope: !144, file: !1, line: 25, type: !11) +!150 = !DILocation(line: 25, column: 25, scope: !144) +!151 = !DILocalVariable(name: "this", arg: 1, scope: !144, type: !127, flags: DIFlagArtificial | DIFlagObjectPointer) +!152 = !DILocation(line: 0, scope: !144) +!153 = !DILocation(line: 25, column: 49, scope: !144) +!154 = !DILocation(line: 25, column: 53, scope: !144) +!155 = !DILocation(line: 25, column: 51, scope: !144) +!156 = !DILocation(line: 25, column: 57, scope: !144) +!157 = !DILocation(line: 25, column: 55, scope: !144) +!158 = !DILocation(line: 25, column: 46, scope: !144) +!159 = !DILocation(line: 25, column: 60, scope: !144) Index: llvm/test/DebugInfo/COFF/fpo-csrs.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/COFF/fpo-csrs.ll @@ -0,0 +1,559 @@ +; RUN: llc < %s | FileCheck %s --check-prefix=ASM +; RUN: llc -filetype=obj < %s | llvm-readobj -codeview | FileCheck %s --check-prefix=OBJ + +; C source: +; int getval(void); +; void usevals(int, ...); +; int csr1() { +; int a = getval(); +; usevals(a); +; usevals(a); +; return a; +; } +; int csr2() { +; int a = getval(); +; int b = getval(); +; usevals(a, b); +; usevals(a, b); +; return a; +; } +; int csr3() { +; int a = getval(); +; int b = getval(); +; int c = getval(); +; usevals(a, b, c); +; usevals(a, b, c); +; return a; +; } +; int csr4() { +; int a = getval(); +; int b = getval(); +; int c = getval(); +; int d = getval(); +; usevals(a, b, c, d); +; usevals(a, b, c, d); +; return a; +; } +; int spill() { +; int a = getval(); +; int b = getval(); +; int c = getval(); +; int d = getval(); +; int e = getval(); +; usevals(a, b, c, d, e); +; usevals(a, b, c, d, e); +; return a; +; } + +; ModuleID = 't.c' +source_filename = "t.c" +target datalayout = "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32" +target triple = "i386-pc-windows-msvc19.11.25508" + +; Function Attrs: nounwind +define i32 @csr1() local_unnamed_addr #0 !dbg !8 { +entry: + %call = tail call i32 @getval() #3, !dbg !14 + tail call void @llvm.dbg.value(metadata i32 %call, metadata !13, metadata !DIExpression()), !dbg !15 + tail call void (i32, ...) @usevals(i32 %call) #3, !dbg !16 + tail call void (i32, ...) @usevals(i32 %call) #3, !dbg !17 + ret i32 %call, !dbg !18 +} + +; ASM-LABEL: _csr1: # @csr1 +; ASM: .cv_fpo_proc _csr1 +; ASM: pushl %esi +; ASM: .cv_fpo_pushreg %esi +; ASM: .cv_fpo_endprologue +; ASM: #DEBUG_VALUE: csr1:a <- %ESI +; ASM: retl +; ASM: .cv_fpo_endproc + +; OBJ-LABEL: SubSectionType: FrameData (0xF5) +; OBJ-NEXT: SubSectionSize: +; OBJ-NEXT: LinkageName: _csr1 +; OBJ-NEXT: FrameData { +; OBJ-NEXT: RvaStart: 0x0 +; OBJ-NEXT: CodeSize: 0x1E +; OBJ-NEXT: LocalSize: 0x0 +; OBJ-NEXT: ParamsSize: 0x0 +; OBJ-NEXT: MaxStackSize: 0x0 +; OBJ-NEXT: FrameFunc: $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = +; OBJ-NEXT: PrologSize: 0x1 +; OBJ-NEXT: SavedRegsSize: 0x0 +; OBJ-NEXT: Flags [ (0x4) +; OBJ-NEXT: IsFunctionStart (0x4) +; OBJ-NEXT: ] +; OBJ-NEXT: } +; OBJ-NEXT: FrameData { +; OBJ-NEXT: RvaStart: 0x1 +; OBJ-NEXT: CodeSize: 0x1D +; OBJ-NEXT: LocalSize: 0x0 +; OBJ-NEXT: ParamsSize: 0x0 +; OBJ-NEXT: MaxStackSize: 0x0 +; OBJ-NEXT: FrameFunc: $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = $esi $T0 4 - ^ = +; OBJ-NEXT: PrologSize: 0x0 +; OBJ-NEXT: SavedRegsSize: 0x4 +; OBJ-NEXT: Flags [ (0x0) +; OBJ-NEXT: ] +; OBJ-NEXT: } +; OBJ-NOT: FrameData + +declare i32 @getval() local_unnamed_addr #1 + +declare void @usevals(i32, ...) local_unnamed_addr #1 + +; Function Attrs: nounwind +define i32 @csr2() local_unnamed_addr #0 !dbg !19 { +entry: + %call = tail call i32 @getval() #3, !dbg !23 + tail call void @llvm.dbg.value(metadata i32 %call, metadata !21, metadata !DIExpression()), !dbg !24 + %call1 = tail call i32 @getval() #3, !dbg !25 + tail call void @llvm.dbg.value(metadata i32 %call1, metadata !22, metadata !DIExpression()), !dbg !26 + tail call void (i32, ...) @usevals(i32 %call, i32 %call1) #3, !dbg !27 + tail call void (i32, ...) @usevals(i32 %call, i32 %call1) #3, !dbg !28 + ret i32 %call, !dbg !29 +} + +; ASM-LABEL: _csr2: # @csr2 +; ASM: .cv_fpo_proc _csr2 +; ASM: pushl %edi +; ASM: .cv_fpo_pushreg %edi +; ASM: pushl %esi +; ASM: .cv_fpo_pushreg %esi +; ASM: .cv_fpo_endprologue +; ASM: #DEBUG_VALUE: csr2:a <- %ESI +; ASM: #DEBUG_VALUE: csr2:b <- %EDI +; ASM: retl +; ASM: .cv_fpo_endproc + +; OBJ-LABEL: SubSectionType: FrameData (0xF5) +; OBJ-NEXT: SubSectionSize: +; OBJ-NEXT: LinkageName: _csr2 +; OBJ-NEXT: FrameData { +; OBJ-NEXT: RvaStart: 0x0 +; OBJ-NEXT: CodeSize: 0x29 +; OBJ-NEXT: LocalSize: 0x0 +; OBJ-NEXT: ParamsSize: 0x0 +; OBJ-NEXT: MaxStackSize: 0x0 +; OBJ-NEXT: FrameFunc: $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = +; OBJ-NEXT: PrologSize: 0x2 +; OBJ-NEXT: SavedRegsSize: 0x0 +; OBJ-NEXT: Flags [ (0x4) +; OBJ-NEXT: IsFunctionStart (0x4) +; OBJ-NEXT: ] +; OBJ-NEXT: } +; OBJ-NEXT: FrameData { +; OBJ-NEXT: RvaStart: 0x1 +; OBJ-NEXT: CodeSize: 0x28 +; OBJ-NEXT: LocalSize: 0x0 +; OBJ-NEXT: ParamsSize: 0x0 +; OBJ-NEXT: MaxStackSize: 0x0 +; OBJ-NEXT: FrameFunc: $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = $edi $T0 4 - ^ = +; OBJ-NEXT: PrologSize: 0x1 +; OBJ-NEXT: SavedRegsSize: 0x4 +; OBJ-NEXT: Flags [ (0x0) +; OBJ-NEXT: ] +; OBJ-NEXT: } +; OBJ-NEXT: FrameData { +; OBJ-NEXT: RvaStart: 0x2 +; OBJ-NEXT: CodeSize: 0x27 +; OBJ-NEXT: LocalSize: 0x0 +; OBJ-NEXT: ParamsSize: 0x0 +; OBJ-NEXT: MaxStackSize: 0x0 +; OBJ-NEXT: FrameFunc: $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = $edi $T0 4 - ^ = $esi $T0 8 - ^ = +; OBJ-NEXT: PrologSize: 0x0 +; OBJ-NEXT: SavedRegsSize: 0x8 +; OBJ-NEXT: Flags [ (0x0) +; OBJ-NEXT: ] +; OBJ-NEXT: } +; OBJ-NOT: FrameData + +; Function Attrs: nounwind +define i32 @csr3() local_unnamed_addr #0 !dbg !30 { +entry: + %call = tail call i32 @getval() #3, !dbg !35 + tail call void @llvm.dbg.value(metadata i32 %call, metadata !32, metadata !DIExpression()), !dbg !36 + %call1 = tail call i32 @getval() #3, !dbg !37 + tail call void @llvm.dbg.value(metadata i32 %call1, metadata !33, metadata !DIExpression()), !dbg !38 + %call2 = tail call i32 @getval() #3, !dbg !39 + tail call void @llvm.dbg.value(metadata i32 %call2, metadata !34, metadata !DIExpression()), !dbg !40 + tail call void (i32, ...) @usevals(i32 %call, i32 %call1, i32 %call2) #3, !dbg !41 + tail call void (i32, ...) @usevals(i32 %call, i32 %call1, i32 %call2) #3, !dbg !42 + ret i32 %call, !dbg !43 +} + +; ASM-LABEL: _csr3: # @csr3 +; ASM: .cv_fpo_proc _csr3 +; ASM: pushl %ebx +; ASM: .cv_fpo_pushreg %ebx +; ASM: pushl %edi +; ASM: .cv_fpo_pushreg %edi +; ASM: pushl %esi +; ASM: .cv_fpo_pushreg %esi +; ASM: .cv_fpo_endprologue +; ASM: #DEBUG_VALUE: csr3:a <- %ESI +; ASM: #DEBUG_VALUE: csr3:b <- %EDI +; ASM: #DEBUG_VALUE: csr3:c <- %EBX +; ASM: retl +; ASM: .cv_fpo_endproc + +; OBJ-LABEL: SubSectionType: FrameData (0xF5) +; OBJ-NEXT: SubSectionSize: +; OBJ-NEXT: LinkageName: _csr3 +; OBJ-NEXT: FrameData { +; OBJ-NEXT: RvaStart: 0x0 +; OBJ-NEXT: CodeSize: 0x34 +; OBJ-NEXT: LocalSize: 0x0 +; OBJ-NEXT: ParamsSize: 0x0 +; OBJ-NEXT: MaxStackSize: 0x0 +; OBJ-NEXT: FrameFunc: $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = +; OBJ-NEXT: PrologSize: 0x3 +; OBJ-NEXT: SavedRegsSize: 0x0 +; OBJ-NEXT: Flags [ (0x4) +; OBJ-NEXT: IsFunctionStart (0x4) +; OBJ-NEXT: ] +; OBJ-NEXT: } +; OBJ-NEXT: FrameData { +; OBJ-NEXT: RvaStart: 0x1 +; OBJ-NEXT: CodeSize: 0x33 +; OBJ-NEXT: LocalSize: 0x0 +; OBJ-NEXT: ParamsSize: 0x0 +; OBJ-NEXT: MaxStackSize: 0x0 +; OBJ-NEXT: FrameFunc: $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = $ebx $T0 4 - ^ = +; OBJ-NEXT: PrologSize: 0x2 +; OBJ-NEXT: SavedRegsSize: 0x4 +; OBJ-NEXT: Flags [ (0x0) +; OBJ-NEXT: ] +; OBJ-NEXT: } +; OBJ-NEXT: FrameData { +; OBJ-NEXT: RvaStart: 0x2 +; OBJ-NEXT: CodeSize: 0x32 +; OBJ-NEXT: LocalSize: 0x0 +; OBJ-NEXT: ParamsSize: 0x0 +; OBJ-NEXT: MaxStackSize: 0x0 +; OBJ-NEXT: FrameFunc: $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = $ebx $T0 4 - ^ = $edi $T0 8 - ^ = +; OBJ-NEXT: PrologSize: 0x1 +; OBJ-NEXT: SavedRegsSize: 0x8 +; OBJ-NEXT: Flags [ (0x0) +; OBJ-NEXT: ] +; OBJ-NEXT: } +; OBJ-NEXT: FrameData { +; OBJ-NEXT: RvaStart: 0x3 +; OBJ-NEXT: CodeSize: 0x31 +; OBJ-NEXT: LocalSize: 0x0 +; OBJ-NEXT: ParamsSize: 0x0 +; OBJ-NEXT: MaxStackSize: 0x0 +; OBJ-NEXT: FrameFunc: $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = $ebx $T0 4 - ^ = $edi $T0 8 - ^ = $esi $T0 12 - ^ = +; OBJ-NEXT: PrologSize: 0x0 +; OBJ-NEXT: SavedRegsSize: 0xC +; OBJ-NEXT: Flags [ (0x0) +; OBJ-NEXT: ] +; OBJ-NEXT: } +; OBJ-NOT: FrameData + +; Function Attrs: nounwind +define i32 @csr4() local_unnamed_addr #0 !dbg !44 { +entry: + %call = tail call i32 @getval() #3, !dbg !50 + tail call void @llvm.dbg.value(metadata i32 %call, metadata !46, metadata !DIExpression()), !dbg !51 + %call1 = tail call i32 @getval() #3, !dbg !52 + tail call void @llvm.dbg.value(metadata i32 %call1, metadata !47, metadata !DIExpression()), !dbg !53 + %call2 = tail call i32 @getval() #3, !dbg !54 + tail call void @llvm.dbg.value(metadata i32 %call2, metadata !48, metadata !DIExpression()), !dbg !55 + %call3 = tail call i32 @getval() #3, !dbg !56 + tail call void @llvm.dbg.value(metadata i32 %call3, metadata !49, metadata !DIExpression()), !dbg !57 + tail call void (i32, ...) @usevals(i32 %call, i32 %call1, i32 %call2, i32 %call3) #3, !dbg !58 + tail call void (i32, ...) @usevals(i32 %call, i32 %call1, i32 %call2, i32 %call3) #3, !dbg !59 + ret i32 %call, !dbg !60 +} + +; ASM-LABEL: _csr4: # @csr4 +; ASM: .cv_fpo_proc _csr4 +; ASM: pushl %ebp +; ASM: .cv_fpo_pushreg %ebp +; ASM: pushl %ebx +; ASM: .cv_fpo_pushreg %ebx +; ASM: pushl %edi +; ASM: .cv_fpo_pushreg %edi +; ASM: pushl %esi +; ASM: .cv_fpo_pushreg %esi +; ASM: .cv_fpo_endprologue +; ASM: #DEBUG_VALUE: csr4:a <- %ESI +; ASM: #DEBUG_VALUE: csr4:b <- %EDI +; ASM: #DEBUG_VALUE: csr4:c <- %EBX +; ASM: #DEBUG_VALUE: csr4:d <- %EBP +; ASM: retl +; ASM: .cv_fpo_endproc + +; OBJ-LABEL: SubSectionType: FrameData (0xF5) +; OBJ-NEXT: SubSectionSize: +; OBJ-NEXT: LinkageName: _csr4 +; OBJ-NEXT: FrameData { +; OBJ-NEXT: RvaStart: 0x0 +; OBJ-NEXT: CodeSize: 0x3F +; OBJ-NEXT: LocalSize: 0x0 +; OBJ-NEXT: ParamsSize: 0x0 +; OBJ-NEXT: MaxStackSize: 0x0 +; OBJ-NEXT: FrameFunc: $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = +; OBJ-NEXT: PrologSize: 0x4 +; OBJ-NEXT: SavedRegsSize: 0x0 +; OBJ-NEXT: Flags [ (0x4) +; OBJ-NEXT: IsFunctionStart (0x4) +; OBJ-NEXT: ] +; OBJ-NEXT: } +; OBJ-NEXT: FrameData { +; OBJ-NEXT: RvaStart: 0x1 +; OBJ-NEXT: CodeSize: 0x3E +; OBJ-NEXT: LocalSize: 0x0 +; OBJ-NEXT: ParamsSize: 0x0 +; OBJ-NEXT: MaxStackSize: 0x0 +; OBJ-NEXT: FrameFunc: $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = +; OBJ-NEXT: PrologSize: 0x3 +; OBJ-NEXT: SavedRegsSize: 0x4 +; OBJ-NEXT: Flags [ (0x0) +; OBJ-NEXT: ] +; OBJ-NEXT: } +; OBJ-NEXT: FrameData { +; OBJ-NEXT: RvaStart: 0x2 +; OBJ-NEXT: CodeSize: 0x3D +; OBJ-NEXT: LocalSize: 0x0 +; OBJ-NEXT: ParamsSize: 0x0 +; OBJ-NEXT: MaxStackSize: 0x0 +; OBJ-NEXT: FrameFunc: $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = $ebx $T0 8 - ^ = +; OBJ-NEXT: PrologSize: 0x2 +; OBJ-NEXT: SavedRegsSize: 0x8 +; OBJ-NEXT: Flags [ (0x0) +; OBJ-NEXT: ] +; OBJ-NEXT: } +; OBJ-NEXT: FrameData { +; OBJ-NEXT: RvaStart: 0x3 +; OBJ-NEXT: CodeSize: 0x3C +; OBJ-NEXT: LocalSize: 0x0 +; OBJ-NEXT: ParamsSize: 0x0 +; OBJ-NEXT: MaxStackSize: 0x0 +; OBJ-NEXT: FrameFunc: $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = $ebx $T0 8 - ^ = $edi $T0 12 - ^ = +; OBJ-NEXT: PrologSize: 0x1 +; OBJ-NEXT: SavedRegsSize: 0xC +; OBJ-NEXT: Flags [ (0x0) +; OBJ-NEXT: ] +; OBJ-NEXT: } +; OBJ-NEXT: FrameData { +; OBJ-NEXT: RvaStart: 0x4 +; OBJ-NEXT: CodeSize: 0x3B +; OBJ-NEXT: LocalSize: 0x0 +; OBJ-NEXT: ParamsSize: 0x0 +; OBJ-NEXT: MaxStackSize: 0x0 +; OBJ-NEXT: FrameFunc: $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = $ebx $T0 8 - ^ = $edi $T0 12 - ^ = $esi $T0 16 - ^ = +; OBJ-NEXT: PrologSize: 0x0 +; OBJ-NEXT: SavedRegsSize: 0x10 +; OBJ-NEXT: Flags [ (0x0) +; OBJ-NEXT: ] +; OBJ-NEXT: } +; OBJ-NOT: FrameData + +; Function Attrs: nounwind +define i32 @spill() local_unnamed_addr #0 !dbg !61 { +entry: + %call = tail call i32 @getval() #3, !dbg !68 + tail call void @llvm.dbg.value(metadata i32 %call, metadata !63, metadata !DIExpression()), !dbg !69 + %call1 = tail call i32 @getval() #3, !dbg !70 + tail call void @llvm.dbg.value(metadata i32 %call1, metadata !64, metadata !DIExpression()), !dbg !71 + %call2 = tail call i32 @getval() #3, !dbg !72 + tail call void @llvm.dbg.value(metadata i32 %call2, metadata !65, metadata !DIExpression()), !dbg !73 + %call3 = tail call i32 @getval() #3, !dbg !74 + tail call void @llvm.dbg.value(metadata i32 %call3, metadata !66, metadata !DIExpression()), !dbg !75 + %call4 = tail call i32 @getval() #3, !dbg !76 + tail call void @llvm.dbg.value(metadata i32 %call4, metadata !67, metadata !DIExpression()), !dbg !77 + tail call void (i32, ...) @usevals(i32 %call, i32 %call1, i32 %call2, i32 %call3, i32 %call4) #3, !dbg !78 + tail call void (i32, ...) @usevals(i32 %call, i32 %call1, i32 %call2, i32 %call3, i32 %call4) #3, !dbg !79 + ret i32 %call, !dbg !80 +} + +; ASM-LABEL: _spill: # @spill +; ASM: .cv_fpo_proc _spill +; ASM: pushl %ebp +; ASM: .cv_fpo_pushreg %ebp +; ASM: pushl %ebx +; ASM: .cv_fpo_pushreg %ebx +; ASM: pushl %edi +; ASM: .cv_fpo_pushreg %edi +; ASM: pushl %esi +; ASM: .cv_fpo_pushreg %esi +; ASM: subl $8, %esp +; ASM: .cv_fpo_stackalloc 8 +; ASM: .cv_fpo_endprologue +; ASM: retl +; ASM: .cv_fpo_endproc + +; OBJ-LABEL: SubSectionType: FrameData (0xF5) +; OBJ-NEXT: SubSectionSize: +; OBJ-NEXT: LinkageName: _spill +; OBJ-NEXT: FrameData { +; OBJ-NEXT: RvaStart: 0x0 +; OBJ-NEXT: CodeSize: 0x5A +; OBJ-NEXT: LocalSize: 0x0 +; OBJ-NEXT: ParamsSize: 0x0 +; OBJ-NEXT: MaxStackSize: 0x0 +; OBJ-NEXT: FrameFunc: $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = +; OBJ-NEXT: PrologSize: 0x7 +; OBJ-NEXT: SavedRegsSize: 0x0 +; OBJ-NEXT: Flags [ (0x4) +; OBJ-NEXT: IsFunctionStart (0x4) +; OBJ-NEXT: ] +; OBJ-NEXT: } +; OBJ-NEXT: FrameData { +; OBJ-NEXT: RvaStart: 0x1 +; OBJ-NEXT: CodeSize: 0x59 +; OBJ-NEXT: LocalSize: 0x0 +; OBJ-NEXT: ParamsSize: 0x0 +; OBJ-NEXT: MaxStackSize: 0x0 +; OBJ-NEXT: FrameFunc: $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = +; OBJ-NEXT: PrologSize: 0x6 +; OBJ-NEXT: SavedRegsSize: 0x4 +; OBJ-NEXT: Flags [ (0x0) +; OBJ-NEXT: ] +; OBJ-NEXT: } +; OBJ-NEXT: FrameData { +; OBJ-NEXT: RvaStart: 0x2 +; OBJ-NEXT: CodeSize: 0x58 +; OBJ-NEXT: LocalSize: 0x0 +; OBJ-NEXT: ParamsSize: 0x0 +; OBJ-NEXT: MaxStackSize: 0x0 +; OBJ-NEXT: FrameFunc: $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = $ebx $T0 8 - ^ = +; OBJ-NEXT: PrologSize: 0x5 +; OBJ-NEXT: SavedRegsSize: 0x8 +; OBJ-NEXT: Flags [ (0x0) +; OBJ-NEXT: ] +; OBJ-NEXT: } +; OBJ-NEXT: FrameData { +; OBJ-NEXT: RvaStart: 0x3 +; OBJ-NEXT: CodeSize: 0x57 +; OBJ-NEXT: LocalSize: 0x0 +; OBJ-NEXT: ParamsSize: 0x0 +; OBJ-NEXT: MaxStackSize: 0x0 +; OBJ-NEXT: FrameFunc: $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = $ebx $T0 8 - ^ = $edi $T0 12 - ^ = +; OBJ-NEXT: PrologSize: 0x4 +; OBJ-NEXT: SavedRegsSize: 0xC +; OBJ-NEXT: Flags [ (0x0) +; OBJ-NEXT: ] +; OBJ-NEXT: } +; OBJ-NEXT: FrameData { +; OBJ-NEXT: RvaStart: 0x4 +; OBJ-NEXT: CodeSize: 0x56 +; OBJ-NEXT: LocalSize: 0x0 +; OBJ-NEXT: ParamsSize: 0x0 +; OBJ-NEXT: MaxStackSize: 0x0 +; OBJ-NEXT: FrameFunc: $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = $ebx $T0 8 - ^ = $edi $T0 12 - ^ = $esi $T0 16 - ^ = +; OBJ-NEXT: PrologSize: 0x3 +; OBJ-NEXT: SavedRegsSize: 0x10 +; OBJ-NEXT: Flags [ (0x0) +; OBJ-NEXT: ] +; OBJ-NEXT: } +; OBJ-NEXT: FrameData { +; OBJ-NEXT: RvaStart: 0x7 +; OBJ-NEXT: CodeSize: 0x53 +; OBJ-NEXT: LocalSize: 0x8 +; OBJ-NEXT: ParamsSize: 0x0 +; OBJ-NEXT: MaxStackSize: 0x0 +; OBJ-NEXT: FrameFunc: $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = $ebx $T0 8 - ^ = $edi $T0 12 - ^ = $esi $T0 16 - ^ = +; OBJ-NEXT: PrologSize: 0x0 +; OBJ-NEXT: SavedRegsSize: 0x10 +; OBJ-NEXT: Flags [ (0x0) +; OBJ-NEXT: ] +; OBJ-NEXT: } +; OBJ-NOT: FrameData + +; Function Attrs: nounwind readnone speculatable +declare void @llvm.dbg.value(metadata, metadata, metadata) #2 + +attributes #0 = { nounwind "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"="pentium4" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "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"="pentium4" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #2 = { nounwind readnone speculatable } +attributes #3 = { nounwind } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5, !6} +!llvm.ident = !{!7} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 6.0.0 ", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) +!1 = !DIFile(filename: "t.c", directory: "C:\5Csrc\5Cllvm-project\5Cbuild", checksumkind: CSK_MD5, checksum: "0b1c85f8a0bfb41380df1fcaeadde306") +!2 = !{} +!3 = !{i32 1, !"NumRegisterParameters", i32 0} +!4 = !{i32 2, !"CodeView", i32 1} +!5 = !{i32 2, !"Debug Info Version", i32 3} +!6 = !{i32 1, !"wchar_size", i32 2} +!7 = !{!"clang version 6.0.0 "} +!8 = distinct !DISubprogram(name: "csr1", scope: !1, file: !1, line: 3, type: !9, isLocal: false, isDefinition: true, scopeLine: 3, isOptimized: true, unit: !0, variables: !12) +!9 = !DISubroutineType(types: !10) +!10 = !{!11} +!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!12 = !{!13} +!13 = !DILocalVariable(name: "a", scope: !8, file: !1, line: 4, type: !11) +!14 = !DILocation(line: 4, column: 11, scope: !8) +!15 = !DILocation(line: 4, column: 7, scope: !8) +!16 = !DILocation(line: 5, column: 3, scope: !8) +!17 = !DILocation(line: 6, column: 3, scope: !8) +!18 = !DILocation(line: 7, column: 3, scope: !8) +!19 = distinct !DISubprogram(name: "csr2", scope: !1, file: !1, line: 9, type: !9, isLocal: false, isDefinition: true, scopeLine: 9, isOptimized: true, unit: !0, variables: !20) +!20 = !{!21, !22} +!21 = !DILocalVariable(name: "a", scope: !19, file: !1, line: 10, type: !11) +!22 = !DILocalVariable(name: "b", scope: !19, file: !1, line: 11, type: !11) +!23 = !DILocation(line: 10, column: 11, scope: !19) +!24 = !DILocation(line: 10, column: 7, scope: !19) +!25 = !DILocation(line: 11, column: 11, scope: !19) +!26 = !DILocation(line: 11, column: 7, scope: !19) +!27 = !DILocation(line: 12, column: 3, scope: !19) +!28 = !DILocation(line: 13, column: 3, scope: !19) +!29 = !DILocation(line: 14, column: 3, scope: !19) +!30 = distinct !DISubprogram(name: "csr3", scope: !1, file: !1, line: 16, type: !9, isLocal: false, isDefinition: true, scopeLine: 16, isOptimized: true, unit: !0, variables: !31) +!31 = !{!32, !33, !34} +!32 = !DILocalVariable(name: "a", scope: !30, file: !1, line: 17, type: !11) +!33 = !DILocalVariable(name: "b", scope: !30, file: !1, line: 18, type: !11) +!34 = !DILocalVariable(name: "c", scope: !30, file: !1, line: 19, type: !11) +!35 = !DILocation(line: 17, column: 11, scope: !30) +!36 = !DILocation(line: 17, column: 7, scope: !30) +!37 = !DILocation(line: 18, column: 11, scope: !30) +!38 = !DILocation(line: 18, column: 7, scope: !30) +!39 = !DILocation(line: 19, column: 11, scope: !30) +!40 = !DILocation(line: 19, column: 7, scope: !30) +!41 = !DILocation(line: 20, column: 3, scope: !30) +!42 = !DILocation(line: 21, column: 3, scope: !30) +!43 = !DILocation(line: 22, column: 3, scope: !30) +!44 = distinct !DISubprogram(name: "csr4", scope: !1, file: !1, line: 24, type: !9, isLocal: false, isDefinition: true, scopeLine: 24, isOptimized: true, unit: !0, variables: !45) +!45 = !{!46, !47, !48, !49} +!46 = !DILocalVariable(name: "a", scope: !44, file: !1, line: 25, type: !11) +!47 = !DILocalVariable(name: "b", scope: !44, file: !1, line: 26, type: !11) +!48 = !DILocalVariable(name: "c", scope: !44, file: !1, line: 27, type: !11) +!49 = !DILocalVariable(name: "d", scope: !44, file: !1, line: 28, type: !11) +!50 = !DILocation(line: 25, column: 11, scope: !44) +!51 = !DILocation(line: 25, column: 7, scope: !44) +!52 = !DILocation(line: 26, column: 11, scope: !44) +!53 = !DILocation(line: 26, column: 7, scope: !44) +!54 = !DILocation(line: 27, column: 11, scope: !44) +!55 = !DILocation(line: 27, column: 7, scope: !44) +!56 = !DILocation(line: 28, column: 11, scope: !44) +!57 = !DILocation(line: 28, column: 7, scope: !44) +!58 = !DILocation(line: 29, column: 3, scope: !44) +!59 = !DILocation(line: 30, column: 3, scope: !44) +!60 = !DILocation(line: 31, column: 3, scope: !44) +!61 = distinct !DISubprogram(name: "spill", scope: !1, file: !1, line: 33, type: !9, isLocal: false, isDefinition: true, scopeLine: 33, isOptimized: true, unit: !0, variables: !62) +!62 = !{!63, !64, !65, !66, !67} +!63 = !DILocalVariable(name: "a", scope: !61, file: !1, line: 34, type: !11) +!64 = !DILocalVariable(name: "b", scope: !61, file: !1, line: 35, type: !11) +!65 = !DILocalVariable(name: "c", scope: !61, file: !1, line: 36, type: !11) +!66 = !DILocalVariable(name: "d", scope: !61, file: !1, line: 37, type: !11) +!67 = !DILocalVariable(name: "e", scope: !61, file: !1, line: 38, type: !11) +!68 = !DILocation(line: 34, column: 11, scope: !61) +!69 = !DILocation(line: 34, column: 7, scope: !61) +!70 = !DILocation(line: 35, column: 11, scope: !61) +!71 = !DILocation(line: 35, column: 7, scope: !61) +!72 = !DILocation(line: 36, column: 11, scope: !61) +!73 = !DILocation(line: 36, column: 7, scope: !61) +!74 = !DILocation(line: 37, column: 11, scope: !61) +!75 = !DILocation(line: 37, column: 7, scope: !61) +!76 = !DILocation(line: 38, column: 11, scope: !61) +!77 = !DILocation(line: 38, column: 7, scope: !61) +!78 = !DILocation(line: 39, column: 3, scope: !61) +!79 = !DILocation(line: 40, column: 3, scope: !61) +!80 = !DILocation(line: 41, column: 3, scope: !61) Index: llvm/test/DebugInfo/COFF/multifunction.ll =================================================================== --- llvm/test/DebugInfo/COFF/multifunction.ll +++ llvm/test/DebugInfo/COFF/multifunction.ll @@ -61,6 +61,7 @@ ; X86-NEXT: .short [[C1_END:.*]]-[[C1_START:.*]] # ; X86: [[COMPILE_END]]: ; X86-NEXT: .p2align 2 +; X86-NEXT: .cv_fpo_data _x ; Symbol subsection for x ; X86-NEXT: .long 241 ; X86-NEXT: .long [[F1_END:.*]]-[[F1_START:.*]] # @@ -87,6 +88,7 @@ ; Line table subsection for x ; X86: .cv_linetable 0, _x, [[END_OF_X]] ; Symbol subsection for y +; X86-NEXT: .cv_fpo_data _y ; X86-NEXT: .long 241 ; X86-NEXT: .long [[COMPILE_END:.*]]-[[COMPILE_START:.*]] # ; X86-NEXT: [[COMPILE_START]]: @@ -112,6 +114,7 @@ ; Line table subsection for y ; X86: .cv_linetable 1, _y, [[END_OF_Y]] ; Symbol subsection for f +; X86-NEXT: .cv_fpo_data _f ; X86-NEXT: .long 241 ; X86-NEXT: .long [[COMPILE_END:.*]]-[[COMPILE_START:.*]] # ; X86-NEXT: [[COMPILE_START]]: @@ -145,6 +148,13 @@ ; OBJ32: ] ; OBJ32: Subsection [ ; OBJ32-NEXT: SubSectionType: Symbols (0xF1) +; OBJ32: Compile3Sym +; OBJ32: ] +; OBJ32: Subsection [ +; OBJ32-NEXT: SubSectionType: FrameData (0xF5) +; OBJ32: ] +; OBJ32: Subsection [ +; OBJ32-NEXT: SubSectionType: Symbols (0xF1) ; OBJ32: {{.*}}Proc{{.*}}Sym { ; OBJ32: Kind: S_LPROC32_ID (0x1146) ; OBJ32: CodeSize: 0x6 @@ -158,6 +168,9 @@ ; OBJ32-NEXT: SubSectionType: Lines (0xF2) ; OBJ32: ] ; OBJ32: Subsection [ +; OBJ32-NEXT: SubSectionType: FrameData (0xF5) +; OBJ32: ] +; OBJ32: Subsection [ ; OBJ32-NEXT: SubSectionType: Symbols (0xF1) ; OBJ32: {{.*}}Proc{{.*}}Sym { ; OBJ32: Kind: S_GPROC32_ID (0x1147) @@ -172,6 +185,9 @@ ; OBJ32-NEXT: SubSectionType: Lines (0xF2) ; OBJ32: ] ; OBJ32: Subsection [ +; OBJ32-NEXT: SubSectionType: FrameData (0xF5) +; OBJ32: ] +; OBJ32: Subsection [ ; OBJ32-NEXT: SubSectionType: Symbols (0xF1) ; OBJ32: {{.*}}Proc{{.*}}Sym { ; OBJ32: Kind: S_GPROC32_ID (0x1147) Index: llvm/test/DebugInfo/COFF/simple.ll =================================================================== --- llvm/test/DebugInfo/COFF/simple.ll +++ llvm/test/DebugInfo/COFF/simple.ll @@ -36,6 +36,7 @@ ; X86: [[C1_END]]: ; X86-NEXT: [[COMPILE_END]]: ; X86-NEXT: .p2align 2 +; X86-NEXT: .cv_fpo_data _f ; X86-NEXT: .long 241 # Symbol subsection for f ; X86-NEXT: .long [[F1_END:.*]]-[[F1_START:.*]] # Subsection size ; X86-NEXT: [[F1_START]]: @@ -70,13 +71,21 @@ ; OBJ32: Characteristics [ (0x42300040) ; OBJ32: ] ; OBJ32: Relocations [ -; OBJ32-NEXT: 0x64 IMAGE_REL_I386_SECREL _f -; OBJ32-NEXT: 0x68 IMAGE_REL_I386_SECTION _f -; OBJ32-NEXT: 0x7C IMAGE_REL_I386_SECREL _f -; OBJ32-NEXT: 0x80 IMAGE_REL_I386_SECTION _f +; OBJ32-NEXT: 0x44 IMAGE_REL_I386_DIR32NB _f +; OBJ32-NEXT: 0x90 IMAGE_REL_I386_SECREL _f +; OBJ32-NEXT: 0x94 IMAGE_REL_I386_SECTION _f +; OBJ32-NEXT: 0xA8 IMAGE_REL_I386_SECREL _f +; OBJ32-NEXT: 0xAC IMAGE_REL_I386_SECTION _f ; OBJ32-NEXT: ] ; OBJ32: Subsection [ ; OBJ32-NEXT: SubSectionType: Symbols (0xF1) +; OBJ32: Compile3Sym +; OBJ32: ] +; OBJ32: Subsection [ +; OBJ32-NEXT: SubSectionType: FrameData (0xF5) +; OBJ32: ] +; OBJ32: Subsection [ +; OBJ32-NEXT: SubSectionType: Symbols (0xF1) ; OBJ32: {{.*}}Proc{{.*}}Sym { ; OBJ32: CodeSize: 0x6 ; OBJ32: DisplayName: f Index: llvm/test/MC/COFF/cv-fpo-csrs.s =================================================================== --- /dev/null +++ llvm/test/MC/COFF/cv-fpo-csrs.s @@ -0,0 +1,141 @@ +# RUN: llvm-mc -filetype=asm < %s -triple i686-windows-msvc | FileCheck %s --check-prefix=ASM +# RUN: llvm-mc -filetype=obj < %s -triple i686-windows-msvc | llvm-readobj -codeview | FileCheck %s --check-prefix=OBJ + +.globl _foo +_foo: + .cv_fpo_proc _foo 4 + pushl %ebp + .cv_fpo_pushreg ebp # Test without % + pushl %ebx + .cv_fpo_pushreg %ebx + pushl %edi + .cv_fpo_pushreg %edi + pushl %esi + .cv_fpo_pushreg esi + subl $20, %esp + .cv_fpo_stackalloc 20 + .cv_fpo_endprologue + + # ASM: .cv_fpo_proc _foo 4 + # ASM: pushl %ebp + # ASM: .cv_fpo_pushreg %ebp + # ASM: pushl %ebx + # ASM: .cv_fpo_pushreg %ebx + # ASM: pushl %edi + # ASM: .cv_fpo_pushreg %edi + # ASM: pushl %esi + # ASM: .cv_fpo_pushreg %esi + # ASM: subl $20, %esp + # ASM: .cv_fpo_stackalloc 20 + # ASM: .cv_fpo_endprologue + + # Clobbers + xorl %ebp, %ebp + xorl %ebx, %ebx + xorl %edi, %edi + xorl %esi, %esi + # Use that stack memory + leal 4(%esp), %eax + movl %eax, (%esp) + calll _bar + + # ASM: calll _bar + + # Epilogue + # FIXME: Get FPO data for this once we get it for DWARF. + addl $20, %esp + popl %esi + popl %edi + popl %ebx + popl %ebp + retl + .cv_fpo_endproc + + # ASM: .cv_fpo_endproc + + .section .debug$S,"dr" + .p2align 2 + .long 4 # Debug section magic + .cv_fpo_data _foo + .cv_stringtable + + # ASM: .cv_fpo_data + +# OBJ: Subsection [ +# OBJ-NEXT: SubSectionType: FrameData (0xF5) +# OBJ-NEXT: SubSectionSize: 0xC4 +# OBJ-NEXT: LinkageName: _foo +# OBJ-NEXT: FrameData { +# OBJ-NEXT: RvaStart: 0x0 +# OBJ-NEXT: CodeSize: 0x23 +# OBJ-NEXT: LocalSize: 0x0 +# OBJ-NEXT: ParamsSize: 0x4 +# OBJ-NEXT: MaxStackSize: 0x0 +# OBJ-NEXT: FrameFunc: $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = +# OBJ-NEXT: PrologSize: 0x7 +# OBJ-NEXT: SavedRegsSize: 0x0 +# OBJ-NEXT: Flags [ (0x4) +# OBJ-NEXT: IsFunctionStart (0x4) +# OBJ-NEXT: ] +# OBJ-NEXT: } +# OBJ-NEXT: FrameData { +# OBJ-NEXT: RvaStart: 0x1 +# OBJ-NEXT: CodeSize: 0x22 +# OBJ-NEXT: LocalSize: 0x0 +# OBJ-NEXT: ParamsSize: 0x4 +# OBJ-NEXT: MaxStackSize: 0x0 +# OBJ-NEXT: FrameFunc: $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = +# OBJ-NEXT: PrologSize: 0x6 +# OBJ-NEXT: SavedRegsSize: 0x4 +# OBJ-NEXT: Flags [ (0x0) +# OBJ-NEXT: ] +# OBJ-NEXT: } +# OBJ-NEXT: FrameData { +# OBJ-NEXT: RvaStart: 0x2 +# OBJ-NEXT: CodeSize: 0x21 +# OBJ-NEXT: LocalSize: 0x0 +# OBJ-NEXT: ParamsSize: 0x4 +# OBJ-NEXT: MaxStackSize: 0x0 +# OBJ-NEXT: FrameFunc: $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = $ebx $T0 8 - ^ = +# OBJ-NEXT: PrologSize: 0x5 +# OBJ-NEXT: SavedRegsSize: 0x8 +# OBJ-NEXT: Flags [ (0x0) +# OBJ-NEXT: ] +# OBJ-NEXT: } +# OBJ-NEXT: FrameData { +# OBJ-NEXT: RvaStart: 0x3 +# OBJ-NEXT: CodeSize: 0x20 +# OBJ-NEXT: LocalSize: 0x0 +# OBJ-NEXT: ParamsSize: 0x4 +# OBJ-NEXT: MaxStackSize: 0x0 +# OBJ-NEXT: FrameFunc: $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = $ebx $T0 8 - ^ = $edi $T0 12 - ^ = +# OBJ-NEXT: PrologSize: 0x4 +# OBJ-NEXT: SavedRegsSize: 0xC +# OBJ-NEXT: Flags [ (0x0) +# OBJ-NEXT: ] +# OBJ-NEXT: } +# OBJ-NEXT: FrameData { +# OBJ-NEXT: RvaStart: 0x4 +# OBJ-NEXT: CodeSize: 0x1F +# OBJ-NEXT: LocalSize: 0x0 +# OBJ-NEXT: ParamsSize: 0x4 +# OBJ-NEXT: MaxStackSize: 0x0 +# OBJ-NEXT: FrameFunc: $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = $ebx $T0 8 - ^ = $edi $T0 12 - ^ = $esi $T0 16 - ^ = +# OBJ-NEXT: PrologSize: 0x3 +# OBJ-NEXT: SavedRegsSize: 0x10 +# OBJ-NEXT: Flags [ (0x0) +# OBJ-NEXT: ] +# OBJ-NEXT: } +# OBJ-NEXT: FrameData { +# OBJ-NEXT: RvaStart: 0x7 +# OBJ-NEXT: CodeSize: 0x1C +# OBJ-NEXT: LocalSize: 0x14 +# OBJ-NEXT: ParamsSize: 0x4 +# OBJ-NEXT: MaxStackSize: 0x0 +# OBJ-NEXT: FrameFunc: $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = $ebx $T0 8 - ^ = $edi $T0 12 - ^ = $esi $T0 16 - ^ = +# OBJ-NEXT: PrologSize: 0x0 +# OBJ-NEXT: SavedRegsSize: 0x10 +# OBJ-NEXT: Flags [ (0x0) +# OBJ-NEXT: ] +# OBJ-NEXT: } +# OBJ-NOT: FrameData Index: llvm/test/MC/COFF/cv-fpo-errors.s =================================================================== --- /dev/null +++ llvm/test/MC/COFF/cv-fpo-errors.s @@ -0,0 +1,47 @@ +# RUN: not llvm-mc < %s -triple i686-windows-msvc -o /dev/null 2>&1 | FileCheck %s --implicit-check-not=error: + +.globl _foo +_foo: + .cv_fpo_proc + # CHECK: :[[@LINE-1]]:{{[0-9]+}}: error: expected symbol name + .cv_fpo_proc 1 + # CHECK: :[[@LINE-1]]:{{[0-9]+}}: error: expected symbol name + .cv_fpo_proc _foo extra + # CHECK: :[[@LINE-1]]:{{[0-9]+}}: error: expected parameter byte count + .cv_fpo_proc _foo 4 extra + # CHECK: :[[@LINE-1]]:{{[0-9]+}}: error: unexpected tokens in '.cv_fpo_proc' directive + .cv_fpo_proc _foo 4 + + pushl %ebp + .cv_fpo_pushreg 1 + # CHECK: :[[@LINE-1]]:{{[0-9]+}}: error: invalid register name in '.cv_fpo_pushreg' directive + .cv_fpo_pushreg ebp + + subl $20, %esp + .cv_fpo_stackalloc asdf + # CHECK: :[[@LINE-1]]:{{[0-9]+}}: error: expected offset in '.cv_fpo_stackalloc' directive + .cv_fpo_stackalloc 20 asdf + # CHECK: :[[@LINE-1]]:{{[0-9]+}}: error: unexpected tokens in '.cv_fpo_stackalloc' directive + .cv_fpo_stackalloc 20 + .cv_fpo_endprologue asdf + # CHECK: :[[@LINE-1]]:{{[0-9]+}}: error: unexpected tokens in '.cv_fpo_endprologue' directive + .cv_fpo_endprologue + + addl $20, %esp + popl %ebp + retl + .cv_fpo_endproc asdf + # CHECK: :[[@LINE-1]]:{{[0-9]+}}: error: unexpected tokens in '.cv_fpo_endproc' directive + .cv_fpo_endproc + + .section .debug$S,"dr" + .p2align 2 + .long 4 # Debug section magic + .cv_fpo_data + # CHECK: :[[@LINE-1]]:{{[0-9]+}}: error: expected symbol name + .cv_fpo_data 1 + # CHECK: :[[@LINE-1]]:{{[0-9]+}}: error: expected symbol name + .cv_fpo_data _foo asdf + # CHECK: :[[@LINE-1]]:{{[0-9]+}}: error: unexpected tokens in '.cv_fpo_data' directive + .cv_fpo_data _foo + .long 0 Index: llvm/test/MC/COFF/cv-fpo-setframe.s =================================================================== --- /dev/null +++ llvm/test/MC/COFF/cv-fpo-setframe.s @@ -0,0 +1,144 @@ +# RUN: llvm-mc -filetype=asm < %s -triple i686-windows-msvc | FileCheck %s --check-prefix=ASM +# RUN: llvm-mc -filetype=obj < %s -triple i686-windows-msvc | llvm-readobj -codeview | FileCheck %s --check-prefix=OBJ + +.globl _foo +_foo: + .cv_fpo_proc _foo 4 + pushl %ebp + .cv_fpo_pushreg %ebp + movl %ebp, %esp + .cv_fpo_setframe %ebp + pushl %ebx + .cv_fpo_pushreg %ebx + pushl %edi + .cv_fpo_pushreg %edi + pushl %esi + .cv_fpo_pushreg esi + subl $20, %esp + .cv_fpo_stackalloc 20 + .cv_fpo_endprologue + + # ASM: .cv_fpo_proc _foo 4 + # ASM: pushl %ebp + # ASM: .cv_fpo_pushreg %ebp + # ASM: movl %ebp, %esp + # ASM: .cv_fpo_setframe %ebp + # ASM: pushl %ebx + # ASM: .cv_fpo_pushreg %ebx + # ASM: pushl %edi + # ASM: .cv_fpo_pushreg %edi + # ASM: pushl %esi + # ASM: .cv_fpo_pushreg %esi + # ASM: subl $20, %esp + # ASM: .cv_fpo_stackalloc 20 + # ASM: .cv_fpo_endprologue + + # Clobbers + xorl %ebx, %ebx + xorl %edi, %edi + xorl %esi, %esi + # Use that stack memory + leal 4(%esp), %eax + movl %eax, (%esp) + calll _bar + + # ASM: calll _bar + + # Epilogue + # FIXME: Get FPO data for this once we get it for DWARF. + addl $20, %esp + popl %esi + popl %edi + popl %ebx + popl %ebp + retl + .cv_fpo_endproc + + # ASM: .cv_fpo_endproc + + .section .debug$S,"dr" + .p2align 2 + .long 4 # Debug section magic + .cv_fpo_data _foo + .cv_stringtable + + # ASM: .cv_fpo_data + +# OBJ: Subsection [ +# OBJ-NEXT: SubSectionType: FrameData (0xF5) +# OBJ-NEXT: SubSectionSize: +# OBJ-NEXT: LinkageName: _foo +# OBJ-NEXT: FrameData { +# OBJ-NEXT: RvaStart: 0x0 +# OBJ-NEXT: CodeSize: 0x23 +# OBJ-NEXT: LocalSize: 0x0 +# OBJ-NEXT: ParamsSize: 0x4 +# OBJ-NEXT: MaxStackSize: 0x0 +# OBJ-NEXT: FrameFunc: $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = +# OBJ-NEXT: PrologSize: 0x9 +# OBJ-NEXT: SavedRegsSize: 0x0 +# OBJ-NEXT: Flags [ (0x4) +# OBJ-NEXT: IsFunctionStart (0x4) +# OBJ-NEXT: ] +# OBJ-NEXT: } +# OBJ-NEXT: FrameData { +# OBJ-NEXT: RvaStart: 0x1 +# OBJ-NEXT: CodeSize: 0x22 +# OBJ-NEXT: LocalSize: 0x0 +# OBJ-NEXT: ParamsSize: 0x4 +# OBJ-NEXT: MaxStackSize: 0x0 +# OBJ-NEXT: FrameFunc: $T0 .raSearch = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = +# OBJ-NEXT: PrologSize: 0x8 +# OBJ-NEXT: SavedRegsSize: 0x4 +# OBJ-NEXT: Flags [ (0x0) +# OBJ-NEXT: ] +# OBJ-NEXT: } +# OBJ-NEXT: FrameData { +# OBJ-NEXT: RvaStart: 0x3 +# OBJ-NEXT: CodeSize: 0x20 +# OBJ-NEXT: LocalSize: 0x0 +# OBJ-NEXT: ParamsSize: 0x4 +# OBJ-NEXT: MaxStackSize: 0x0 +# OBJ-NEXT: FrameFunc: $T0 $ebp 4 + = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = +# OBJ-NEXT: PrologSize: 0x6 +# OBJ-NEXT: SavedRegsSize: 0x4 +# OBJ-NEXT: Flags [ (0x0) +# OBJ-NEXT: ] +# OBJ-NEXT: } +# OBJ-NEXT: FrameData { +# OBJ-NEXT: RvaStart: 0x4 +# OBJ-NEXT: CodeSize: 0x1F +# OBJ-NEXT: LocalSize: 0x0 +# OBJ-NEXT: ParamsSize: 0x4 +# OBJ-NEXT: MaxStackSize: 0x0 +# OBJ-NEXT: FrameFunc: $T0 $ebp 4 + = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = $ebx $T0 8 - ^ = +# OBJ-NEXT: PrologSize: 0x5 +# OBJ-NEXT: SavedRegsSize: 0x8 +# OBJ-NEXT: Flags [ (0x0) +# OBJ-NEXT: ] +# OBJ-NEXT: } +# OBJ-NEXT: FrameData { +# OBJ-NEXT: RvaStart: 0x5 +# OBJ-NEXT: CodeSize: 0x1E +# OBJ-NEXT: LocalSize: 0x0 +# OBJ-NEXT: ParamsSize: 0x4 +# OBJ-NEXT: MaxStackSize: 0x0 +# OBJ-NEXT: FrameFunc: $T0 $ebp 4 + = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = $ebx $T0 8 - ^ = $edi $T0 12 - ^ = +# OBJ-NEXT: PrologSize: 0x4 +# OBJ-NEXT: SavedRegsSize: 0xC +# OBJ-NEXT: Flags [ (0x0) +# OBJ-NEXT: ] +# OBJ-NEXT: } +# OBJ-NEXT: FrameData { +# OBJ-NEXT: RvaStart: 0x6 +# OBJ-NEXT: CodeSize: 0x1D +# OBJ-NEXT: LocalSize: 0x0 +# OBJ-NEXT: ParamsSize: 0x4 +# OBJ-NEXT: MaxStackSize: 0x0 +# OBJ-NEXT: FrameFunc: $T0 $ebp 4 + = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 4 - ^ = $ebx $T0 8 - ^ = $edi $T0 12 - ^ = $esi $T0 16 - ^ = +# OBJ-NEXT: PrologSize: 0x3 +# OBJ-NEXT: SavedRegsSize: 0x10 +# OBJ-NEXT: Flags [ (0x0) +# OBJ-NEXT: ] +# OBJ-NEXT: } +# OBJ-NOT: FrameData