1// Copyright 2014 The Go Authors.  All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package ppc64asm
6
7import (
8	"bytes"
9	"fmt"
10	"strings"
11)
12
13// GNUSyntax returns the GNU assembler syntax for the instruction, as defined by GNU binutils.
14// This form typically matches the syntax defined in the Power ISA Reference Manual.
15func GNUSyntax(inst Inst) string {
16	var buf bytes.Buffer
17	// When there are all 0s, identify them as the disassembler
18	// in binutils would.
19	if inst.Enc == 0 {
20		return ".long 0x0"
21	} else if inst.Op == 0 {
22		return "error: unknown instruction"
23	}
24	buf.WriteString(inst.Op.String())
25	sep := " "
26	for i, arg := range inst.Args[:] {
27		if arg == nil {
28			break
29		}
30		text := gnuArg(&inst, i, arg)
31		if text == "" {
32			continue
33		}
34		buf.WriteString(sep)
35		sep = ","
36		buf.WriteString(text)
37	}
38	return buf.String()
39}
40
41// gnuArg formats arg (which is the argIndex's arg in inst) according to GNU rules.
42// NOTE: because GNUSyntax is the only caller of this func, and it receives a copy
43//       of inst, it's ok to modify inst.Args here.
44func gnuArg(inst *Inst, argIndex int, arg Arg) string {
45	// special cases for load/store instructions
46	if _, ok := arg.(Offset); ok {
47		if argIndex+1 == len(inst.Args) || inst.Args[argIndex+1] == nil {
48			panic(fmt.Errorf("wrong table: offset not followed by register"))
49		}
50	}
51	switch arg := arg.(type) {
52	case Reg:
53		if isLoadStoreOp(inst.Op) && argIndex == 1 && arg == R0 {
54			return "0"
55		}
56		return arg.String()
57	case CondReg:
58		if arg == CR0 && strings.HasPrefix(inst.Op.String(), "cmp") {
59			return "" // don't show cr0 for cmp instructions
60		} else if arg >= CR0 {
61			return fmt.Sprintf("cr%d", int(arg-CR0))
62		}
63		bit := [4]string{"lt", "gt", "eq", "so"}[(arg-Cond0LT)%4]
64		if arg <= Cond0SO {
65			return bit
66		}
67		return fmt.Sprintf("4*cr%d+%s", int(arg-Cond0LT)/4, bit)
68	case Imm:
69		return fmt.Sprintf("%d", arg)
70	case SpReg:
71		return fmt.Sprintf("%d", int(arg))
72	case PCRel:
73		return fmt.Sprintf(".%+#x", int(arg))
74	case Label:
75		return fmt.Sprintf("%#x", uint32(arg))
76	case Offset:
77		reg := inst.Args[argIndex+1].(Reg)
78		removeArg(inst, argIndex+1)
79		if reg == R0 {
80			return fmt.Sprintf("%d(0)", int(arg))
81		}
82		return fmt.Sprintf("%d(r%d)", int(arg), reg-R0)
83	}
84	return fmt.Sprintf("???(%v)", arg)
85}
86
87// removeArg removes the arg in inst.Args[index].
88func removeArg(inst *Inst, index int) {
89	for i := index; i < len(inst.Args); i++ {
90		if i+1 < len(inst.Args) {
91			inst.Args[i] = inst.Args[i+1]
92		} else {
93			inst.Args[i] = nil
94		}
95	}
96}
97
98// isLoadStoreOp returns true if op is a load or store instruction
99func isLoadStoreOp(op Op) bool {
100	switch op {
101	case LBZ, LBZU, LBZX, LBZUX:
102		return true
103	case LHZ, LHZU, LHZX, LHZUX:
104		return true
105	case LHA, LHAU, LHAX, LHAUX:
106		return true
107	case LWZ, LWZU, LWZX, LWZUX:
108		return true
109	case LWA, LWAX, LWAUX:
110		return true
111	case LD, LDU, LDX, LDUX:
112		return true
113	case LQ:
114		return true
115	case STB, STBU, STBX, STBUX:
116		return true
117	case STH, STHU, STHX, STHUX:
118		return true
119	case STW, STWU, STWX, STWUX:
120		return true
121	case STD, STDU, STDX, STDUX:
122		return true
123	case STQ:
124		return true
125	case LHBRX, LWBRX, STHBRX, STWBRX:
126		return true
127	}
128	return false
129}
130