| 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 | } |