1// Copyright 2015 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
5// This file encapsulates some of the odd characteristics of the ARM
6// instruction set, to minimize its interaction with the core of the
7// assembler.
8
9package arch
10
11import (
12	"strings"
13
14	"cmd/internal/obj"
15	"cmd/internal/obj/arm"
16)
17
18var armLS = map[string]uint8{
19	"U":  arm.C_UBIT,
20	"S":  arm.C_SBIT,
21	"W":  arm.C_WBIT,
22	"P":  arm.C_PBIT,
23	"PW": arm.C_WBIT | arm.C_PBIT,
24	"WP": arm.C_WBIT | arm.C_PBIT,
25}
26
27var armSCOND = map[string]uint8{
28	"EQ":  arm.C_SCOND_EQ,
29	"NE":  arm.C_SCOND_NE,
30	"CS":  arm.C_SCOND_HS,
31	"HS":  arm.C_SCOND_HS,
32	"CC":  arm.C_SCOND_LO,
33	"LO":  arm.C_SCOND_LO,
34	"MI":  arm.C_SCOND_MI,
35	"PL":  arm.C_SCOND_PL,
36	"VS":  arm.C_SCOND_VS,
37	"VC":  arm.C_SCOND_VC,
38	"HI":  arm.C_SCOND_HI,
39	"LS":  arm.C_SCOND_LS,
40	"GE":  arm.C_SCOND_GE,
41	"LT":  arm.C_SCOND_LT,
42	"GT":  arm.C_SCOND_GT,
43	"LE":  arm.C_SCOND_LE,
44	"AL":  arm.C_SCOND_NONE,
45	"U":   arm.C_UBIT,
46	"S":   arm.C_SBIT,
47	"W":   arm.C_WBIT,
48	"P":   arm.C_PBIT,
49	"PW":  arm.C_WBIT | arm.C_PBIT,
50	"WP":  arm.C_WBIT | arm.C_PBIT,
51	"F":   arm.C_FBIT,
52	"IBW": arm.C_WBIT | arm.C_PBIT | arm.C_UBIT,
53	"IAW": arm.C_WBIT | arm.C_UBIT,
54	"DBW": arm.C_WBIT | arm.C_PBIT,
55	"DAW": arm.C_WBIT,
56	"IB":  arm.C_PBIT | arm.C_UBIT,
57	"IA":  arm.C_UBIT,
58	"DB":  arm.C_PBIT,
59	"DA":  0,
60}
61
62var armJump = map[string]bool{
63	"B":    true,
64	"BL":   true,
65	"BX":   true,
66	"BEQ":  true,
67	"BNE":  true,
68	"BCS":  true,
69	"BHS":  true,
70	"BCC":  true,
71	"BLO":  true,
72	"BMI":  true,
73	"BPL":  true,
74	"BVS":  true,
75	"BVC":  true,
76	"BHI":  true,
77	"BLS":  true,
78	"BGE":  true,
79	"BLT":  true,
80	"BGT":  true,
81	"BLE":  true,
82	"CALL": true,
83	"JMP":  true,
84}
85
86func jumpArm(word string) bool {
87	return armJump[word]
88}
89
90// IsARMCMP reports whether the op (as defined by an arm.A* constant) is
91// one of the comparison instructions that require special handling.
92func IsARMCMP(op obj.As) bool {
93	switch op {
94	case arm.ACMN, arm.ACMP, arm.ATEQ, arm.ATST:
95		return true
96	}
97	return false
98}
99
100// IsARMSTREX reports whether the op (as defined by an arm.A* constant) is
101// one of the STREX-like instructions that require special handling.
102func IsARMSTREX(op obj.As) bool {
103	switch op {
104	case arm.ASTREX, arm.ASTREXD, arm.ASWPW, arm.ASWPBU:
105		return true
106	}
107	return false
108}
109
110// MCR is not defined by the obj/arm; instead we define it privately here.
111// It is encoded as an MRC with a bit inside the instruction word,
112// passed to arch.ARMMRCOffset.
113const aMCR = arm.ALAST + 1
114
115// IsARMMRC reports whether the op (as defined by an arm.A* constant) is
116// MRC or MCR
117func IsARMMRC(op obj.As) bool {
118	switch op {
119	case arm.AMRC, aMCR: // Note: aMCR is defined in this package.
120		return true
121	}
122	return false
123}
124
125// IsARMBFX reports whether the op (as defined by an arm.A* constant) is one the
126// BFX-like instructions which are in the form of "op $width, $LSB, (Reg,) Reg".
127func IsARMBFX(op obj.As) bool {
128	switch op {
129	case arm.ABFX, arm.ABFXU, arm.ABFC, arm.ABFI:
130		return true
131	}
132	return false
133}
134
135// IsARMFloatCmp reports whether the op is a floating comparison instruction.
136func IsARMFloatCmp(op obj.As) bool {
137	switch op {
138	case arm.ACMPF, arm.ACMPD:
139		return true
140	}
141	return false
142}
143
144// ARMMRCOffset implements the peculiar encoding of the MRC and MCR instructions.
145// The difference between MRC and MCR is represented by a bit high in the word, not
146// in the usual way by the opcode itself. Asm must use AMRC for both instructions, so
147// we return the opcode for MRC so that asm doesn't need to import obj/arm.
148func ARMMRCOffset(op obj.As, cond string, x0, x1, x2, x3, x4, x5 int64) (offset int64, op0 obj.As, ok bool) {
149	op1 := int64(0)
150	if op == arm.AMRC {
151		op1 = 1
152	}
153	bits, ok := ParseARMCondition(cond)
154	if !ok {
155		return
156	}
157	offset = (0xe << 24) | // opcode
158		(op1 << 20) | // MCR/MRC
159		((int64(bits) ^ arm.C_SCOND_XOR) << 28) | // scond
160		((x0 & 15) << 8) | //coprocessor number
161		((x1 & 7) << 21) | // coprocessor operation
162		((x2 & 15) << 12) | // ARM register
163		((x3 & 15) << 16) | // Crn
164		((x4 & 15) << 0) | // Crm
165		((x5 & 7) << 5) | // coprocessor information
166		(1 << 4) /* must be set */
167	return offset, arm.AMRC, true
168}
169
170// IsARMMULA reports whether the op (as defined by an arm.A* constant) is
171// MULA, MULS, MMULA, MMULS, MULABB, MULAWB or MULAWT, the 4-operand instructions.
172func IsARMMULA(op obj.As) bool {
173	switch op {
174	case arm.AMULA, arm.AMULS, arm.AMMULA, arm.AMMULS, arm.AMULABB, arm.AMULAWB, arm.AMULAWT:
175		return true
176	}
177	return false
178}
179
180var bcode = []obj.As{
181	arm.ABEQ,
182	arm.ABNE,
183	arm.ABCS,
184	arm.ABCC,
185	arm.ABMI,
186	arm.ABPL,
187	arm.ABVS,
188	arm.ABVC,
189	arm.ABHI,
190	arm.ABLS,
191	arm.ABGE,
192	arm.ABLT,
193	arm.ABGT,
194	arm.ABLE,
195	arm.AB,
196	obj.ANOP,
197}
198
199// ARMConditionCodes handles the special condition code situation for the ARM.
200// It returns a boolean to indicate success; failure means cond was unrecognized.
201func ARMConditionCodes(prog *obj.Prog, cond string) bool {
202	if cond == "" {
203		return true
204	}
205	bits, ok := ParseARMCondition(cond)
206	if !ok {
207		return false
208	}
209	/* hack to make B.NE etc. work: turn it into the corresponding conditional */
210	if prog.As == arm.AB {
211		prog.As = bcode[(bits^arm.C_SCOND_XOR)&0xf]
212		bits = (bits &^ 0xf) | arm.C_SCOND_NONE
213	}
214	prog.Scond = bits
215	return true
216}
217
218// ParseARMCondition parses the conditions attached to an ARM instruction.
219// The input is a single string consisting of period-separated condition
220// codes, such as ".P.W". An initial period is ignored.
221func ParseARMCondition(cond string) (uint8, bool) {
222	return parseARMCondition(cond, armLS, armSCOND)
223}
224
225func parseARMCondition(cond string, ls, scond map[string]uint8) (uint8, bool) {
226	cond = strings.TrimPrefix(cond, ".")
227	if cond == "" {
228		return arm.C_SCOND_NONE, true
229	}
230	names := strings.Split(cond, ".")
231	bits := uint8(0)
232	for _, name := range names {
233		if b, present := ls[name]; present {
234			bits |= b
235			continue
236		}
237		if b, present := scond[name]; present {
238			bits = (bits &^ arm.C_SCOND) | b
239			continue
240		}
241		return 0, false
242	}
243	return bits, true
244}
245
246func armRegisterNumber(name string, n int16) (int16, bool) {
247	if n < 0 || 15 < n {
248		return 0, false
249	}
250	switch name {
251	case "R":
252		return arm.REG_R0 + n, true
253	case "F":
254		return arm.REG_F0 + n, true
255	}
256	return 0, false
257}
258