root / target-arm / op_helper.c @ 6ddbc6e4
History | View | Annotate | Download (10.6 kB)
1 |
/*
|
---|---|
2 |
* ARM helper routines
|
3 |
*
|
4 |
* Copyright (c) 2005-2007 CodeSourcery, LLC
|
5 |
*
|
6 |
* This library is free software; you can redistribute it and/or
|
7 |
* modify it under the terms of the GNU Lesser General Public
|
8 |
* License as published by the Free Software Foundation; either
|
9 |
* version 2 of the License, or (at your option) any later version.
|
10 |
*
|
11 |
* This library is distributed in the hope that it will be useful,
|
12 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14 |
* Lesser General Public License for more details.
|
15 |
*
|
16 |
* You should have received a copy of the GNU Lesser General Public
|
17 |
* License along with this library; if not, write to the Free Software
|
18 |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
19 |
*/
|
20 |
#include "exec.h" |
21 |
#include "helpers.h" |
22 |
|
23 |
void raise_exception(int tt) |
24 |
{ |
25 |
env->exception_index = tt; |
26 |
cpu_loop_exit(); |
27 |
} |
28 |
|
29 |
/* thread support */
|
30 |
|
31 |
spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED; |
32 |
|
33 |
void cpu_lock(void) |
34 |
{ |
35 |
spin_lock(&global_cpu_lock); |
36 |
} |
37 |
|
38 |
void cpu_unlock(void) |
39 |
{ |
40 |
spin_unlock(&global_cpu_lock); |
41 |
} |
42 |
|
43 |
/* VFP support. */
|
44 |
|
45 |
void do_vfp_abss(void) |
46 |
{ |
47 |
FT0s = float32_abs(FT0s); |
48 |
} |
49 |
|
50 |
void do_vfp_absd(void) |
51 |
{ |
52 |
FT0d = float64_abs(FT0d); |
53 |
} |
54 |
|
55 |
void do_vfp_sqrts(void) |
56 |
{ |
57 |
FT0s = float32_sqrt(FT0s, &env->vfp.fp_status); |
58 |
} |
59 |
|
60 |
void do_vfp_sqrtd(void) |
61 |
{ |
62 |
FT0d = float64_sqrt(FT0d, &env->vfp.fp_status); |
63 |
} |
64 |
|
65 |
/* XXX: check quiet/signaling case */
|
66 |
#define DO_VFP_cmp(p, size) \
|
67 |
void do_vfp_cmp##p(void) \ |
68 |
{ \ |
69 |
uint32_t flags; \ |
70 |
switch(float ## size ## _compare_quiet(FT0##p, FT1##p, &env->vfp.fp_status)) {\ |
71 |
case 0: flags = 0x6; break;\ |
72 |
case -1: flags = 0x8; break;\ |
73 |
case 1: flags = 0x2; break;\ |
74 |
default: case 2: flags = 0x3; break;\ |
75 |
}\ |
76 |
env->vfp.xregs[ARM_VFP_FPSCR] = (flags << 28)\
|
77 |
| (env->vfp.xregs[ARM_VFP_FPSCR] & 0x0fffffff); \
|
78 |
FORCE_RET(); \ |
79 |
}\ |
80 |
\ |
81 |
void do_vfp_cmpe##p(void) \ |
82 |
{ \ |
83 |
uint32_t flags; \ |
84 |
switch(float ## size ## _compare(FT0##p, FT1##p, &env->vfp.fp_status)) {\ |
85 |
case 0: flags = 0x6; break;\ |
86 |
case -1: flags = 0x8; break;\ |
87 |
case 1: flags = 0x2; break;\ |
88 |
default: case 2: flags = 0x3; break;\ |
89 |
}\ |
90 |
env->vfp.xregs[ARM_VFP_FPSCR] = (flags << 28)\
|
91 |
| (env->vfp.xregs[ARM_VFP_FPSCR] & 0x0fffffff); \
|
92 |
FORCE_RET(); \ |
93 |
} |
94 |
DO_VFP_cmp(s, 32)
|
95 |
DO_VFP_cmp(d, 64)
|
96 |
#undef DO_VFP_cmp
|
97 |
|
98 |
/* Convert host exception flags to vfp form. */
|
99 |
static inline int vfp_exceptbits_from_host(int host_bits) |
100 |
{ |
101 |
int target_bits = 0; |
102 |
|
103 |
if (host_bits & float_flag_invalid)
|
104 |
target_bits |= 1;
|
105 |
if (host_bits & float_flag_divbyzero)
|
106 |
target_bits |= 2;
|
107 |
if (host_bits & float_flag_overflow)
|
108 |
target_bits |= 4;
|
109 |
if (host_bits & float_flag_underflow)
|
110 |
target_bits |= 8;
|
111 |
if (host_bits & float_flag_inexact)
|
112 |
target_bits |= 0x10;
|
113 |
return target_bits;
|
114 |
} |
115 |
|
116 |
/* Convert vfp exception flags to target form. */
|
117 |
static inline int vfp_exceptbits_to_host(int target_bits) |
118 |
{ |
119 |
int host_bits = 0; |
120 |
|
121 |
if (target_bits & 1) |
122 |
host_bits |= float_flag_invalid; |
123 |
if (target_bits & 2) |
124 |
host_bits |= float_flag_divbyzero; |
125 |
if (target_bits & 4) |
126 |
host_bits |= float_flag_overflow; |
127 |
if (target_bits & 8) |
128 |
host_bits |= float_flag_underflow; |
129 |
if (target_bits & 0x10) |
130 |
host_bits |= float_flag_inexact; |
131 |
return host_bits;
|
132 |
} |
133 |
|
134 |
void do_vfp_set_fpscr(void) |
135 |
{ |
136 |
int i;
|
137 |
uint32_t changed; |
138 |
|
139 |
changed = env->vfp.xregs[ARM_VFP_FPSCR]; |
140 |
env->vfp.xregs[ARM_VFP_FPSCR] = (T0 & 0xffc8ffff);
|
141 |
env->vfp.vec_len = (T0 >> 16) & 7; |
142 |
env->vfp.vec_stride = (T0 >> 20) & 3; |
143 |
|
144 |
changed ^= T0; |
145 |
if (changed & (3 << 22)) { |
146 |
i = (T0 >> 22) & 3; |
147 |
switch (i) {
|
148 |
case 0: |
149 |
i = float_round_nearest_even; |
150 |
break;
|
151 |
case 1: |
152 |
i = float_round_up; |
153 |
break;
|
154 |
case 2: |
155 |
i = float_round_down; |
156 |
break;
|
157 |
case 3: |
158 |
i = float_round_to_zero; |
159 |
break;
|
160 |
} |
161 |
set_float_rounding_mode(i, &env->vfp.fp_status); |
162 |
} |
163 |
|
164 |
i = vfp_exceptbits_to_host((T0 >> 8) & 0x1f); |
165 |
set_float_exception_flags(i, &env->vfp.fp_status); |
166 |
/* XXX: FZ and DN are not implemented. */
|
167 |
} |
168 |
|
169 |
void do_vfp_get_fpscr(void) |
170 |
{ |
171 |
int i;
|
172 |
|
173 |
T0 = (env->vfp.xregs[ARM_VFP_FPSCR] & 0xffc8ffff) | (env->vfp.vec_len << 16) |
174 |
| (env->vfp.vec_stride << 20);
|
175 |
i = get_float_exception_flags(&env->vfp.fp_status); |
176 |
T0 |= vfp_exceptbits_from_host(i); |
177 |
} |
178 |
|
179 |
float32 helper_recps_f32(float32 a, float32 b) |
180 |
{ |
181 |
float_status *s = &env->vfp.fp_status; |
182 |
float32 two = int32_to_float32(2, s);
|
183 |
return float32_sub(two, float32_mul(a, b, s), s);
|
184 |
} |
185 |
|
186 |
float32 helper_rsqrts_f32(float32 a, float32 b) |
187 |
{ |
188 |
float_status *s = &env->vfp.fp_status; |
189 |
float32 three = int32_to_float32(3, s);
|
190 |
return float32_sub(three, float32_mul(a, b, s), s);
|
191 |
} |
192 |
|
193 |
/* TODO: The architecture specifies the value that the estimate functions
|
194 |
should return. We return the exact reciprocal/root instead. */
|
195 |
float32 helper_recpe_f32(float32 a) |
196 |
{ |
197 |
float_status *s = &env->vfp.fp_status; |
198 |
float32 one = int32_to_float32(1, s);
|
199 |
return float32_div(one, a, s);
|
200 |
} |
201 |
|
202 |
float32 helper_rsqrte_f32(float32 a) |
203 |
{ |
204 |
float_status *s = &env->vfp.fp_status; |
205 |
float32 one = int32_to_float32(1, s);
|
206 |
return float32_div(one, float32_sqrt(a, s), s);
|
207 |
} |
208 |
|
209 |
uint32_t helper_recpe_u32(uint32_t a) |
210 |
{ |
211 |
float_status *s = &env->vfp.fp_status; |
212 |
float32 tmp; |
213 |
tmp = int32_to_float32(a, s); |
214 |
tmp = float32_scalbn(tmp, -32, s);
|
215 |
tmp = helper_recpe_f32(tmp); |
216 |
tmp = float32_scalbn(tmp, 31, s);
|
217 |
return float32_to_int32(tmp, s);
|
218 |
} |
219 |
|
220 |
uint32_t helper_rsqrte_u32(uint32_t a) |
221 |
{ |
222 |
float_status *s = &env->vfp.fp_status; |
223 |
float32 tmp; |
224 |
tmp = int32_to_float32(a, s); |
225 |
tmp = float32_scalbn(tmp, -32, s);
|
226 |
tmp = helper_rsqrte_f32(tmp); |
227 |
tmp = float32_scalbn(tmp, 31, s);
|
228 |
return float32_to_int32(tmp, s);
|
229 |
} |
230 |
|
231 |
void helper_neon_tbl(int rn, int maxindex) |
232 |
{ |
233 |
uint32_t val; |
234 |
uint32_t mask; |
235 |
uint32_t tmp; |
236 |
int index;
|
237 |
int shift;
|
238 |
uint64_t *table; |
239 |
table = (uint64_t *)&env->vfp.regs[rn]; |
240 |
val = 0;
|
241 |
mask = 0;
|
242 |
for (shift = 0; shift < 32; shift += 8) { |
243 |
index = (T1 >> shift) & 0xff;
|
244 |
if (index <= maxindex) {
|
245 |
tmp = (table[index >> 3] >> (index & 7)) & 0xff; |
246 |
val |= tmp << shift; |
247 |
} else {
|
248 |
val |= T0 & (0xff << shift);
|
249 |
} |
250 |
} |
251 |
T0 = val; |
252 |
} |
253 |
|
254 |
#if !defined(CONFIG_USER_ONLY)
|
255 |
|
256 |
#define MMUSUFFIX _mmu
|
257 |
#ifdef __s390__
|
258 |
# define GETPC() ((void*)((unsigned long)__builtin_return_address(0) & 0x7fffffffUL)) |
259 |
#else
|
260 |
# define GETPC() (__builtin_return_address(0)) |
261 |
#endif
|
262 |
|
263 |
#define SHIFT 0 |
264 |
#include "softmmu_template.h" |
265 |
|
266 |
#define SHIFT 1 |
267 |
#include "softmmu_template.h" |
268 |
|
269 |
#define SHIFT 2 |
270 |
#include "softmmu_template.h" |
271 |
|
272 |
#define SHIFT 3 |
273 |
#include "softmmu_template.h" |
274 |
|
275 |
/* try to fill the TLB and return an exception if error. If retaddr is
|
276 |
NULL, it means that the function was called in C code (i.e. not
|
277 |
from generated code or from helper.c) */
|
278 |
/* XXX: fix it to restore all registers */
|
279 |
void tlb_fill (target_ulong addr, int is_write, int mmu_idx, void *retaddr) |
280 |
{ |
281 |
TranslationBlock *tb; |
282 |
CPUState *saved_env; |
283 |
unsigned long pc; |
284 |
int ret;
|
285 |
|
286 |
/* XXX: hack to restore env in all cases, even if not called from
|
287 |
generated code */
|
288 |
saved_env = env; |
289 |
env = cpu_single_env; |
290 |
ret = cpu_arm_handle_mmu_fault(env, addr, is_write, mmu_idx, 1);
|
291 |
if (__builtin_expect(ret, 0)) { |
292 |
if (retaddr) {
|
293 |
/* now we have a real cpu fault */
|
294 |
pc = (unsigned long)retaddr; |
295 |
tb = tb_find_pc(pc); |
296 |
if (tb) {
|
297 |
/* the PC is inside the translated code. It means that we have
|
298 |
a virtual CPU fault */
|
299 |
cpu_restore_state(tb, env, pc, NULL);
|
300 |
} |
301 |
} |
302 |
raise_exception(env->exception_index); |
303 |
} |
304 |
env = saved_env; |
305 |
} |
306 |
#endif
|
307 |
|
308 |
#define SIGNBIT (uint32_t)0x80000000 |
309 |
uint32_t HELPER(add_setq)(uint32_t a, uint32_t b) |
310 |
{ |
311 |
uint32_t res = a + b; |
312 |
if (((res ^ a) & SIGNBIT) && !((a ^ b) & SIGNBIT))
|
313 |
env->QF = 1;
|
314 |
return res;
|
315 |
} |
316 |
|
317 |
uint32_t HELPER(add_saturate)(uint32_t a, uint32_t b) |
318 |
{ |
319 |
uint32_t res = a + b; |
320 |
if (((res ^ a) & SIGNBIT) && !((a ^ b) & SIGNBIT)) {
|
321 |
env->QF = 1;
|
322 |
res = ~(((int32_t)a >> 31) ^ SIGNBIT);
|
323 |
} |
324 |
return res;
|
325 |
} |
326 |
|
327 |
uint32_t HELPER(sub_saturate)(uint32_t a, uint32_t b) |
328 |
{ |
329 |
uint32_t res = a - b; |
330 |
if (((res ^ a) & SIGNBIT) && ((a ^ b) & SIGNBIT)) {
|
331 |
env->QF = 1;
|
332 |
res = ~(((int32_t)a >> 31) ^ SIGNBIT);
|
333 |
} |
334 |
return res;
|
335 |
} |
336 |
|
337 |
uint32_t HELPER(double_saturate)(int32_t val) |
338 |
{ |
339 |
uint32_t res; |
340 |
if (val >= 0x40000000) { |
341 |
res = ~SIGNBIT; |
342 |
env->QF = 1;
|
343 |
} else if (val <= (int32_t)0xc0000000) { |
344 |
res = SIGNBIT; |
345 |
env->QF = 1;
|
346 |
} else {
|
347 |
res = val << 1;
|
348 |
} |
349 |
return res;
|
350 |
} |
351 |
|
352 |
uint32_t HELPER(add_usaturate)(uint32_t a, uint32_t b) |
353 |
{ |
354 |
uint32_t res = a + b; |
355 |
if (res < a) {
|
356 |
env->QF = 1;
|
357 |
res = ~0;
|
358 |
} |
359 |
return res;
|
360 |
} |
361 |
|
362 |
uint32_t HELPER(sub_usaturate)(uint32_t a, uint32_t b) |
363 |
{ |
364 |
uint32_t res = a - b; |
365 |
if (res > a) {
|
366 |
env->QF = 1;
|
367 |
res = 0;
|
368 |
} |
369 |
return res;
|
370 |
} |
371 |
|
372 |
/* Signed saturation. */
|
373 |
static inline uint32_t do_ssat(int32_t val, int shift) |
374 |
{ |
375 |
int32_t top; |
376 |
uint32_t mask; |
377 |
|
378 |
shift = PARAM1; |
379 |
top = val >> shift; |
380 |
mask = (1u << shift) - 1; |
381 |
if (top > 0) { |
382 |
env->QF = 1;
|
383 |
return mask;
|
384 |
} else if (top < -1) { |
385 |
env->QF = 1;
|
386 |
return ~mask;
|
387 |
} |
388 |
return val;
|
389 |
} |
390 |
|
391 |
/* Unsigned saturation. */
|
392 |
static inline uint32_t do_usat(int32_t val, int shift) |
393 |
{ |
394 |
uint32_t max; |
395 |
|
396 |
shift = PARAM1; |
397 |
max = (1u << shift) - 1; |
398 |
if (val < 0) { |
399 |
env->QF = 1;
|
400 |
return 0; |
401 |
} else if (val > max) { |
402 |
env->QF = 1;
|
403 |
return max;
|
404 |
} |
405 |
return val;
|
406 |
} |
407 |
|
408 |
/* Signed saturate. */
|
409 |
uint32_t HELPER(ssat)(uint32_t x, uint32_t shift) |
410 |
{ |
411 |
return do_ssat(x, shift);
|
412 |
} |
413 |
|
414 |
/* Dual halfword signed saturate. */
|
415 |
uint32_t HELPER(ssat16)(uint32_t x, uint32_t shift) |
416 |
{ |
417 |
uint32_t res; |
418 |
|
419 |
res = (uint16_t)do_ssat((int16_t)x, shift); |
420 |
res |= do_ssat(((int32_t)x) >> 16, shift) << 16; |
421 |
return res;
|
422 |
} |
423 |
|
424 |
/* Unsigned saturate. */
|
425 |
uint32_t HELPER(usat)(uint32_t x, uint32_t shift) |
426 |
{ |
427 |
return do_usat(x, shift);
|
428 |
} |
429 |
|
430 |
/* Dual halfword unsigned saturate. */
|
431 |
uint32_t HELPER(usat16)(uint32_t x, uint32_t shift) |
432 |
{ |
433 |
uint32_t res; |
434 |
|
435 |
res = (uint16_t)do_usat((int16_t)x, shift); |
436 |
res |= do_usat(((int32_t)x) >> 16, shift) << 16; |
437 |
return res;
|
438 |
} |