106f32e7eSjoerg //===-- X86WinCOFFTargetStreamer.cpp ----------------------------*- C++ -*-===//
206f32e7eSjoerg //
306f32e7eSjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
406f32e7eSjoerg // See https://llvm.org/LICENSE.txt for license information.
506f32e7eSjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
606f32e7eSjoerg //
706f32e7eSjoerg //===----------------------------------------------------------------------===//
806f32e7eSjoerg
906f32e7eSjoerg #include "X86MCTargetDesc.h"
1006f32e7eSjoerg #include "X86TargetStreamer.h"
1106f32e7eSjoerg #include "llvm/DebugInfo/CodeView/CodeView.h"
1206f32e7eSjoerg #include "llvm/MC/MCCodeView.h"
1306f32e7eSjoerg #include "llvm/MC/MCContext.h"
1406f32e7eSjoerg #include "llvm/MC/MCInstPrinter.h"
1506f32e7eSjoerg #include "llvm/MC/MCRegisterInfo.h"
1606f32e7eSjoerg #include "llvm/MC/MCSubtargetInfo.h"
1706f32e7eSjoerg #include "llvm/Support/FormattedStream.h"
1806f32e7eSjoerg
1906f32e7eSjoerg using namespace llvm;
2006f32e7eSjoerg using namespace llvm::codeview;
2106f32e7eSjoerg
2206f32e7eSjoerg namespace {
2306f32e7eSjoerg /// Implements Windows x86-only directives for assembly emission.
2406f32e7eSjoerg class X86WinCOFFAsmTargetStreamer : public X86TargetStreamer {
2506f32e7eSjoerg formatted_raw_ostream &OS;
2606f32e7eSjoerg MCInstPrinter &InstPrinter;
2706f32e7eSjoerg
2806f32e7eSjoerg public:
X86WinCOFFAsmTargetStreamer(MCStreamer & S,formatted_raw_ostream & OS,MCInstPrinter & InstPrinter)2906f32e7eSjoerg X86WinCOFFAsmTargetStreamer(MCStreamer &S, formatted_raw_ostream &OS,
3006f32e7eSjoerg MCInstPrinter &InstPrinter)
3106f32e7eSjoerg : X86TargetStreamer(S), OS(OS), InstPrinter(InstPrinter) {}
3206f32e7eSjoerg
3306f32e7eSjoerg bool emitFPOProc(const MCSymbol *ProcSym, unsigned ParamsSize,
3406f32e7eSjoerg SMLoc L) override;
3506f32e7eSjoerg bool emitFPOEndPrologue(SMLoc L) override;
3606f32e7eSjoerg bool emitFPOEndProc(SMLoc L) override;
3706f32e7eSjoerg bool emitFPOData(const MCSymbol *ProcSym, SMLoc L) override;
3806f32e7eSjoerg bool emitFPOPushReg(unsigned Reg, SMLoc L) override;
3906f32e7eSjoerg bool emitFPOStackAlloc(unsigned StackAlloc, SMLoc L) override;
4006f32e7eSjoerg bool emitFPOStackAlign(unsigned Align, SMLoc L) override;
4106f32e7eSjoerg bool emitFPOSetFrame(unsigned Reg, SMLoc L) override;
4206f32e7eSjoerg };
4306f32e7eSjoerg
4406f32e7eSjoerg /// Represents a single FPO directive.
4506f32e7eSjoerg struct FPOInstruction {
4606f32e7eSjoerg MCSymbol *Label;
4706f32e7eSjoerg enum Operation {
4806f32e7eSjoerg PushReg,
4906f32e7eSjoerg StackAlloc,
5006f32e7eSjoerg StackAlign,
5106f32e7eSjoerg SetFrame,
5206f32e7eSjoerg } Op;
5306f32e7eSjoerg unsigned RegOrOffset;
5406f32e7eSjoerg };
5506f32e7eSjoerg
5606f32e7eSjoerg struct FPOData {
5706f32e7eSjoerg const MCSymbol *Function = nullptr;
5806f32e7eSjoerg MCSymbol *Begin = nullptr;
5906f32e7eSjoerg MCSymbol *PrologueEnd = nullptr;
6006f32e7eSjoerg MCSymbol *End = nullptr;
6106f32e7eSjoerg unsigned ParamsSize = 0;
6206f32e7eSjoerg
6306f32e7eSjoerg SmallVector<FPOInstruction, 5> Instructions;
6406f32e7eSjoerg };
6506f32e7eSjoerg
6606f32e7eSjoerg /// Implements Windows x86-only directives for object emission.
6706f32e7eSjoerg class X86WinCOFFTargetStreamer : public X86TargetStreamer {
6806f32e7eSjoerg /// Map from function symbol to its FPO data.
6906f32e7eSjoerg DenseMap<const MCSymbol *, std::unique_ptr<FPOData>> AllFPOData;
7006f32e7eSjoerg
7106f32e7eSjoerg /// Current FPO data created by .cv_fpo_proc.
7206f32e7eSjoerg std::unique_ptr<FPOData> CurFPOData;
7306f32e7eSjoerg
haveOpenFPOData()7406f32e7eSjoerg bool haveOpenFPOData() { return !!CurFPOData; }
7506f32e7eSjoerg
7606f32e7eSjoerg /// Diagnoses an error at L if we are not in an FPO prologue. Return true on
7706f32e7eSjoerg /// error.
7806f32e7eSjoerg bool checkInFPOPrologue(SMLoc L);
7906f32e7eSjoerg
8006f32e7eSjoerg MCSymbol *emitFPOLabel();
8106f32e7eSjoerg
getContext()8206f32e7eSjoerg MCContext &getContext() { return getStreamer().getContext(); }
8306f32e7eSjoerg
8406f32e7eSjoerg public:
X86WinCOFFTargetStreamer(MCStreamer & S)8506f32e7eSjoerg X86WinCOFFTargetStreamer(MCStreamer &S) : X86TargetStreamer(S) {}
8606f32e7eSjoerg
8706f32e7eSjoerg bool emitFPOProc(const MCSymbol *ProcSym, unsigned ParamsSize,
8806f32e7eSjoerg SMLoc L) override;
8906f32e7eSjoerg bool emitFPOEndPrologue(SMLoc L) override;
9006f32e7eSjoerg bool emitFPOEndProc(SMLoc L) override;
9106f32e7eSjoerg bool emitFPOData(const MCSymbol *ProcSym, SMLoc L) override;
9206f32e7eSjoerg bool emitFPOPushReg(unsigned Reg, SMLoc L) override;
9306f32e7eSjoerg bool emitFPOStackAlloc(unsigned StackAlloc, SMLoc L) override;
9406f32e7eSjoerg bool emitFPOStackAlign(unsigned Align, SMLoc L) override;
9506f32e7eSjoerg bool emitFPOSetFrame(unsigned Reg, SMLoc L) override;
9606f32e7eSjoerg };
9706f32e7eSjoerg } // end namespace
9806f32e7eSjoerg
emitFPOProc(const MCSymbol * ProcSym,unsigned ParamsSize,SMLoc L)9906f32e7eSjoerg bool X86WinCOFFAsmTargetStreamer::emitFPOProc(const MCSymbol *ProcSym,
10006f32e7eSjoerg unsigned ParamsSize, SMLoc L) {
10106f32e7eSjoerg OS << "\t.cv_fpo_proc\t";
10206f32e7eSjoerg ProcSym->print(OS, getStreamer().getContext().getAsmInfo());
10306f32e7eSjoerg OS << ' ' << ParamsSize << '\n';
10406f32e7eSjoerg return false;
10506f32e7eSjoerg }
10606f32e7eSjoerg
emitFPOEndPrologue(SMLoc L)10706f32e7eSjoerg bool X86WinCOFFAsmTargetStreamer::emitFPOEndPrologue(SMLoc L) {
10806f32e7eSjoerg OS << "\t.cv_fpo_endprologue\n";
10906f32e7eSjoerg return false;
11006f32e7eSjoerg }
11106f32e7eSjoerg
emitFPOEndProc(SMLoc L)11206f32e7eSjoerg bool X86WinCOFFAsmTargetStreamer::emitFPOEndProc(SMLoc L) {
11306f32e7eSjoerg OS << "\t.cv_fpo_endproc\n";
11406f32e7eSjoerg return false;
11506f32e7eSjoerg }
11606f32e7eSjoerg
emitFPOData(const MCSymbol * ProcSym,SMLoc L)11706f32e7eSjoerg bool X86WinCOFFAsmTargetStreamer::emitFPOData(const MCSymbol *ProcSym,
11806f32e7eSjoerg SMLoc L) {
11906f32e7eSjoerg OS << "\t.cv_fpo_data\t";
12006f32e7eSjoerg ProcSym->print(OS, getStreamer().getContext().getAsmInfo());
12106f32e7eSjoerg OS << '\n';
12206f32e7eSjoerg return false;
12306f32e7eSjoerg }
12406f32e7eSjoerg
emitFPOPushReg(unsigned Reg,SMLoc L)12506f32e7eSjoerg bool X86WinCOFFAsmTargetStreamer::emitFPOPushReg(unsigned Reg, SMLoc L) {
12606f32e7eSjoerg OS << "\t.cv_fpo_pushreg\t";
12706f32e7eSjoerg InstPrinter.printRegName(OS, Reg);
12806f32e7eSjoerg OS << '\n';
12906f32e7eSjoerg return false;
13006f32e7eSjoerg }
13106f32e7eSjoerg
emitFPOStackAlloc(unsigned StackAlloc,SMLoc L)13206f32e7eSjoerg bool X86WinCOFFAsmTargetStreamer::emitFPOStackAlloc(unsigned StackAlloc,
13306f32e7eSjoerg SMLoc L) {
13406f32e7eSjoerg OS << "\t.cv_fpo_stackalloc\t" << StackAlloc << '\n';
13506f32e7eSjoerg return false;
13606f32e7eSjoerg }
13706f32e7eSjoerg
emitFPOStackAlign(unsigned Align,SMLoc L)13806f32e7eSjoerg bool X86WinCOFFAsmTargetStreamer::emitFPOStackAlign(unsigned Align, SMLoc L) {
13906f32e7eSjoerg OS << "\t.cv_fpo_stackalign\t" << Align << '\n';
14006f32e7eSjoerg return false;
14106f32e7eSjoerg }
14206f32e7eSjoerg
emitFPOSetFrame(unsigned Reg,SMLoc L)14306f32e7eSjoerg bool X86WinCOFFAsmTargetStreamer::emitFPOSetFrame(unsigned Reg, SMLoc L) {
14406f32e7eSjoerg OS << "\t.cv_fpo_setframe\t";
14506f32e7eSjoerg InstPrinter.printRegName(OS, Reg);
14606f32e7eSjoerg OS << '\n';
14706f32e7eSjoerg return false;
14806f32e7eSjoerg }
14906f32e7eSjoerg
checkInFPOPrologue(SMLoc L)15006f32e7eSjoerg bool X86WinCOFFTargetStreamer::checkInFPOPrologue(SMLoc L) {
15106f32e7eSjoerg if (!haveOpenFPOData() || CurFPOData->PrologueEnd) {
15206f32e7eSjoerg getContext().reportError(
15306f32e7eSjoerg L,
15406f32e7eSjoerg "directive must appear between .cv_fpo_proc and .cv_fpo_endprologue");
15506f32e7eSjoerg return true;
15606f32e7eSjoerg }
15706f32e7eSjoerg return false;
15806f32e7eSjoerg }
15906f32e7eSjoerg
emitFPOLabel()16006f32e7eSjoerg MCSymbol *X86WinCOFFTargetStreamer::emitFPOLabel() {
16106f32e7eSjoerg MCSymbol *Label = getContext().createTempSymbol("cfi", true);
162*da58b97aSjoerg getStreamer().emitLabel(Label);
16306f32e7eSjoerg return Label;
16406f32e7eSjoerg }
16506f32e7eSjoerg
emitFPOProc(const MCSymbol * ProcSym,unsigned ParamsSize,SMLoc L)16606f32e7eSjoerg bool X86WinCOFFTargetStreamer::emitFPOProc(const MCSymbol *ProcSym,
16706f32e7eSjoerg unsigned ParamsSize, SMLoc L) {
16806f32e7eSjoerg if (haveOpenFPOData()) {
16906f32e7eSjoerg getContext().reportError(
17006f32e7eSjoerg L, "opening new .cv_fpo_proc before closing previous frame");
17106f32e7eSjoerg return true;
17206f32e7eSjoerg }
17306f32e7eSjoerg CurFPOData = std::make_unique<FPOData>();
17406f32e7eSjoerg CurFPOData->Function = ProcSym;
17506f32e7eSjoerg CurFPOData->Begin = emitFPOLabel();
17606f32e7eSjoerg CurFPOData->ParamsSize = ParamsSize;
17706f32e7eSjoerg return false;
17806f32e7eSjoerg }
17906f32e7eSjoerg
emitFPOEndProc(SMLoc L)18006f32e7eSjoerg bool X86WinCOFFTargetStreamer::emitFPOEndProc(SMLoc L) {
18106f32e7eSjoerg if (!haveOpenFPOData()) {
18206f32e7eSjoerg getContext().reportError(L, ".cv_fpo_endproc must appear after .cv_proc");
18306f32e7eSjoerg return true;
18406f32e7eSjoerg }
18506f32e7eSjoerg if (!CurFPOData->PrologueEnd) {
18606f32e7eSjoerg // Complain if there were prologue setup instructions but no end prologue.
18706f32e7eSjoerg if (!CurFPOData->Instructions.empty()) {
18806f32e7eSjoerg getContext().reportError(L, "missing .cv_fpo_endprologue");
18906f32e7eSjoerg CurFPOData->Instructions.clear();
19006f32e7eSjoerg }
19106f32e7eSjoerg
19206f32e7eSjoerg // Claim there is a zero-length prologue to make the label math work out
19306f32e7eSjoerg // later.
19406f32e7eSjoerg CurFPOData->PrologueEnd = CurFPOData->Begin;
19506f32e7eSjoerg }
19606f32e7eSjoerg
19706f32e7eSjoerg CurFPOData->End = emitFPOLabel();
19806f32e7eSjoerg const MCSymbol *Fn = CurFPOData->Function;
19906f32e7eSjoerg AllFPOData.insert({Fn, std::move(CurFPOData)});
20006f32e7eSjoerg return false;
20106f32e7eSjoerg }
20206f32e7eSjoerg
emitFPOSetFrame(unsigned Reg,SMLoc L)20306f32e7eSjoerg bool X86WinCOFFTargetStreamer::emitFPOSetFrame(unsigned Reg, SMLoc L) {
20406f32e7eSjoerg if (checkInFPOPrologue(L))
20506f32e7eSjoerg return true;
20606f32e7eSjoerg FPOInstruction Inst;
20706f32e7eSjoerg Inst.Label = emitFPOLabel();
20806f32e7eSjoerg Inst.Op = FPOInstruction::SetFrame;
20906f32e7eSjoerg Inst.RegOrOffset = Reg;
21006f32e7eSjoerg CurFPOData->Instructions.push_back(Inst);
21106f32e7eSjoerg return false;
21206f32e7eSjoerg }
21306f32e7eSjoerg
emitFPOPushReg(unsigned Reg,SMLoc L)21406f32e7eSjoerg bool X86WinCOFFTargetStreamer::emitFPOPushReg(unsigned Reg, SMLoc L) {
21506f32e7eSjoerg if (checkInFPOPrologue(L))
21606f32e7eSjoerg return true;
21706f32e7eSjoerg FPOInstruction Inst;
21806f32e7eSjoerg Inst.Label = emitFPOLabel();
21906f32e7eSjoerg Inst.Op = FPOInstruction::PushReg;
22006f32e7eSjoerg Inst.RegOrOffset = Reg;
22106f32e7eSjoerg CurFPOData->Instructions.push_back(Inst);
22206f32e7eSjoerg return false;
22306f32e7eSjoerg }
22406f32e7eSjoerg
emitFPOStackAlloc(unsigned StackAlloc,SMLoc L)22506f32e7eSjoerg bool X86WinCOFFTargetStreamer::emitFPOStackAlloc(unsigned StackAlloc, SMLoc L) {
22606f32e7eSjoerg if (checkInFPOPrologue(L))
22706f32e7eSjoerg return true;
22806f32e7eSjoerg FPOInstruction Inst;
22906f32e7eSjoerg Inst.Label = emitFPOLabel();
23006f32e7eSjoerg Inst.Op = FPOInstruction::StackAlloc;
23106f32e7eSjoerg Inst.RegOrOffset = StackAlloc;
23206f32e7eSjoerg CurFPOData->Instructions.push_back(Inst);
23306f32e7eSjoerg return false;
23406f32e7eSjoerg }
23506f32e7eSjoerg
emitFPOStackAlign(unsigned Align,SMLoc L)23606f32e7eSjoerg bool X86WinCOFFTargetStreamer::emitFPOStackAlign(unsigned Align, SMLoc L) {
23706f32e7eSjoerg if (checkInFPOPrologue(L))
23806f32e7eSjoerg return true;
23906f32e7eSjoerg if (!llvm::any_of(CurFPOData->Instructions, [](const FPOInstruction &Inst) {
24006f32e7eSjoerg return Inst.Op == FPOInstruction::SetFrame;
24106f32e7eSjoerg })) {
24206f32e7eSjoerg getContext().reportError(
24306f32e7eSjoerg L, "a frame register must be established before aligning the stack");
24406f32e7eSjoerg return true;
24506f32e7eSjoerg }
24606f32e7eSjoerg FPOInstruction Inst;
24706f32e7eSjoerg Inst.Label = emitFPOLabel();
24806f32e7eSjoerg Inst.Op = FPOInstruction::StackAlign;
24906f32e7eSjoerg Inst.RegOrOffset = Align;
25006f32e7eSjoerg CurFPOData->Instructions.push_back(Inst);
25106f32e7eSjoerg return false;
25206f32e7eSjoerg }
25306f32e7eSjoerg
emitFPOEndPrologue(SMLoc L)25406f32e7eSjoerg bool X86WinCOFFTargetStreamer::emitFPOEndPrologue(SMLoc L) {
25506f32e7eSjoerg if (checkInFPOPrologue(L))
25606f32e7eSjoerg return true;
25706f32e7eSjoerg CurFPOData->PrologueEnd = emitFPOLabel();
25806f32e7eSjoerg return false;
25906f32e7eSjoerg }
26006f32e7eSjoerg
26106f32e7eSjoerg namespace {
26206f32e7eSjoerg struct RegSaveOffset {
RegSaveOffset__anon1b24917d0311::RegSaveOffset26306f32e7eSjoerg RegSaveOffset(unsigned Reg, unsigned Offset) : Reg(Reg), Offset(Offset) {}
26406f32e7eSjoerg
26506f32e7eSjoerg unsigned Reg = 0;
26606f32e7eSjoerg unsigned Offset = 0;
26706f32e7eSjoerg };
26806f32e7eSjoerg
26906f32e7eSjoerg struct FPOStateMachine {
FPOStateMachine__anon1b24917d0311::FPOStateMachine27006f32e7eSjoerg explicit FPOStateMachine(const FPOData *FPO) : FPO(FPO) {}
27106f32e7eSjoerg
27206f32e7eSjoerg const FPOData *FPO = nullptr;
27306f32e7eSjoerg unsigned FrameReg = 0;
27406f32e7eSjoerg unsigned FrameRegOff = 0;
27506f32e7eSjoerg unsigned CurOffset = 0;
27606f32e7eSjoerg unsigned LocalSize = 0;
27706f32e7eSjoerg unsigned SavedRegSize = 0;
27806f32e7eSjoerg unsigned StackOffsetBeforeAlign = 0;
27906f32e7eSjoerg unsigned StackAlign = 0;
28006f32e7eSjoerg unsigned Flags = 0; // FIXME: Set HasSEH / HasEH.
28106f32e7eSjoerg
28206f32e7eSjoerg SmallString<128> FrameFunc;
28306f32e7eSjoerg
28406f32e7eSjoerg SmallVector<RegSaveOffset, 4> RegSaveOffsets;
28506f32e7eSjoerg
28606f32e7eSjoerg void emitFrameDataRecord(MCStreamer &OS, MCSymbol *Label);
28706f32e7eSjoerg };
28806f32e7eSjoerg } // end namespace
28906f32e7eSjoerg
printFPOReg(const MCRegisterInfo * MRI,unsigned LLVMReg)29006f32e7eSjoerg static Printable printFPOReg(const MCRegisterInfo *MRI, unsigned LLVMReg) {
29106f32e7eSjoerg return Printable([MRI, LLVMReg](raw_ostream &OS) {
29206f32e7eSjoerg switch (LLVMReg) {
29306f32e7eSjoerg // MSVC only seems to emit symbolic register names for EIP, EBP, and ESP,
29406f32e7eSjoerg // but the format seems to support more than that, so we emit them.
29506f32e7eSjoerg case X86::EAX: OS << "$eax"; break;
29606f32e7eSjoerg case X86::EBX: OS << "$ebx"; break;
29706f32e7eSjoerg case X86::ECX: OS << "$ecx"; break;
29806f32e7eSjoerg case X86::EDX: OS << "$edx"; break;
29906f32e7eSjoerg case X86::EDI: OS << "$edi"; break;
30006f32e7eSjoerg case X86::ESI: OS << "$esi"; break;
30106f32e7eSjoerg case X86::ESP: OS << "$esp"; break;
30206f32e7eSjoerg case X86::EBP: OS << "$ebp"; break;
30306f32e7eSjoerg case X86::EIP: OS << "$eip"; break;
30406f32e7eSjoerg // Otherwise, get the codeview register number and print $N.
30506f32e7eSjoerg default:
30606f32e7eSjoerg OS << '$' << MRI->getCodeViewRegNum(LLVMReg);
30706f32e7eSjoerg break;
30806f32e7eSjoerg }
30906f32e7eSjoerg });
31006f32e7eSjoerg }
31106f32e7eSjoerg
emitFrameDataRecord(MCStreamer & OS,MCSymbol * Label)31206f32e7eSjoerg void FPOStateMachine::emitFrameDataRecord(MCStreamer &OS, MCSymbol *Label) {
31306f32e7eSjoerg unsigned CurFlags = Flags;
31406f32e7eSjoerg if (Label == FPO->Begin)
31506f32e7eSjoerg CurFlags |= FrameData::IsFunctionStart;
31606f32e7eSjoerg
31706f32e7eSjoerg // Compute the new FrameFunc string.
31806f32e7eSjoerg FrameFunc.clear();
31906f32e7eSjoerg raw_svector_ostream FuncOS(FrameFunc);
32006f32e7eSjoerg const MCRegisterInfo *MRI = OS.getContext().getRegisterInfo();
32106f32e7eSjoerg assert((StackAlign == 0 || FrameReg != 0) &&
32206f32e7eSjoerg "cannot align stack without frame reg");
32306f32e7eSjoerg StringRef CFAVar = StackAlign == 0 ? "$T0" : "$T1";
32406f32e7eSjoerg
32506f32e7eSjoerg if (FrameReg) {
32606f32e7eSjoerg // CFA is FrameReg + FrameRegOff.
32706f32e7eSjoerg FuncOS << CFAVar << ' ' << printFPOReg(MRI, FrameReg) << ' ' << FrameRegOff
32806f32e7eSjoerg << " + = ";
32906f32e7eSjoerg
33006f32e7eSjoerg // Assign $T0, the VFRAME register, the value of ESP after it is aligned.
33106f32e7eSjoerg // Starting from the CFA, we subtract the size of all pushed registers, and
33206f32e7eSjoerg // align the result. While we don't store any CSRs in this area, $T0 is used
33306f32e7eSjoerg // by S_DEFRANGE_FRAMEPOINTER_REL records to find local variables.
33406f32e7eSjoerg if (StackAlign) {
33506f32e7eSjoerg FuncOS << "$T0 " << CFAVar << ' ' << StackOffsetBeforeAlign << " - "
33606f32e7eSjoerg << StackAlign << " @ = ";
33706f32e7eSjoerg }
33806f32e7eSjoerg } else {
33906f32e7eSjoerg // The address of return address is ESP + CurOffset, but we use .raSearch to
34006f32e7eSjoerg // match MSVC. This seems to ask the debugger to subtract some combination
34106f32e7eSjoerg // of LocalSize and SavedRegSize from ESP and grovel around in that memory
34206f32e7eSjoerg // to find the address of a plausible return address.
34306f32e7eSjoerg FuncOS << CFAVar << " .raSearch = ";
34406f32e7eSjoerg }
34506f32e7eSjoerg
34606f32e7eSjoerg // Caller's $eip should be dereferenced CFA, and $esp should be CFA plus 4.
34706f32e7eSjoerg FuncOS << "$eip " << CFAVar << " ^ = ";
34806f32e7eSjoerg FuncOS << "$esp " << CFAVar << " 4 + = ";
34906f32e7eSjoerg
35006f32e7eSjoerg // Each saved register is stored at an unchanging negative CFA offset.
35106f32e7eSjoerg for (RegSaveOffset RO : RegSaveOffsets)
35206f32e7eSjoerg FuncOS << printFPOReg(MRI, RO.Reg) << ' ' << CFAVar << ' ' << RO.Offset
35306f32e7eSjoerg << " - ^ = ";
35406f32e7eSjoerg
35506f32e7eSjoerg // Add it to the CV string table.
35606f32e7eSjoerg CodeViewContext &CVCtx = OS.getContext().getCVContext();
35706f32e7eSjoerg unsigned FrameFuncStrTabOff = CVCtx.addToStringTable(FuncOS.str()).second;
35806f32e7eSjoerg
35906f32e7eSjoerg // MSVC has only ever been observed to emit a MaxStackSize of zero.
36006f32e7eSjoerg unsigned MaxStackSize = 0;
36106f32e7eSjoerg
36206f32e7eSjoerg // The FrameData record format is:
36306f32e7eSjoerg // ulittle32_t RvaStart;
36406f32e7eSjoerg // ulittle32_t CodeSize;
36506f32e7eSjoerg // ulittle32_t LocalSize;
36606f32e7eSjoerg // ulittle32_t ParamsSize;
36706f32e7eSjoerg // ulittle32_t MaxStackSize;
36806f32e7eSjoerg // ulittle32_t FrameFunc; // String table offset
36906f32e7eSjoerg // ulittle16_t PrologSize;
37006f32e7eSjoerg // ulittle16_t SavedRegsSize;
37106f32e7eSjoerg // ulittle32_t Flags;
37206f32e7eSjoerg
37306f32e7eSjoerg OS.emitAbsoluteSymbolDiff(Label, FPO->Begin, 4); // RvaStart
37406f32e7eSjoerg OS.emitAbsoluteSymbolDiff(FPO->End, Label, 4); // CodeSize
375*da58b97aSjoerg OS.emitInt32(LocalSize);
376*da58b97aSjoerg OS.emitInt32(FPO->ParamsSize);
377*da58b97aSjoerg OS.emitInt32(MaxStackSize);
378*da58b97aSjoerg OS.emitInt32(FrameFuncStrTabOff); // FrameFunc
37906f32e7eSjoerg OS.emitAbsoluteSymbolDiff(FPO->PrologueEnd, Label, 2);
380*da58b97aSjoerg OS.emitInt16(SavedRegSize);
381*da58b97aSjoerg OS.emitInt32(CurFlags);
38206f32e7eSjoerg }
38306f32e7eSjoerg
38406f32e7eSjoerg /// Compute and emit the real CodeView FrameData subsection.
emitFPOData(const MCSymbol * ProcSym,SMLoc L)38506f32e7eSjoerg bool X86WinCOFFTargetStreamer::emitFPOData(const MCSymbol *ProcSym, SMLoc L) {
38606f32e7eSjoerg MCStreamer &OS = getStreamer();
38706f32e7eSjoerg MCContext &Ctx = OS.getContext();
38806f32e7eSjoerg
38906f32e7eSjoerg auto I = AllFPOData.find(ProcSym);
39006f32e7eSjoerg if (I == AllFPOData.end()) {
39106f32e7eSjoerg Ctx.reportError(L, Twine("no FPO data found for symbol ") +
39206f32e7eSjoerg ProcSym->getName());
39306f32e7eSjoerg return true;
39406f32e7eSjoerg }
39506f32e7eSjoerg const FPOData *FPO = I->second.get();
39606f32e7eSjoerg assert(FPO->Begin && FPO->End && FPO->PrologueEnd && "missing FPO label");
39706f32e7eSjoerg
39806f32e7eSjoerg MCSymbol *FrameBegin = Ctx.createTempSymbol(),
39906f32e7eSjoerg *FrameEnd = Ctx.createTempSymbol();
40006f32e7eSjoerg
401*da58b97aSjoerg OS.emitInt32(unsigned(DebugSubsectionKind::FrameData));
40206f32e7eSjoerg OS.emitAbsoluteSymbolDiff(FrameEnd, FrameBegin, 4);
403*da58b97aSjoerg OS.emitLabel(FrameBegin);
40406f32e7eSjoerg
40506f32e7eSjoerg // Start with the RVA of the function in question.
406*da58b97aSjoerg OS.emitValue(MCSymbolRefExpr::create(FPO->Function,
40706f32e7eSjoerg MCSymbolRefExpr::VK_COFF_IMGREL32, Ctx),
40806f32e7eSjoerg 4);
40906f32e7eSjoerg
41006f32e7eSjoerg // Emit a sequence of FrameData records.
41106f32e7eSjoerg FPOStateMachine FSM(FPO);
41206f32e7eSjoerg
41306f32e7eSjoerg FSM.emitFrameDataRecord(OS, FPO->Begin);
41406f32e7eSjoerg for (const FPOInstruction &Inst : FPO->Instructions) {
41506f32e7eSjoerg switch (Inst.Op) {
41606f32e7eSjoerg case FPOInstruction::PushReg:
41706f32e7eSjoerg FSM.CurOffset += 4;
41806f32e7eSjoerg FSM.SavedRegSize += 4;
41906f32e7eSjoerg FSM.RegSaveOffsets.push_back({Inst.RegOrOffset, FSM.CurOffset});
42006f32e7eSjoerg break;
42106f32e7eSjoerg case FPOInstruction::SetFrame:
42206f32e7eSjoerg FSM.FrameReg = Inst.RegOrOffset;
42306f32e7eSjoerg FSM.FrameRegOff = FSM.CurOffset;
42406f32e7eSjoerg break;
42506f32e7eSjoerg case FPOInstruction::StackAlign:
42606f32e7eSjoerg FSM.StackOffsetBeforeAlign = FSM.CurOffset;
42706f32e7eSjoerg FSM.StackAlign = Inst.RegOrOffset;
42806f32e7eSjoerg break;
42906f32e7eSjoerg case FPOInstruction::StackAlloc:
43006f32e7eSjoerg FSM.CurOffset += Inst.RegOrOffset;
43106f32e7eSjoerg FSM.LocalSize += Inst.RegOrOffset;
43206f32e7eSjoerg // No need to emit FrameData for stack allocations with a frame pointer.
43306f32e7eSjoerg if (FSM.FrameReg)
43406f32e7eSjoerg continue;
43506f32e7eSjoerg break;
43606f32e7eSjoerg }
43706f32e7eSjoerg FSM.emitFrameDataRecord(OS, Inst.Label);
43806f32e7eSjoerg }
43906f32e7eSjoerg
440*da58b97aSjoerg OS.emitValueToAlignment(4, 0);
441*da58b97aSjoerg OS.emitLabel(FrameEnd);
44206f32e7eSjoerg return false;
44306f32e7eSjoerg }
44406f32e7eSjoerg
createX86AsmTargetStreamer(MCStreamer & S,formatted_raw_ostream & OS,MCInstPrinter * InstPrinter,bool IsVerboseAsm)44506f32e7eSjoerg MCTargetStreamer *llvm::createX86AsmTargetStreamer(MCStreamer &S,
44606f32e7eSjoerg formatted_raw_ostream &OS,
44706f32e7eSjoerg MCInstPrinter *InstPrinter,
44806f32e7eSjoerg bool IsVerboseAsm) {
44906f32e7eSjoerg // FIXME: This makes it so we textually assemble COFF directives on ELF.
45006f32e7eSjoerg // That's kind of nonsensical.
45106f32e7eSjoerg return new X86WinCOFFAsmTargetStreamer(S, OS, *InstPrinter);
45206f32e7eSjoerg }
45306f32e7eSjoerg
45406f32e7eSjoerg MCTargetStreamer *
createX86ObjectTargetStreamer(MCStreamer & S,const MCSubtargetInfo & STI)45506f32e7eSjoerg llvm::createX86ObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI) {
45606f32e7eSjoerg // No need to register a target streamer.
45706f32e7eSjoerg if (!STI.getTargetTriple().isOSBinFormatCOFF())
45806f32e7eSjoerg return nullptr;
45906f32e7eSjoerg // Registers itself to the MCStreamer.
46006f32e7eSjoerg return new X86WinCOFFTargetStreamer(S);
46106f32e7eSjoerg }
462