blob: 46e7eec4c8eb4f5ce2dcb4c5aa2c28eca0edeef4 [file] [log] [blame]
Scott Michel266bc8f2007-12-04 22:23:35 +00001//===-- SPUAsmPrinter.cpp - Print machine instrs to Cell SPU assembly -------=//
2//
3// The LLVM Compiler Infrastructure
4//
Chris Lattner4ee451d2007-12-29 20:36:04 +00005// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
Scott Michel266bc8f2007-12-04 22:23:35 +00007//
8//===----------------------------------------------------------------------===//
9//
10// This file contains a printer that converts from our internal representation
11// of machine-dependent LLVM code to Cell SPU assembly language. This printer
12// is the output mechanism used by `llc'.
13//
Scott Michel266bc8f2007-12-04 22:23:35 +000014//===----------------------------------------------------------------------===//
15
16#define DEBUG_TYPE "asmprinter"
17#include "SPU.h"
18#include "SPUTargetMachine.h"
19#include "llvm/Constants.h"
20#include "llvm/DerivedTypes.h"
21#include "llvm/Module.h"
22#include "llvm/Assembly/Writer.h"
23#include "llvm/CodeGen/AsmPrinter.h"
24#include "llvm/CodeGen/DwarfWriter.h"
25#include "llvm/CodeGen/MachineModuleInfo.h"
26#include "llvm/CodeGen/MachineFunctionPass.h"
27#include "llvm/CodeGen/MachineInstr.h"
28#include "llvm/Support/Mangler.h"
29#include "llvm/Support/MathExtras.h"
30#include "llvm/Support/CommandLine.h"
31#include "llvm/Support/Debug.h"
32#include "llvm/Support/Compiler.h"
33#include "llvm/Target/TargetAsmInfo.h"
34#include "llvm/Target/MRegisterInfo.h"
35#include "llvm/Target/TargetInstrInfo.h"
36#include "llvm/Target/TargetOptions.h"
37#include "llvm/ADT/Statistic.h"
38#include "llvm/ADT/StringExtras.h"
39#include <set>
40using namespace llvm;
41
42namespace {
43 STATISTIC(EmittedInsts, "Number of machine instrs printed");
44
45 const std::string bss_section(".bss");
46
47 struct VISIBILITY_HIDDEN SPUAsmPrinter : public AsmPrinter {
48 std::set<std::string> FnStubs, GVStubs;
49
50 SPUAsmPrinter(std::ostream &O, TargetMachine &TM, const TargetAsmInfo *T) :
51 AsmPrinter(O, TM, T)
52 {
53 }
54
55 virtual const char *getPassName() const {
56 return "STI CBEA SPU Assembly Printer";
57 }
58
59 SPUTargetMachine &getTM() {
60 return static_cast<SPUTargetMachine&>(TM);
61 }
62
63 /// printInstruction - This method is automatically generated by tablegen
64 /// from the instruction set description. This method returns true if the
65 /// machine instruction was sufficiently described to print it, otherwise it
66 /// returns false.
67 bool printInstruction(const MachineInstr *MI);
68
69 void printMachineInstruction(const MachineInstr *MI);
70 void printOp(const MachineOperand &MO);
71
72 /// printRegister - Print register according to target requirements.
73 ///
74 void printRegister(const MachineOperand &MO, bool R0AsZero) {
75 unsigned RegNo = MO.getReg();
76 assert(MRegisterInfo::isPhysicalRegister(RegNo) && "Not physreg??");
77 O << TM.getRegisterInfo()->get(RegNo).Name;
78 }
79
80 void printOperand(const MachineInstr *MI, unsigned OpNo) {
81 const MachineOperand &MO = MI->getOperand(OpNo);
82 if (MO.isRegister()) {
83 assert(MRegisterInfo::isPhysicalRegister(MO.getReg())&&"Not physreg??");
84 O << TM.getRegisterInfo()->get(MO.getReg()).Name;
85 } else if (MO.isImmediate()) {
86 O << MO.getImmedValue();
87 } else {
88 printOp(MO);
89 }
90 }
91
92 bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
93 unsigned AsmVariant, const char *ExtraCode);
94 bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,
95 unsigned AsmVariant, const char *ExtraCode);
96
97
98 void
99 printS7ImmOperand(const MachineInstr *MI, unsigned OpNo)
100 {
101 int value = MI->getOperand(OpNo).getImmedValue();
102 value = (value << (32 - 7)) >> (32 - 7);
103
104 assert((value >= -(1 << 8) && value <= (1 << 7) - 1)
105 && "Invalid s7 argument");
106 O << value;
107 }
108
109 void
110 printU7ImmOperand(const MachineInstr *MI, unsigned OpNo)
111 {
112 unsigned int value = MI->getOperand(OpNo).getImmedValue();
113 assert(value < (1 << 8) && "Invalid u7 argument");
114 O << value;
115 }
116
117 void
118 printMemRegImmS7(const MachineInstr *MI, unsigned OpNo)
119 {
120 char value = MI->getOperand(OpNo).getImmedValue();
121 O << (int) value;
122 O << "(";
123 printOperand(MI, OpNo+1);
124 O << ")";
125 }
126
127 void
128 printS16ImmOperand(const MachineInstr *MI, unsigned OpNo)
129 {
130 O << (short) MI->getOperand(OpNo).getImmedValue();
131 }
132
133 void
134 printU16ImmOperand(const MachineInstr *MI, unsigned OpNo)
135 {
136 O << (unsigned short)MI->getOperand(OpNo).getImmedValue();
137 }
138
139 void
140 printU32ImmOperand(const MachineInstr *MI, unsigned OpNo)
141 {
142 O << (unsigned)MI->getOperand(OpNo).getImmedValue();
143 }
144
145 void
146 printMemRegReg(const MachineInstr *MI, unsigned OpNo) {
147 // When used as the base register, r0 reads constant zero rather than
148 // the value contained in the register. For this reason, the darwin
149 // assembler requires that we print r0 as 0 (no r) when used as the base.
150 const MachineOperand &MO = MI->getOperand(OpNo);
151 O << TM.getRegisterInfo()->get(MO.getReg()).Name;
152 O << ", ";
153 printOperand(MI, OpNo+1);
154 }
155
156 void
157 printU18ImmOperand(const MachineInstr *MI, unsigned OpNo)
158 {
159 unsigned int value = MI->getOperand(OpNo).getImmedValue();
160 assert(value <= (1 << 19) - 1 && "Invalid u18 argument");
161 O << value;
162 }
163
164 void
165 printS10ImmOperand(const MachineInstr *MI, unsigned OpNo)
166 {
167 short value = (short) (((int) MI->getOperand(OpNo).getImmedValue() << 16)
168 >> 16);
169 assert((value >= -(1 << 9) && value <= (1 << 9) - 1)
170 && "Invalid s10 argument");
171 O << value;
172 }
173
174 void
175 printU10ImmOperand(const MachineInstr *MI, unsigned OpNo)
176 {
177 short value = (short) (((int) MI->getOperand(OpNo).getImmedValue() << 16)
178 >> 16);
179 assert((value <= (1 << 10) - 1) && "Invalid u10 argument");
180 O << value;
181 }
182
183 void
184 printMemRegImmS10(const MachineInstr *MI, unsigned OpNo)
185 {
186 const MachineOperand &MO = MI->getOperand(OpNo);
187 assert(MO.isImmediate()
188 && "printMemRegImmS10 first operand is not immedate");
189 printS10ImmOperand(MI, OpNo);
190 O << "(";
191 printOperand(MI, OpNo+1);
192 O << ")";
193 }
194
195 void
196 printAddr256K(const MachineInstr *MI, unsigned OpNo)
197 {
198 /* Note: operand 1 is an offset or symbol name. Operand 2 is
199 ignored. */
200 if (MI->getOperand(OpNo).isImmediate()) {
201 printS16ImmOperand(MI, OpNo);
202 } else {
203 printOp(MI->getOperand(OpNo));
204 }
205 }
206
207 void printCallOperand(const MachineInstr *MI, unsigned OpNo) {
208 printOp(MI->getOperand(OpNo));
209 }
210
211 void printPCRelativeOperand(const MachineInstr *MI, unsigned OpNo) {
212 printOp(MI->getOperand(OpNo));
213 O << "-.";
214 }
215
216 void printSymbolHi(const MachineInstr *MI, unsigned OpNo) {
217 if (MI->getOperand(OpNo).isImmediate()) {
218 printS16ImmOperand(MI, OpNo);
219 } else {
220 printOp(MI->getOperand(OpNo));
221 O << "@h";
222 }
223 }
224
225 void printSymbolLo(const MachineInstr *MI, unsigned OpNo) {
226 if (MI->getOperand(OpNo).isImmediate()) {
227 printS16ImmOperand(MI, OpNo);
228 } else {
229 printOp(MI->getOperand(OpNo));
230 O << "@l";
231 }
232 }
233
234 /// Print local store address
235 void printSymbolLSA(const MachineInstr *MI, unsigned OpNo) {
236 printOp(MI->getOperand(OpNo));
237 }
238
239 void printROTHNeg7Imm(const MachineInstr *MI, unsigned OpNo) {
240 if (MI->getOperand(OpNo).isImmediate()) {
241 int value = (int) MI->getOperand(OpNo).getImmedValue();
242 assert((value >= 0 && value < 16)
243 && "Invalid negated immediate rotate 7-bit argument");
244 O << -value;
245 } else {
246 assert(0 && "Invalid/non-immediate rotate amount in printRotateNeg7Imm");
247 }
248 }
249
250 void printROTNeg7Imm(const MachineInstr *MI, unsigned OpNo) {
251 if (MI->getOperand(OpNo).isImmediate()) {
252 int value = (int) MI->getOperand(OpNo).getImmedValue();
253 assert((value >= 0 && value < 32)
254 && "Invalid negated immediate rotate 7-bit argument");
255 O << -value;
256 } else {
257 assert(0 && "Invalid/non-immediate rotate amount in printRotateNeg7Imm");
258 }
259 }
260
261 virtual bool runOnMachineFunction(MachineFunction &F) = 0;
262 virtual bool doFinalization(Module &M) = 0;
263 };
264
265 /// LinuxAsmPrinter - SPU assembly printer, customized for Linux
266 struct VISIBILITY_HIDDEN LinuxAsmPrinter : public SPUAsmPrinter {
267
268 DwarfWriter DW;
269
270 LinuxAsmPrinter(std::ostream &O, SPUTargetMachine &TM,
271 const TargetAsmInfo *T) :
272 SPUAsmPrinter(O, TM, T),
273 DW(O, this, T)
274 { }
275
276 virtual const char *getPassName() const {
277 return "STI CBEA SPU Assembly Printer";
278 }
279
280 bool runOnMachineFunction(MachineFunction &F);
281 bool doInitialization(Module &M);
282 bool doFinalization(Module &M);
283
284 void getAnalysisUsage(AnalysisUsage &AU) const {
285 AU.setPreservesAll();
286 AU.addRequired<MachineModuleInfo>();
287 SPUAsmPrinter::getAnalysisUsage(AU);
288 }
289
290 /// getSectionForFunction - Return the section that we should emit the
291 /// specified function body into.
292 virtual std::string getSectionForFunction(const Function &F) const;
293 };
294} // end of anonymous namespace
295
296// Include the auto-generated portion of the assembly writer
297#include "SPUGenAsmWriter.inc"
298
299void SPUAsmPrinter::printOp(const MachineOperand &MO) {
300 switch (MO.getType()) {
301 case MachineOperand::MO_Immediate:
302 cerr << "printOp() does not handle immediate values\n";
303 abort();
304 return;
305
306 case MachineOperand::MO_MachineBasicBlock:
307 printBasicBlockLabel(MO.getMachineBasicBlock());
308 return;
309 case MachineOperand::MO_JumpTableIndex:
310 O << TAI->getPrivateGlobalPrefix() << "JTI" << getFunctionNumber()
311 << '_' << MO.getJumpTableIndex();
312 // FIXME: PIC relocation model
313 return;
314 case MachineOperand::MO_ConstantPoolIndex:
315 O << TAI->getPrivateGlobalPrefix() << "CPI" << getFunctionNumber()
316 << '_' << MO.getConstantPoolIndex();
317 return;
318 case MachineOperand::MO_ExternalSymbol:
319 // Computing the address of an external symbol, not calling it.
320 if (TM.getRelocationModel() != Reloc::Static) {
321 std::string Name(TAI->getGlobalPrefix()); Name += MO.getSymbolName();
322 GVStubs.insert(Name);
323 O << "L" << Name << "$non_lazy_ptr";
324 return;
325 }
326 O << TAI->getGlobalPrefix() << MO.getSymbolName();
327 return;
328 case MachineOperand::MO_GlobalAddress: {
329 // Computing the address of a global symbol, not calling it.
330 GlobalValue *GV = MO.getGlobal();
331 std::string Name = Mang->getValueName(GV);
332
333 // External or weakly linked global variables need non-lazily-resolved
334 // stubs
335 if (TM.getRelocationModel() != Reloc::Static) {
336 if (((GV->isDeclaration() || GV->hasWeakLinkage() ||
337 GV->hasLinkOnceLinkage()))) {
338 GVStubs.insert(Name);
339 O << "L" << Name << "$non_lazy_ptr";
340 return;
341 }
342 }
343 O << Name;
344
345 if (GV->hasExternalWeakLinkage())
346 ExtWeakSymbols.insert(GV);
347 return;
348 }
349
350 default:
351 O << "<unknown operand type: " << MO.getType() << ">";
352 return;
353 }
354}
355
356/// PrintAsmOperand - Print out an operand for an inline asm expression.
357///
358bool SPUAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
359 unsigned AsmVariant,
360 const char *ExtraCode) {
361 // Does this asm operand have a single letter operand modifier?
362 if (ExtraCode && ExtraCode[0]) {
363 if (ExtraCode[1] != 0) return true; // Unknown modifier.
364
365 switch (ExtraCode[0]) {
366 default: return true; // Unknown modifier.
367 case 'L': // Write second word of DImode reference.
368 // Verify that this operand has two consecutive registers.
369 if (!MI->getOperand(OpNo).isRegister() ||
370 OpNo+1 == MI->getNumOperands() ||
371 !MI->getOperand(OpNo+1).isRegister())
372 return true;
373 ++OpNo; // Return the high-part.
374 break;
375 }
376 }
377
378 printOperand(MI, OpNo);
379 return false;
380}
381
382bool SPUAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
383 unsigned OpNo,
384 unsigned AsmVariant,
385 const char *ExtraCode) {
386 if (ExtraCode && ExtraCode[0])
387 return true; // Unknown modifier.
388 printMemRegReg(MI, OpNo);
389 return false;
390}
391
392/// printMachineInstruction -- Print out a single PowerPC MI in Darwin syntax
393/// to the current output stream.
394///
395void SPUAsmPrinter::printMachineInstruction(const MachineInstr *MI) {
396 ++EmittedInsts;
397 printInstruction(MI);
398}
399
400
401
402std::string LinuxAsmPrinter::getSectionForFunction(const Function &F) const {
403 switch (F.getLinkage()) {
404 default: assert(0 && "Unknown linkage type!");
405 case Function::ExternalLinkage:
406 case Function::InternalLinkage: return TAI->getTextSection();
407 case Function::WeakLinkage:
408 case Function::LinkOnceLinkage:
409 return ""; // Print nothing for the time being...
410 }
411}
412
413/// runOnMachineFunction - This uses the printMachineInstruction()
414/// method to print assembly for each instruction.
415///
416bool
417LinuxAsmPrinter::runOnMachineFunction(MachineFunction &MF)
418{
419 DW.SetModuleInfo(&getAnalysis<MachineModuleInfo>());
420
421 SetupMachineFunction(MF);
422 O << "\n\n";
423
424 // Print out constants referenced by the function
425 EmitConstantPool(MF.getConstantPool());
426
427 // Print out labels for the function.
428 const Function *F = MF.getFunction();
429
430 SwitchToTextSection(getSectionForFunction(*F).c_str(), F);
431 EmitAlignment(3, F);
432
433 switch (F->getLinkage()) {
434 default: assert(0 && "Unknown linkage type!");
435 case Function::InternalLinkage: // Symbols default to internal.
436 break;
437 case Function::ExternalLinkage:
438 O << "\t.global\t" << CurrentFnName << "\n"
439 << "\t.type\t" << CurrentFnName << ", @function\n";
440 break;
441 case Function::WeakLinkage:
442 case Function::LinkOnceLinkage:
443 O << "\t.global\t" << CurrentFnName << "\n";
444 O << "\t.weak_definition\t" << CurrentFnName << "\n";
445 break;
446 }
447 O << CurrentFnName << ":\n";
448
449 // Emit pre-function debug information.
450 DW.BeginFunction(&MF);
451
452 // Print out code for the function.
453 for (MachineFunction::const_iterator I = MF.begin(), E = MF.end();
454 I != E; ++I) {
455 // Print a label for the basic block.
456 if (I != MF.begin()) {
457 printBasicBlockLabel(I, true);
458 O << '\n';
459 }
460 for (MachineBasicBlock::const_iterator II = I->begin(), E = I->end();
461 II != E; ++II) {
462 // Print the assembly for the instruction.
463 O << "\t";
464 printMachineInstruction(II);
465 }
466 }
467
468 O << "\t.size\t" << CurrentFnName << ",.-" << CurrentFnName << "\n";
469
470 // Print out jump tables referenced by the function.
471 EmitJumpTableInfo(MF.getJumpTableInfo(), MF);
472
473 // Emit post-function debug information.
474 DW.EndFunction();
475
476 // We didn't modify anything.
477 return false;
478}
479
480
481bool LinuxAsmPrinter::doInitialization(Module &M) {
482 bool Result = AsmPrinter::doInitialization(M);
483 SwitchToTextSection(TAI->getTextSection());
484 // Emit initial debug information.
485 DW.BeginModule(&M);
486 return Result;
487}
488
489bool LinuxAsmPrinter::doFinalization(Module &M) {
490 const TargetData *TD = TM.getTargetData();
491
492 // Print out module-level global variables here.
493 for (Module::const_global_iterator I = M.global_begin(), E = M.global_end();
494 I != E; ++I) {
495 if (!I->hasInitializer()) continue; // External global require no code
496
497 // Check to see if this is a special global used by LLVM, if so, emit it.
498 if (EmitSpecialLLVMGlobal(I))
499 continue;
500
501 std::string name = Mang->getValueName(I);
502 Constant *C = I->getInitializer();
503 unsigned Size = TD->getTypeStoreSize(C->getType());
504 unsigned Align = TD->getPreferredAlignmentLog(I);
505
506 if (C->isNullValue() && /* FIXME: Verify correct */
507 (I->hasInternalLinkage() || I->hasWeakLinkage() ||
508 I->hasLinkOnceLinkage() ||
509 (I->hasExternalLinkage() && !I->hasSection()))) {
510 if (Size == 0) Size = 1; // .comm Foo, 0 is undefined, avoid it.
511 if (I->hasExternalLinkage()) {
512 // External linkage globals -> .bss section
513 // FIXME: Want to set the global variable's section so that
514 // SwitchToDataSection emits the ".section" directive
515 SwitchToDataSection("\t.section\t.bss", I);
516 O << "\t.global\t" << name << '\n';
517 O << "\t.align\t" << Align << '\n';
518 O << "\t.type\t" << name << ", @object\n";
519 O << "\t.size\t" << name << ", " << Size << '\n';
520 O << name << ":\n";
521 O << "\t.zero\t" << Size;
522 } else if (I->hasInternalLinkage()) {
523 SwitchToDataSection("\t.data", I);
524 O << TAI->getLCOMMDirective() << name << "," << Size << "," << Align;
525 } else {
526 SwitchToDataSection("\t.data", I);
527 O << ".comm " << name << "," << Size;
528 }
529 O << "\t\t# '" << I->getName() << "'\n";
530 } else {
531 switch (I->getLinkage()) {
532 case GlobalValue::LinkOnceLinkage:
533 case GlobalValue::WeakLinkage:
534 O << "\t.global " << name << '\n'
535 << "\t.weak_definition " << name << '\n';
536 SwitchToDataSection(".section __DATA,__datacoal_nt,coalesced", I);
537 break;
538 case GlobalValue::AppendingLinkage:
539 // FIXME: appending linkage variables should go into a section of
540 // their name or something. For now, just emit them as external.
541 case GlobalValue::ExternalLinkage:
542 // If external or appending, declare as a global symbol
543 O << "\t.global " << name << "\n";
544 // FALL THROUGH
545 case GlobalValue::InternalLinkage:
546 if (I->isConstant()) {
547 const ConstantArray *CVA = dyn_cast<ConstantArray>(C);
548 if (TAI->getCStringSection() && CVA && CVA->isCString()) {
549 SwitchToDataSection(TAI->getCStringSection(), I);
550 break;
551 }
552 }
553
554 SwitchToDataSection("\t.data", I);
555 break;
556 default:
557 cerr << "Unknown linkage type!";
558 abort();
559 }
560
561 EmitAlignment(Align, I);
562 O << name << ":\t\t\t\t# '" << I->getName() << "'\n";
563
564 // If the initializer is a extern weak symbol, remember to emit the weak
565 // reference!
566 if (const GlobalValue *GV = dyn_cast<GlobalValue>(C))
567 if (GV->hasExternalWeakLinkage())
568 ExtWeakSymbols.insert(GV);
569
570 EmitGlobalConstant(C);
571 O << '\n';
572 }
573 }
574
575 // Output stubs for dynamically-linked functions
576 if (TM.getRelocationModel() == Reloc::PIC_) {
577 for (std::set<std::string>::iterator i = FnStubs.begin(), e = FnStubs.end();
578 i != e; ++i) {
579 SwitchToTextSection(".section __TEXT,__picsymbolstub1,symbol_stubs,"
580 "pure_instructions,32");
581 EmitAlignment(4);
582 O << "L" << *i << "$stub:\n";
583 O << "\t.indirect_symbol " << *i << "\n";
584 O << "\tmflr r0\n";
585 O << "\tbcl 20,31,L0$" << *i << "\n";
586 O << "L0$" << *i << ":\n";
587 O << "\tmflr r11\n";
588 O << "\taddis r11,r11,ha16(L" << *i << "$lazy_ptr-L0$" << *i << ")\n";
589 O << "\tmtlr r0\n";
590 O << "\tlwzu r12,lo16(L" << *i << "$lazy_ptr-L0$" << *i << ")(r11)\n";
591 O << "\tmtctr r12\n";
592 O << "\tbctr\n";
593 SwitchToDataSection(".lazy_symbol_pointer");
594 O << "L" << *i << "$lazy_ptr:\n";
595 O << "\t.indirect_symbol " << *i << "\n";
596 O << "\t.long dyld_stub_binding_helper\n";
597 }
598 } else {
599 for (std::set<std::string>::iterator i = FnStubs.begin(), e = FnStubs.end();
600 i != e; ++i) {
601 SwitchToTextSection(".section __TEXT,__symbol_stub1,symbol_stubs,"
602 "pure_instructions,16");
603 EmitAlignment(4);
604 O << "L" << *i << "$stub:\n";
605 O << "\t.indirect_symbol " << *i << "\n";
606 O << "\tlis r11,ha16(L" << *i << "$lazy_ptr)\n";
607 O << "\tlwzu r12,lo16(L" << *i << "$lazy_ptr)(r11)\n";
608 O << "\tmtctr r12\n";
609 O << "\tbctr\n";
610 SwitchToDataSection(".lazy_symbol_pointer");
611 O << "L" << *i << "$lazy_ptr:\n";
612 O << "\t.indirect_symbol " << *i << "\n";
613 O << "\t.long dyld_stub_binding_helper\n";
614 }
615 }
616
617 O << "\n";
618
619 // Output stubs for external and common global variables.
620 if (GVStubs.begin() != GVStubs.end()) {
621 SwitchToDataSection(".non_lazy_symbol_pointer");
622 for (std::set<std::string>::iterator I = GVStubs.begin(),
623 E = GVStubs.end(); I != E; ++I) {
624 O << "L" << *I << "$non_lazy_ptr:\n";
625 O << "\t.indirect_symbol " << *I << "\n";
626 O << "\t.long\t0\n";
627 }
628 }
629
630 // Emit initial debug information.
631 DW.EndModule();
632
633 // Emit ident information
Scott Michel86c041f2007-12-20 00:44:13 +0000634 O << "\t.ident\t\"(llvm 2.2+) STI CBEA Cell SPU backend\"\n";
Scott Michel266bc8f2007-12-04 22:23:35 +0000635
636 return AsmPrinter::doFinalization(M);
637}
638
639
640
641/// createSPUCodePrinterPass - Returns a pass that prints the Cell SPU
642/// assembly code for a MachineFunction to the given output stream, in a format
643/// that the Linux SPU assembler can deal with.
644///
645FunctionPass *llvm::createSPUAsmPrinterPass(std::ostream &o,
646 SPUTargetMachine &tm) {
647 return new LinuxAsmPrinter(o, tm, tm.getTargetAsmInfo());
648}
649