LLVM API Documentation
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 }