filename | src/sh4/ia32abi.h |
changeset | 906:268ea359f884 |
prev | 905:4c17ebd9ef5e |
next | 907:5ecafd8d7923 |
author | nkeynes |
date | Thu Oct 30 00:06:49 2008 +0000 (15 years ago) |
permissions | -rw-r--r-- |
last change | Change xlat_get_native_pc to pass in the expected code region - this lets the Mac unwind implementation range test the IP address (which works) rather than EBP (which doesn't for some reason). Remove the test in configure that prevents fomit-frame-pointer being used in Mac builds. |
view | annotate | diff | log | raw |
1 /**
2 * $Id$
3 *
4 * Provides the implementation for the ia32 ABI (eg prologue, epilogue, and
5 * calling conventions)
6 *
7 * Copyright (c) 2007 Nathan Keynes.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 */
20 #ifndef lxdream_ia32abi_H
21 #define lxdream_ia32abi_H 1
23 #define load_ptr( reg, ptr ) load_imm32( reg, (uint32_t)ptr );
25 /**
26 * Note: clobbers EAX to make the indirect call - this isn't usually
27 * a problem since the callee will usually clobber it anyway.
28 */
29 static inline void call_func0( void *ptr )
30 {
31 load_imm32(R_EAX, (uint32_t)ptr);
32 CALL_r32(R_EAX);
33 }
35 #ifdef HAVE_FASTCALL
36 static inline void call_func1( void *ptr, int arg1 )
37 {
38 if( arg1 != R_EAX ) {
39 MOV_r32_r32( arg1, R_EAX );
40 }
41 load_imm32(R_ECX, (uint32_t)ptr);
42 CALL_r32(R_ECX);
43 }
45 static inline void call_func2( void *ptr, int arg1, int arg2 )
46 {
47 if( arg2 != R_EDX ) {
48 MOV_r32_r32( arg2, R_EDX );
49 }
50 if( arg1 != R_EAX ) {
51 MOV_r32_r32( arg1, R_EAX );
52 }
53 load_imm32(R_ECX, (uint32_t)ptr);
54 CALL_r32(R_ECX);
55 }
57 /**
58 * Write a double (64-bit) value into memory, with the first word in arg2a, and
59 * the second in arg2b
60 */
61 static inline void MEM_WRITE_DOUBLE( int addr, int arg2a, int arg2b )
62 {
63 PUSH_r32(arg2b);
64 PUSH_r32(addr);
65 call_func2(sh4_write_long, addr, arg2a);
66 POP_r32(R_EAX);
67 POP_r32(R_EDX);
68 ADD_imm8s_r32(4, R_EAX);
69 call_func0(sh4_write_long);
70 }
72 /**
73 * Read a double (64-bit) value from memory, writing the first word into arg2a
74 * and the second into arg2b. The addr must not be in EAX
75 */
76 static inline void MEM_READ_DOUBLE( int addr, int arg2a, int arg2b )
77 {
78 PUSH_r32(addr);
79 call_func1(sh4_read_long, addr);
80 POP_r32(R_ECX);
81 PUSH_r32(R_EAX);
82 MOV_r32_r32(R_ECX, R_EAX);
83 ADD_imm8s_r32(4, R_EAX);
84 call_func0(sh4_read_long);
85 if( arg2b != R_EAX ) {
86 MOV_r32_r32(R_EAX, arg2b);
87 }
88 POP_r32(arg2a);
89 }
90 #else
91 static inline void call_func1( void *ptr, int arg1 )
92 {
93 PUSH_r32(arg1);
94 call_func0(ptr);
95 ADD_imm8s_r32( 4, R_ESP );
96 }
98 static inline void call_func2( void *ptr, int arg1, int arg2 )
99 {
100 PUSH_r32(arg2);
101 PUSH_r32(arg1);
102 call_func0(ptr);
103 ADD_imm8s_r32( 8, R_ESP );
104 }
106 /**
107 * Write a double (64-bit) value into memory, with the first word in arg2a, and
108 * the second in arg2b
109 */
110 static inline void MEM_WRITE_DOUBLE( int addr, int arg2a, int arg2b )
111 {
112 ADD_imm8s_r32( 4, addr );
113 PUSH_r32(arg2b);
114 PUSH_r32(addr);
115 ADD_imm8s_r32( -4, addr );
116 PUSH_r32(arg2a);
117 PUSH_r32(addr);
118 call_func0(sh4_write_long);
119 ADD_imm8s_r32( 8, R_ESP );
120 call_func0(sh4_write_long);
121 ADD_imm8s_r32( 8, R_ESP );
122 }
124 /**
125 * Read a double (64-bit) value from memory, writing the first word into arg2a
126 * and the second into arg2b. The addr must not be in EAX
127 */
128 static inline void MEM_READ_DOUBLE( int addr, int arg2a, int arg2b )
129 {
130 PUSH_r32(addr);
131 call_func0(sh4_read_long);
132 POP_r32(R_ECX);
133 PUSH_r32(R_EAX);
134 ADD_imm8s_r32( 4, R_ECX );
135 PUSH_r32(R_ECX);
136 call_func0(sh4_read_long);
137 ADD_imm8s_r32( 4, R_ESP );
138 MOV_r32_r32( R_EAX, arg2b );
139 POP_r32(arg2a);
140 }
141 #endif
143 /**
144 * Emit the 'start of block' assembly. Sets up the stack frame and save
145 * SI/DI as required
146 */
147 void enter_block( )
148 {
149 PUSH_r32(R_EBP);
150 /* mov &sh4r, ebp */
151 load_ptr( R_EBP, ((uint8_t *)&sh4r) + 128 );
153 #ifdef STACK_ALIGN
154 sh4_x86.stack_posn = 8;
155 #endif
156 }
158 /**
159 * Exit the block with sh4r.pc already written
160 */
161 void exit_block_pcset( sh4addr_t pc )
162 {
163 load_imm32( R_ECX, ((pc - sh4_x86.block_start_pc)>>1)*sh4_cpu_period ); // 5
164 ADD_r32_sh4r( R_ECX, REG_OFFSET(slice_cycle) ); // 6
165 load_spreg( R_EAX, R_PC );
166 if( sh4_x86.tlb_on ) {
167 call_func1(xlat_get_code_by_vma,R_EAX);
168 } else {
169 call_func1(xlat_get_code,R_EAX);
170 }
171 POP_r32(R_EBP);
172 RET();
173 }
175 /**
176 * Exit the block with sh4r.new_pc written with the target pc
177 */
178 void exit_block_newpcset( sh4addr_t pc )
179 {
180 load_imm32( R_ECX, ((pc - sh4_x86.block_start_pc)>>1)*sh4_cpu_period ); // 5
181 ADD_r32_sh4r( R_ECX, REG_OFFSET(slice_cycle) ); // 6
182 load_spreg( R_EAX, R_NEW_PC );
183 store_spreg( R_EAX, R_PC );
184 if( sh4_x86.tlb_on ) {
185 call_func1(xlat_get_code_by_vma,R_EAX);
186 } else {
187 call_func1(xlat_get_code,R_EAX);
188 }
189 POP_r32(R_EBP);
190 RET();
191 }
193 #define EXIT_BLOCK_SIZE(pc) (24 + (IS_IN_ICACHE(pc)?5:CALL_FUNC1_SIZE))
196 /**
197 * Exit the block to an absolute PC
198 */
199 void exit_block( sh4addr_t pc, sh4addr_t endpc )
200 {
201 load_imm32( R_ECX, pc ); // 5
202 store_spreg( R_ECX, REG_OFFSET(pc) ); // 3
203 if( IS_IN_ICACHE(pc) ) {
204 MOV_moff32_EAX( xlat_get_lut_entry(GET_ICACHE_PHYS(pc)) ); // 5
205 } else if( sh4_x86.tlb_on ) {
206 call_func1(xlat_get_code_by_vma,R_ECX);
207 } else {
208 call_func1(xlat_get_code,R_ECX);
209 }
210 AND_imm8s_r32( 0xFC, R_EAX ); // 3
211 load_imm32( R_ECX, ((endpc - sh4_x86.block_start_pc)>>1)*sh4_cpu_period ); // 5
212 ADD_r32_sh4r( R_ECX, REG_OFFSET(slice_cycle) ); // 6
213 POP_r32(R_EBP);
214 RET();
215 }
217 #define EXIT_BLOCK_REL_SIZE(pc) (27 + (IS_IN_ICACHE(pc)?5:CALL_FUNC1_SIZE))
219 /**
220 * Exit the block to a relative PC
221 */
222 void exit_block_rel( sh4addr_t pc, sh4addr_t endpc )
223 {
224 load_imm32( R_ECX, pc - sh4_x86.block_start_pc ); // 5
225 ADD_sh4r_r32( R_PC, R_ECX );
226 store_spreg( R_ECX, REG_OFFSET(pc) ); // 3
227 if( IS_IN_ICACHE(pc) ) {
228 MOV_moff32_EAX( xlat_get_lut_entry(GET_ICACHE_PHYS(pc)) ); // 5
229 } else if( sh4_x86.tlb_on ) {
230 call_func1(xlat_get_code_by_vma,R_ECX);
231 } else {
232 call_func1(xlat_get_code,R_ECX);
233 }
234 AND_imm8s_r32( 0xFC, R_EAX ); // 3
235 load_imm32( R_ECX, ((endpc - sh4_x86.block_start_pc)>>1)*sh4_cpu_period ); // 5
236 ADD_r32_sh4r( R_ECX, REG_OFFSET(slice_cycle) ); // 6
237 POP_r32(R_EBP);
238 RET();
239 }
241 /**
242 * Write the block trailer (exception handling block)
243 */
244 void sh4_translate_end_block( sh4addr_t pc ) {
245 if( sh4_x86.branch_taken == FALSE ) {
246 // Didn't exit unconditionally already, so write the termination here
247 exit_block_rel( pc, pc );
248 }
249 if( sh4_x86.backpatch_posn != 0 ) {
250 unsigned int i;
251 // Raise exception
252 uint8_t *end_ptr = xlat_output;
253 MOV_r32_r32( R_EDX, R_ECX );
254 ADD_r32_r32( R_EDX, R_ECX );
255 ADD_r32_sh4r( R_ECX, R_PC );
256 MOV_moff32_EAX( &sh4_cpu_period );
257 MUL_r32( R_EDX );
258 ADD_r32_sh4r( R_EAX, REG_OFFSET(slice_cycle) );
260 call_func0( sh4_raise_exception );
261 ADD_imm8s_r32( 4, R_ESP );
262 load_spreg( R_EAX, R_PC );
263 if( sh4_x86.tlb_on ) {
264 call_func1(xlat_get_code_by_vma,R_EAX);
265 } else {
266 call_func1(xlat_get_code,R_EAX);
267 }
268 POP_r32(R_EBP);
269 RET();
271 // Exception already raised - just cleanup
272 uint8_t *preexc_ptr = xlat_output;
273 MOV_r32_r32( R_EDX, R_ECX );
274 ADD_r32_r32( R_EDX, R_ECX );
275 ADD_r32_sh4r( R_ECX, R_SPC );
276 MOV_moff32_EAX( &sh4_cpu_period );
277 MUL_r32( R_EDX );
278 ADD_r32_sh4r( R_EAX, REG_OFFSET(slice_cycle) );
279 load_spreg( R_EAX, R_PC );
280 if( sh4_x86.tlb_on ) {
281 call_func1(xlat_get_code_by_vma,R_EAX);
282 } else {
283 call_func1(xlat_get_code,R_EAX);
284 }
285 POP_r32(R_EBP);
286 RET();
288 for( i=0; i< sh4_x86.backpatch_posn; i++ ) {
289 uint32_t *fixup_addr = (uint32_t *)&xlat_current_block->code[sh4_x86.backpatch_list[i].fixup_offset];
290 *fixup_addr = xlat_output - (uint8_t *)&xlat_current_block->code[sh4_x86.backpatch_list[i].fixup_offset] - 4;
291 if( sh4_x86.backpatch_list[i].exc_code < 0 ) {
292 load_imm32( R_EDX, sh4_x86.backpatch_list[i].fixup_icount );
293 int stack_adj = -1 - sh4_x86.backpatch_list[i].exc_code;
294 if( stack_adj > 0 ) {
295 ADD_imm8s_r32( stack_adj, R_ESP );
296 }
297 int rel = preexc_ptr - xlat_output;
298 JMP_rel(rel);
299 } else {
300 PUSH_imm32( sh4_x86.backpatch_list[i].exc_code );
301 load_imm32( R_EDX, sh4_x86.backpatch_list[i].fixup_icount );
302 int rel = end_ptr - xlat_output;
303 JMP_rel(rel);
304 }
305 }
306 }
307 }
309 /**
310 * The unwind methods only work if we compiled with DWARF2 frame information
311 * (ie -fexceptions), otherwise we have to use the direct frame scan.
312 */
313 #ifdef HAVE_EXCEPTIONS
314 #include <unwind.h>
316 struct UnwindInfo {
317 int have_result;
318 void *pc;
319 };
321 _Unwind_Reason_Code xlat_check_frame( struct _Unwind_Context *context, void *arg )
322 {
323 void *ebp = (void *)_Unwind_GetGR(context, 5);
324 void *expect = (((uint8_t *)&sh4r) + 128 );
325 struct UnwindInfo *info = arg;
326 if( ebp == expect ) {
327 info->have_result = 1;
328 info->pc = (void *)_Unwind_GetIP(context);
329 } else if( info->have_result ) {
330 return _URC_NORMAL_STOP;
331 }
333 return _URC_NO_REASON;
334 }
336 void *xlat_get_native_pc( void *code, uint32_t code_size )
337 {
338 struct _Unwind_Exception exc;
339 struct UnwindInfo info;
341 info.have_result = 0;
342 void *result = NULL;
343 _Unwind_Backtrace( xlat_check_frame, &info );
344 if( info.have_result )
345 return info.pc;
346 return NULL;
347 }
348 #else
349 void *xlat_get_native_pc( void *code, uint32_t code_size )
350 {
351 void *result = NULL;
352 asm(
353 "mov %%ebp, %%eax\n\t"
354 "mov $0x8, %%ecx\n\t"
355 "mov %1, %%edx\n"
356 "frame_loop: test %%eax, %%eax\n\t"
357 "je frame_not_found\n\t"
358 "cmp (%%eax), %%edx\n\t"
359 "je frame_found\n\t"
360 "sub $0x1, %%ecx\n\t"
361 "je frame_not_found\n\t"
362 "movl (%%eax), %%eax\n\t"
363 "jmp frame_loop\n"
364 "frame_found: movl 0x4(%%eax), %0\n"
365 "frame_not_found:"
366 : "=r" (result)
367 : "r" (((uint8_t *)&sh4r) + 128 )
368 : "eax", "ecx", "edx" );
369 return result;
370 }
371 #endif
373 #endif /* !lxdream_ia32abi_H */
.