func GNUSyntax()

in ppc64/ppc64asm/gnu.go [22:358]


func GNUSyntax(inst Inst, pc uint64) string {
	var buf bytes.Buffer
	// When there are all 0s, identify them as the disassembler
	// in binutils would.
	if inst.Enc == 0 {
		return ".long 0x0"
	} else if inst.Op == 0 {
		return "error: unknown instruction"
	}

	PC := pc
	// Special handling for some ops
	startArg := 0
	sep := " "
	opName := inst.Op.String()
	argList := inst.Args[:]

	switch opName {
	case "bc", "bcl", "bca", "bcla", "bclr", "bclrl", "bcctr", "bcctrl", "bctar", "bctarl":
		sfx := inst.Op.String()[2:]
		bo := int(inst.Args[0].(Imm))
		bi := inst.Args[1].(CondReg)
		atsfx := [4]string{"", "?", "-", "+"}
		decsfx := [2]string{"dnz", "dz"}

		//BO field is... complicated (z == ignored bit, at == prediction hint)
		//Paraphrased from ISA 3.1 Book I Section 2.4:
		//
		//0000z -> decrement ctr, b if ctr != 0 and CRbi == 0
		//0001z -> decrement ctr, b if ctr == 0 and CRbi == 0
		//001at -> b if CRbi == 0
		//0100z -> decrement ctr, b if ctr != 0 and CRbi == 1
		//0101z -> decrement ctr, b if ctr == 0 and CRbi == 1
		//011at -> b if CRbi == 1
		//1a00t -> decrement ctr, b if ctr != 0
		//1a01t -> decrement ctr, b if ctr == 0
		//1z1zz -> b always

		// Decoding (in this order) we get
		// BO & 0b00100 == 0b00000 -> dz if BO[1], else dnz (not simplified for bcctrl forms)
		// BO & 0b10000 == 0b10000 -> (bc and bca forms not simplified), at = B[4]B[0] if B[2] != 0, done
		// BO & 0b10000 == 0b00000 -> t if BO[3], else f
		// BO & 0b10100 == 0b00100 -> at = B[0:1]

		// BI fields rename as follows:
		// less than            : lt BI%4==0 && test == t
		// less than or equal   : le BI%4==1 && test == f
		// equal 		: eq BI%4==2 && test == t
		// greater than or equal: ge BI%4==0 && test == f
		// greater than		: gt BI%4==1 && test == t
		// not less than	: nl BI%4==0 && test == f
		// not equal		: ne BI%4==2 && test == f
		// not greater than	: ng BI%4==1 && test == f
		// summary overflow	: so BI%4==3 && test == t
		// not summary overflow : ns BI%4==3 && test == f
		// unordered		: un BI%4==3 && test == t
		// not unordered	: nu BI%4==3 && test == f
		//
		// Note, there are only 8 possible tests, but quite a few more
		// ways to name fields.  For simplicity, we choose those in condBit.

		at := 0   // 0 == no hint, 1 == reserved, 2 == not likely, 3 == likely
		form := 1 // 1 == n/a,  0 == cr bit not set, 4 == cr bit set
		cr := (bi - Cond0LT) / 4
		bh := -1 // Only for lr/tar/ctr variants.
		switch opName {
		case "bclr", "bclrl", "bcctr", "bcctrl", "bctar", "bctarl":
			bh = int(inst.Args[2].(Imm))
		}

		if bo&0x14 == 0x14 {
			if bo == 0x14 && bi == Cond0LT { // preferred form of unconditional branch
				// Likewise, avoid printing fake b/ba/bl/bla
				if opName != "bc" && opName != "bca" && opName != "bcl" && opName != "bcla" {
					startArg = 2
				}
			}
		} else if bo&0x04 == 0 { // ctr is decremented
			if opName != "bcctr" && opName != "bcctrl" {
				startArg = 1
				tf := ""
				if bo&0x10 == 0x00 {
					tf = "f"
					if bo&0x08 == 0x08 {
						tf = "t"
					}
				}
				sfx = decsfx[(bo>>1)&1] + tf + sfx
			}
			if bo&0x10 == 0x10 {
				if opName != "bcctr" && opName != "bcctrl" {
					startArg = 2
				}
				if bi != Cond0LT {
					// A non-zero BI bit was encoded, but ignored by BO
					startArg = 0
				}
				at = ((bo & 0x8) >> 2) | (bo & 0x1)
			} else if bo&0x4 == 0x4 {
				at = bo & 0x3
			}
		} else if bo&0x10 == 0x10 { // BI field is not used
			if opName != "bca" && opName != "bc" {
				at = ((bo & 0x8) >> 2) | (bo & 0x1)
				startArg = 2
			}
			// If BI is encoded as a bit other than 0, no mnemonic.
			if bo&0x14 == 0x14 {
				startArg = 0
			}
		} else {
			form = (bo & 0x8) >> 1
			startArg = 2
			if bo&0x14 == 0x04 {
				at = bo & 0x3
			}
		}
		sfx += atsfx[at]

		if form != 1 {
			bit := int((bi-Cond0LT)%4) | (^form)&0x4
			sfx = condBit[bit] + sfx
		}

		if at != 1 && startArg > 0 && bh <= 0 {
			str := fmt.Sprintf("b%s", sfx)
			if startArg > 1 && (cr != 0 || bh > 0) {
				str += fmt.Sprintf(" cr%d", cr)
				sep = ","
			}
			buf.WriteString(str)
			if startArg < 2 && bh == 0 {
				str := fmt.Sprintf(" %s",
					gnuArg(&inst, 1, inst.Args[1], PC))
				buf.WriteString(str)
				startArg = 3
			} else if bh == 0 {
				startArg = 3
			}
		} else {
			if startArg == 0 || bh > 0 || at == 1 {
				buf.WriteString(inst.Op.String())
				buf.WriteString(atsfx[at])
				startArg = 0
			} else {
				buf.WriteString("b" + sfx)
			}
			if bh == 0 {
				str := fmt.Sprintf(" %d,%s", bo, gnuArg(&inst, 1, inst.Args[1], PC))
				buf.WriteString(str)
				startArg = 3
			}
		}

	case "mtspr":
		opcode := inst.Op.String()
		buf.WriteString(opcode[0:2])
		switch spr := inst.Args[0].(type) {
		case SpReg:
			switch spr {
			case 1:
				buf.WriteString("xer")
				startArg = 1
			case 8:
				buf.WriteString("lr")
				startArg = 1
			case 9:
				buf.WriteString("ctr")
				startArg = 1
			default:
				buf.WriteString("spr")
			}
		default:
			buf.WriteString("spr")
		}

	case "mfspr":
		opcode := inst.Op.String()
		buf.WriteString(opcode[0:2])
		arg := inst.Args[0]
		switch spr := inst.Args[1].(type) {
		case SpReg:
			switch spr {
			case 1:
				buf.WriteString("xer ")
				buf.WriteString(gnuArg(&inst, 0, arg, PC))
				startArg = 2
			case 8:
				buf.WriteString("lr ")
				buf.WriteString(gnuArg(&inst, 0, arg, PC))
				startArg = 2
			case 9:
				buf.WriteString("ctr ")
				buf.WriteString(gnuArg(&inst, 0, arg, PC))
				startArg = 2
			case 268:
				buf.WriteString("tb ")
				buf.WriteString(gnuArg(&inst, 0, arg, PC))
				startArg = 2
			default:
				buf.WriteString("spr")
			}
		default:
			buf.WriteString("spr")
		}

	case "mtfsfi", "mtfsfi.":
		buf.WriteString(opName)
		l := inst.Args[2].(Imm)
		if l == 0 {
			// L == 0 is an extended mnemonic for the same.
			asm := fmt.Sprintf(" %s,%s",
				gnuArg(&inst, 0, inst.Args[0], PC),
				gnuArg(&inst, 1, inst.Args[1], PC))
			buf.WriteString(asm)
			startArg = 3
		}

	case "paste.":
		buf.WriteString(opName)
		l := inst.Args[2].(Imm)
		if l == 1 {
			// L == 1 is an extended mnemonic for the same.
			asm := fmt.Sprintf(" %s,%s",
				gnuArg(&inst, 0, inst.Args[0], PC),
				gnuArg(&inst, 1, inst.Args[1], PC))
			buf.WriteString(asm)
			startArg = 3
		}

	case "mtfsf", "mtfsf.":
		buf.WriteString(opName)
		l := inst.Args[3].(Imm)
		if l == 0 {
			// L == 0 is an extended mnemonic for the same.
			asm := fmt.Sprintf(" %s,%s,%s",
				gnuArg(&inst, 0, inst.Args[0], PC),
				gnuArg(&inst, 1, inst.Args[1], PC),
				gnuArg(&inst, 2, inst.Args[2], PC))
			buf.WriteString(asm)
			startArg = 4
		}

	case "sync":
		lsc := inst.Args[0].(Imm)<<4 | inst.Args[1].(Imm)
		switch lsc {
		case 0x00:
			buf.WriteString("hwsync")
			startArg = 2
		case 0x10:
			buf.WriteString("lwsync")
			startArg = 2
		default:
			buf.WriteString(opName)
		}

	case "lbarx", "lharx", "lwarx", "ldarx":
		// If EH == 0, omit printing EH.
		eh := inst.Args[3].(Imm)
		if eh == 0 {
			argList = inst.Args[:3]
		}
		buf.WriteString(inst.Op.String())

	case "paddi":
		// There are several extended mnemonics.  Notably, "pla" is
		// the only valid mnemonic for paddi (R=1), In this case, RA must
		// always be 0.  Otherwise it is invalid.
		r := inst.Args[3].(Imm)
		ra := inst.Args[1].(Reg)
		str := opName
		if ra == R0 {
			name := []string{"pli", "pla"}
			str = fmt.Sprintf("%s %s,%s",
				name[r&1],
				gnuArg(&inst, 0, inst.Args[0], PC),
				gnuArg(&inst, 2, inst.Args[2], PC))
			startArg = 4
		} else {
			str = fmt.Sprintf("%s %s,%s,%s", opName,
				gnuArg(&inst, 0, inst.Args[0], PC),
				gnuArg(&inst, 1, inst.Args[1], PC),
				gnuArg(&inst, 2, inst.Args[2], PC))
			startArg = 4
			if r == 1 {
				// This is an illegal encoding (ra != 0 && r == 1) on ISA 3.1.
				v := uint64(inst.Enc)<<32 | uint64(inst.SuffixEnc)
				return fmt.Sprintf(".quad 0x%x", v)
			}
		}
		buf.WriteString(str)

	default:
		// Prefixed load/stores do not print the displacement register when R==1 (they are PCrel).
		// This also implies RA should be 0.  Likewise, when R==0, printing of R can be omitted.
		if strings.HasPrefix(opName, "pl") || strings.HasPrefix(opName, "pst") {
			r := inst.Args[3].(Imm)
			ra := inst.Args[2].(Reg)
			d := inst.Args[1].(Offset)
			if r == 1 && ra == R0 {
				str := fmt.Sprintf("%s %s,%d", opName, gnuArg(&inst, 0, inst.Args[0], PC), d)
				buf.WriteString(str)
				startArg = 4
			} else {
				str := fmt.Sprintf("%s %s,%d(%s)", opName,
					gnuArg(&inst, 0, inst.Args[0], PC),
					d,
					gnuArg(&inst, 2, inst.Args[2], PC))
				if r == 1 {
					// This is an invalid encoding (ra != 0 && r == 1) on ISA 3.1.
					v := uint64(inst.Enc)<<32 | uint64(inst.SuffixEnc)
					return fmt.Sprintf(".quad 0x%x", v)
				}
				buf.WriteString(str)
				startArg = 4
			}
		} else {
			buf.WriteString(opName)
		}
	}
	for i, arg := range argList {
		if arg == nil {
			break
		}
		if i < startArg {
			continue
		}
		text := gnuArg(&inst, i, arg, PC)
		if text == "" {
			continue
		}
		buf.WriteString(sep)
		sep = ","
		buf.WriteString(text)
	}
	return buf.String()
}