1//===-- SparcAsmPrinter.cpp - Sparc LLVM assembly writer ------------------===//
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// This file contains a printer that converts from our internal representation
10// of machine-dependent LLVM code to GAS-format SPARC assembly language.
11//
12//===----------------------------------------------------------------------===//
13
14#include "MCTargetDesc/SparcInstPrinter.h"
15#include "MCTargetDesc/SparcMCAsmInfo.h"
16#include "MCTargetDesc/SparcMCTargetDesc.h"
17#include "MCTargetDesc/SparcTargetStreamer.h"
18#include "Sparc.h"
19#include "SparcInstrInfo.h"
20#include "SparcTargetMachine.h"
21#include "TargetInfo/SparcTargetInfo.h"
22#include "llvm/BinaryFormat/ELF.h"
23#include "llvm/CodeGen/AsmPrinter.h"
24#include "llvm/CodeGen/MachineInstr.h"
25#include "llvm/CodeGen/MachineModuleInfoImpls.h"
26#include "llvm/CodeGen/MachineRegisterInfo.h"
27#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h"
28#include "llvm/IR/Mangler.h"
29#include "llvm/MC/MCAsmInfo.h"
30#include "llvm/MC/MCContext.h"
31#include "llvm/MC/MCInst.h"
32#include "llvm/MC/MCStreamer.h"
33#include "llvm/MC/MCSymbol.h"
34#include "llvm/MC/TargetRegistry.h"
35#include "llvm/Support/Compiler.h"
36#include "llvm/Support/raw_ostream.h"
37using namespace llvm;
38
39#define DEBUG_TYPE "asm-printer"
40
41namespace {
42class SparcAsmPrinter : public AsmPrinter {
43 SparcTargetStreamer &getTargetStreamer() {
44 return static_cast<SparcTargetStreamer &>(
45 *OutStreamer->getTargetStreamer());
46 }
47
48public:
49 explicit SparcAsmPrinter(TargetMachine &TM,
50 std::unique_ptr<MCStreamer> Streamer)
51 : AsmPrinter(TM, std::move(Streamer), ID) {}
52
53 StringRef getPassName() const override { return "Sparc Assembly Printer"; }
54
55 void printOperand(const MachineInstr *MI, int opNum, raw_ostream &OS);
56 void printMemOperand(const MachineInstr *MI, int opNum, raw_ostream &OS);
57
58 void emitFunctionBodyStart() override;
59 void emitInstruction(const MachineInstr *MI) override;
60
61 static const char *getRegisterName(MCRegister Reg) {
62 return SparcInstPrinter::getRegisterName(Reg);
63 }
64
65 bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
66 const char *ExtraCode, raw_ostream &O) override;
67 bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,
68 const char *ExtraCode, raw_ostream &O) override;
69 void PrintSymbolOperand(const MachineOperand &MO, raw_ostream &O) override;
70
71 void LowerGETPCXAndEmitMCInsts(const MachineInstr *MI,
72 const MCSubtargetInfo &STI);
73
74 MCOperand lowerOperand(const MachineOperand &MO) const;
75
76private:
77 void lowerToMCInst(const MachineInstr *MI, MCInst &OutMI);
78
79public:
80 static char ID;
81};
82} // end of anonymous namespace
83
84static MCOperand createSparcMCOperand(uint16_t Kind, MCSymbol *Sym,
85 MCContext &OutContext) {
86 const MCSymbolRefExpr *MCSym = MCSymbolRefExpr::create(Symbol: Sym, Ctx&: OutContext);
87 auto *expr = MCSpecifierExpr::create(Expr: MCSym, S: Kind, Ctx&: OutContext);
88 return MCOperand::createExpr(Val: expr);
89}
90static MCOperand createPCXCallOP(MCSymbol *Label,
91 MCContext &OutContext) {
92 return MCOperand::createExpr(Val: MCSymbolRefExpr::create(Symbol: Label, Ctx&: OutContext));
93}
94
95static MCOperand createPCXRelExprOp(uint16_t Spec, MCSymbol *GOTLabel,
96 MCSymbol *StartLabel, MCSymbol *CurLabel,
97 MCContext &OutContext) {
98 const MCSymbolRefExpr *GOT = MCSymbolRefExpr::create(Symbol: GOTLabel, Ctx&: OutContext);
99 const MCSymbolRefExpr *Start = MCSymbolRefExpr::create(Symbol: StartLabel,
100 Ctx&: OutContext);
101 const MCSymbolRefExpr *Cur = MCSymbolRefExpr::create(Symbol: CurLabel,
102 Ctx&: OutContext);
103
104 const MCBinaryExpr *Sub = MCBinaryExpr::createSub(LHS: Cur, RHS: Start, Ctx&: OutContext);
105 const MCBinaryExpr *Add = MCBinaryExpr::createAdd(LHS: GOT, RHS: Sub, Ctx&: OutContext);
106 auto *expr = MCSpecifierExpr::create(Expr: Add, S: Spec, Ctx&: OutContext);
107 return MCOperand::createExpr(Val: expr);
108}
109
110static void EmitCall(MCStreamer &OutStreamer,
111 MCOperand &Callee,
112 const MCSubtargetInfo &STI)
113{
114 MCInst CallInst;
115 CallInst.setOpcode(SP::CALL);
116 CallInst.addOperand(Op: Callee);
117 OutStreamer.emitInstruction(Inst: CallInst, STI);
118}
119
120static void EmitRDPC(MCStreamer &OutStreamer, MCOperand &RD,
121 const MCSubtargetInfo &STI) {
122 MCInst RDPCInst;
123 RDPCInst.setOpcode(SP::RDASR);
124 RDPCInst.addOperand(Op: RD);
125 RDPCInst.addOperand(Op: MCOperand::createReg(Reg: SP::ASR5));
126 OutStreamer.emitInstruction(Inst: RDPCInst, STI);
127}
128
129static void EmitSETHI(MCStreamer &OutStreamer,
130 MCOperand &Imm, MCOperand &RD,
131 const MCSubtargetInfo &STI)
132{
133 MCInst SETHIInst;
134 SETHIInst.setOpcode(SP::SETHIi);
135 SETHIInst.addOperand(Op: RD);
136 SETHIInst.addOperand(Op: Imm);
137 OutStreamer.emitInstruction(Inst: SETHIInst, STI);
138}
139
140static void EmitBinary(MCStreamer &OutStreamer, unsigned Opcode,
141 MCOperand &RS1, MCOperand &Src2, MCOperand &RD,
142 const MCSubtargetInfo &STI)
143{
144 MCInst Inst;
145 Inst.setOpcode(Opcode);
146 Inst.addOperand(Op: RD);
147 Inst.addOperand(Op: RS1);
148 Inst.addOperand(Op: Src2);
149 OutStreamer.emitInstruction(Inst, STI);
150}
151
152static void EmitOR(MCStreamer &OutStreamer,
153 MCOperand &RS1, MCOperand &Imm, MCOperand &RD,
154 const MCSubtargetInfo &STI) {
155 EmitBinary(OutStreamer, Opcode: SP::ORri, RS1, Src2&: Imm, RD, STI);
156}
157
158static void EmitADD(MCStreamer &OutStreamer,
159 MCOperand &RS1, MCOperand &RS2, MCOperand &RD,
160 const MCSubtargetInfo &STI) {
161 EmitBinary(OutStreamer, Opcode: SP::ADDrr, RS1, Src2&: RS2, RD, STI);
162}
163
164static void EmitSHL(MCStreamer &OutStreamer,
165 MCOperand &RS1, MCOperand &Imm, MCOperand &RD,
166 const MCSubtargetInfo &STI) {
167 EmitBinary(OutStreamer, Opcode: SP::SLLri, RS1, Src2&: Imm, RD, STI);
168}
169
170static void emitHiLo(MCStreamer &OutStreamer, MCSymbol *GOTSym, uint16_t HiKind,
171 uint16_t LoKind, MCOperand &RD, MCContext &OutContext,
172 const MCSubtargetInfo &STI) {
173 MCOperand hi = createSparcMCOperand(Kind: HiKind, Sym: GOTSym, OutContext);
174 MCOperand lo = createSparcMCOperand(Kind: LoKind, Sym: GOTSym, OutContext);
175 EmitSETHI(OutStreamer, Imm&: hi, RD, STI);
176 EmitOR(OutStreamer, RS1&: RD, Imm&: lo, RD, STI);
177}
178
179void SparcAsmPrinter::LowerGETPCXAndEmitMCInsts(const MachineInstr *MI,
180 const MCSubtargetInfo &STI)
181{
182 MCSymbol *GOTLabel =
183 OutContext.getOrCreateSymbol(Name: Twine("_GLOBAL_OFFSET_TABLE_"));
184
185 const MachineOperand &MO = MI->getOperand(i: 0);
186 assert(MO.getReg() != SP::O7 &&
187 "%o7 is assigned as destination for getpcx!");
188
189 MCOperand MCRegOP = MCOperand::createReg(Reg: MO.getReg());
190
191
192 if (!isPositionIndependent()) {
193 // Just load the address of GOT to MCRegOP.
194 switch(TM.getCodeModel()) {
195 default:
196 llvm_unreachable("Unsupported absolute code model");
197 case CodeModel::Small:
198 emitHiLo(OutStreamer&: *OutStreamer, GOTSym: GOTLabel, HiKind: ELF::R_SPARC_HI22, LoKind: ELF::R_SPARC_LO10,
199 RD&: MCRegOP, OutContext, STI);
200 break;
201 case CodeModel::Medium: {
202 emitHiLo(OutStreamer&: *OutStreamer, GOTSym: GOTLabel, HiKind: ELF::R_SPARC_H44, LoKind: ELF::R_SPARC_M44,
203 RD&: MCRegOP, OutContext, STI);
204 MCOperand imm = MCOperand::createExpr(Val: MCConstantExpr::create(Value: 12,
205 Ctx&: OutContext));
206 EmitSHL(OutStreamer&: *OutStreamer, RS1&: MCRegOP, Imm&: imm, RD&: MCRegOP, STI);
207 MCOperand lo =
208 createSparcMCOperand(Kind: ELF::R_SPARC_L44, Sym: GOTLabel, OutContext);
209 EmitOR(OutStreamer&: *OutStreamer, RS1&: MCRegOP, Imm&: lo, RD&: MCRegOP, STI);
210 break;
211 }
212 case CodeModel::Large: {
213 emitHiLo(OutStreamer&: *OutStreamer, GOTSym: GOTLabel, HiKind: ELF::R_SPARC_HH22, LoKind: ELF::R_SPARC_HM10,
214 RD&: MCRegOP, OutContext, STI);
215 MCOperand imm = MCOperand::createExpr(Val: MCConstantExpr::create(Value: 32,
216 Ctx&: OutContext));
217 EmitSHL(OutStreamer&: *OutStreamer, RS1&: MCRegOP, Imm&: imm, RD&: MCRegOP, STI);
218 // Use register %o7 to load the lower 32 bits.
219 MCOperand RegO7 = MCOperand::createReg(Reg: SP::O7);
220 emitHiLo(OutStreamer&: *OutStreamer, GOTSym: GOTLabel, HiKind: ELF::R_SPARC_HI22, LoKind: ELF::R_SPARC_LO10,
221 RD&: RegO7, OutContext, STI);
222 EmitADD(OutStreamer&: *OutStreamer, RS1&: MCRegOP, RS2&: RegO7, RD&: MCRegOP, STI);
223 }
224 }
225 return;
226 }
227
228 MCSymbol *StartLabel = OutContext.createTempSymbol();
229 MCSymbol *EndLabel = OutContext.createTempSymbol();
230 MCSymbol *SethiLabel = OutContext.createTempSymbol();
231
232 MCOperand RegO7 = MCOperand::createReg(Reg: SP::O7);
233
234 // <StartLabel>:
235 // <GET-PC> // This will be either `call <EndLabel>` or `rd %pc, %o7`.
236 // <SethiLabel>:
237 // sethi %hi(_GLOBAL_OFFSET_TABLE_+(<SethiLabel>-<StartLabel>)), <MO>
238 // <EndLabel>:
239 // or <MO>, %lo(_GLOBAL_OFFSET_TABLE_+(<EndLabel>-<StartLabel>))), <MO>
240 // add <MO>, %o7, <MO>
241
242 OutStreamer->emitLabel(Symbol: StartLabel);
243 if (!STI.getTargetTriple().isSPARC64() ||
244 STI.hasFeature(Feature: Sparc::TuneSlowRDPC)) {
245 MCOperand Callee = createPCXCallOP(Label: EndLabel, OutContext);
246 EmitCall(OutStreamer&: *OutStreamer, Callee, STI);
247 } else {
248 // TODO find out whether it is possible to store PC
249 // in other registers, to enable leaf function optimization.
250 // (On the other hand, approx. over 97.8% of GETPCXes happen
251 // in non-leaf functions, so would this be worth the effort?)
252 EmitRDPC(OutStreamer&: *OutStreamer, RD&: RegO7, STI);
253 }
254 OutStreamer->emitLabel(Symbol: SethiLabel);
255 MCOperand hiImm = createPCXRelExprOp(Spec: ELF::R_SPARC_PC22, GOTLabel, StartLabel,
256 CurLabel: SethiLabel, OutContext);
257 EmitSETHI(OutStreamer&: *OutStreamer, Imm&: hiImm, RD&: MCRegOP, STI);
258 OutStreamer->emitLabel(Symbol: EndLabel);
259 MCOperand loImm = createPCXRelExprOp(Spec: ELF::R_SPARC_PC10, GOTLabel, StartLabel,
260 CurLabel: EndLabel, OutContext);
261 EmitOR(OutStreamer&: *OutStreamer, RS1&: MCRegOP, Imm&: loImm, RD&: MCRegOP, STI);
262 EmitADD(OutStreamer&: *OutStreamer, RS1&: MCRegOP, RS2&: RegO7, RD&: MCRegOP, STI);
263}
264
265MCOperand SparcAsmPrinter::lowerOperand(const MachineOperand &MO) const {
266 switch (MO.getType()) {
267 default:
268 llvm_unreachable("unknown operand type");
269 break;
270 case MachineOperand::MO_Register:
271 if (MO.isImplicit())
272 break;
273 return MCOperand::createReg(Reg: MO.getReg());
274
275 case MachineOperand::MO_Immediate:
276 return MCOperand::createImm(Val: MO.getImm());
277
278 case MachineOperand::MO_MachineBasicBlock:
279 case MachineOperand::MO_GlobalAddress:
280 case MachineOperand::MO_BlockAddress:
281 case MachineOperand::MO_ExternalSymbol:
282 case MachineOperand::MO_ConstantPoolIndex: {
283 auto RelType = MO.getTargetFlags();
284 const MCSymbol *Symbol = nullptr;
285 switch (MO.getType()) {
286 default:
287 llvm_unreachable("");
288 case MachineOperand::MO_MachineBasicBlock:
289 Symbol = MO.getMBB()->getSymbol();
290 break;
291 case MachineOperand::MO_GlobalAddress:
292 Symbol = getSymbol(GV: MO.getGlobal());
293 break;
294 case MachineOperand::MO_BlockAddress:
295 Symbol = GetBlockAddressSymbol(BA: MO.getBlockAddress());
296 break;
297 case MachineOperand::MO_ExternalSymbol:
298 Symbol = GetExternalSymbolSymbol(Sym: MO.getSymbolName());
299 break;
300 case MachineOperand::MO_ConstantPoolIndex:
301 Symbol = GetCPISymbol(CPID: MO.getIndex());
302 break;
303 }
304
305 const MCExpr *expr = MCSymbolRefExpr::create(Symbol, Ctx&: OutContext);
306 if (RelType)
307 expr = MCSpecifierExpr::create(Expr: expr, S: RelType, Ctx&: OutContext);
308 return MCOperand::createExpr(Val: expr);
309 }
310
311 case MachineOperand::MO_RegisterMask:
312 break;
313 }
314 return MCOperand();
315}
316
317void SparcAsmPrinter::lowerToMCInst(const MachineInstr *MI, MCInst &OutMI) {
318 OutMI.setOpcode(MI->getOpcode());
319
320 for (const MachineOperand &MO : MI->operands()) {
321 MCOperand MCOp = lowerOperand(MO);
322 if (MCOp.isValid())
323 OutMI.addOperand(Op: MCOp);
324 }
325}
326
327void SparcAsmPrinter::emitInstruction(const MachineInstr *MI) {
328 Sparc_MC::verifyInstructionPredicates(Opcode: MI->getOpcode(),
329 Features: getSubtargetInfo().getFeatureBits());
330 if (MI->isBundle()) {
331 const MachineBasicBlock *MBB = MI->getParent();
332 MachineBasicBlock::const_instr_iterator I = ++MI->getIterator();
333 while (I != MBB->instr_end() && I->isInsideBundle()) {
334 emitInstruction(MI: &*I);
335 ++I;
336 }
337 return;
338 }
339
340 switch (MI->getOpcode()) {
341 default: break;
342 case TargetOpcode::DBG_VALUE:
343 // FIXME: Debug Value.
344 return;
345 case SP::CASArr:
346 case SP::SWAPrr:
347 case SP::SWAPri:
348 case SP::LDSTUBrr:
349 case SP::LDSTUBri:
350 case SP::LDSTUBArr:
351 if (MF->getSubtarget<SparcSubtarget>().fixTN0011())
352 OutStreamer->emitCodeAlignment(Alignment: Align(16), STI: getSubtargetInfo());
353 break;
354 case SP::GETPCX:
355 LowerGETPCXAndEmitMCInsts(MI, STI: getSubtargetInfo());
356 return;
357 }
358 MachineBasicBlock::const_instr_iterator I = MI->getIterator();
359 MachineBasicBlock::const_instr_iterator E = MI->getParent()->instr_end();
360 do {
361 MCInst TmpInst;
362 lowerToMCInst(MI: &*I, OutMI&: TmpInst);
363 EmitToStreamer(S&: *OutStreamer, Inst: TmpInst);
364 } while ((++I != E) && I->isInsideBundle()); // Delay slot check.
365}
366
367void SparcAsmPrinter::emitFunctionBodyStart() {
368 if (!MF->getSubtarget<SparcSubtarget>().is64Bit())
369 return;
370
371 const MachineRegisterInfo &MRI = MF->getRegInfo();
372 const unsigned globalRegs[] = { SP::G2, SP::G3, SP::G6, SP::G7, 0 };
373 for (unsigned i = 0; globalRegs[i] != 0; ++i) {
374 unsigned reg = globalRegs[i];
375 if (MRI.use_empty(RegNo: reg))
376 continue;
377
378 if (reg == SP::G6 || reg == SP::G7)
379 getTargetStreamer().emitSparcRegisterIgnore(reg);
380 else
381 getTargetStreamer().emitSparcRegisterScratch(reg);
382 }
383}
384
385void SparcAsmPrinter::printOperand(const MachineInstr *MI, int opNum,
386 raw_ostream &O) {
387 const DataLayout &DL = getDataLayout();
388 const MachineOperand &MO = MI->getOperand(i: opNum);
389 switch (MO.getType()) {
390 case MachineOperand::MO_Register:
391 O << "%" << StringRef(getRegisterName(Reg: MO.getReg())).lower();
392 break;
393
394 case MachineOperand::MO_Immediate:
395 O << MO.getImm();
396 break;
397 case MachineOperand::MO_MachineBasicBlock:
398 MO.getMBB()->getSymbol()->print(OS&: O, MAI);
399 return;
400 case MachineOperand::MO_GlobalAddress:
401 PrintSymbolOperand(MO, O);
402 break;
403 case MachineOperand::MO_BlockAddress:
404 O << GetBlockAddressSymbol(BA: MO.getBlockAddress())->getName();
405 break;
406 case MachineOperand::MO_ExternalSymbol:
407 O << MO.getSymbolName();
408 break;
409 case MachineOperand::MO_ConstantPoolIndex:
410 O << DL.getInternalSymbolPrefix() << "CPI" << getFunctionNumber() << "_"
411 << MO.getIndex();
412 break;
413 case MachineOperand::MO_Metadata:
414 MO.getMetadata()->printAsOperand(OS&: O, M: MMI->getModule());
415 break;
416 default:
417 llvm_unreachable("<unknown operand type>");
418 }
419}
420
421void SparcAsmPrinter::PrintSymbolOperand(const MachineOperand &MO,
422 raw_ostream &O) {
423 const unsigned RelType = MO.getTargetFlags();
424 StringRef Specifier = Sparc::getSpecifierName(S: RelType);
425 if (!Specifier.empty())
426 O << '%' << Specifier << '(';
427 AsmPrinter::PrintSymbolOperand(MO, OS&: O);
428 if (!Specifier.empty())
429 O << ')';
430}
431
432void SparcAsmPrinter::printMemOperand(const MachineInstr *MI, int opNum,
433 raw_ostream &O) {
434 printOperand(MI, opNum, O);
435
436 if (MI->getOperand(i: opNum+1).isReg() &&
437 MI->getOperand(i: opNum+1).getReg() == SP::G0)
438 return; // don't print "+%g0"
439 if (MI->getOperand(i: opNum+1).isImm() &&
440 MI->getOperand(i: opNum+1).getImm() == 0)
441 return; // don't print "+0"
442
443 O << "+";
444 printOperand(MI, opNum: opNum+1, O);
445}
446
447/// PrintAsmOperand - Print out an operand for an inline asm expression.
448///
449bool SparcAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
450 const char *ExtraCode,
451 raw_ostream &O) {
452 if (ExtraCode && ExtraCode[0]) {
453 if (ExtraCode[1] != 0) return true; // Unknown modifier.
454
455 switch (ExtraCode[0]) {
456 default:
457 // See if this is a generic print operand
458 return AsmPrinter::PrintAsmOperand(MI, OpNo, ExtraCode, OS&: O);
459 case 'L': // Low order register of a twin word register operand
460 case 'H': // High order register of a twin word register operand
461 {
462 const SparcSubtarget &Subtarget = MF->getSubtarget<SparcSubtarget>();
463 const MachineOperand &MO = MI->getOperand(i: OpNo);
464 const SparcRegisterInfo *RegisterInfo = Subtarget.getRegisterInfo();
465 Register MOReg = MO.getReg();
466
467 Register HiReg, LoReg;
468 if (!SP::IntPairRegClass.contains(Reg: MOReg)) {
469 // If we aren't given a register pair already, find out which pair it
470 // belongs to. Note that here, the specified register operand, which
471 // refers to the high part of the twinword, needs to be an even-numbered
472 // register.
473 MOReg = RegisterInfo->getMatchingSuperReg(Reg: MOReg, SubIdx: SP::sub_even,
474 RC: &SP::IntPairRegClass);
475 if (!MOReg) {
476 SMLoc Loc;
477 OutContext.reportError(
478 L: Loc, Msg: "Hi part of pair should point to an even-numbered register");
479 OutContext.reportError(
480 L: Loc, Msg: "(note that in some cases it might be necessary to manually "
481 "bind the input/output registers instead of relying on "
482 "automatic allocation)");
483 return true;
484 }
485 }
486
487 HiReg = RegisterInfo->getSubReg(Reg: MOReg, Idx: SP::sub_even);
488 LoReg = RegisterInfo->getSubReg(Reg: MOReg, Idx: SP::sub_odd);
489
490 Register Reg;
491 switch (ExtraCode[0]) {
492 case 'L':
493 Reg = LoReg;
494 break;
495 case 'H':
496 Reg = HiReg;
497 break;
498 }
499
500 O << '%' << SparcInstPrinter::getRegisterName(Reg);
501 return false;
502 }
503 case 'f':
504 case 'r':
505 break;
506 }
507 }
508
509 printOperand(MI, opNum: OpNo, O);
510
511 return false;
512}
513
514bool SparcAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
515 unsigned OpNo,
516 const char *ExtraCode,
517 raw_ostream &O) {
518 if (ExtraCode && ExtraCode[0])
519 return true; // Unknown modifier
520
521 O << '[';
522 printMemOperand(MI, opNum: OpNo, O);
523 O << ']';
524
525 return false;
526}
527
528char SparcAsmPrinter::ID = 0;
529
530INITIALIZE_PASS(SparcAsmPrinter, "sparc-asm-printer", "Sparc Assembly Printer",
531 false, false)
532
533// Force static initialization.
534extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void
535LLVMInitializeSparcAsmPrinter() {
536 RegisterAsmPrinter<SparcAsmPrinter> X(getTheSparcTarget());
537 RegisterAsmPrinter<SparcAsmPrinter> Y(getTheSparcV9Target());
538 RegisterAsmPrinter<SparcAsmPrinter> Z(getTheSparcelTarget());
539}
540