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