arm.go raw
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
9 package arch
10
11 import (
12 "strings"
13
14 "github.com/twitchyliquid64/golang-asm/obj"
15 "github.com/twitchyliquid64/golang-asm/obj/arm"
16 )
17
18 var 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
27 var 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
62 var 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
86 func 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.
92 func 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.
102 func 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.
113 const aMCR = arm.ALAST + 1
114
115 // IsARMMRC reports whether the op (as defined by an arm.A* constant) is
116 // MRC or MCR
117 func 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".
127 func 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.
136 func 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.
148 func 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.
172 func 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
180 var 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.
201 func 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.
221 func ParseARMCondition(cond string) (uint8, bool) {
222 return parseARMCondition(cond, armLS, armSCOND)
223 }
224
225 func 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
246 func 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