LLVM API Documentation

SPUAsmPrinter.cpp

Go to the documentation of this file.
00001 //===-- SPUAsmPrinter.cpp - Print machine instrs to Cell SPU assembly -------=//
00002 //
00003 //                     The LLVM Compiler Infrastructure
00004 //
00005 // This file is distributed under the University of Illinois Open Source
00006 // License. See LICENSE.TXT for details.
00007 //
00008 //===----------------------------------------------------------------------===//
00009 //
00010 // This file contains a printer that converts from our internal representation
00011 // of machine-dependent LLVM code to Cell SPU assembly language. This printer
00012 // is the output mechanism used by `llc'.
00013 //
00014 //===----------------------------------------------------------------------===//
00015 
00016 #define DEBUG_TYPE "asmprinter"
00017 #include "SPU.h"
00018 #include "SPUTargetMachine.h"
00019 #include "llvm/Constants.h"
00020 #include "llvm/DerivedTypes.h"
00021 #include "llvm/Module.h"
00022 #include "llvm/Assembly/Writer.h"
00023 #include "llvm/CodeGen/AsmPrinter.h"
00024 #include "llvm/CodeGen/DwarfWriter.h"
00025 #include "llvm/CodeGen/MachineModuleInfo.h"
00026 #include "llvm/CodeGen/MachineFunctionPass.h"
00027 #include "llvm/CodeGen/MachineInstr.h"
00028 #include "llvm/Support/Mangler.h"
00029 #include "llvm/Support/MathExtras.h"
00030 #include "llvm/Support/CommandLine.h"
00031 #include "llvm/Support/Debug.h"
00032 #include "llvm/Support/Compiler.h"
00033 #include "llvm/Support/raw_ostream.h"
00034 #include "llvm/Target/TargetAsmInfo.h"
00035 #include "llvm/Target/TargetRegisterInfo.h"
00036 #include "llvm/Target/TargetInstrInfo.h"
00037 #include "llvm/Target/TargetOptions.h"
00038 #include "llvm/ADT/Statistic.h"
00039 #include "llvm/ADT/StringExtras.h"
00040 #include <set>
00041 using namespace llvm;
00042 
00043 namespace {
00044   STATISTIC(EmittedInsts, "Number of machine instrs printed");
00045 
00046   const std::string bss_section(".bss");
00047 
00048   struct VISIBILITY_HIDDEN SPUAsmPrinter : public AsmPrinter {
00049     std::set<std::string> FnStubs, GVStubs;
00050 
00051     SPUAsmPrinter(raw_ostream &O, TargetMachine &TM, const TargetAsmInfo *T) :
00052       AsmPrinter(O, TM, T)
00053     {
00054     }
00055 
00056     virtual const char *getPassName() const {
00057       return "STI CBEA SPU Assembly Printer";
00058     }
00059 
00060     SPUTargetMachine &getTM() {
00061       return static_cast<SPUTargetMachine&>(TM);
00062     }
00063 
00064     /// printInstruction - This method is automatically generated by tablegen
00065     /// from the instruction set description.  This method returns true if the
00066     /// machine instruction was sufficiently described to print it, otherwise it
00067     /// returns false.
00068     bool printInstruction(const MachineInstr *MI);
00069 
00070     void printMachineInstruction(const MachineInstr *MI);
00071     void printOp(const MachineOperand &MO);
00072 
00073     /// printRegister - Print register according to target requirements.
00074     ///
00075     void printRegister(const MachineOperand &MO, bool R0AsZero) {
00076       unsigned RegNo = MO.getReg();
00077       assert(TargetRegisterInfo::isPhysicalRegister(RegNo) &&
00078              "Not physreg??");
00079       O << TM.getRegisterInfo()->get(RegNo).AsmName;
00080     }
00081 
00082     void printOperand(const MachineInstr *MI, unsigned OpNo) {
00083       const MachineOperand &MO = MI->getOperand(OpNo);
00084       if (MO.isReg()) {
00085         assert(TargetRegisterInfo::isPhysicalRegister(MO.getReg())&&"Not physreg??");
00086         O << TM.getRegisterInfo()->get(MO.getReg()).AsmName;
00087       } else if (MO.isImm()) {
00088         O << MO.getImm();
00089       } else {
00090         printOp(MO);
00091       }
00092     }
00093 
00094     bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
00095                          unsigned AsmVariant, const char *ExtraCode);
00096     bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo,
00097                                unsigned AsmVariant, const char *ExtraCode);
00098 
00099 
00100     void
00101     printS7ImmOperand(const MachineInstr *MI, unsigned OpNo)
00102     {
00103       int value = MI->getOperand(OpNo).getImm();
00104       value = (value << (32 - 7)) >> (32 - 7);
00105 
00106       assert((value >= -(1 << 8) && value <= (1 << 7) - 1)
00107              && "Invalid s7 argument");
00108       O << value;
00109     }
00110 
00111     void
00112     printU7ImmOperand(const MachineInstr *MI, unsigned OpNo)
00113     {
00114       unsigned int value = MI->getOperand(OpNo).getImm();
00115       assert(value < (1 << 8) && "Invalid u7 argument");
00116       O << value;
00117     }
00118 
00119     void
00120     printShufAddr(const MachineInstr *MI, unsigned OpNo)
00121     {
00122       char value = MI->getOperand(OpNo).getImm();
00123       O << (int) value;
00124       O << "(";
00125       printOperand(MI, OpNo+1);
00126       O << ")";
00127     }
00128 
00129     void
00130     printS16ImmOperand(const MachineInstr *MI, unsigned OpNo)
00131     {
00132       O << (short) MI->getOperand(OpNo).getImm();
00133     }
00134 
00135     void
00136     printU16ImmOperand(const MachineInstr *MI, unsigned OpNo)
00137     {
00138       O << (unsigned short)MI->getOperand(OpNo).getImm();
00139     }
00140 
00141     void
00142     printU32ImmOperand(const MachineInstr *MI, unsigned OpNo)
00143     {
00144       O << (unsigned)MI->getOperand(OpNo).getImm();
00145     }
00146 
00147     void
00148     printMemRegReg(const MachineInstr *MI, unsigned OpNo) {
00149       // When used as the base register, r0 reads constant zero rather than
00150       // the value contained in the register.  For this reason, the darwin
00151       // assembler requires that we print r0 as 0 (no r) when used as the base.
00152       const MachineOperand &MO = MI->getOperand(OpNo);
00153       O << TM.getRegisterInfo()->get(MO.getReg()).AsmName;
00154       O << ", ";
00155       printOperand(MI, OpNo+1);
00156     }
00157 
00158     void
00159     printU18ImmOperand(const MachineInstr *MI, unsigned OpNo)
00160     {
00161       unsigned int value = MI->getOperand(OpNo).getImm();
00162       assert(value <= (1 << 19) - 1 && "Invalid u18 argument");
00163       O << value;
00164     }
00165 
00166     void
00167     printS10ImmOperand(const MachineInstr *MI, unsigned OpNo)
00168     {
00169       short value = (short) (((int) MI->getOperand(OpNo).getImm() << 16)
00170                              >> 16);
00171       assert((value >= -(1 << 9) && value <= (1 << 9) - 1)
00172              && "Invalid s10 argument");
00173       O << value;
00174     }
00175 
00176     void
00177     printU10ImmOperand(const MachineInstr *MI, unsigned OpNo)
00178     {
00179       short value = (short) (((int) MI->getOperand(OpNo).getImm() << 16)
00180                              >> 16);
00181       assert((value <= (1 << 10) - 1) && "Invalid u10 argument");
00182       O << value;
00183     }
00184 
00185     void
00186     printDFormAddr(const MachineInstr *MI, unsigned OpNo)
00187     {
00188       assert(MI->getOperand(OpNo).isImm() &&
00189              "printDFormAddr first operand is not immediate");
00190       int64_t value = int64_t(MI->getOperand(OpNo).getImm());
00191       int16_t value16 = int16_t(value);
00192       assert((value16 >= -(1 << (9+4)) && value16 <= (1 << (9+4)) - 1)
00193              && "Invalid dform s10 offset argument");
00194       O << (value16 & ~0xf) << "(";
00195       printOperand(MI, OpNo+1);
00196       O << ")";
00197     }
00198 
00199     void
00200     printAddr256K(const MachineInstr *MI, unsigned OpNo)
00201     {
00202       /* Note: operand 1 is an offset or symbol name. */
00203       if (MI->getOperand(OpNo).isImm()) {
00204         printS16ImmOperand(MI, OpNo);
00205       } else {
00206         printOp(MI->getOperand(OpNo));
00207         if (MI->getOperand(OpNo+1).isImm()) {
00208           int displ = int(MI->getOperand(OpNo+1).getImm());
00209           if (displ > 0)
00210             O << "+" << displ;
00211           else if (displ < 0)
00212             O << displ;
00213         }
00214       }
00215     }
00216 
00217     void printCallOperand(const MachineInstr *MI, unsigned OpNo) {
00218       printOp(MI->getOperand(OpNo));
00219     }
00220 
00221     void printPCRelativeOperand(const MachineInstr *MI, unsigned OpNo) {
00222       // Used to generate a ".-<target>", but it turns out that the assembler
00223       // really wants the target.
00224       //
00225       // N.B.: This operand is used for call targets. Branch hints are another
00226       // animal entirely.
00227       printOp(MI->getOperand(OpNo));
00228     }
00229 
00230     void printHBROperand(const MachineInstr *MI, unsigned OpNo) {
00231       // HBR operands are generated in front of branches, hence, the
00232       // program counter plus the target.
00233       O << ".+";
00234       printOp(MI->getOperand(OpNo));
00235     }
00236 
00237     void printSymbolHi(const MachineInstr *MI, unsigned OpNo) {
00238       if (MI->getOperand(OpNo).isImm()) {
00239         printS16ImmOperand(MI, OpNo);
00240       } else {
00241         printOp(MI->getOperand(OpNo));
00242         O << "@h";
00243       }
00244     }
00245 
00246     void printSymbolLo(const MachineInstr *MI, unsigned OpNo) {
00247       if (MI->getOperand(OpNo).isImm()) {
00248         printS16ImmOperand(MI, OpNo);
00249       } else {
00250         printOp(MI->getOperand(OpNo));
00251         O << "@l";
00252       }
00253     }
00254 
00255     /// Print local store address
00256     void printSymbolLSA(const MachineInstr *MI, unsigned OpNo) {
00257       printOp(MI->getOperand(OpNo));
00258     }
00259 
00260     void printROTHNeg7Imm(const MachineInstr *MI, unsigned OpNo) {
00261       if (MI->getOperand(OpNo).isImm()) {
00262         int value = (int) MI->getOperand(OpNo).getImm();
00263         assert((value >= 0 && value < 16)
00264                && "Invalid negated immediate rotate 7-bit argument");
00265         O << -value;
00266       } else {
00267         assert(0 &&"Invalid/non-immediate rotate amount in printRotateNeg7Imm");
00268       }
00269     }
00270 
00271     void printROTNeg7Imm(const MachineInstr *MI, unsigned OpNo) {
00272       if (MI->getOperand(OpNo).isImm()) {
00273         int value = (int) MI->getOperand(OpNo).getImm();
00274         assert((value >= 0 && value <= 32)
00275                && "Invalid negated immediate rotate 7-bit argument");
00276         O << -value;
00277       } else {
00278         assert(0 &&"Invalid/non-immediate rotate amount in printRotateNeg7Imm");
00279       }
00280     }
00281 
00282     virtual bool runOnMachineFunction(MachineFunction &F) = 0;
00283     //! Assembly printer cleanup after function has been emitted
00284     virtual bool doFinalization(Module &M) = 0;
00285   };
00286 
00287   /// LinuxAsmPrinter - SPU assembly printer, customized for Linux
00288   struct VISIBILITY_HIDDEN LinuxAsmPrinter : public SPUAsmPrinter {
00289 
00290     DwarfWriter *DW;
00291     MachineModuleInfo *MMI;
00292 
00293     LinuxAsmPrinter(raw_ostream &O, SPUTargetMachine &TM,
00294                     const TargetAsmInfo *T) :
00295       SPUAsmPrinter(O, TM, T),
00296       DW(0),
00297       MMI(0)
00298     { }
00299 
00300     virtual const char *getPassName() const {
00301       return "STI CBEA SPU Assembly Printer";
00302     }
00303 
00304     bool runOnMachineFunction(MachineFunction &F);
00305     bool doInitialization(Module &M);
00306     //! Dump globals, perform cleanup after function emission
00307     bool doFinalization(Module &M);
00308 
00309     void getAnalysisUsage(AnalysisUsage &AU) const {
00310       AU.setPreservesAll();
00311       AU.addRequired<MachineModuleInfo>();
00312       AU.addRequired<DwarfWriter>();
00313       SPUAsmPrinter::getAnalysisUsage(AU);
00314     }
00315 
00316     //! Emit a global variable according to its section and type
00317     void printModuleLevelGV(const GlobalVariable* GVar);
00318   };
00319 } // end of anonymous namespace
00320 
00321 // Include the auto-generated portion of the assembly writer
00322 #include "SPUGenAsmWriter.inc"
00323 
00324 void SPUAsmPrinter::printOp(const MachineOperand &MO) {
00325   switch (MO.getType()) {
00326   case MachineOperand::MO_Immediate:
00327     cerr << "printOp() does not handle immediate values\n";
00328     abort();
00329     return;
00330 
00331   case MachineOperand::MO_MachineBasicBlock:
00332     printBasicBlockLabel(MO.getMBB());
00333     return;
00334   case MachineOperand::MO_JumpTableIndex:
00335     O << TAI->getPrivateGlobalPrefix() << "JTI" << getFunctionNumber()
00336       << '_' << MO.getIndex();
00337     return;
00338   case MachineOperand::MO_ConstantPoolIndex:
00339     O << TAI->getPrivateGlobalPrefix() << "CPI" << getFunctionNumber()
00340       << '_' << MO.getIndex();
00341     return;
00342   case MachineOperand::MO_ExternalSymbol:
00343     // Computing the address of an external symbol, not calling it.
00344     if (TM.getRelocationModel() != Reloc::Static) {
00345       std::string Name(TAI->getGlobalPrefix()); Name += MO.getSymbolName();
00346       GVStubs.insert(Name);
00347       O << "L" << Name << "$non_lazy_ptr";
00348       return;
00349     }
00350     O << TAI->getGlobalPrefix() << MO.getSymbolName();
00351     return;
00352   case MachineOperand::MO_GlobalAddress: {
00353     // Computing the address of a global symbol, not calling it.
00354     GlobalValue *GV = MO.getGlobal();
00355     std::string Name = Mang->getValueName(GV);
00356 
00357     // External or weakly linked global variables need non-lazily-resolved
00358     // stubs
00359     if (TM.getRelocationModel() != Reloc::Static) {
00360       if (((GV->isDeclaration() || GV->hasWeakLinkage() ||
00361             GV->hasLinkOnceLinkage() || GV->hasCommonLinkage()))) {
00362         GVStubs.insert(Name);
00363         O << "L" << Name << "$non_lazy_ptr";
00364         return;
00365       }
00366     }
00367     O << Name;
00368 
00369     if (GV->hasExternalWeakLinkage())
00370       ExtWeakSymbols.insert(GV);
00371     return;
00372   }
00373 
00374   default:
00375     O << "<unknown operand type: " << MO.getType() << ">";
00376     return;
00377   }
00378 }
00379 
00380 /// PrintAsmOperand - Print out an operand for an inline asm expression.
00381 ///
00382 bool SPUAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
00383                                     unsigned AsmVariant,
00384                                     const char *ExtraCode) {
00385   // Does this asm operand have a single letter operand modifier?
00386   if (ExtraCode && ExtraCode[0]) {
00387     if (ExtraCode[1] != 0) return true; // Unknown modifier.
00388 
00389     switch (ExtraCode[0]) {
00390     default: return true;  // Unknown modifier.
00391     case 'L': // Write second word of DImode reference.
00392       // Verify that this operand has two consecutive registers.
00393       if (!MI->getOperand(OpNo).isReg() ||
00394           OpNo+1 == MI->getNumOperands() ||
00395           !MI->getOperand(OpNo+1).isReg())
00396         return true;
00397       ++OpNo;   // Return the high-part.
00398       break;
00399     }
00400   }
00401 
00402   printOperand(MI, OpNo);
00403   return false;
00404 }
00405 
00406 bool SPUAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
00407                                           unsigned OpNo,
00408                                           unsigned AsmVariant,
00409                                           const char *ExtraCode) {
00410   if (ExtraCode && ExtraCode[0])
00411     return true; // Unknown modifier.
00412   printMemRegReg(MI, OpNo);
00413   return false;
00414 }
00415 
00416 /// printMachineInstruction -- Print out a single PowerPC MI in Darwin syntax
00417 /// to the current output stream.
00418 ///
00419 void SPUAsmPrinter::printMachineInstruction(const MachineInstr *MI) {
00420   ++EmittedInsts;
00421   printInstruction(MI);
00422 }
00423 
00424 /// runOnMachineFunction - This uses the printMachineInstruction()
00425 /// method to print assembly for each instruction.
00426 ///
00427 bool
00428 LinuxAsmPrinter::runOnMachineFunction(MachineFunction &MF)
00429 {
00430   SetupMachineFunction(MF);
00431   O << "\n\n";
00432 
00433   // Print out constants referenced by the function
00434   EmitConstantPool(MF.getConstantPool());
00435 
00436   // Print out labels for the function.
00437   const Function *F = MF.getFunction();
00438 
00439   SwitchToSection(TAI->SectionForGlobal(F));
00440   EmitAlignment(3, F);
00441 
00442   switch (F->getLinkage()) {
00443   default: assert(0 && "Unknown linkage type!");
00444   case Function::PrivateLinkage:
00445   case Function::InternalLinkage:  // Symbols default to internal.
00446     break;
00447   case Function::ExternalLinkage:
00448     O << "\t.global\t" << CurrentFnName << "\n"
00449       << "\t.type\t" << CurrentFnName << ", @function\n";
00450     break;
00451   case Function::WeakLinkage:
00452   case Function::LinkOnceLinkage:
00453     O << "\t.global\t" << CurrentFnName << "\n";
00454     O << "\t.weak_definition\t" << CurrentFnName << "\n";
00455     break;
00456   }
00457   O << CurrentFnName << ":\n";
00458 
00459   // Emit pre-function debug information.
00460   DW->BeginFunction(&MF);
00461 
00462   // Print out code for the function.
00463   for (MachineFunction::const_iterator I = MF.begin(), E = MF.end();
00464        I != E; ++I) {
00465     // Print a label for the basic block.
00466     if (I != MF.begin()) {
00467       printBasicBlockLabel(I, true, true);
00468       O << '\n';
00469     }
00470     for (MachineBasicBlock::const_iterator II = I->begin(), E = I->end();
00471          II != E; ++II) {
00472       // Print the assembly for the instruction.
00473       printMachineInstruction(II);
00474     }
00475   }
00476 
00477   O << "\t.size\t" << CurrentFnName << ",.-" << CurrentFnName << "\n";
00478 
00479   // Print out jump tables referenced by the function.
00480   EmitJumpTableInfo(MF.getJumpTableInfo(), MF);
00481 
00482   // Emit post-function debug information.
00483   DW->EndFunction(&MF);
00484 
00485   // We didn't modify anything.
00486   return false;
00487 }
00488 
00489 
00490 bool LinuxAsmPrinter::doInitialization(Module &M) {
00491   bool Result = AsmPrinter::doInitialization(M);
00492   SwitchToTextSection("\t.text");
00493   // Emit initial debug information.
00494   DW = getAnalysisIfAvailable<DwarfWriter>();
00495   assert(DW && "Dwarf Writer is not available");
00496   MMI = getAnalysisIfAvailable<MachineModuleInfo>();
00497   DW->BeginModule(&M, MMI, O, this, TAI);
00498   return Result;
00499 }
00500 
00501 /// PrintUnmangledNameSafely - Print out the printable characters in the name.
00502 /// Don't print things like \n or \0.
00503 static void PrintUnmangledNameSafely(const Value *V, raw_ostream &OS) {
00504   for (const char *Name = V->getNameStart(), *E = Name+V->getNameLen();
00505        Name != E; ++Name)
00506     if (isprint(*Name))
00507       OS << *Name;
00508 }
00509 
00510 /*!
00511   Emit a global variable according to its section, alignment, etc.
00512 
00513   \note This code was shamelessly copied from the PowerPC's assembly printer,
00514   which sort of screams for some kind of refactorization of common code.
00515  */
00516 void LinuxAsmPrinter::printModuleLevelGV(const GlobalVariable* GVar) {
00517   const TargetData *TD = TM.getTargetData();
00518 
00519   if (!GVar->hasInitializer())
00520     return;
00521 
00522   // Check to see if this is a special global used by LLVM, if so, emit it.
00523   if (EmitSpecialLLVMGlobal(GVar))
00524     return;
00525 
00526   std::string name = Mang->getValueName(GVar);
00527 
00528   printVisibility(name, GVar->getVisibility());
00529 
00530   Constant *C = GVar->getInitializer();
00531   const Type *Type = C->getType();
00532   unsigned Size = TD->getTypePaddedSize(Type);
00533   unsigned Align = TD->getPreferredAlignmentLog(GVar);
00534 
00535   SwitchToSection(TAI->SectionForGlobal(GVar));
00536 
00537   if (C->isNullValue() && /* FIXME: Verify correct */
00538       !GVar->hasSection() &&
00539       (GVar->hasLocalLinkage() || GVar->hasExternalLinkage() ||
00540        GVar->mayBeOverridden())) {
00541       if (Size == 0) Size = 1;   // .comm Foo, 0 is undefined, avoid it.
00542 
00543       if (GVar->hasExternalLinkage()) {
00544         O << "\t.global " << name << '\n';
00545         O << "\t.type " << name << ", @object\n";
00546         O << name << ":\n";
00547         O << "\t.zero " << Size << '\n';
00548       } else if (GVar->hasLocalLinkage()) {
00549         O << TAI->getLCOMMDirective() << name << ',' << Size;
00550       } else {
00551         O << ".comm " << name << ',' << Size;
00552       }
00553       O << "\t\t" << TAI->getCommentString() << " '";
00554       PrintUnmangledNameSafely(GVar, O);
00555       O << "'\n";
00556       return;
00557   }
00558 
00559   switch (GVar->getLinkage()) {
00560     // Should never be seen for the CellSPU platform...
00561    case GlobalValue::LinkOnceLinkage:
00562    case GlobalValue::WeakLinkage:
00563    case GlobalValue::CommonLinkage:
00564     O << "\t.global " << name << '\n'
00565       << "\t.type " << name << ", @object\n"
00566       << "\t.weak " << name << '\n';
00567     break;
00568    case GlobalValue::AppendingLinkage:
00569     // FIXME: appending linkage variables should go into a section of
00570     // their name or something.  For now, just emit them as external.
00571    case GlobalValue::ExternalLinkage:
00572     // If external or appending, declare as a global symbol
00573     O << "\t.global " << name << '\n'
00574       << "\t.type " << name << ", @object\n";
00575     // FALL THROUGH
00576    case GlobalValue::PrivateLinkage:
00577    case GlobalValue::InternalLinkage:
00578     break;
00579    default:
00580     cerr << "Unknown linkage type!";
00581     abort();
00582   }
00583 
00584   EmitAlignment(Align, GVar);
00585   O << name << ":\t\t\t\t" << TAI->getCommentString() << " '";
00586   PrintUnmangledNameSafely(GVar, O);
00587   O << "'\n";
00588 
00589   // If the initializer is a extern weak symbol, remember to emit the weak
00590   // reference!
00591   if (const GlobalValue *GV = dyn_cast<GlobalValue>(C))
00592     if (GV->hasExternalWeakLinkage())
00593       ExtWeakSymbols.insert(GV);
00594 
00595   EmitGlobalConstant(C);
00596   O << '\n';
00597 }
00598 
00599 bool LinuxAsmPrinter::doFinalization(Module &M) {
00600   // Print out module-level global variables here.
00601   for (Module::const_global_iterator I = M.global_begin(), E = M.global_end();
00602        I != E; ++I)
00603     printModuleLevelGV(I);
00604 
00605   // Emit initial debug information.
00606   DW->EndModule();
00607 
00608   return AsmPrinter::doFinalization(M);
00609 }
00610 
00611 /// createSPUCodePrinterPass - Returns a pass that prints the Cell SPU
00612 /// assembly code for a MachineFunction to the given output stream, in a format
00613 /// that the Linux SPU assembler can deal with.
00614 ///
00615 FunctionPass *llvm::createSPUAsmPrinterPass(raw_ostream &o,
00616                                             SPUTargetMachine &tm) {
00617   return new LinuxAsmPrinter(o, tm, tm.getTargetAsmInfo());
00618 }