1 //===-- X86WinCOFFTargetStreamer.cpp ----------------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "X86MCTargetDesc.h"
10 #include "X86TargetStreamer.h"
11 #include "llvm/DebugInfo/CodeView/CodeView.h"
12 #include "llvm/MC/MCCodeView.h"
13 #include "llvm/MC/MCContext.h"
14 #include "llvm/MC/MCInstPrinter.h"
15 #include "llvm/MC/MCRegisterInfo.h"
16 #include "llvm/MC/MCSubtargetInfo.h"
17 #include "llvm/Support/FormattedStream.h"
20 using namespace llvm::codeview
;
23 /// Implements Windows x86-only directives for assembly emission.
24 class X86WinCOFFAsmTargetStreamer
: public X86TargetStreamer
{
25 formatted_raw_ostream
&OS
;
26 MCInstPrinter
&InstPrinter
;
29 X86WinCOFFAsmTargetStreamer(MCStreamer
&S
, formatted_raw_ostream
&OS
,
30 MCInstPrinter
&InstPrinter
)
31 : X86TargetStreamer(S
), OS(OS
), InstPrinter(InstPrinter
) {}
33 bool emitFPOProc(const MCSymbol
*ProcSym
, unsigned ParamsSize
,
35 bool emitFPOEndPrologue(SMLoc L
) override
;
36 bool emitFPOEndProc(SMLoc L
) override
;
37 bool emitFPOData(const MCSymbol
*ProcSym
, SMLoc L
) override
;
38 bool emitFPOPushReg(unsigned Reg
, SMLoc L
) override
;
39 bool emitFPOStackAlloc(unsigned StackAlloc
, SMLoc L
) override
;
40 bool emitFPOStackAlign(unsigned Align
, SMLoc L
) override
;
41 bool emitFPOSetFrame(unsigned Reg
, SMLoc L
) override
;
44 /// Represents a single FPO directive.
45 struct FPOInstruction
{
57 const MCSymbol
*Function
= nullptr;
58 MCSymbol
*Begin
= nullptr;
59 MCSymbol
*PrologueEnd
= nullptr;
60 MCSymbol
*End
= nullptr;
61 unsigned ParamsSize
= 0;
63 SmallVector
<FPOInstruction
, 5> Instructions
;
66 /// Implements Windows x86-only directives for object emission.
67 class X86WinCOFFTargetStreamer
: public X86TargetStreamer
{
68 /// Map from function symbol to its FPO data.
69 DenseMap
<const MCSymbol
*, std::unique_ptr
<FPOData
>> AllFPOData
;
71 /// Current FPO data created by .cv_fpo_proc.
72 std::unique_ptr
<FPOData
> CurFPOData
;
74 bool haveOpenFPOData() { return !!CurFPOData
; }
76 /// Diagnoses an error at L if we are not in an FPO prologue. Return true on
78 bool checkInFPOPrologue(SMLoc L
);
80 MCSymbol
*emitFPOLabel();
82 MCContext
&getContext() { return getStreamer().getContext(); }
85 X86WinCOFFTargetStreamer(MCStreamer
&S
) : X86TargetStreamer(S
) {}
87 bool emitFPOProc(const MCSymbol
*ProcSym
, unsigned ParamsSize
,
89 bool emitFPOEndPrologue(SMLoc L
) override
;
90 bool emitFPOEndProc(SMLoc L
) override
;
91 bool emitFPOData(const MCSymbol
*ProcSym
, SMLoc L
) override
;
92 bool emitFPOPushReg(unsigned Reg
, SMLoc L
) override
;
93 bool emitFPOStackAlloc(unsigned StackAlloc
, SMLoc L
) override
;
94 bool emitFPOStackAlign(unsigned Align
, SMLoc L
) override
;
95 bool emitFPOSetFrame(unsigned Reg
, SMLoc L
) override
;
99 bool X86WinCOFFAsmTargetStreamer::emitFPOProc(const MCSymbol
*ProcSym
,
100 unsigned ParamsSize
, SMLoc L
) {
101 OS
<< "\t.cv_fpo_proc\t";
102 ProcSym
->print(OS
, getStreamer().getContext().getAsmInfo());
103 OS
<< ' ' << ParamsSize
<< '\n';
107 bool X86WinCOFFAsmTargetStreamer::emitFPOEndPrologue(SMLoc L
) {
108 OS
<< "\t.cv_fpo_endprologue\n";
112 bool X86WinCOFFAsmTargetStreamer::emitFPOEndProc(SMLoc L
) {
113 OS
<< "\t.cv_fpo_endproc\n";
117 bool X86WinCOFFAsmTargetStreamer::emitFPOData(const MCSymbol
*ProcSym
,
119 OS
<< "\t.cv_fpo_data\t";
120 ProcSym
->print(OS
, getStreamer().getContext().getAsmInfo());
125 bool X86WinCOFFAsmTargetStreamer::emitFPOPushReg(unsigned Reg
, SMLoc L
) {
126 OS
<< "\t.cv_fpo_pushreg\t";
127 InstPrinter
.printRegName(OS
, Reg
);
132 bool X86WinCOFFAsmTargetStreamer::emitFPOStackAlloc(unsigned StackAlloc
,
134 OS
<< "\t.cv_fpo_stackalloc\t" << StackAlloc
<< '\n';
138 bool X86WinCOFFAsmTargetStreamer::emitFPOStackAlign(unsigned Align
, SMLoc L
) {
139 OS
<< "\t.cv_fpo_stackalign\t" << Align
<< '\n';
143 bool X86WinCOFFAsmTargetStreamer::emitFPOSetFrame(unsigned Reg
, SMLoc L
) {
144 OS
<< "\t.cv_fpo_setframe\t";
145 InstPrinter
.printRegName(OS
, Reg
);
150 bool X86WinCOFFTargetStreamer::checkInFPOPrologue(SMLoc L
) {
151 if (!haveOpenFPOData() || CurFPOData
->PrologueEnd
) {
152 getContext().reportError(
154 "directive must appear between .cv_fpo_proc and .cv_fpo_endprologue");
160 MCSymbol
*X86WinCOFFTargetStreamer::emitFPOLabel() {
161 MCSymbol
*Label
= getContext().createTempSymbol("cfi", true);
162 getStreamer().EmitLabel(Label
);
166 bool X86WinCOFFTargetStreamer::emitFPOProc(const MCSymbol
*ProcSym
,
167 unsigned ParamsSize
, SMLoc L
) {
168 if (haveOpenFPOData()) {
169 getContext().reportError(
170 L
, "opening new .cv_fpo_proc before closing previous frame");
173 CurFPOData
= std::make_unique
<FPOData
>();
174 CurFPOData
->Function
= ProcSym
;
175 CurFPOData
->Begin
= emitFPOLabel();
176 CurFPOData
->ParamsSize
= ParamsSize
;
180 bool X86WinCOFFTargetStreamer::emitFPOEndProc(SMLoc L
) {
181 if (!haveOpenFPOData()) {
182 getContext().reportError(L
, ".cv_fpo_endproc must appear after .cv_proc");
185 if (!CurFPOData
->PrologueEnd
) {
186 // Complain if there were prologue setup instructions but no end prologue.
187 if (!CurFPOData
->Instructions
.empty()) {
188 getContext().reportError(L
, "missing .cv_fpo_endprologue");
189 CurFPOData
->Instructions
.clear();
192 // Claim there is a zero-length prologue to make the label math work out
194 CurFPOData
->PrologueEnd
= CurFPOData
->Begin
;
197 CurFPOData
->End
= emitFPOLabel();
198 const MCSymbol
*Fn
= CurFPOData
->Function
;
199 AllFPOData
.insert({Fn
, std::move(CurFPOData
)});
203 bool X86WinCOFFTargetStreamer::emitFPOSetFrame(unsigned Reg
, SMLoc L
) {
204 if (checkInFPOPrologue(L
))
207 Inst
.Label
= emitFPOLabel();
208 Inst
.Op
= FPOInstruction::SetFrame
;
209 Inst
.RegOrOffset
= Reg
;
210 CurFPOData
->Instructions
.push_back(Inst
);
214 bool X86WinCOFFTargetStreamer::emitFPOPushReg(unsigned Reg
, SMLoc L
) {
215 if (checkInFPOPrologue(L
))
218 Inst
.Label
= emitFPOLabel();
219 Inst
.Op
= FPOInstruction::PushReg
;
220 Inst
.RegOrOffset
= Reg
;
221 CurFPOData
->Instructions
.push_back(Inst
);
225 bool X86WinCOFFTargetStreamer::emitFPOStackAlloc(unsigned StackAlloc
, SMLoc L
) {
226 if (checkInFPOPrologue(L
))
229 Inst
.Label
= emitFPOLabel();
230 Inst
.Op
= FPOInstruction::StackAlloc
;
231 Inst
.RegOrOffset
= StackAlloc
;
232 CurFPOData
->Instructions
.push_back(Inst
);
236 bool X86WinCOFFTargetStreamer::emitFPOStackAlign(unsigned Align
, SMLoc L
) {
237 if (checkInFPOPrologue(L
))
239 if (!llvm::any_of(CurFPOData
->Instructions
, [](const FPOInstruction
&Inst
) {
240 return Inst
.Op
== FPOInstruction::SetFrame
;
242 getContext().reportError(
243 L
, "a frame register must be established before aligning the stack");
247 Inst
.Label
= emitFPOLabel();
248 Inst
.Op
= FPOInstruction::StackAlign
;
249 Inst
.RegOrOffset
= Align
;
250 CurFPOData
->Instructions
.push_back(Inst
);
254 bool X86WinCOFFTargetStreamer::emitFPOEndPrologue(SMLoc L
) {
255 if (checkInFPOPrologue(L
))
257 CurFPOData
->PrologueEnd
= emitFPOLabel();
262 struct RegSaveOffset
{
263 RegSaveOffset(unsigned Reg
, unsigned Offset
) : Reg(Reg
), Offset(Offset
) {}
269 struct FPOStateMachine
{
270 explicit FPOStateMachine(const FPOData
*FPO
) : FPO(FPO
) {}
272 const FPOData
*FPO
= nullptr;
273 unsigned FrameReg
= 0;
274 unsigned FrameRegOff
= 0;
275 unsigned CurOffset
= 0;
276 unsigned LocalSize
= 0;
277 unsigned SavedRegSize
= 0;
278 unsigned StackOffsetBeforeAlign
= 0;
279 unsigned StackAlign
= 0;
280 unsigned Flags
= 0; // FIXME: Set HasSEH / HasEH.
282 SmallString
<128> FrameFunc
;
284 SmallVector
<RegSaveOffset
, 4> RegSaveOffsets
;
286 void emitFrameDataRecord(MCStreamer
&OS
, MCSymbol
*Label
);
290 static Printable
printFPOReg(const MCRegisterInfo
*MRI
, unsigned LLVMReg
) {
291 return Printable([MRI
, LLVMReg
](raw_ostream
&OS
) {
293 // MSVC only seems to emit symbolic register names for EIP, EBP, and ESP,
294 // but the format seems to support more than that, so we emit them.
295 case X86::EAX
: OS
<< "$eax"; break;
296 case X86::EBX
: OS
<< "$ebx"; break;
297 case X86::ECX
: OS
<< "$ecx"; break;
298 case X86::EDX
: OS
<< "$edx"; break;
299 case X86::EDI
: OS
<< "$edi"; break;
300 case X86::ESI
: OS
<< "$esi"; break;
301 case X86::ESP
: OS
<< "$esp"; break;
302 case X86::EBP
: OS
<< "$ebp"; break;
303 case X86::EIP
: OS
<< "$eip"; break;
304 // Otherwise, get the codeview register number and print $N.
306 OS
<< '$' << MRI
->getCodeViewRegNum(LLVMReg
);
312 void FPOStateMachine::emitFrameDataRecord(MCStreamer
&OS
, MCSymbol
*Label
) {
313 unsigned CurFlags
= Flags
;
314 if (Label
== FPO
->Begin
)
315 CurFlags
|= FrameData::IsFunctionStart
;
317 // Compute the new FrameFunc string.
319 raw_svector_ostream
FuncOS(FrameFunc
);
320 const MCRegisterInfo
*MRI
= OS
.getContext().getRegisterInfo();
321 assert((StackAlign
== 0 || FrameReg
!= 0) &&
322 "cannot align stack without frame reg");
323 StringRef CFAVar
= StackAlign
== 0 ? "$T0" : "$T1";
326 // CFA is FrameReg + FrameRegOff.
327 FuncOS
<< CFAVar
<< ' ' << printFPOReg(MRI
, FrameReg
) << ' ' << FrameRegOff
330 // Assign $T0, the VFRAME register, the value of ESP after it is aligned.
331 // Starting from the CFA, we subtract the size of all pushed registers, and
332 // align the result. While we don't store any CSRs in this area, $T0 is used
333 // by S_DEFRANGE_FRAMEPOINTER_REL records to find local variables.
335 FuncOS
<< "$T0 " << CFAVar
<< ' ' << StackOffsetBeforeAlign
<< " - "
336 << StackAlign
<< " @ = ";
339 // The address of return address is ESP + CurOffset, but we use .raSearch to
340 // match MSVC. This seems to ask the debugger to subtract some combination
341 // of LocalSize and SavedRegSize from ESP and grovel around in that memory
342 // to find the address of a plausible return address.
343 FuncOS
<< CFAVar
<< " .raSearch = ";
346 // Caller's $eip should be dereferenced CFA, and $esp should be CFA plus 4.
347 FuncOS
<< "$eip " << CFAVar
<< " ^ = ";
348 FuncOS
<< "$esp " << CFAVar
<< " 4 + = ";
350 // Each saved register is stored at an unchanging negative CFA offset.
351 for (RegSaveOffset RO
: RegSaveOffsets
)
352 FuncOS
<< printFPOReg(MRI
, RO
.Reg
) << ' ' << CFAVar
<< ' ' << RO
.Offset
355 // Add it to the CV string table.
356 CodeViewContext
&CVCtx
= OS
.getContext().getCVContext();
357 unsigned FrameFuncStrTabOff
= CVCtx
.addToStringTable(FuncOS
.str()).second
;
359 // MSVC has only ever been observed to emit a MaxStackSize of zero.
360 unsigned MaxStackSize
= 0;
362 // The FrameData record format is:
363 // ulittle32_t RvaStart;
364 // ulittle32_t CodeSize;
365 // ulittle32_t LocalSize;
366 // ulittle32_t ParamsSize;
367 // ulittle32_t MaxStackSize;
368 // ulittle32_t FrameFunc; // String table offset
369 // ulittle16_t PrologSize;
370 // ulittle16_t SavedRegsSize;
371 // ulittle32_t Flags;
373 OS
.emitAbsoluteSymbolDiff(Label
, FPO
->Begin
, 4); // RvaStart
374 OS
.emitAbsoluteSymbolDiff(FPO
->End
, Label
, 4); // CodeSize
375 OS
.EmitIntValue(LocalSize
, 4);
376 OS
.EmitIntValue(FPO
->ParamsSize
, 4);
377 OS
.EmitIntValue(MaxStackSize
, 4);
378 OS
.EmitIntValue(FrameFuncStrTabOff
, 4); // FrameFunc
379 OS
.emitAbsoluteSymbolDiff(FPO
->PrologueEnd
, Label
, 2);
380 OS
.EmitIntValue(SavedRegSize
, 2);
381 OS
.EmitIntValue(CurFlags
, 4);
384 /// Compute and emit the real CodeView FrameData subsection.
385 bool X86WinCOFFTargetStreamer::emitFPOData(const MCSymbol
*ProcSym
, SMLoc L
) {
386 MCStreamer
&OS
= getStreamer();
387 MCContext
&Ctx
= OS
.getContext();
389 auto I
= AllFPOData
.find(ProcSym
);
390 if (I
== AllFPOData
.end()) {
391 Ctx
.reportError(L
, Twine("no FPO data found for symbol ") +
395 const FPOData
*FPO
= I
->second
.get();
396 assert(FPO
->Begin
&& FPO
->End
&& FPO
->PrologueEnd
&& "missing FPO label");
398 MCSymbol
*FrameBegin
= Ctx
.createTempSymbol(),
399 *FrameEnd
= Ctx
.createTempSymbol();
401 OS
.EmitIntValue(unsigned(DebugSubsectionKind::FrameData
), 4);
402 OS
.emitAbsoluteSymbolDiff(FrameEnd
, FrameBegin
, 4);
403 OS
.EmitLabel(FrameBegin
);
405 // Start with the RVA of the function in question.
406 OS
.EmitValue(MCSymbolRefExpr::create(FPO
->Function
,
407 MCSymbolRefExpr::VK_COFF_IMGREL32
, Ctx
),
410 // Emit a sequence of FrameData records.
411 FPOStateMachine
FSM(FPO
);
413 FSM
.emitFrameDataRecord(OS
, FPO
->Begin
);
414 for (const FPOInstruction
&Inst
: FPO
->Instructions
) {
416 case FPOInstruction::PushReg
:
418 FSM
.SavedRegSize
+= 4;
419 FSM
.RegSaveOffsets
.push_back({Inst
.RegOrOffset
, FSM
.CurOffset
});
421 case FPOInstruction::SetFrame
:
422 FSM
.FrameReg
= Inst
.RegOrOffset
;
423 FSM
.FrameRegOff
= FSM
.CurOffset
;
425 case FPOInstruction::StackAlign
:
426 FSM
.StackOffsetBeforeAlign
= FSM
.CurOffset
;
427 FSM
.StackAlign
= Inst
.RegOrOffset
;
429 case FPOInstruction::StackAlloc
:
430 FSM
.CurOffset
+= Inst
.RegOrOffset
;
431 FSM
.LocalSize
+= Inst
.RegOrOffset
;
432 // No need to emit FrameData for stack allocations with a frame pointer.
437 FSM
.emitFrameDataRecord(OS
, Inst
.Label
);
440 OS
.EmitValueToAlignment(4, 0);
441 OS
.EmitLabel(FrameEnd
);
445 MCTargetStreamer
*llvm::createX86AsmTargetStreamer(MCStreamer
&S
,
446 formatted_raw_ostream
&OS
,
447 MCInstPrinter
*InstPrinter
,
449 // FIXME: This makes it so we textually assemble COFF directives on ELF.
450 // That's kind of nonsensical.
451 return new X86WinCOFFAsmTargetStreamer(S
, OS
, *InstPrinter
);
455 llvm::createX86ObjectTargetStreamer(MCStreamer
&S
, const MCSubtargetInfo
&STI
) {
456 // No need to register a target streamer.
457 if (!STI
.getTargetTriple().isOSBinFormatCOFF())
459 // Registers itself to the MCStreamer.
460 return new X86WinCOFFTargetStreamer(S
);