/src/llvm-project/llvm/lib/Target/X86/MCTargetDesc/X86WinCOFFTargetStreamer.cpp
Line | Count | Source (jump to first uncovered line) |
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" |
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/MC/MCSymbol.h" |
18 | | #include "llvm/Support/FormattedStream.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 { |
26 | | formatted_raw_ostream &OS; |
27 | | MCInstPrinter &InstPrinter; |
28 | | |
29 | | public: |
30 | | X86WinCOFFAsmTargetStreamer(MCStreamer &S, formatted_raw_ostream &OS, |
31 | | MCInstPrinter &InstPrinter) |
32 | 0 | : 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. |
70 | | DenseMap<const MCSymbol *, std::unique_ptr<FPOData>> AllFPOData; |
71 | | |
72 | | /// Current FPO data created by .cv_fpo_proc. |
73 | | std::unique_ptr<FPOData> CurFPOData; |
74 | | |
75 | 0 | 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 | 0 | MCContext &getContext() { return getStreamer().getContext(); } |
84 | | |
85 | | public: |
86 | 0 | 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 | 0 | unsigned ParamsSize, SMLoc L) { |
102 | 0 | OS << "\t.cv_fpo_proc\t"; |
103 | 0 | ProcSym->print(OS, getStreamer().getContext().getAsmInfo()); |
104 | 0 | OS << ' ' << ParamsSize << '\n'; |
105 | 0 | return false; |
106 | 0 | } |
107 | | |
108 | 0 | bool X86WinCOFFAsmTargetStreamer::emitFPOEndPrologue(SMLoc L) { |
109 | 0 | OS << "\t.cv_fpo_endprologue\n"; |
110 | 0 | return false; |
111 | 0 | } |
112 | | |
113 | 0 | bool X86WinCOFFAsmTargetStreamer::emitFPOEndProc(SMLoc L) { |
114 | 0 | OS << "\t.cv_fpo_endproc\n"; |
115 | 0 | return false; |
116 | 0 | } |
117 | | |
118 | | bool X86WinCOFFAsmTargetStreamer::emitFPOData(const MCSymbol *ProcSym, |
119 | 0 | SMLoc L) { |
120 | 0 | OS << "\t.cv_fpo_data\t"; |
121 | 0 | ProcSym->print(OS, getStreamer().getContext().getAsmInfo()); |
122 | 0 | OS << '\n'; |
123 | 0 | return false; |
124 | 0 | } |
125 | | |
126 | 0 | bool X86WinCOFFAsmTargetStreamer::emitFPOPushReg(unsigned Reg, SMLoc L) { |
127 | 0 | OS << "\t.cv_fpo_pushreg\t"; |
128 | 0 | InstPrinter.printRegName(OS, Reg); |
129 | 0 | OS << '\n'; |
130 | 0 | return false; |
131 | 0 | } |
132 | | |
133 | | bool X86WinCOFFAsmTargetStreamer::emitFPOStackAlloc(unsigned StackAlloc, |
134 | 0 | SMLoc L) { |
135 | 0 | OS << "\t.cv_fpo_stackalloc\t" << StackAlloc << '\n'; |
136 | 0 | return false; |
137 | 0 | } |
138 | | |
139 | 0 | bool X86WinCOFFAsmTargetStreamer::emitFPOStackAlign(unsigned Align, SMLoc L) { |
140 | 0 | OS << "\t.cv_fpo_stackalign\t" << Align << '\n'; |
141 | 0 | return false; |
142 | 0 | } |
143 | | |
144 | 0 | bool X86WinCOFFAsmTargetStreamer::emitFPOSetFrame(unsigned Reg, SMLoc L) { |
145 | 0 | OS << "\t.cv_fpo_setframe\t"; |
146 | 0 | InstPrinter.printRegName(OS, Reg); |
147 | 0 | OS << '\n'; |
148 | 0 | return false; |
149 | 0 | } |
150 | | |
151 | 0 | bool X86WinCOFFTargetStreamer::checkInFPOPrologue(SMLoc L) { |
152 | 0 | if (!haveOpenFPOData() || CurFPOData->PrologueEnd) { |
153 | 0 | getContext().reportError( |
154 | 0 | L, |
155 | 0 | "directive must appear between .cv_fpo_proc and .cv_fpo_endprologue"); |
156 | 0 | return true; |
157 | 0 | } |
158 | 0 | return false; |
159 | 0 | } |
160 | | |
161 | 0 | MCSymbol *X86WinCOFFTargetStreamer::emitFPOLabel() { |
162 | 0 | MCSymbol *Label = getContext().createTempSymbol("cfi", true); |
163 | 0 | getStreamer().emitLabel(Label); |
164 | 0 | return Label; |
165 | 0 | } |
166 | | |
167 | | bool X86WinCOFFTargetStreamer::emitFPOProc(const MCSymbol *ProcSym, |
168 | 0 | unsigned ParamsSize, SMLoc L) { |
169 | 0 | if (haveOpenFPOData()) { |
170 | 0 | getContext().reportError( |
171 | 0 | L, "opening new .cv_fpo_proc before closing previous frame"); |
172 | 0 | return true; |
173 | 0 | } |
174 | 0 | CurFPOData = std::make_unique<FPOData>(); |
175 | 0 | CurFPOData->Function = ProcSym; |
176 | 0 | CurFPOData->Begin = emitFPOLabel(); |
177 | 0 | CurFPOData->ParamsSize = ParamsSize; |
178 | 0 | return false; |
179 | 0 | } |
180 | | |
181 | 0 | bool X86WinCOFFTargetStreamer::emitFPOEndProc(SMLoc L) { |
182 | 0 | if (!haveOpenFPOData()) { |
183 | 0 | getContext().reportError(L, ".cv_fpo_endproc must appear after .cv_proc"); |
184 | 0 | return true; |
185 | 0 | } |
186 | 0 | if (!CurFPOData->PrologueEnd) { |
187 | | // Complain if there were prologue setup instructions but no end prologue. |
188 | 0 | if (!CurFPOData->Instructions.empty()) { |
189 | 0 | getContext().reportError(L, "missing .cv_fpo_endprologue"); |
190 | 0 | CurFPOData->Instructions.clear(); |
191 | 0 | } |
192 | | |
193 | | // Claim there is a zero-length prologue to make the label math work out |
194 | | // later. |
195 | 0 | CurFPOData->PrologueEnd = CurFPOData->Begin; |
196 | 0 | } |
197 | |
|
198 | 0 | CurFPOData->End = emitFPOLabel(); |
199 | 0 | const MCSymbol *Fn = CurFPOData->Function; |
200 | 0 | AllFPOData.insert({Fn, std::move(CurFPOData)}); |
201 | 0 | return false; |
202 | 0 | } |
203 | | |
204 | 0 | bool X86WinCOFFTargetStreamer::emitFPOSetFrame(unsigned Reg, SMLoc L) { |
205 | 0 | if (checkInFPOPrologue(L)) |
206 | 0 | return true; |
207 | 0 | FPOInstruction Inst; |
208 | 0 | Inst.Label = emitFPOLabel(); |
209 | 0 | Inst.Op = FPOInstruction::SetFrame; |
210 | 0 | Inst.RegOrOffset = Reg; |
211 | 0 | CurFPOData->Instructions.push_back(Inst); |
212 | 0 | return false; |
213 | 0 | } |
214 | | |
215 | 0 | bool X86WinCOFFTargetStreamer::emitFPOPushReg(unsigned Reg, SMLoc L) { |
216 | 0 | if (checkInFPOPrologue(L)) |
217 | 0 | return true; |
218 | 0 | FPOInstruction Inst; |
219 | 0 | Inst.Label = emitFPOLabel(); |
220 | 0 | Inst.Op = FPOInstruction::PushReg; |
221 | 0 | Inst.RegOrOffset = Reg; |
222 | 0 | CurFPOData->Instructions.push_back(Inst); |
223 | 0 | return false; |
224 | 0 | } |
225 | | |
226 | 0 | bool X86WinCOFFTargetStreamer::emitFPOStackAlloc(unsigned StackAlloc, SMLoc L) { |
227 | 0 | if (checkInFPOPrologue(L)) |
228 | 0 | return true; |
229 | 0 | FPOInstruction Inst; |
230 | 0 | Inst.Label = emitFPOLabel(); |
231 | 0 | Inst.Op = FPOInstruction::StackAlloc; |
232 | 0 | Inst.RegOrOffset = StackAlloc; |
233 | 0 | CurFPOData->Instructions.push_back(Inst); |
234 | 0 | return false; |
235 | 0 | } |
236 | | |
237 | 0 | bool X86WinCOFFTargetStreamer::emitFPOStackAlign(unsigned Align, SMLoc L) { |
238 | 0 | if (checkInFPOPrologue(L)) |
239 | 0 | return true; |
240 | 0 | if (llvm::none_of(CurFPOData->Instructions, [](const FPOInstruction &Inst) { |
241 | 0 | return Inst.Op == FPOInstruction::SetFrame; |
242 | 0 | })) { |
243 | 0 | getContext().reportError( |
244 | 0 | L, "a frame register must be established before aligning the stack"); |
245 | 0 | return true; |
246 | 0 | } |
247 | 0 | FPOInstruction Inst; |
248 | 0 | Inst.Label = emitFPOLabel(); |
249 | 0 | Inst.Op = FPOInstruction::StackAlign; |
250 | 0 | Inst.RegOrOffset = Align; |
251 | 0 | CurFPOData->Instructions.push_back(Inst); |
252 | 0 | return false; |
253 | 0 | } |
254 | | |
255 | 0 | bool X86WinCOFFTargetStreamer::emitFPOEndPrologue(SMLoc L) { |
256 | 0 | if (checkInFPOPrologue(L)) |
257 | 0 | return true; |
258 | 0 | CurFPOData->PrologueEnd = emitFPOLabel(); |
259 | 0 | return false; |
260 | 0 | } |
261 | | |
262 | | namespace { |
263 | | struct RegSaveOffset { |
264 | 0 | RegSaveOffset(unsigned Reg, unsigned Offset) : Reg(Reg), Offset(Offset) {} |
265 | | |
266 | | unsigned Reg = 0; |
267 | | unsigned Offset = 0; |
268 | | }; |
269 | | |
270 | | struct FPOStateMachine { |
271 | 0 | 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 | 0 | static Printable printFPOReg(const MCRegisterInfo *MRI, unsigned LLVMReg) { |
292 | 0 | return Printable([MRI, LLVMReg](raw_ostream &OS) { |
293 | 0 | 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 | 0 | case X86::EAX: OS << "$eax"; break; |
297 | 0 | case X86::EBX: OS << "$ebx"; break; |
298 | 0 | case X86::ECX: OS << "$ecx"; break; |
299 | 0 | case X86::EDX: OS << "$edx"; break; |
300 | 0 | case X86::EDI: OS << "$edi"; break; |
301 | 0 | case X86::ESI: OS << "$esi"; break; |
302 | 0 | case X86::ESP: OS << "$esp"; break; |
303 | 0 | case X86::EBP: OS << "$ebp"; break; |
304 | 0 | case X86::EIP: OS << "$eip"; break; |
305 | | // Otherwise, get the codeview register number and print $N. |
306 | 0 | default: |
307 | 0 | OS << '$' << MRI->getCodeViewRegNum(LLVMReg); |
308 | 0 | break; |
309 | 0 | } |
310 | 0 | }); |
311 | 0 | } |
312 | | |
313 | 0 | void FPOStateMachine::emitFrameDataRecord(MCStreamer &OS, MCSymbol *Label) { |
314 | 0 | unsigned CurFlags = Flags; |
315 | 0 | if (Label == FPO->Begin) |
316 | 0 | CurFlags |= FrameData::IsFunctionStart; |
317 | | |
318 | | // Compute the new FrameFunc string. |
319 | 0 | FrameFunc.clear(); |
320 | 0 | raw_svector_ostream FuncOS(FrameFunc); |
321 | 0 | const MCRegisterInfo *MRI = OS.getContext().getRegisterInfo(); |
322 | 0 | assert((StackAlign == 0 || FrameReg != 0) && |
323 | 0 | "cannot align stack without frame reg"); |
324 | 0 | StringRef CFAVar = StackAlign == 0 ? "$T0" : "$T1"; |
325 | |
|
326 | 0 | if (FrameReg) { |
327 | | // CFA is FrameReg + FrameRegOff. |
328 | 0 | FuncOS << CFAVar << ' ' << printFPOReg(MRI, FrameReg) << ' ' << FrameRegOff |
329 | 0 | << " + = "; |
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 | 0 | if (StackAlign) { |
336 | 0 | FuncOS << "$T0 " << CFAVar << ' ' << StackOffsetBeforeAlign << " - " |
337 | 0 | << StackAlign << " @ = "; |
338 | 0 | } |
339 | 0 | } 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 | 0 | FuncOS << CFAVar << " .raSearch = "; |
345 | 0 | } |
346 | | |
347 | | // Caller's $eip should be dereferenced CFA, and $esp should be CFA plus 4. |
348 | 0 | FuncOS << "$eip " << CFAVar << " ^ = "; |
349 | 0 | FuncOS << "$esp " << CFAVar << " 4 + = "; |
350 | | |
351 | | // Each saved register is stored at an unchanging negative CFA offset. |
352 | 0 | for (RegSaveOffset RO : RegSaveOffsets) |
353 | 0 | FuncOS << printFPOReg(MRI, RO.Reg) << ' ' << CFAVar << ' ' << RO.Offset |
354 | 0 | << " - ^ = "; |
355 | | |
356 | | // Add it to the CV string table. |
357 | 0 | CodeViewContext &CVCtx = OS.getContext().getCVContext(); |
358 | 0 | unsigned FrameFuncStrTabOff = CVCtx.addToStringTable(FuncOS.str()).second; |
359 | | |
360 | | // MSVC has only ever been observed to emit a MaxStackSize of zero. |
361 | 0 | 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 | 0 | OS.emitAbsoluteSymbolDiff(Label, FPO->Begin, 4); // RvaStart |
375 | 0 | OS.emitAbsoluteSymbolDiff(FPO->End, Label, 4); // CodeSize |
376 | 0 | OS.emitInt32(LocalSize); |
377 | 0 | OS.emitInt32(FPO->ParamsSize); |
378 | 0 | OS.emitInt32(MaxStackSize); |
379 | 0 | OS.emitInt32(FrameFuncStrTabOff); // FrameFunc |
380 | 0 | OS.emitAbsoluteSymbolDiff(FPO->PrologueEnd, Label, 2); |
381 | 0 | OS.emitInt16(SavedRegSize); |
382 | 0 | OS.emitInt32(CurFlags); |
383 | 0 | } |
384 | | |
385 | | /// Compute and emit the real CodeView FrameData subsection. |
386 | 0 | bool X86WinCOFFTargetStreamer::emitFPOData(const MCSymbol *ProcSym, SMLoc L) { |
387 | 0 | MCStreamer &OS = getStreamer(); |
388 | 0 | MCContext &Ctx = OS.getContext(); |
389 | |
|
390 | 0 | auto I = AllFPOData.find(ProcSym); |
391 | 0 | if (I == AllFPOData.end()) { |
392 | 0 | Ctx.reportError(L, Twine("no FPO data found for symbol ") + |
393 | 0 | ProcSym->getName()); |
394 | 0 | return true; |
395 | 0 | } |
396 | 0 | const FPOData *FPO = I->second.get(); |
397 | 0 | assert(FPO->Begin && FPO->End && FPO->PrologueEnd && "missing FPO label"); |
398 | | |
399 | 0 | MCSymbol *FrameBegin = Ctx.createTempSymbol(), |
400 | 0 | *FrameEnd = Ctx.createTempSymbol(); |
401 | |
|
402 | 0 | OS.emitInt32(unsigned(DebugSubsectionKind::FrameData)); |
403 | 0 | OS.emitAbsoluteSymbolDiff(FrameEnd, FrameBegin, 4); |
404 | 0 | OS.emitLabel(FrameBegin); |
405 | | |
406 | | // Start with the RVA of the function in question. |
407 | 0 | OS.emitValue(MCSymbolRefExpr::create(FPO->Function, |
408 | 0 | MCSymbolRefExpr::VK_COFF_IMGREL32, Ctx), |
409 | 0 | 4); |
410 | | |
411 | | // Emit a sequence of FrameData records. |
412 | 0 | FPOStateMachine FSM(FPO); |
413 | |
|
414 | 0 | FSM.emitFrameDataRecord(OS, FPO->Begin); |
415 | 0 | for (const FPOInstruction &Inst : FPO->Instructions) { |
416 | 0 | switch (Inst.Op) { |
417 | 0 | case FPOInstruction::PushReg: |
418 | 0 | FSM.CurOffset += 4; |
419 | 0 | FSM.SavedRegSize += 4; |
420 | 0 | FSM.RegSaveOffsets.push_back({Inst.RegOrOffset, FSM.CurOffset}); |
421 | 0 | break; |
422 | 0 | case FPOInstruction::SetFrame: |
423 | 0 | FSM.FrameReg = Inst.RegOrOffset; |
424 | 0 | FSM.FrameRegOff = FSM.CurOffset; |
425 | 0 | break; |
426 | 0 | case FPOInstruction::StackAlign: |
427 | 0 | FSM.StackOffsetBeforeAlign = FSM.CurOffset; |
428 | 0 | FSM.StackAlign = Inst.RegOrOffset; |
429 | 0 | break; |
430 | 0 | case FPOInstruction::StackAlloc: |
431 | 0 | FSM.CurOffset += Inst.RegOrOffset; |
432 | 0 | FSM.LocalSize += Inst.RegOrOffset; |
433 | | // No need to emit FrameData for stack allocations with a frame pointer. |
434 | 0 | if (FSM.FrameReg) |
435 | 0 | continue; |
436 | 0 | break; |
437 | 0 | } |
438 | 0 | FSM.emitFrameDataRecord(OS, Inst.Label); |
439 | 0 | } |
440 | | |
441 | 0 | OS.emitValueToAlignment(Align(4), 0); |
442 | 0 | OS.emitLabel(FrameEnd); |
443 | 0 | return false; |
444 | 0 | } |
445 | | |
446 | | MCTargetStreamer *llvm::createX86AsmTargetStreamer(MCStreamer &S, |
447 | | formatted_raw_ostream &OS, |
448 | | MCInstPrinter *InstPrinter, |
449 | 0 | bool IsVerboseAsm) { |
450 | | // FIXME: This makes it so we textually assemble COFF directives on ELF. |
451 | | // That's kind of nonsensical. |
452 | 0 | return new X86WinCOFFAsmTargetStreamer(S, OS, *InstPrinter); |
453 | 0 | } |
454 | | |
455 | | MCTargetStreamer * |
456 | 0 | llvm::createX86ObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI) { |
457 | | // No need to register a target streamer. |
458 | 0 | if (!STI.getTargetTriple().isOSBinFormatCOFF()) |
459 | 0 | return nullptr; |
460 | | // Registers itself to the MCStreamer. |
461 | 0 | return new X86WinCOFFTargetStreamer(S); |
462 | 0 | } |