| // Copyright 2014 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package x86asm |
| |
| import ( |
| "fmt" |
| "strings" |
| ) |
| |
| // GNUSyntax returns the GNU assembler syntax for the instruction, as defined by GNU binutils. |
| // This general form is often called ``AT&T syntax'' as a reference to AT&T System V Unix. |
| func GNUSyntax(inst Inst) string { |
| // Rewrite instruction to mimic GNU peculiarities. |
| // Note that inst has been passed by value and contains |
| // no pointers, so any changes we make here are local |
| // and will not propagate back out to the caller. |
| |
| // Adjust opcode [sic]. |
| switch inst.Op { |
| case FDIV, FDIVR, FSUB, FSUBR, FDIVP, FDIVRP, FSUBP, FSUBRP: |
| // DC E0, DC F0: libopcodes swaps FSUBR/FSUB and FDIVR/FDIV, at least |
| // if you believe the Intel manual is correct (the encoding is irregular as given; |
| // libopcodes uses the more regular expected encoding). |
| // TODO(rsc): Test to ensure Intel manuals are correct and report to libopcodes maintainers? |
| // NOTE: iant thinks this is deliberate, but we can't find the history. |
| _, reg1 := inst.Args[0].(Reg) |
| _, reg2 := inst.Args[1].(Reg) |
| if reg1 && reg2 && (inst.Opcode>>24 == 0xDC || inst.Opcode>>24 == 0xDE) { |
| switch inst.Op { |
| case FDIV: |
| inst.Op = FDIVR |
| case FDIVR: |
| inst.Op = FDIV |
| case FSUB: |
| inst.Op = FSUBR |
| case FSUBR: |
| inst.Op = FSUB |
| case FDIVP: |
| inst.Op = FDIVRP |
| case FDIVRP: |
| inst.Op = FDIVP |
| case FSUBP: |
| inst.Op = FSUBRP |
| case FSUBRP: |
| inst.Op = FSUBP |
| } |
| } |
| |
| case MOVNTSD: |
| // MOVNTSD is F2 0F 2B /r. |
| // MOVNTSS is F3 0F 2B /r (supposedly; not in manuals). |
| // Usually inner prefixes win for display, |
| // so that F3 F2 0F 2B 11 is REP MOVNTSD |
| // and F2 F3 0F 2B 11 is REPN MOVNTSS. |
| // Libopcodes always prefers MOVNTSS regardless of prefix order. |
| if countPrefix(&inst, 0xF3) > 0 { |
| found := false |
| for i := len(inst.Prefix) - 1; i >= 0; i-- { |
| switch inst.Prefix[i] & 0xFF { |
| case 0xF3: |
| if !found { |
| found = true |
| inst.Prefix[i] |= PrefixImplicit |
| } |
| case 0xF2: |
| inst.Prefix[i] &^= PrefixImplicit |
| } |
| } |
| inst.Op = MOVNTSS |
| } |
| } |
| |
| // Add implicit arguments. |
| switch inst.Op { |
| case MONITOR: |
| inst.Args[0] = EDX |
| inst.Args[1] = ECX |
| inst.Args[2] = EAX |
| if inst.AddrSize == 16 { |
| inst.Args[2] = AX |
| } |
| |
| case MWAIT: |
| if inst.Mode == 64 { |
| inst.Args[0] = RCX |
| inst.Args[1] = RAX |
| } else { |
| inst.Args[0] = ECX |
| inst.Args[1] = EAX |
| } |
| } |
| |
| // Adjust which prefixes will be displayed. |
| // The rule is to display all the prefixes not implied by |
| // the usual instruction display, that is, all the prefixes |
| // except the ones with PrefixImplicit set. |
| // However, of course, there are exceptions to the rule. |
| switch inst.Op { |
| case CRC32: |
| // CRC32 has a mandatory F2 prefix. |
| // If there are multiple F2s and no F3s, the extra F2s do not print. |
| // (And Decode has already marked them implicit.) |
| // However, if there is an F3 anywhere, then the extra F2s do print. |
| // If there are multiple F2 prefixes *and* an (ignored) F3, |
| // then libopcodes prints the extra F2s as REPNs. |
| if countPrefix(&inst, 0xF2) > 1 { |
| unmarkImplicit(&inst, 0xF2) |
| markLastImplicit(&inst, 0xF2) |
| } |
| |
| // An unused data size override should probably be shown, |
| // to distinguish DATA16 CRC32B from plain CRC32B, |
| // but libopcodes always treats the final override as implicit |
| // and the others as explicit. |
| unmarkImplicit(&inst, PrefixDataSize) |
| markLastImplicit(&inst, PrefixDataSize) |
| |
| case CVTSI2SD, CVTSI2SS: |
| if !isMem(inst.Args[1]) { |
| markLastImplicit(&inst, PrefixDataSize) |
| } |
| |
| case CVTSD2SI, CVTSS2SI, CVTTSD2SI, CVTTSS2SI, |
| ENTER, FLDENV, FNSAVE, FNSTENV, FRSTOR, LGDT, LIDT, LRET, |
| POP, PUSH, RET, SGDT, SIDT, SYSRET, XBEGIN: |
| markLastImplicit(&inst, PrefixDataSize) |
| |
| case LOOP, LOOPE, LOOPNE, MONITOR: |
| markLastImplicit(&inst, PrefixAddrSize) |
| |
| case MOV: |
| // The 16-bit and 32-bit forms of MOV Sreg, dst and MOV src, Sreg |
| // cannot be distinguished when src or dst refers to memory, because |
| // Sreg is always a 16-bit value, even when we're doing a 32-bit |
| // instruction. Because the instruction tables distinguished these two, |
| // any operand size prefix has been marked as used (to decide which |
| // branch to take). Unmark it, so that it will show up in disassembly, |
| // so that the reader can tell the size of memory operand. |
| // up with the same arguments |
| dst, _ := inst.Args[0].(Reg) |
| src, _ := inst.Args[1].(Reg) |
| if ES <= src && src <= GS && isMem(inst.Args[0]) || ES <= dst && dst <= GS && isMem(inst.Args[1]) { |
| unmarkImplicit(&inst, PrefixDataSize) |
| } |
| |
| case MOVDQU: |
| if countPrefix(&inst, 0xF3) > 1 { |
| unmarkImplicit(&inst, 0xF3) |
| markLastImplicit(&inst, 0xF3) |
| } |
| |
| case MOVQ2DQ: |
| markLastImplicit(&inst, PrefixDataSize) |
| |
| case SLDT, SMSW, STR, FXRSTOR, XRSTOR, XSAVE, XSAVEOPT, CMPXCHG8B: |
| if isMem(inst.Args[0]) { |
| unmarkImplicit(&inst, PrefixDataSize) |
| } |
| |
| case SYSEXIT: |
| unmarkImplicit(&inst, PrefixDataSize) |
| } |
| |
| if isCondJmp[inst.Op] || isLoop[inst.Op] || inst.Op == JCXZ || inst.Op == JECXZ || inst.Op == JRCXZ { |
| if countPrefix(&inst, PrefixCS) > 0 && countPrefix(&inst, PrefixDS) > 0 { |
| for i, p := range inst.Prefix { |
| switch p & 0xFFF { |
| case PrefixPN, PrefixPT: |
| inst.Prefix[i] &= 0xF0FF // cut interpretation bits, producing original segment prefix |
| } |
| } |
| } |
| } |
| |
| // XACQUIRE/XRELEASE adjustment. |
| if inst.Op == MOV { |
| // MOV into memory is a candidate for turning REP into XRELEASE. |
| // However, if the REP is followed by a REPN, that REPN blocks the |
| // conversion. |
| haveREPN := false |
| for i := len(inst.Prefix) - 1; i >= 0; i-- { |
| switch inst.Prefix[i] &^ PrefixIgnored { |
| case PrefixREPN: |
| haveREPN = true |
| case PrefixXRELEASE: |
| if haveREPN { |
| inst.Prefix[i] = PrefixREP |
| } |
| } |
| } |
| } |
| |
| // We only format the final F2/F3 as XRELEASE/XACQUIRE. |
| haveXA := false |
| haveXR := false |
| for i := len(inst.Prefix) - 1; i >= 0; i-- { |
| switch inst.Prefix[i] &^ PrefixIgnored { |
| case PrefixXRELEASE: |
| if !haveXR { |
| haveXR = true |
| } else { |
| inst.Prefix[i] = PrefixREP |
| } |
| |
| case PrefixXACQUIRE: |
| if !haveXA { |
| haveXA = true |
| } else { |
| inst.Prefix[i] = PrefixREPN |
| } |
| } |
| } |
| |
| // Determine opcode. |
| op := strings.ToLower(inst.Op.String()) |
| if alt := gnuOp[inst.Op]; alt != "" { |
| op = alt |
| } |
| |
| // Determine opcode suffix. |
| // Libopcodes omits the suffix if the width of the operation |
| // can be inferred from a register arguments. For example, |
| // add $1, %ebx has no suffix because you can tell from the |
| // 32-bit register destination that it is a 32-bit add, |
| // but in addl $1, (%ebx), the destination is memory, so the |
| // size is not evident without the l suffix. |
| needSuffix := true |
| SuffixLoop: |
| for i, a := range inst.Args { |
| if a == nil { |
| break |
| } |
| switch a := a.(type) { |
| case Reg: |
| switch inst.Op { |
| case MOVSX, MOVZX: |
| continue |
| |
| case SHL, SHR, RCL, RCR, ROL, ROR, SAR: |
| if i == 1 { |
| // shift count does not tell us operand size |
| continue |
| } |
| |
| case CRC32: |
| // The source argument does tell us operand size, |
| // but libopcodes still always puts a suffix on crc32. |
| continue |
| |
| case PUSH, POP: |
| // Even though segment registers are 16-bit, push and pop |
| // can save/restore them from 32-bit slots, so they |
| // do not imply operand size. |
| if ES <= a && a <= GS { |
| continue |
| } |
| |
| case CVTSI2SD, CVTSI2SS: |
| // The integer register argument takes priority. |
| if X0 <= a && a <= X15 { |
| continue |
| } |
| } |
| |
| if AL <= a && a <= R15 || ES <= a && a <= GS || X0 <= a && a <= X15 || M0 <= a && a <= M7 { |
| needSuffix = false |
| break SuffixLoop |
| } |
| } |
| } |
| |
| if needSuffix { |
| switch inst.Op { |
| case CMPXCHG8B, FLDCW, FNSTCW, FNSTSW, LDMXCSR, LLDT, LMSW, LTR, PCLMULQDQ, |
| SETA, SETAE, SETB, SETBE, SETE, SETG, SETGE, SETL, SETLE, SETNE, SETNO, SETNP, SETNS, SETO, SETP, SETS, |
| SLDT, SMSW, STMXCSR, STR, VERR, VERW: |
| // For various reasons, libopcodes emits no suffix for these instructions. |
| |
| case CRC32: |
| op += byteSizeSuffix(argBytes(&inst, inst.Args[1])) |
| |
| case LGDT, LIDT, SGDT, SIDT: |
| op += byteSizeSuffix(inst.DataSize / 8) |
| |
| case MOVZX, MOVSX: |
| // Integer size conversions get two suffixes. |
| op = op[:4] + byteSizeSuffix(argBytes(&inst, inst.Args[1])) + byteSizeSuffix(argBytes(&inst, inst.Args[0])) |
| |
| case LOOP, LOOPE, LOOPNE: |
| // Add w suffix to indicate use of CX register instead of ECX. |
| if inst.AddrSize == 16 { |
| op += "w" |
| } |
| |
| case CALL, ENTER, JMP, LCALL, LEAVE, LJMP, LRET, RET, SYSRET, XBEGIN: |
| // Add w suffix to indicate use of 16-bit target. |
| // Exclude JMP rel8. |
| if inst.Opcode>>24 == 0xEB { |
| break |
| } |
| if inst.DataSize == 16 && inst.Mode != 16 { |
| markLastImplicit(&inst, PrefixDataSize) |
| op += "w" |
| } else if inst.Mode == 64 { |
| op += "q" |
| } |
| |
| case FRSTOR, FNSAVE, FNSTENV, FLDENV: |
| // Add s suffix to indicate shortened FPU state (I guess). |
| if inst.DataSize == 16 { |
| op += "s" |
| } |
| |
| case PUSH, POP: |
| if markLastImplicit(&inst, PrefixDataSize) { |
| op += byteSizeSuffix(inst.DataSize / 8) |
| } else if inst.Mode == 64 { |
| op += "q" |
| } else { |
| op += byteSizeSuffix(inst.MemBytes) |
| } |
| |
| default: |
| if isFloat(inst.Op) { |
| // I can't explain any of this, but it's what libopcodes does. |
| switch inst.MemBytes { |
| default: |
| if (inst.Op == FLD || inst.Op == FSTP) && isMem(inst.Args[0]) { |
| op += "t" |
| } |
| case 4: |
| if isFloatInt(inst.Op) { |
| op += "l" |
| } else { |
| op += "s" |
| } |
| case 8: |
| if isFloatInt(inst.Op) { |
| op += "ll" |
| } else { |
| op += "l" |
| } |
| } |
| break |
| } |
| |
| op += byteSizeSuffix(inst.MemBytes) |
| } |
| } |
| |
| // Adjust special case opcodes. |
| switch inst.Op { |
| case 0: |
| if inst.Prefix[0] != 0 { |
| return strings.ToLower(inst.Prefix[0].String()) |
| } |
| |
| case INT: |
| if inst.Opcode>>24 == 0xCC { |
| inst.Args[0] = nil |
| op = "int3" |
| } |
| |
| case CMPPS, CMPPD, CMPSD_XMM, CMPSS: |
| imm, ok := inst.Args[2].(Imm) |
| if ok && 0 <= imm && imm < 8 { |
| inst.Args[2] = nil |
| op = cmppsOps[imm] + op[3:] |
| } |
| |
| case PCLMULQDQ: |
| imm, ok := inst.Args[2].(Imm) |
| if ok && imm&^0x11 == 0 { |
| inst.Args[2] = nil |
| op = pclmulqOps[(imm&0x10)>>3|(imm&1)] |
| } |
| |
| case XLATB: |
| if markLastImplicit(&inst, PrefixAddrSize) { |
| op = "xlat" // not xlatb |
| } |
| } |
| |
| // Build list of argument strings. |
| var ( |
| usedPrefixes bool // segment prefixes consumed by Mem formatting |
| args []string // formatted arguments |
| ) |
| for i, a := range inst.Args { |
| if a == nil { |
| break |
| } |
| switch inst.Op { |
| case MOVSB, MOVSW, MOVSD, MOVSQ, OUTSB, OUTSW, OUTSD: |
| if i == 0 { |
| usedPrefixes = true // disable use of prefixes for first argument |
| } else { |
| usedPrefixes = false |
| } |
| } |
| if a == Imm(1) && (inst.Opcode>>24)&^1 == 0xD0 { |
| continue |
| } |
| args = append(args, gnuArg(&inst, a, &usedPrefixes)) |
| } |
| |
| // The default is to print the arguments in reverse Intel order. |
| // A few instructions inhibit this behavior. |
| switch inst.Op { |
| case BOUND, LCALL, ENTER, LJMP: |
| // no reverse |
| default: |
| // reverse args |
| for i, j := 0, len(args)-1; i < j; i, j = i+1, j-1 { |
| args[i], args[j] = args[j], args[i] |
| } |
| } |
| |
| // Build prefix string. |
| // Must be after argument formatting, which can turn off segment prefixes. |
| var ( |
| prefix = "" // output string |
| numAddr = 0 |
| numData = 0 |
| implicitData = false |
| ) |
| for _, p := range inst.Prefix { |
| if p&0xFF == PrefixDataSize && p&PrefixImplicit != 0 { |
| implicitData = true |
| } |
| } |
| for _, p := range inst.Prefix { |
| if p == 0 || p.IsVEX() { |
| break |
| } |
| if p&PrefixImplicit != 0 { |
| continue |
| } |
| switch p &^ (PrefixIgnored | PrefixInvalid) { |
| default: |
| if p.IsREX() { |
| if p&0xFF == PrefixREX { |
| prefix += "rex " |
| } else { |
| prefix += "rex." + p.String()[4:] + " " |
| } |
| break |
| } |
| prefix += strings.ToLower(p.String()) + " " |
| |
| case PrefixPN: |
| op += ",pn" |
| continue |
| |
| case PrefixPT: |
| op += ",pt" |
| continue |
| |
| case PrefixAddrSize, PrefixAddr16, PrefixAddr32: |
| // For unknown reasons, if the addr16 prefix is repeated, |
| // libopcodes displays all but the last as addr32, even though |
| // the addressing form used in a memory reference is clearly |
| // still 16-bit. |
| n := 32 |
| if inst.Mode == 32 { |
| n = 16 |
| } |
| numAddr++ |
| if countPrefix(&inst, PrefixAddrSize) > numAddr { |
| n = inst.Mode |
| } |
| prefix += fmt.Sprintf("addr%d ", n) |
| continue |
| |
| case PrefixData16, PrefixData32: |
| if implicitData && countPrefix(&inst, PrefixDataSize) > 1 { |
| // Similar to the addr32 logic above, but it only kicks in |
| // when something used the data size prefix (one is implicit). |
| n := 16 |
| if inst.Mode == 16 { |
| n = 32 |
| } |
| numData++ |
| if countPrefix(&inst, PrefixDataSize) > numData { |
| if inst.Mode == 16 { |
| n = 16 |
| } else { |
| n = 32 |
| } |
| } |
| prefix += fmt.Sprintf("data%d ", n) |
| continue |
| } |
| prefix += strings.ToLower(p.String()) + " " |
| } |
| } |
| |
| // Finally! Put it all together. |
| text := prefix + op |
| if args != nil { |
| text += " " |
| // Indirect call/jmp gets a star to distinguish from direct jump address. |
| if (inst.Op == CALL || inst.Op == JMP || inst.Op == LJMP || inst.Op == LCALL) && (isMem(inst.Args[0]) || isReg(inst.Args[0])) { |
| text += "*" |
| } |
| text += strings.Join(args, ",") |
| } |
| return text |
| } |
| |
| // gnuArg returns the GNU syntax for the argument x from the instruction inst. |
| // If *usedPrefixes is false and x is a Mem, then the formatting |
| // includes any segment prefixes and sets *usedPrefixes to true. |
| func gnuArg(inst *Inst, x Arg, usedPrefixes *bool) string { |
| if x == nil { |
| return "<nil>" |
| } |
| switch x := x.(type) { |
| case Reg: |
| switch inst.Op { |
| case CVTSI2SS, CVTSI2SD, CVTSS2SI, CVTSD2SI, CVTTSD2SI, CVTTSS2SI: |
| if inst.DataSize == 16 && EAX <= x && x <= R15L { |
| x -= EAX - AX |
| } |
| |
| case IN, INSB, INSW, INSD, OUT, OUTSB, OUTSW, OUTSD: |
| // DX is the port, but libopcodes prints it as if it were a memory reference. |
| if x == DX { |
| return "(%dx)" |
| } |
| case VMOVDQA, VMOVDQU, VMOVNTDQA, VMOVNTDQ: |
| return strings.Replace(gccRegName[x], "xmm", "ymm", -1) |
| } |
| return gccRegName[x] |
| case Mem: |
| seg := "" |
| var haveCS, haveDS, haveES, haveFS, haveGS, haveSS bool |
| switch x.Segment { |
| case CS: |
| haveCS = true |
| case DS: |
| haveDS = true |
| case ES: |
| haveES = true |
| case FS: |
| haveFS = true |
| case GS: |
| haveGS = true |
| case SS: |
| haveSS = true |
| } |
| switch inst.Op { |
| case INSB, INSW, INSD, STOSB, STOSW, STOSD, STOSQ, SCASB, SCASW, SCASD, SCASQ: |
| // These do not accept segment prefixes, at least in the GNU rendering. |
| default: |
| if *usedPrefixes { |
| break |
| } |
| for i := len(inst.Prefix) - 1; i >= 0; i-- { |
| p := inst.Prefix[i] &^ PrefixIgnored |
| if p == 0 { |
| continue |
| } |
| switch p { |
| case PrefixCS: |
| if !haveCS { |
| haveCS = true |
| inst.Prefix[i] |= PrefixImplicit |
| } |
| case PrefixDS: |
| if !haveDS { |
| haveDS = true |
| inst.Prefix[i] |= PrefixImplicit |
| } |
| case PrefixES: |
| if !haveES { |
| haveES = true |
| inst.Prefix[i] |= PrefixImplicit |
| } |
| case PrefixFS: |
| if !haveFS { |
| haveFS = true |
| inst.Prefix[i] |= PrefixImplicit |
| } |
| case PrefixGS: |
| if !haveGS { |
| haveGS = true |
| inst.Prefix[i] |= PrefixImplicit |
| } |
| case PrefixSS: |
| if !haveSS { |
| haveSS = true |
| inst.Prefix[i] |= PrefixImplicit |
| } |
| } |
| } |
| *usedPrefixes = true |
| } |
| if haveCS { |
| seg += "%cs:" |
| } |
| if haveDS { |
| seg += "%ds:" |
| } |
| if haveSS { |
| seg += "%ss:" |
| } |
| if haveES { |
| seg += "%es:" |
| } |
| if haveFS { |
| seg += "%fs:" |
| } |
| if haveGS { |
| seg += "%gs:" |
| } |
| disp := "" |
| if x.Disp != 0 { |
| disp = fmt.Sprintf("%#x", x.Disp) |
| } |
| if x.Scale == 0 || x.Index == 0 && x.Scale == 1 && (x.Base == ESP || x.Base == RSP || x.Base == 0 && inst.Mode == 64) { |
| if x.Base == 0 { |
| return seg + disp |
| } |
| return fmt.Sprintf("%s%s(%s)", seg, disp, gccRegName[x.Base]) |
| } |
| base := gccRegName[x.Base] |
| if x.Base == 0 { |
| base = "" |
| } |
| index := gccRegName[x.Index] |
| if x.Index == 0 { |
| if inst.AddrSize == 64 { |
| index = "%riz" |
| } else { |
| index = "%eiz" |
| } |
| } |
| if AX <= x.Base && x.Base <= DI { |
| // 16-bit addressing - no scale |
| return fmt.Sprintf("%s%s(%s,%s)", seg, disp, base, index) |
| } |
| return fmt.Sprintf("%s%s(%s,%s,%d)", seg, disp, base, index, x.Scale) |
| case Rel: |
| return fmt.Sprintf(".%+#x", int32(x)) |
| case Imm: |
| if inst.Mode == 32 { |
| return fmt.Sprintf("$%#x", uint32(x)) |
| } |
| return fmt.Sprintf("$%#x", int64(x)) |
| } |
| return x.String() |
| } |
| |
| var gccRegName = [...]string{ |
| 0: "REG0", |
| AL: "%al", |
| CL: "%cl", |
| BL: "%bl", |
| DL: "%dl", |
| AH: "%ah", |
| CH: "%ch", |
| BH: "%bh", |
| DH: "%dh", |
| SPB: "%spl", |
| BPB: "%bpl", |
| SIB: "%sil", |
| DIB: "%dil", |
| R8B: "%r8b", |
| R9B: "%r9b", |
| R10B: "%r10b", |
| R11B: "%r11b", |
| R12B: "%r12b", |
| R13B: "%r13b", |
| R14B: "%r14b", |
| R15B: "%r15b", |
| AX: "%ax", |
| CX: "%cx", |
| BX: "%bx", |
| DX: "%dx", |
| SP: "%sp", |
| BP: "%bp", |
| SI: "%si", |
| DI: "%di", |
| R8W: "%r8w", |
| R9W: "%r9w", |
| R10W: "%r10w", |
| R11W: "%r11w", |
| R12W: "%r12w", |
| R13W: "%r13w", |
| R14W: "%r14w", |
| R15W: "%r15w", |
| EAX: "%eax", |
| ECX: "%ecx", |
| EDX: "%edx", |
| EBX: "%ebx", |
| ESP: "%esp", |
| EBP: "%ebp", |
| ESI: "%esi", |
| EDI: "%edi", |
| R8L: "%r8d", |
| R9L: "%r9d", |
| R10L: "%r10d", |
| R11L: "%r11d", |
| R12L: "%r12d", |
| R13L: "%r13d", |
| R14L: "%r14d", |
| R15L: "%r15d", |
| RAX: "%rax", |
| RCX: "%rcx", |
| RDX: "%rdx", |
| RBX: "%rbx", |
| RSP: "%rsp", |
| RBP: "%rbp", |
| RSI: "%rsi", |
| RDI: "%rdi", |
| R8: "%r8", |
| R9: "%r9", |
| R10: "%r10", |
| R11: "%r11", |
| R12: "%r12", |
| R13: "%r13", |
| R14: "%r14", |
| R15: "%r15", |
| IP: "%ip", |
| EIP: "%eip", |
| RIP: "%rip", |
| F0: "%st", |
| F1: "%st(1)", |
| F2: "%st(2)", |
| F3: "%st(3)", |
| F4: "%st(4)", |
| F5: "%st(5)", |
| F6: "%st(6)", |
| F7: "%st(7)", |
| M0: "%mm0", |
| M1: "%mm1", |
| M2: "%mm2", |
| M3: "%mm3", |
| M4: "%mm4", |
| M5: "%mm5", |
| M6: "%mm6", |
| M7: "%mm7", |
| X0: "%xmm0", |
| X1: "%xmm1", |
| X2: "%xmm2", |
| X3: "%xmm3", |
| X4: "%xmm4", |
| X5: "%xmm5", |
| X6: "%xmm6", |
| X7: "%xmm7", |
| X8: "%xmm8", |
| X9: "%xmm9", |
| X10: "%xmm10", |
| X11: "%xmm11", |
| X12: "%xmm12", |
| X13: "%xmm13", |
| X14: "%xmm14", |
| X15: "%xmm15", |
| CS: "%cs", |
| SS: "%ss", |
| DS: "%ds", |
| ES: "%es", |
| FS: "%fs", |
| GS: "%gs", |
| GDTR: "%gdtr", |
| IDTR: "%idtr", |
| LDTR: "%ldtr", |
| MSW: "%msw", |
| TASK: "%task", |
| CR0: "%cr0", |
| CR1: "%cr1", |
| CR2: "%cr2", |
| CR3: "%cr3", |
| CR4: "%cr4", |
| CR5: "%cr5", |
| CR6: "%cr6", |
| CR7: "%cr7", |
| CR8: "%cr8", |
| CR9: "%cr9", |
| CR10: "%cr10", |
| CR11: "%cr11", |
| CR12: "%cr12", |
| CR13: "%cr13", |
| CR14: "%cr14", |
| CR15: "%cr15", |
| DR0: "%db0", |
| DR1: "%db1", |
| DR2: "%db2", |
| DR3: "%db3", |
| DR4: "%db4", |
| DR5: "%db5", |
| DR6: "%db6", |
| DR7: "%db7", |
| TR0: "%tr0", |
| TR1: "%tr1", |
| TR2: "%tr2", |
| TR3: "%tr3", |
| TR4: "%tr4", |
| TR5: "%tr5", |
| TR6: "%tr6", |
| TR7: "%tr7", |
| } |
| |
| var gnuOp = map[Op]string{ |
| CBW: "cbtw", |
| CDQ: "cltd", |
| CMPSD: "cmpsl", |
| CMPSD_XMM: "cmpsd", |
| CWD: "cwtd", |
| CWDE: "cwtl", |
| CQO: "cqto", |
| INSD: "insl", |
| IRET: "iretw", |
| IRETD: "iret", |
| IRETQ: "iretq", |
| LODSB: "lods", |
| LODSD: "lods", |
| LODSQ: "lods", |
| LODSW: "lods", |
| MOVSD: "movsl", |
| MOVSD_XMM: "movsd", |
| OUTSD: "outsl", |
| POPA: "popaw", |
| POPAD: "popa", |
| POPF: "popfw", |
| POPFD: "popf", |
| PUSHA: "pushaw", |
| PUSHAD: "pusha", |
| PUSHF: "pushfw", |
| PUSHFD: "pushf", |
| SCASB: "scas", |
| SCASD: "scas", |
| SCASQ: "scas", |
| SCASW: "scas", |
| STOSB: "stos", |
| STOSD: "stos", |
| STOSQ: "stos", |
| STOSW: "stos", |
| XLATB: "xlat", |
| } |
| |
| var cmppsOps = []string{ |
| "cmpeq", |
| "cmplt", |
| "cmple", |
| "cmpunord", |
| "cmpneq", |
| "cmpnlt", |
| "cmpnle", |
| "cmpord", |
| } |
| |
| var pclmulqOps = []string{ |
| "pclmullqlqdq", |
| "pclmulhqlqdq", |
| "pclmullqhqdq", |
| "pclmulhqhqdq", |
| } |
| |
| func countPrefix(inst *Inst, target Prefix) int { |
| n := 0 |
| for _, p := range inst.Prefix { |
| if p&0xFF == target&0xFF { |
| n++ |
| } |
| } |
| return n |
| } |
| |
| func markLastImplicit(inst *Inst, prefix Prefix) bool { |
| for i := len(inst.Prefix) - 1; i >= 0; i-- { |
| p := inst.Prefix[i] |
| if p&0xFF == prefix { |
| inst.Prefix[i] |= PrefixImplicit |
| return true |
| } |
| } |
| return false |
| } |
| |
| func unmarkImplicit(inst *Inst, prefix Prefix) { |
| for i := len(inst.Prefix) - 1; i >= 0; i-- { |
| p := inst.Prefix[i] |
| if p&0xFF == prefix { |
| inst.Prefix[i] &^= PrefixImplicit |
| } |
| } |
| } |
| |
| func byteSizeSuffix(b int) string { |
| switch b { |
| case 1: |
| return "b" |
| case 2: |
| return "w" |
| case 4: |
| return "l" |
| case 8: |
| return "q" |
| } |
| return "" |
| } |
| |
| func argBytes(inst *Inst, arg Arg) int { |
| if isMem(arg) { |
| return inst.MemBytes |
| } |
| return regBytes(arg) |
| } |
| |
| func isFloat(op Op) bool { |
| switch op { |
| case FADD, FCOM, FCOMP, FDIV, FDIVR, FIADD, FICOM, FICOMP, FIDIV, FIDIVR, FILD, FIMUL, FIST, FISTP, FISTTP, FISUB, FISUBR, FLD, FMUL, FST, FSTP, FSUB, FSUBR: |
| return true |
| } |
| return false |
| } |
| |
| func isFloatInt(op Op) bool { |
| switch op { |
| case FIADD, FICOM, FICOMP, FIDIV, FIDIVR, FILD, FIMUL, FIST, FISTP, FISTTP, FISUB, FISUBR: |
| return true |
| } |
| return false |
| } |