| //===-- X86ATTInstPrinter.cpp - AT&T assembly instruction printing --------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file includes code for rendering MCInst instances as AT&T-style |
| // assembly. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #define DEBUG_TYPE "asm-printer" |
| #include "llvm/MC/MCInst.h" |
| #include "X86ATTAsmPrinter.h" |
| #include "llvm/Support/raw_ostream.h" |
| using namespace llvm; |
| |
| // Include the auto-generated portion of the assembly writer. |
| #define MachineInstr MCInst |
| #define NO_ASM_WRITER_BOILERPLATE |
| #include "X86GenAsmWriter.inc" |
| #undef MachineInstr |
| |
| void X86ATTAsmPrinter::printSSECC(const MCInst *MI, unsigned Op) { |
| unsigned char value = MI->getOperand(Op).getImm(); |
| assert(value <= 7 && "Invalid ssecc argument!"); |
| switch (value) { |
| case 0: O << "eq"; break; |
| case 1: O << "lt"; break; |
| case 2: O << "le"; break; |
| case 3: O << "unord"; break; |
| case 4: O << "neq"; break; |
| case 5: O << "nlt"; break; |
| case 6: O << "nle"; break; |
| case 7: O << "ord"; break; |
| } |
| } |
| |
| |
| void X86ATTAsmPrinter::printPICLabel(const MCInst *MI, unsigned Op) { |
| assert(0 && |
| "This is only used for MOVPC32r, should lower before asm printing!"); |
| } |
| |
| |
| void X86ATTAsmPrinter::printOperand(const MCInst *MI, unsigned OpNo, |
| const char *Modifier, bool NotRIPRel) { |
| //assert(Modifier == 0 && "Modifiers should not be used"); |
| |
| const MCOperand &Op = MI->getOperand(OpNo); |
| if (Op.isReg()) { |
| O << '%'; |
| unsigned Reg = Op.getReg(); |
| #if 0 |
| if (Modifier && strncmp(Modifier, "subreg", strlen("subreg")) == 0) { |
| MVT VT = (strcmp(Modifier+6,"64") == 0) ? |
| MVT::i64 : ((strcmp(Modifier+6, "32") == 0) ? MVT::i32 : |
| ((strcmp(Modifier+6,"16") == 0) ? MVT::i16 : MVT::i8)); |
| Reg = getX86SubSuperRegister(Reg, VT); |
| } |
| #endif |
| O << TRI->getAsmName(Reg); |
| return; |
| } else if (Op.isImm()) { |
| //if (!Modifier || (strcmp(Modifier, "debug") && strcmp(Modifier, "mem") && |
| // strcmp(Modifier, "call"))) |
| O << '$'; |
| O << Op.getImm(); |
| return; |
| } |
| |
| O << "<<UNKNOWN OPERAND KIND>>"; |
| |
| |
| #if 0 |
| const MachineOperand &MO = MI->getOperand(OpNo); |
| switch (MO.getType()) { |
| case MachineOperand::MO_Register: { |
| assert(TargetRegisterInfo::isPhysicalRegister(MO.getReg()) && |
| "Virtual registers should not make it this far!"); |
| O << '%'; |
| unsigned Reg = MO.getReg(); |
| if (Modifier && strncmp(Modifier, "subreg", strlen("subreg")) == 0) { |
| MVT VT = (strcmp(Modifier+6,"64") == 0) ? |
| MVT::i64 : ((strcmp(Modifier+6, "32") == 0) ? MVT::i32 : |
| ((strcmp(Modifier+6,"16") == 0) ? MVT::i16 : MVT::i8)); |
| Reg = getX86SubSuperRegister(Reg, VT); |
| } |
| O << TRI->getAsmName(Reg); |
| return; |
| } |
| |
| case MachineOperand::MO_Immediate: |
| if (!Modifier || (strcmp(Modifier, "debug") && |
| strcmp(Modifier, "mem") && |
| strcmp(Modifier, "call"))) |
| O << '$'; |
| O << MO.getImm(); |
| return; |
| case MachineOperand::MO_MachineBasicBlock: |
| printBasicBlockLabel(MO.getMBB(), false, false, VerboseAsm); |
| return; |
| case MachineOperand::MO_JumpTableIndex: { |
| bool isMemOp = Modifier && !strcmp(Modifier, "mem"); |
| if (!isMemOp) O << '$'; |
| O << TAI->getPrivateGlobalPrefix() << "JTI" << getFunctionNumber() << '_' |
| << MO.getIndex(); |
| |
| if (TM.getRelocationModel() == Reloc::PIC_) { |
| if (Subtarget->isPICStyleStub()) |
| O << "-\"" << TAI->getPrivateGlobalPrefix() << getFunctionNumber() |
| << "$pb\""; |
| else if (Subtarget->isPICStyleGOT()) |
| O << "@GOTOFF"; |
| } |
| |
| if (isMemOp && Subtarget->isPICStyleRIPRel() && !NotRIPRel) |
| O << "(%rip)"; |
| return; |
| } |
| case MachineOperand::MO_ConstantPoolIndex: { |
| bool isMemOp = Modifier && !strcmp(Modifier, "mem"); |
| if (!isMemOp) O << '$'; |
| O << TAI->getPrivateGlobalPrefix() << "CPI" << getFunctionNumber() << '_' |
| << MO.getIndex(); |
| |
| if (TM.getRelocationModel() == Reloc::PIC_) { |
| if (Subtarget->isPICStyleStub()) |
| O << "-\"" << TAI->getPrivateGlobalPrefix() << getFunctionNumber() |
| << "$pb\""; |
| else if (Subtarget->isPICStyleGOT()) |
| O << "@GOTOFF"; |
| } |
| |
| printOffset(MO.getOffset()); |
| |
| if (isMemOp && Subtarget->isPICStyleRIPRel() && !NotRIPRel) |
| O << "(%rip)"; |
| return; |
| } |
| case MachineOperand::MO_GlobalAddress: { |
| bool isCallOp = Modifier && !strcmp(Modifier, "call"); |
| bool isMemOp = Modifier && !strcmp(Modifier, "mem"); |
| bool needCloseParen = false; |
| |
| const GlobalValue *GV = MO.getGlobal(); |
| const GlobalVariable *GVar = dyn_cast<GlobalVariable>(GV); |
| if (!GVar) { |
| // If GV is an alias then use the aliasee for determining |
| // thread-localness. |
| if (const GlobalAlias *GA = dyn_cast<GlobalAlias>(GV)) |
| GVar = dyn_cast_or_null<GlobalVariable>(GA->resolveAliasedGlobal(false)); |
| } |
| |
| bool isThreadLocal = GVar && GVar->isThreadLocal(); |
| |
| std::string Name = Mang->getValueName(GV); |
| decorateName(Name, GV); |
| |
| if (!isMemOp && !isCallOp) |
| O << '$'; |
| else if (Name[0] == '$') { |
| // The name begins with a dollar-sign. In order to avoid having it look |
| // like an integer immediate to the assembler, enclose it in parens. |
| O << '('; |
| needCloseParen = true; |
| } |
| |
| if (shouldPrintStub(TM, Subtarget)) { |
| // Link-once, declaration, or Weakly-linked global variables need |
| // non-lazily-resolved stubs |
| if (GV->isDeclaration() || GV->isWeakForLinker()) { |
| // Dynamically-resolved functions need a stub for the function. |
| if (isCallOp && isa<Function>(GV)) { |
| // Function stubs are no longer needed for Mac OS X 10.5 and up. |
| if (Subtarget->isTargetDarwin() && Subtarget->getDarwinVers() >= 9) { |
| O << Name; |
| } else { |
| FnStubs.insert(Name); |
| printSuffixedName(Name, "$stub"); |
| } |
| } else if (GV->hasHiddenVisibility()) { |
| if (!GV->isDeclaration() && !GV->hasCommonLinkage()) |
| // Definition is not definitely in the current translation unit. |
| O << Name; |
| else { |
| HiddenGVStubs.insert(Name); |
| printSuffixedName(Name, "$non_lazy_ptr"); |
| } |
| } else { |
| GVStubs.insert(Name); |
| printSuffixedName(Name, "$non_lazy_ptr"); |
| } |
| } else { |
| if (GV->hasDLLImportLinkage()) |
| O << "__imp_"; |
| O << Name; |
| } |
| |
| if (!isCallOp && TM.getRelocationModel() == Reloc::PIC_) |
| O << '-' << getPICLabelString(getFunctionNumber(), TAI, Subtarget); |
| } else { |
| if (GV->hasDLLImportLinkage()) { |
| O << "__imp_"; |
| } |
| O << Name; |
| |
| if (isCallOp) { |
| if (shouldPrintPLT(TM, Subtarget)) { |
| // Assemble call via PLT for externally visible symbols |
| if (!GV->hasHiddenVisibility() && !GV->hasProtectedVisibility() && |
| !GV->hasLocalLinkage()) |
| O << "@PLT"; |
| } |
| if (Subtarget->isTargetCygMing() && GV->isDeclaration()) |
| // Save function name for later type emission |
| FnStubs.insert(Name); |
| } |
| } |
| |
| if (GV->hasExternalWeakLinkage()) |
| ExtWeakSymbols.insert(GV); |
| |
| printOffset(MO.getOffset()); |
| |
| if (isThreadLocal) { |
| TLSModel::Model model = getTLSModel(GVar, TM.getRelocationModel()); |
| switch (model) { |
| case TLSModel::GeneralDynamic: |
| O << "@TLSGD"; |
| break; |
| case TLSModel::LocalDynamic: |
| // O << "@TLSLD"; // local dynamic not implemented |
| O << "@TLSGD"; |
| break; |
| case TLSModel::InitialExec: |
| if (Subtarget->is64Bit()) { |
| assert (!NotRIPRel); |
| O << "@GOTTPOFF(%rip)"; |
| } else { |
| O << "@INDNTPOFF"; |
| } |
| break; |
| case TLSModel::LocalExec: |
| if (Subtarget->is64Bit()) |
| O << "@TPOFF"; |
| else |
| O << "@NTPOFF"; |
| break; |
| default: |
| assert (0 && "Unknown TLS model"); |
| } |
| } else if (isMemOp) { |
| if (shouldPrintGOT(TM, Subtarget)) { |
| if (Subtarget->GVRequiresExtraLoad(GV, TM, false)) |
| O << "@GOT"; |
| else |
| O << "@GOTOFF"; |
| } else if (Subtarget->isPICStyleRIPRel() && !NotRIPRel) { |
| if (TM.getRelocationModel() != Reloc::Static) { |
| if (Subtarget->GVRequiresExtraLoad(GV, TM, false)) |
| O << "@GOTPCREL"; |
| |
| if (needCloseParen) { |
| needCloseParen = false; |
| O << ')'; |
| } |
| } |
| |
| // Use rip when possible to reduce code size, except when |
| // index or base register are also part of the address. e.g. |
| // foo(%rip)(%rcx,%rax,4) is not legal |
| O << "(%rip)"; |
| } |
| } |
| |
| if (needCloseParen) |
| O << ')'; |
| |
| return; |
| } |
| case MachineOperand::MO_ExternalSymbol: { |
| bool isCallOp = Modifier && !strcmp(Modifier, "call"); |
| bool isMemOp = Modifier && !strcmp(Modifier, "mem"); |
| bool needCloseParen = false; |
| std::string Name(TAI->getGlobalPrefix()); |
| Name += MO.getSymbolName(); |
| // Print function stub suffix unless it's Mac OS X 10.5 and up. |
| if (isCallOp && shouldPrintStub(TM, Subtarget) && |
| !(Subtarget->isTargetDarwin() && Subtarget->getDarwinVers() >= 9)) { |
| FnStubs.insert(Name); |
| printSuffixedName(Name, "$stub"); |
| return; |
| } |
| if (!isMemOp && !isCallOp) |
| O << '$'; |
| else if (Name[0] == '$') { |
| // The name begins with a dollar-sign. In order to avoid having it look |
| // like an integer immediate to the assembler, enclose it in parens. |
| O << '('; |
| needCloseParen = true; |
| } |
| |
| O << Name; |
| |
| if (shouldPrintPLT(TM, Subtarget)) { |
| std::string GOTName(TAI->getGlobalPrefix()); |
| GOTName+="_GLOBAL_OFFSET_TABLE_"; |
| if (Name == GOTName) |
| // HACK! Emit extra offset to PC during printing GOT offset to |
| // compensate for the size of popl instruction. The resulting code |
| // should look like: |
| // call .piclabel |
| // piclabel: |
| // popl %some_register |
| // addl $_GLOBAL_ADDRESS_TABLE_ + [.-piclabel], %some_register |
| O << " + [.-" |
| << getPICLabelString(getFunctionNumber(), TAI, Subtarget) << ']'; |
| |
| if (isCallOp) |
| O << "@PLT"; |
| } |
| |
| if (needCloseParen) |
| O << ')'; |
| |
| if (!isCallOp && Subtarget->isPICStyleRIPRel()) |
| O << "(%rip)"; |
| |
| return; |
| } |
| default: |
| O << "<unknown operand type>"; return; |
| } |
| #endif |
| } |
| |
| void X86ATTAsmPrinter::printLeaMemReference(const MCInst *MI, unsigned Op, |
| const char *Modifier, |
| bool NotRIPRel) { |
| const MCOperand &BaseReg = MI->getOperand(Op); |
| const MCOperand &IndexReg = MI->getOperand(Op+2); |
| const MCOperand &DispSpec = MI->getOperand(Op+3); |
| |
| NotRIPRel |= IndexReg.getReg() || BaseReg.getReg(); |
| if (DispSpec.isImm()) { |
| int64_t DispVal = DispSpec.getImm(); |
| if (DispVal || (!IndexReg.getReg() && !BaseReg.getReg())) |
| O << DispVal; |
| } else { |
| abort(); |
| //assert(DispSpec.isGlobal() || DispSpec.isCPI() || |
| // DispSpec.isJTI() || DispSpec.isSymbol()); |
| //printOperand(MI, Op+3, "mem", NotRIPRel); |
| } |
| |
| if (IndexReg.getReg() || BaseReg.getReg()) { |
| unsigned ScaleVal = MI->getOperand(Op+1).getImm(); |
| unsigned BaseRegOperand = 0, IndexRegOperand = 2; |
| |
| // There are cases where we can end up with ESP/RSP in the indexreg slot. |
| // If this happens, swap the base/index register to support assemblers that |
| // don't work when the index is *SP. |
| // FIXME: REMOVE THIS. |
| if (IndexReg.getReg() == X86::ESP || IndexReg.getReg() == X86::RSP) { |
| assert(ScaleVal == 1 && "Scale not supported for stack pointer!"); |
| abort(); |
| //std::swap(BaseReg, IndexReg); |
| //std::swap(BaseRegOperand, IndexRegOperand); |
| } |
| |
| O << '('; |
| if (BaseReg.getReg()) |
| printOperand(MI, Op+BaseRegOperand, Modifier); |
| |
| if (IndexReg.getReg()) { |
| O << ','; |
| printOperand(MI, Op+IndexRegOperand, Modifier); |
| if (ScaleVal != 1) |
| O << ',' << ScaleVal; |
| } |
| O << ')'; |
| } |
| } |
| |
| void X86ATTAsmPrinter::printMemReference(const MCInst *MI, unsigned Op, |
| const char *Modifier, bool NotRIPRel){ |
| //assert(isMem(MI, Op) && "Invalid memory reference!"); |
| const MCOperand &Segment = MI->getOperand(Op+4); |
| if (Segment.getReg()) { |
| printOperand(MI, Op+4, Modifier); |
| O << ':'; |
| } |
| printLeaMemReference(MI, Op, Modifier, NotRIPRel); |
| } |