LLVM 22.0.0git
X86WinCOFFTargetStreamer.cpp
Go to the documentation of this file.
1//===-- X86WinCOFFTargetStreamer.cpp ----------------------------*- C++ -*-===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8
9#include "X86MCTargetDesc.h"
10#include "X86TargetStreamer.h"
12#include "llvm/MC/MCCodeView.h"
13#include "llvm/MC/MCContext.h"
17#include "llvm/MC/MCSymbol.h"
19
20using namespace llvm;
21using namespace llvm::codeview;
22
23namespace {
24/// Implements Windows x86-only directives for assembly emission.
25class X86WinCOFFAsmTargetStreamer : public X86TargetStreamer {
27 MCInstPrinter &InstPrinter;
28
29public:
30 X86WinCOFFAsmTargetStreamer(MCStreamer &S, formatted_raw_ostream &OS,
31 MCInstPrinter &InstPrinter)
32 : X86TargetStreamer(S), OS(OS), InstPrinter(InstPrinter) {}
33
34 void emitCode16() override;
35 void emitCode32() override;
36 void emitCode64() override;
37
38 bool emitFPOProc(const MCSymbol *ProcSym, unsigned ParamsSize,
39 SMLoc L) override;
40 bool emitFPOEndPrologue(SMLoc L) override;
41 bool emitFPOEndProc(SMLoc L) override;
42 bool emitFPOData(const MCSymbol *ProcSym, SMLoc L) override;
43 bool emitFPOPushReg(MCRegister Reg, SMLoc L) override;
44 bool emitFPOStackAlloc(unsigned StackAlloc, SMLoc L) override;
45 bool emitFPOStackAlign(unsigned Align, SMLoc L) override;
46 bool emitFPOSetFrame(MCRegister Reg, SMLoc L) override;
47};
48
49/// Represents a single FPO directive.
50struct FPOInstruction {
52 enum Operation {
53 PushReg,
54 StackAlloc,
55 StackAlign,
56 SetFrame,
57 } Op;
58 unsigned RegOrOffset;
59};
60
61struct FPOData {
62 const MCSymbol *Function = nullptr;
63 MCSymbol *Begin = nullptr;
64 MCSymbol *PrologueEnd = nullptr;
65 MCSymbol *End = nullptr;
66 unsigned ParamsSize = 0;
67
69};
70
71/// Implements Windows x86-only directives for object emission.
72class X86WinCOFFTargetStreamer : public X86TargetStreamer {
73 /// Map from function symbol to its FPO data.
75
76 /// Current FPO data created by .cv_fpo_proc.
77 std::unique_ptr<FPOData> CurFPOData;
78
79 bool haveOpenFPOData() { return !!CurFPOData; }
80
81 /// Diagnoses an error at L if we are not in an FPO prologue. Return true on
82 /// error.
83 bool checkInFPOPrologue(SMLoc L);
84
85 MCSymbol *emitFPOLabel();
86
87public:
88 X86WinCOFFTargetStreamer(MCStreamer &S) : X86TargetStreamer(S) {}
89
90 bool emitFPOProc(const MCSymbol *ProcSym, unsigned ParamsSize,
91 SMLoc L) override;
92 bool emitFPOEndPrologue(SMLoc L) override;
93 bool emitFPOEndProc(SMLoc L) override;
94 bool emitFPOData(const MCSymbol *ProcSym, SMLoc L) override;
95 bool emitFPOPushReg(MCRegister Reg, SMLoc L) override;
96 bool emitFPOStackAlloc(unsigned StackAlloc, SMLoc L) override;
97 bool emitFPOStackAlign(unsigned Align, SMLoc L) override;
98 bool emitFPOSetFrame(MCRegister Reg, SMLoc L) override;
99};
100} // end namespace
101
102void X86WinCOFFAsmTargetStreamer::emitCode16() { OS << "\t.code16\n"; }
103
104void X86WinCOFFAsmTargetStreamer::emitCode32() { OS << "\t.code32\n"; }
105
106void X86WinCOFFAsmTargetStreamer::emitCode64() { OS << "\t.code64\n"; }
107
108bool X86WinCOFFAsmTargetStreamer::emitFPOProc(const MCSymbol *ProcSym,
109 unsigned ParamsSize, SMLoc L) {
110 OS << "\t.cv_fpo_proc\t";
111 ProcSym->print(OS, getContext().getAsmInfo());
112 OS << ' ' << ParamsSize << '\n';
113 return false;
114}
115
116bool X86WinCOFFAsmTargetStreamer::emitFPOEndPrologue(SMLoc L) {
117 OS << "\t.cv_fpo_endprologue\n";
118 return false;
119}
120
121bool X86WinCOFFAsmTargetStreamer::emitFPOEndProc(SMLoc L) {
122 OS << "\t.cv_fpo_endproc\n";
123 return false;
124}
125
126bool X86WinCOFFAsmTargetStreamer::emitFPOData(const MCSymbol *ProcSym,
127 SMLoc L) {
128 OS << "\t.cv_fpo_data\t";
129 ProcSym->print(OS, getStreamer().getContext().getAsmInfo());
130 OS << '\n';
131 return false;
132}
133
134bool X86WinCOFFAsmTargetStreamer::emitFPOPushReg(MCRegister Reg, SMLoc L) {
135 OS << "\t.cv_fpo_pushreg\t";
136 InstPrinter.printRegName(OS, Reg);
137 OS << '\n';
138 return false;
139}
140
141bool X86WinCOFFAsmTargetStreamer::emitFPOStackAlloc(unsigned StackAlloc,
142 SMLoc L) {
143 OS << "\t.cv_fpo_stackalloc\t" << StackAlloc << '\n';
144 return false;
145}
146
147bool X86WinCOFFAsmTargetStreamer::emitFPOStackAlign(unsigned Align, SMLoc L) {
148 OS << "\t.cv_fpo_stackalign\t" << Align << '\n';
149 return false;
150}
151
152bool X86WinCOFFAsmTargetStreamer::emitFPOSetFrame(MCRegister Reg, SMLoc L) {
153 OS << "\t.cv_fpo_setframe\t";
154 InstPrinter.printRegName(OS, Reg);
155 OS << '\n';
156 return false;
157}
158
159bool X86WinCOFFTargetStreamer::checkInFPOPrologue(SMLoc L) {
160 if (!haveOpenFPOData() || CurFPOData->PrologueEnd) {
161 getContext().reportError(
162 L,
163 "directive must appear between .cv_fpo_proc and .cv_fpo_endprologue");
164 return true;
165 }
166 return false;
167}
168
169MCSymbol *X86WinCOFFTargetStreamer::emitFPOLabel() {
170 MCSymbol *Label = getContext().createTempSymbol("cfi", true);
171 getStreamer().emitLabel(Label);
172 return Label;
173}
174
175bool X86WinCOFFTargetStreamer::emitFPOProc(const MCSymbol *ProcSym,
176 unsigned ParamsSize, SMLoc L) {
177 if (haveOpenFPOData()) {
178 getContext().reportError(
179 L, "opening new .cv_fpo_proc before closing previous frame");
180 return true;
181 }
182 CurFPOData = std::make_unique<FPOData>();
183 CurFPOData->Function = ProcSym;
184 CurFPOData->Begin = emitFPOLabel();
185 CurFPOData->ParamsSize = ParamsSize;
186 return false;
187}
188
189bool X86WinCOFFTargetStreamer::emitFPOEndProc(SMLoc L) {
190 if (!haveOpenFPOData()) {
191 getContext().reportError(L, ".cv_fpo_endproc must appear after .cv_proc");
192 return true;
193 }
194 if (!CurFPOData->PrologueEnd) {
195 // Complain if there were prologue setup instructions but no end prologue.
196 if (!CurFPOData->Instructions.empty()) {
197 getContext().reportError(L, "missing .cv_fpo_endprologue");
198 CurFPOData->Instructions.clear();
199 }
200
201 // Claim there is a zero-length prologue to make the label math work out
202 // later.
203 CurFPOData->PrologueEnd = CurFPOData->Begin;
204 }
205
206 CurFPOData->End = emitFPOLabel();
207 const MCSymbol *Fn = CurFPOData->Function;
208 AllFPOData.insert({Fn, std::move(CurFPOData)});
209 return false;
210}
211
212bool X86WinCOFFTargetStreamer::emitFPOSetFrame(MCRegister Reg, SMLoc L) {
213 if (checkInFPOPrologue(L))
214 return true;
215 FPOInstruction Inst;
216 Inst.Label = emitFPOLabel();
217 Inst.Op = FPOInstruction::SetFrame;
218 Inst.RegOrOffset = Reg;
219 CurFPOData->Instructions.push_back(Inst);
220 return false;
221}
222
223bool X86WinCOFFTargetStreamer::emitFPOPushReg(MCRegister Reg, SMLoc L) {
224 if (checkInFPOPrologue(L))
225 return true;
226 FPOInstruction Inst;
227 Inst.Label = emitFPOLabel();
228 Inst.Op = FPOInstruction::PushReg;
229 Inst.RegOrOffset = Reg;
230 CurFPOData->Instructions.push_back(Inst);
231 return false;
232}
233
234bool X86WinCOFFTargetStreamer::emitFPOStackAlloc(unsigned StackAlloc, SMLoc L) {
235 if (checkInFPOPrologue(L))
236 return true;
237 FPOInstruction Inst;
238 Inst.Label = emitFPOLabel();
239 Inst.Op = FPOInstruction::StackAlloc;
240 Inst.RegOrOffset = StackAlloc;
241 CurFPOData->Instructions.push_back(Inst);
242 return false;
243}
244
245bool X86WinCOFFTargetStreamer::emitFPOStackAlign(unsigned Align, SMLoc L) {
246 if (checkInFPOPrologue(L))
247 return true;
248 if (llvm::none_of(CurFPOData->Instructions, [](const FPOInstruction &Inst) {
249 return Inst.Op == FPOInstruction::SetFrame;
250 })) {
251 getContext().reportError(
252 L, "a frame register must be established before aligning the stack");
253 return true;
254 }
255 FPOInstruction Inst;
256 Inst.Label = emitFPOLabel();
257 Inst.Op = FPOInstruction::StackAlign;
258 Inst.RegOrOffset = Align;
259 CurFPOData->Instructions.push_back(Inst);
260 return false;
261}
262
263bool X86WinCOFFTargetStreamer::emitFPOEndPrologue(SMLoc L) {
264 if (checkInFPOPrologue(L))
265 return true;
266 CurFPOData->PrologueEnd = emitFPOLabel();
267 return false;
268}
269
270namespace {
271struct RegSaveOffset {
272 RegSaveOffset(unsigned Reg, unsigned Offset) : Reg(Reg), Offset(Offset) {}
273
274 unsigned Reg = 0;
275 unsigned Offset = 0;
276};
277
278struct FPOStateMachine {
279 explicit FPOStateMachine(const FPOData *FPO) : FPO(FPO) {}
280
281 const FPOData *FPO = nullptr;
282 unsigned FrameReg = 0;
283 unsigned FrameRegOff = 0;
284 unsigned CurOffset = 0;
285 unsigned LocalSize = 0;
286 unsigned SavedRegSize = 0;
287 unsigned StackOffsetBeforeAlign = 0;
288 unsigned StackAlign = 0;
289 unsigned Flags = 0; // FIXME: Set HasSEH / HasEH.
290
291 SmallString<128> FrameFunc;
292
293 SmallVector<RegSaveOffset, 4> RegSaveOffsets;
294
295 void emitFrameDataRecord(MCStreamer &OS, MCSymbol *Label);
296};
297} // end namespace
298
299static Printable printFPOReg(const MCRegisterInfo *MRI, unsigned LLVMReg) {
300 return Printable([MRI, LLVMReg](raw_ostream &OS) {
301 switch (LLVMReg) {
302 // MSVC only seems to emit symbolic register names for EIP, EBP, and ESP,
303 // but the format seems to support more than that, so we emit them.
304 case X86::EAX: OS << "$eax"; break;
305 case X86::EBX: OS << "$ebx"; break;
306 case X86::ECX: OS << "$ecx"; break;
307 case X86::EDX: OS << "$edx"; break;
308 case X86::EDI: OS << "$edi"; break;
309 case X86::ESI: OS << "$esi"; break;
310 case X86::ESP: OS << "$esp"; break;
311 case X86::EBP: OS << "$ebp"; break;
312 case X86::EIP: OS << "$eip"; break;
313 // Otherwise, get the codeview register number and print $N.
314 default:
315 OS << '$' << MRI->getCodeViewRegNum(LLVMReg);
316 break;
317 }
318 });
319}
320
321void FPOStateMachine::emitFrameDataRecord(MCStreamer &OS, MCSymbol *Label) {
322 unsigned CurFlags = Flags;
323 if (Label == FPO->Begin)
324 CurFlags |= FrameData::IsFunctionStart;
325
326 // Compute the new FrameFunc string.
327 FrameFunc.clear();
328 raw_svector_ostream FuncOS(FrameFunc);
329 const MCRegisterInfo *MRI = OS.getContext().getRegisterInfo();
330 assert((StackAlign == 0 || FrameReg != 0) &&
331 "cannot align stack without frame reg");
332 StringRef CFAVar = StackAlign == 0 ? "$T0" : "$T1";
333
334 if (FrameReg) {
335 // CFA is FrameReg + FrameRegOff.
336 FuncOS << CFAVar << ' ' << printFPOReg(MRI, FrameReg) << ' ' << FrameRegOff
337 << " + = ";
338
339 // Assign $T0, the VFRAME register, the value of ESP after it is aligned.
340 // Starting from the CFA, we subtract the size of all pushed registers, and
341 // align the result. While we don't store any CSRs in this area, $T0 is used
342 // by S_DEFRANGE_FRAMEPOINTER_REL records to find local variables.
343 if (StackAlign) {
344 FuncOS << "$T0 " << CFAVar << ' ' << StackOffsetBeforeAlign << " - "
345 << StackAlign << " @ = ";
346 }
347 } else {
348 // The address of return address is ESP + CurOffset, but we use .raSearch to
349 // match MSVC. This seems to ask the debugger to subtract some combination
350 // of LocalSize and SavedRegSize from ESP and grovel around in that memory
351 // to find the address of a plausible return address.
352 FuncOS << CFAVar << " .raSearch = ";
353 }
354
355 // Caller's $eip should be dereferenced CFA, and $esp should be CFA plus 4.
356 FuncOS << "$eip " << CFAVar << " ^ = ";
357 FuncOS << "$esp " << CFAVar << " 4 + = ";
358
359 // Each saved register is stored at an unchanging negative CFA offset.
360 for (RegSaveOffset RO : RegSaveOffsets)
361 FuncOS << printFPOReg(MRI, RO.Reg) << ' ' << CFAVar << ' ' << RO.Offset
362 << " - ^ = ";
363
364 // Add it to the CV string table.
365 CodeViewContext &CVCtx = OS.getContext().getCVContext();
366 unsigned FrameFuncStrTabOff = CVCtx.addToStringTable(FuncOS.str()).second;
367
368 // MSVC has only ever been observed to emit a MaxStackSize of zero.
369 unsigned MaxStackSize = 0;
370
371 // The FrameData record format is:
372 // ulittle32_t RvaStart;
373 // ulittle32_t CodeSize;
374 // ulittle32_t LocalSize;
375 // ulittle32_t ParamsSize;
376 // ulittle32_t MaxStackSize;
377 // ulittle32_t FrameFunc; // String table offset
378 // ulittle16_t PrologSize;
379 // ulittle16_t SavedRegsSize;
380 // ulittle32_t Flags;
381
382 OS.emitAbsoluteSymbolDiff(Label, FPO->Begin, 4); // RvaStart
383 OS.emitAbsoluteSymbolDiff(FPO->End, Label, 4); // CodeSize
384 OS.emitInt32(LocalSize);
385 OS.emitInt32(FPO->ParamsSize);
386 OS.emitInt32(MaxStackSize);
387 OS.emitInt32(FrameFuncStrTabOff); // FrameFunc
388 OS.emitAbsoluteSymbolDiff(FPO->PrologueEnd, Label, 2);
389 OS.emitInt16(SavedRegSize);
390 OS.emitInt32(CurFlags);
391}
392
393/// Compute and emit the real CodeView FrameData subsection.
394bool X86WinCOFFTargetStreamer::emitFPOData(const MCSymbol *ProcSym, SMLoc L) {
395 MCStreamer &OS = getStreamer();
396 MCContext &Ctx = OS.getContext();
397
398 auto I = AllFPOData.find(ProcSym);
399 if (I == AllFPOData.end()) {
400 Ctx.reportError(L, Twine("no FPO data found for symbol ") +
401 ProcSym->getName());
402 return true;
403 }
404 const FPOData *FPO = I->second.get();
405 assert(FPO->Begin && FPO->End && FPO->PrologueEnd && "missing FPO label");
406
407 MCSymbol *FrameBegin = Ctx.createTempSymbol(),
408 *FrameEnd = Ctx.createTempSymbol();
409
410 OS.emitInt32(unsigned(DebugSubsectionKind::FrameData));
411 OS.emitAbsoluteSymbolDiff(FrameEnd, FrameBegin, 4);
412 OS.emitLabel(FrameBegin);
413
414 // Start with the RVA of the function in question.
415 OS.emitValue(MCSymbolRefExpr::create(FPO->Function,
417 4);
418
419 // Emit a sequence of FrameData records.
420 FPOStateMachine FSM(FPO);
421
422 FSM.emitFrameDataRecord(OS, FPO->Begin);
423 for (const FPOInstruction &Inst : FPO->Instructions) {
424 switch (Inst.Op) {
425 case FPOInstruction::PushReg:
426 FSM.CurOffset += 4;
427 FSM.SavedRegSize += 4;
428 FSM.RegSaveOffsets.push_back({Inst.RegOrOffset, FSM.CurOffset});
429 break;
430 case FPOInstruction::SetFrame:
431 FSM.FrameReg = Inst.RegOrOffset;
432 FSM.FrameRegOff = FSM.CurOffset;
433 break;
434 case FPOInstruction::StackAlign:
435 FSM.StackOffsetBeforeAlign = FSM.CurOffset;
436 FSM.StackAlign = Inst.RegOrOffset;
437 break;
438 case FPOInstruction::StackAlloc:
439 FSM.CurOffset += Inst.RegOrOffset;
440 FSM.LocalSize += Inst.RegOrOffset;
441 // No need to emit FrameData for stack allocations with a frame pointer.
442 if (FSM.FrameReg)
443 continue;
444 break;
445 }
446 FSM.emitFrameDataRecord(OS, Inst.Label);
447 }
448
449 OS.emitValueToAlignment(Align(4), 0);
450 OS.emitLabel(FrameEnd);
451 return false;
452}
453
456 MCInstPrinter *InstPrinter) {
457 // FIXME: This makes it so we textually assemble COFF directives on ELF.
458 // That's kind of nonsensical.
459 return new X86WinCOFFAsmTargetStreamer(S, OS, *InstPrinter);
460}
461
464 // No need for a special target streamer.
466 return new X86TargetStreamer(S);
467 // Registers itself to the MCStreamer.
468 return new X86WinCOFFTargetStreamer(S);
469}
unsigned const MachineRegisterInfo * MRI
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
bool End
Definition: ELF_riscv.cpp:480
#define I(x, y, z)
Definition: MD5.cpp:58
PowerPC Reduce CR logical Operation
raw_pwrite_stream & OS
static Printable printFPOReg(const MCRegisterInfo *MRI, unsigned LLVMReg)
Holds state from .cv_file and .cv_loc directives for later emission.
Definition: MCCodeView.h:144
std::pair< StringRef, unsigned > addToStringTable(StringRef S)
Add something to the string table.
Definition: MCCodeView.cpp:136
This class represents an Operation in the Expression.
Context object for machine code objects.
Definition: MCContext.h:83
LLVM_ABI MCSymbol * createTempSymbol()
Create a temporary symbol with a unique name.
Definition: MCContext.cpp:386
LLVM_ABI void reportError(SMLoc L, const Twine &Msg)
Definition: MCContext.cpp:1115
This is an instance of a target assembly language printer that converts an MCInst to valid target ass...
Definition: MCInstPrinter.h:46
MCRegisterInfo base class - We assume that the target defines a static array of MCRegisterDesc object...
Wrapper class representing physical registers. Should be passed by value.
Definition: MCRegister.h:33
Streaming machine code generation interface.
Definition: MCStreamer.h:220
Generic base class for all target subtargets.
const Triple & getTargetTriple() const
static const MCSymbolRefExpr * create(const MCSymbol *Symbol, MCContext &Ctx, SMLoc Loc=SMLoc())
Definition: MCExpr.h:214
MCSymbol - Instances of this class represent a symbol name in the MC file, and MCSymbols are created ...
Definition: MCSymbol.h:42
Target specific streamer interface.
Definition: MCStreamer.h:93
Simple wrapper around std::function<void(raw_ostream&)>.
Definition: Printable.h:38
Represents a location in source code.
Definition: SMLoc.h:23
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
Definition: SmallString.h:26
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1197
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:55
bool isOSBinFormatCOFF() const
Tests whether the OS uses the COFF binary format.
Definition: Triple.h:771
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition: Twine.h:82
X86 target streamer implementing x86-only assembly directives.
virtual bool emitFPOPushReg(MCRegister Reg, SMLoc L={})
virtual bool emitFPOProc(const MCSymbol *ProcSym, unsigned ParamsSize, SMLoc L={})
virtual bool emitFPOEndPrologue(SMLoc L={})
virtual bool emitFPOData(const MCSymbol *ProcSym, SMLoc L={})
virtual bool emitFPOEndProc(SMLoc L={})
virtual bool emitFPOStackAlign(unsigned Align, SMLoc L={})
virtual bool emitFPOSetFrame(MCRegister Reg, SMLoc L={})
virtual bool emitFPOStackAlloc(unsigned StackAlloc, SMLoc L={})
formatted_raw_ostream - A raw_ostream that wraps another one and keeps track of line and column posit...
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition: raw_ostream.h:53
A raw_ostream that writes to an SmallVector or SmallString.
Definition: raw_ostream.h:692
Reg
All possible values of the reg field in the ModR/M byte.
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
MCTargetStreamer * createX86ObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI)
Implements X86-only directives for object files.
MCTargetStreamer * createX86AsmTargetStreamer(MCStreamer &S, formatted_raw_ostream &OS, MCInstPrinter *InstPrinter)
Implements X86-only directives for assembly emission.
bool none_of(R &&Range, UnaryPredicate P)
Provide wrappers to std::none_of which take ranges instead of having to pass begin/end explicitly.
Definition: STLExtras.h:1758
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition: Alignment.h:39