451ab91e |
1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
2 | * Mupen64plus - assemble.c * |
3 | * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * |
4 | * Copyright (C) 2007 Richard Goedeken (Richard42) * |
5 | * Copyright (C) 2002 Hacktarux * |
6 | * * |
7 | * This program is free software; you can redistribute it and/or modify * |
8 | * it under the terms of the GNU General Public License as published by * |
9 | * the Free Software Foundation; either version 2 of the License, or * |
10 | * (at your option) any later version. * |
11 | * * |
12 | * This program is distributed in the hope that it will be useful, * |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * |
15 | * GNU General Public License for more details. * |
16 | * * |
17 | * You should have received a copy of the GNU General Public License * |
18 | * along with this program; if not, write to the * |
19 | * Free Software Foundation, Inc., * |
20 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * |
21 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
22 | |
23 | #include <stdlib.h> |
24 | #include <stdio.h> |
25 | |
26 | #include "assemble.h" |
27 | |
28 | #include "api/m64p_types.h" |
29 | #include "api/callbacks.h" |
30 | #include "r4300/recomph.h" |
31 | #include "r4300/recomp.h" |
32 | #include "r4300/r4300.h" |
33 | |
34 | /* Placeholder for RIP-relative offsets is maxmimum 32-bit signed value. |
35 | * So, if recompiled code is run without running passe2() first, it will |
36 | * cause an exception. |
37 | */ |
38 | #define REL_PLACEHOLDER 0x7fffffff |
39 | |
40 | typedef struct _jump_table |
41 | { |
42 | unsigned int mi_addr; |
43 | unsigned int pc_addr; |
44 | unsigned int absolute64; |
45 | } jump_table; |
46 | |
47 | static jump_table *jumps_table = NULL; |
48 | static int jumps_number = 0, max_jumps_number = 0; |
49 | |
50 | typedef struct _riprelative_table |
51 | { |
52 | unsigned int pc_addr; /* index in bytes from start of x86_64 code block to the displacement value to write */ |
53 | unsigned int extra_bytes; /* number of remaining instruction bytes (immediate data) after 4-byte displacement */ |
54 | unsigned char *global_dst; /* 64-bit pointer to the data object */ |
55 | } riprelative_table; |
56 | |
57 | static riprelative_table *riprel_table = NULL; |
58 | static int riprel_number = 0, max_riprel_number = 0; |
59 | |
60 | /* Static Functions */ |
61 | |
62 | void add_jump(unsigned int pc_addr, unsigned int mi_addr, unsigned int absolute64) |
63 | { |
64 | if (jumps_number == max_jumps_number) |
65 | { |
66 | max_jumps_number += 512; |
67 | jumps_table = realloc(jumps_table, max_jumps_number*sizeof(jump_table)); |
68 | } |
69 | jumps_table[jumps_number].pc_addr = pc_addr; |
70 | jumps_table[jumps_number].mi_addr = mi_addr; |
71 | jumps_table[jumps_number].absolute64 = absolute64; |
72 | jumps_number++; |
73 | } |
74 | |
75 | /* Global Functions */ |
76 | |
77 | void init_assembler(void *block_jumps_table, int block_jumps_number, void *block_riprel_table, int block_riprel_number) |
78 | { |
79 | if (block_jumps_table) |
80 | { |
81 | jumps_table = block_jumps_table; |
82 | jumps_number = block_jumps_number; |
83 | if (jumps_number <= 512) |
84 | max_jumps_number = 512; |
85 | else |
86 | max_jumps_number = (jumps_number + 511) & 0xfffffe00; |
87 | } |
88 | else |
89 | { |
90 | jumps_table = malloc(512*sizeof(jump_table)); |
91 | jumps_number = 0; |
92 | max_jumps_number = 512; |
93 | } |
94 | |
95 | if (block_riprel_table) |
96 | { |
97 | riprel_table = block_riprel_table; |
98 | riprel_number = block_riprel_number; |
99 | if (riprel_number <= 512) |
100 | max_riprel_number = 512; |
101 | else |
102 | max_riprel_number = (riprel_number + 511) & 0xfffffe00; |
103 | } |
104 | else |
105 | { |
106 | riprel_table = malloc(512 * sizeof(riprelative_table)); |
107 | riprel_number = 0; |
108 | max_riprel_number = 512; |
109 | } |
110 | } |
111 | |
112 | void free_assembler(void **block_jumps_table, int *block_jumps_number, void **block_riprel_table, int *block_riprel_number) |
113 | { |
114 | *block_jumps_table = jumps_table; |
115 | *block_jumps_number = jumps_number; |
116 | *block_riprel_table = riprel_table; |
117 | *block_riprel_number = riprel_number; |
118 | } |
119 | |
120 | void passe2(precomp_instr *dest, int start, int end, precomp_block *block) |
121 | { |
122 | unsigned int i; |
123 | |
124 | build_wrappers(dest, start, end, block); |
125 | |
126 | /* First, fix up all the jumps. This involves a table lookup to find the offset into the block of x86_64 code for |
127 | * for start of a recompiled r4300i instruction corresponding to the given jump destination address in the N64 |
128 | * address space. Next, the relative offset between this destination and the location of the jump instruction is |
129 | * computed and stored in memory, so that the jump will branch to the right place in the recompiled code. |
130 | */ |
131 | for (i = 0; i < jumps_number; i++) |
132 | { |
133 | precomp_instr *jump_instr = dest + ((jumps_table[i].mi_addr - dest[0].addr) / 4); |
134 | unsigned int jmp_offset_loc = jumps_table[i].pc_addr; |
135 | unsigned char *addr_dest = NULL; |
136 | /* calculate the destination address to jump to */ |
137 | if (jump_instr->reg_cache_infos.need_map) |
138 | { |
139 | addr_dest = jump_instr->reg_cache_infos.jump_wrapper; |
140 | } |
141 | else |
142 | { |
143 | addr_dest = block->code + jump_instr->local_addr; |
144 | } |
145 | /* write either a 32-bit IP-relative offset or a 64-bit absolute address */ |
146 | if (jumps_table[i].absolute64) |
147 | { |
148 | *((unsigned long long *) (block->code + jmp_offset_loc)) = (unsigned long long) addr_dest; |
149 | } |
150 | else |
151 | { |
152 | long jump_rel_offset = (long) (addr_dest - (block->code + jmp_offset_loc + 4)); |
153 | *((int *) (block->code + jmp_offset_loc)) = (int) jump_rel_offset; |
154 | if (jump_rel_offset >= 0x7fffffffLL || jump_rel_offset < -0x80000000LL) |
155 | { |
156 | DebugMessage(M64MSG_ERROR, "assembler pass2 error: offset too big for relative jump from %p to %p", |
157 | (block->code + jmp_offset_loc + 4), addr_dest); |
158 | asm(" int $3; "); |
159 | } |
160 | } |
161 | } |
162 | |
163 | /* Next, fix up all of the RIP-relative memory accesses. This is unique to the x86_64 architecture, because |
164 | * the 32-bit absolute displacement addressing mode is not available (and there's no 64-bit absolute displacement |
165 | * mode either). |
166 | */ |
167 | for (i = 0; i < riprel_number; i++) |
168 | { |
169 | unsigned char *rel_offset_ptr = block->code + riprel_table[i].pc_addr; |
170 | long rip_rel_offset = (long) (riprel_table[i].global_dst - (rel_offset_ptr + 4 + riprel_table[i].extra_bytes)); |
171 | if (rip_rel_offset >= 0x7fffffffLL || rip_rel_offset < -0x80000000LL) |
172 | { |
173 | DebugMessage(M64MSG_ERROR, "assembler pass2 error: offset too big between mem target: %p and code position: %p", |
174 | riprel_table[i].global_dst, rel_offset_ptr); |
175 | asm(" int $3; "); |
176 | } |
177 | *((int *) rel_offset_ptr) = (int) rip_rel_offset; |
178 | } |
179 | |
180 | } |
181 | |
182 | static unsigned int g_jump_start8 = 0; |
183 | static unsigned int g_jump_start32 = 0; |
184 | |
185 | void jump_start_rel8(void) |
186 | { |
187 | g_jump_start8 = code_length; |
188 | } |
189 | |
190 | void jump_start_rel32(void) |
191 | { |
192 | g_jump_start32 = code_length; |
193 | } |
194 | |
195 | void jump_end_rel8(void) |
196 | { |
197 | unsigned int jump_end = code_length; |
198 | int jump_vec = jump_end - g_jump_start8; |
199 | |
200 | if (jump_vec > 127 || jump_vec < -128) |
201 | { |
202 | DebugMessage(M64MSG_ERROR, "Error: 8-bit relative jump too long! From %x to %x", g_jump_start8, jump_end); |
203 | asm(" int $3; "); |
204 | } |
205 | |
206 | code_length = g_jump_start8 - 1; |
207 | put8(jump_vec); |
208 | code_length = jump_end; |
209 | } |
210 | |
211 | void jump_end_rel32(void) |
212 | { |
213 | unsigned int jump_end = code_length; |
214 | int jump_vec = jump_end - g_jump_start32; |
215 | |
216 | code_length = g_jump_start32 - 4; |
217 | put32(jump_vec); |
218 | code_length = jump_end; |
219 | } |