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