bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Tiny Code Generator for QEMU |
| 3 | * |
| 4 | * Copyright (c) 2008 Fabrice Bellard |
| 5 | * |
| 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 7 | * of this software and associated documentation files (the "Software"), to deal |
| 8 | * in the Software without restriction, including without limitation the rights |
| 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 10 | * copies of the Software, and to permit persons to whom the Software is |
| 11 | * furnished to do so, subject to the following conditions: |
| 12 | * |
| 13 | * The above copyright notice and this permission notice shall be included in |
| 14 | * all copies or substantial portions of the Software. |
| 15 | * |
| 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| 22 | * THE SOFTWARE. |
| 23 | */ |
| 24 | |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 25 | /* define it to use liveness analysis (better code) */ |
| 26 | #define USE_LIVENESS_ANALYSIS |
| 27 | |
aurel32 | cca8298 | 2009-04-16 09:58:30 +0000 | [diff] [blame] | 28 | #include "config.h" |
| 29 | |
Juan Quintela | 092c73e | 2009-07-27 16:13:04 +0200 | [diff] [blame] | 30 | #ifndef CONFIG_DEBUG_TCG |
aurel32 | cca8298 | 2009-04-16 09:58:30 +0000 | [diff] [blame] | 31 | /* define it to suppress various consistency checks (faster) */ |
| 32 | #define NDEBUG |
| 33 | #endif |
| 34 | |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 35 | #include <stdarg.h> |
| 36 | #include <stdlib.h> |
| 37 | #include <stdio.h> |
| 38 | #include <string.h> |
| 39 | #include <inttypes.h> |
bellard | 3fe43da | 2008-02-04 22:03:16 +0000 | [diff] [blame] | 40 | #ifdef _WIN32 |
| 41 | #include <malloc.h> |
| 42 | #endif |
malc | b29fe3e | 2008-11-18 01:42:22 +0000 | [diff] [blame] | 43 | #ifdef _AIX |
| 44 | #include <alloca.h> |
| 45 | #endif |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 46 | |
aurel32 | ca10f86 | 2008-04-11 21:35:42 +0000 | [diff] [blame] | 47 | #include "qemu-common.h" |
malc | 902b3d5 | 2008-12-10 19:18:40 +0000 | [diff] [blame] | 48 | #include "cache-utils.h" |
Paul Brook | 379f669 | 2009-07-17 12:48:08 +0100 | [diff] [blame] | 49 | #include "host-utils.h" |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 50 | |
| 51 | /* Note: the long term plan is to reduce the dependancies on the QEMU |
| 52 | CPU definitions. Currently they are used for qemu_ld/st |
| 53 | instructions */ |
| 54 | #define NO_CPU_IO_DEFS |
| 55 | #include "cpu.h" |
| 56 | #include "exec-all.h" |
| 57 | |
| 58 | #include "tcg-op.h" |
| 59 | #include "elf.h" |
| 60 | |
Paul Brook | 379f669 | 2009-07-17 12:48:08 +0100 | [diff] [blame] | 61 | #if defined(CONFIG_USE_GUEST_BASE) && !defined(TCG_TARGET_HAS_GUEST_BASE) |
| 62 | #error GUEST_BASE not supported on this host. |
| 63 | #endif |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 64 | |
| 65 | static void patch_reloc(uint8_t *code_ptr, int type, |
aurel32 | f54b3f9 | 2008-04-12 20:14:54 +0000 | [diff] [blame] | 66 | tcg_target_long value, tcg_target_long addend); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 67 | |
blueswir1 | 2edd089 | 2009-02-10 19:00:31 +0000 | [diff] [blame] | 68 | static TCGOpDef tcg_op_defs[] = { |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 69 | #define DEF(s, n, copy_size) { #s, 0, 0, n, n, 0, copy_size }, |
Aurelien Jarno | 6692b04 | 2009-09-29 17:00:28 +0200 | [diff] [blame] | 70 | #define DEF2(s, oargs, iargs, cargs, flags) { #s, oargs, iargs, cargs, iargs + oargs + cargs, flags, 0 }, |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 71 | #include "tcg-opc.h" |
| 72 | #undef DEF |
| 73 | #undef DEF2 |
| 74 | }; |
| 75 | |
blueswir1 | b1d8e52 | 2008-10-26 13:43:07 +0000 | [diff] [blame] | 76 | static TCGRegSet tcg_target_available_regs[2]; |
| 77 | static TCGRegSet tcg_target_call_clobber_regs; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 78 | |
| 79 | /* XXX: move that inside the context */ |
| 80 | uint16_t *gen_opc_ptr; |
| 81 | TCGArg *gen_opparam_ptr; |
| 82 | |
| 83 | static inline void tcg_out8(TCGContext *s, uint8_t v) |
| 84 | { |
| 85 | *s->code_ptr++ = v; |
| 86 | } |
| 87 | |
| 88 | static inline void tcg_out16(TCGContext *s, uint16_t v) |
| 89 | { |
| 90 | *(uint16_t *)s->code_ptr = v; |
| 91 | s->code_ptr += 2; |
| 92 | } |
| 93 | |
| 94 | static inline void tcg_out32(TCGContext *s, uint32_t v) |
| 95 | { |
| 96 | *(uint32_t *)s->code_ptr = v; |
| 97 | s->code_ptr += 4; |
| 98 | } |
| 99 | |
| 100 | /* label relocation processing */ |
| 101 | |
| 102 | void tcg_out_reloc(TCGContext *s, uint8_t *code_ptr, int type, |
| 103 | int label_index, long addend) |
| 104 | { |
| 105 | TCGLabel *l; |
| 106 | TCGRelocation *r; |
| 107 | |
| 108 | l = &s->labels[label_index]; |
| 109 | if (l->has_value) { |
pbrook | 623e265 | 2008-02-10 14:09:09 +0000 | [diff] [blame] | 110 | /* FIXME: This may break relocations on RISC targets that |
| 111 | modify instruction fields in place. The caller may not have |
| 112 | written the initial value. */ |
aurel32 | f54b3f9 | 2008-04-12 20:14:54 +0000 | [diff] [blame] | 113 | patch_reloc(code_ptr, type, l->u.value, addend); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 114 | } else { |
| 115 | /* add a new relocation entry */ |
| 116 | r = tcg_malloc(sizeof(TCGRelocation)); |
| 117 | r->type = type; |
| 118 | r->ptr = code_ptr; |
| 119 | r->addend = addend; |
| 120 | r->next = l->u.first_reloc; |
| 121 | l->u.first_reloc = r; |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | static void tcg_out_label(TCGContext *s, int label_index, |
| 126 | tcg_target_long value) |
| 127 | { |
| 128 | TCGLabel *l; |
| 129 | TCGRelocation *r; |
| 130 | |
| 131 | l = &s->labels[label_index]; |
| 132 | if (l->has_value) |
| 133 | tcg_abort(); |
| 134 | r = l->u.first_reloc; |
| 135 | while (r != NULL) { |
aurel32 | f54b3f9 | 2008-04-12 20:14:54 +0000 | [diff] [blame] | 136 | patch_reloc(r->ptr, r->type, value, r->addend); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 137 | r = r->next; |
| 138 | } |
| 139 | l->has_value = 1; |
| 140 | l->u.value = value; |
| 141 | } |
| 142 | |
| 143 | int gen_new_label(void) |
| 144 | { |
| 145 | TCGContext *s = &tcg_ctx; |
| 146 | int idx; |
| 147 | TCGLabel *l; |
| 148 | |
| 149 | if (s->nb_labels >= TCG_MAX_LABELS) |
| 150 | tcg_abort(); |
| 151 | idx = s->nb_labels++; |
| 152 | l = &s->labels[idx]; |
| 153 | l->has_value = 0; |
| 154 | l->u.first_reloc = NULL; |
| 155 | return idx; |
| 156 | } |
| 157 | |
| 158 | #include "tcg-target.c" |
| 159 | |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 160 | /* pool based memory allocation */ |
| 161 | void *tcg_malloc_internal(TCGContext *s, int size) |
| 162 | { |
| 163 | TCGPool *p; |
| 164 | int pool_size; |
| 165 | |
| 166 | if (size > TCG_POOL_CHUNK_SIZE) { |
| 167 | /* big malloc: insert a new pool (XXX: could optimize) */ |
| 168 | p = qemu_malloc(sizeof(TCGPool) + size); |
| 169 | p->size = size; |
| 170 | if (s->pool_current) |
| 171 | s->pool_current->next = p; |
| 172 | else |
| 173 | s->pool_first = p; |
| 174 | p->next = s->pool_current; |
| 175 | } else { |
| 176 | p = s->pool_current; |
| 177 | if (!p) { |
| 178 | p = s->pool_first; |
| 179 | if (!p) |
| 180 | goto new_pool; |
| 181 | } else { |
| 182 | if (!p->next) { |
| 183 | new_pool: |
| 184 | pool_size = TCG_POOL_CHUNK_SIZE; |
| 185 | p = qemu_malloc(sizeof(TCGPool) + pool_size); |
| 186 | p->size = pool_size; |
| 187 | p->next = NULL; |
| 188 | if (s->pool_current) |
| 189 | s->pool_current->next = p; |
| 190 | else |
| 191 | s->pool_first = p; |
| 192 | } else { |
| 193 | p = p->next; |
| 194 | } |
| 195 | } |
| 196 | } |
| 197 | s->pool_current = p; |
| 198 | s->pool_cur = p->data + size; |
| 199 | s->pool_end = p->data + p->size; |
| 200 | return p->data; |
| 201 | } |
| 202 | |
| 203 | void tcg_pool_reset(TCGContext *s) |
| 204 | { |
| 205 | s->pool_cur = s->pool_end = NULL; |
| 206 | s->pool_current = NULL; |
| 207 | } |
| 208 | |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 209 | void tcg_context_init(TCGContext *s) |
| 210 | { |
| 211 | int op, total_args, n; |
| 212 | TCGOpDef *def; |
| 213 | TCGArgConstraint *args_ct; |
| 214 | int *sorted_args; |
| 215 | |
| 216 | memset(s, 0, sizeof(*s)); |
| 217 | s->temps = s->static_temps; |
| 218 | s->nb_globals = 0; |
| 219 | |
| 220 | /* Count total number of arguments and allocate the corresponding |
| 221 | space */ |
| 222 | total_args = 0; |
| 223 | for(op = 0; op < NB_OPS; op++) { |
| 224 | def = &tcg_op_defs[op]; |
| 225 | n = def->nb_iargs + def->nb_oargs; |
| 226 | total_args += n; |
| 227 | } |
| 228 | |
| 229 | args_ct = qemu_malloc(sizeof(TCGArgConstraint) * total_args); |
| 230 | sorted_args = qemu_malloc(sizeof(int) * total_args); |
| 231 | |
| 232 | for(op = 0; op < NB_OPS; op++) { |
| 233 | def = &tcg_op_defs[op]; |
| 234 | def->args_ct = args_ct; |
| 235 | def->sorted_args = sorted_args; |
| 236 | n = def->nb_iargs + def->nb_oargs; |
| 237 | sorted_args += n; |
| 238 | args_ct += n; |
| 239 | } |
| 240 | |
| 241 | tcg_target_init(s); |
bellard | b03cce8 | 2008-05-10 10:52:05 +0000 | [diff] [blame] | 242 | |
| 243 | /* init global prologue and epilogue */ |
| 244 | s->code_buf = code_gen_prologue; |
| 245 | s->code_ptr = s->code_buf; |
| 246 | tcg_target_qemu_prologue(s); |
| 247 | flush_icache_range((unsigned long)s->code_buf, |
| 248 | (unsigned long)s->code_ptr); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 249 | } |
| 250 | |
| 251 | void tcg_set_frame(TCGContext *s, int reg, |
| 252 | tcg_target_long start, tcg_target_long size) |
| 253 | { |
| 254 | s->frame_start = start; |
| 255 | s->frame_end = start + size; |
| 256 | s->frame_reg = reg; |
| 257 | } |
| 258 | |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 259 | void tcg_func_start(TCGContext *s) |
| 260 | { |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 261 | int i; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 262 | tcg_pool_reset(s); |
| 263 | s->nb_temps = s->nb_globals; |
bellard | 641d5fb | 2008-05-25 17:24:00 +0000 | [diff] [blame] | 264 | for(i = 0; i < (TCG_TYPE_COUNT * 2); i++) |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 265 | s->first_free_temp[i] = -1; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 266 | s->labels = tcg_malloc(sizeof(TCGLabel) * TCG_MAX_LABELS); |
| 267 | s->nb_labels = 0; |
| 268 | s->current_frame_offset = s->frame_start; |
| 269 | |
| 270 | gen_opc_ptr = gen_opc_buf; |
| 271 | gen_opparam_ptr = gen_opparam_buf; |
| 272 | } |
| 273 | |
| 274 | static inline void tcg_temp_alloc(TCGContext *s, int n) |
| 275 | { |
| 276 | if (n > TCG_MAX_TEMPS) |
| 277 | tcg_abort(); |
| 278 | } |
| 279 | |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 280 | static inline int tcg_global_reg_new_internal(TCGType type, int reg, |
| 281 | const char *name) |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 282 | { |
| 283 | TCGContext *s = &tcg_ctx; |
| 284 | TCGTemp *ts; |
| 285 | int idx; |
| 286 | |
| 287 | #if TCG_TARGET_REG_BITS == 32 |
| 288 | if (type != TCG_TYPE_I32) |
| 289 | tcg_abort(); |
| 290 | #endif |
| 291 | if (tcg_regset_test_reg(s->reserved_regs, reg)) |
| 292 | tcg_abort(); |
| 293 | idx = s->nb_globals; |
| 294 | tcg_temp_alloc(s, s->nb_globals + 1); |
| 295 | ts = &s->temps[s->nb_globals]; |
| 296 | ts->base_type = type; |
| 297 | ts->type = type; |
| 298 | ts->fixed_reg = 1; |
| 299 | ts->reg = reg; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 300 | ts->name = name; |
| 301 | s->nb_globals++; |
| 302 | tcg_regset_set_reg(s->reserved_regs, reg); |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 303 | return idx; |
| 304 | } |
| 305 | |
| 306 | TCGv_i32 tcg_global_reg_new_i32(int reg, const char *name) |
| 307 | { |
| 308 | int idx; |
| 309 | |
| 310 | idx = tcg_global_reg_new_internal(TCG_TYPE_I32, reg, name); |
| 311 | return MAKE_TCGV_I32(idx); |
| 312 | } |
| 313 | |
| 314 | TCGv_i64 tcg_global_reg_new_i64(int reg, const char *name) |
| 315 | { |
| 316 | int idx; |
| 317 | |
| 318 | idx = tcg_global_reg_new_internal(TCG_TYPE_I64, reg, name); |
| 319 | return MAKE_TCGV_I64(idx); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 320 | } |
| 321 | |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 322 | static inline int tcg_global_mem_new_internal(TCGType type, int reg, |
| 323 | tcg_target_long offset, |
| 324 | const char *name) |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 325 | { |
| 326 | TCGContext *s = &tcg_ctx; |
| 327 | TCGTemp *ts; |
| 328 | int idx; |
| 329 | |
| 330 | idx = s->nb_globals; |
| 331 | #if TCG_TARGET_REG_BITS == 32 |
| 332 | if (type == TCG_TYPE_I64) { |
| 333 | char buf[64]; |
ths | c588979 | 2008-06-07 04:31:49 +0000 | [diff] [blame] | 334 | tcg_temp_alloc(s, s->nb_globals + 2); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 335 | ts = &s->temps[s->nb_globals]; |
| 336 | ts->base_type = type; |
| 337 | ts->type = TCG_TYPE_I32; |
| 338 | ts->fixed_reg = 0; |
| 339 | ts->mem_allocated = 1; |
| 340 | ts->mem_reg = reg; |
| 341 | #ifdef TCG_TARGET_WORDS_BIGENDIAN |
| 342 | ts->mem_offset = offset + 4; |
| 343 | #else |
| 344 | ts->mem_offset = offset; |
| 345 | #endif |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 346 | pstrcpy(buf, sizeof(buf), name); |
| 347 | pstrcat(buf, sizeof(buf), "_0"); |
| 348 | ts->name = strdup(buf); |
| 349 | ts++; |
| 350 | |
| 351 | ts->base_type = type; |
| 352 | ts->type = TCG_TYPE_I32; |
| 353 | ts->fixed_reg = 0; |
| 354 | ts->mem_allocated = 1; |
| 355 | ts->mem_reg = reg; |
| 356 | #ifdef TCG_TARGET_WORDS_BIGENDIAN |
| 357 | ts->mem_offset = offset; |
| 358 | #else |
| 359 | ts->mem_offset = offset + 4; |
| 360 | #endif |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 361 | pstrcpy(buf, sizeof(buf), name); |
| 362 | pstrcat(buf, sizeof(buf), "_1"); |
| 363 | ts->name = strdup(buf); |
| 364 | |
| 365 | s->nb_globals += 2; |
| 366 | } else |
| 367 | #endif |
| 368 | { |
| 369 | tcg_temp_alloc(s, s->nb_globals + 1); |
| 370 | ts = &s->temps[s->nb_globals]; |
| 371 | ts->base_type = type; |
| 372 | ts->type = type; |
| 373 | ts->fixed_reg = 0; |
| 374 | ts->mem_allocated = 1; |
| 375 | ts->mem_reg = reg; |
| 376 | ts->mem_offset = offset; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 377 | ts->name = name; |
| 378 | s->nb_globals++; |
| 379 | } |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 380 | return idx; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 381 | } |
| 382 | |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 383 | TCGv_i32 tcg_global_mem_new_i32(int reg, tcg_target_long offset, |
| 384 | const char *name) |
| 385 | { |
| 386 | int idx; |
| 387 | |
| 388 | idx = tcg_global_mem_new_internal(TCG_TYPE_I32, reg, offset, name); |
| 389 | return MAKE_TCGV_I32(idx); |
| 390 | } |
| 391 | |
| 392 | TCGv_i64 tcg_global_mem_new_i64(int reg, tcg_target_long offset, |
| 393 | const char *name) |
| 394 | { |
| 395 | int idx; |
| 396 | |
| 397 | idx = tcg_global_mem_new_internal(TCG_TYPE_I64, reg, offset, name); |
| 398 | return MAKE_TCGV_I64(idx); |
| 399 | } |
| 400 | |
| 401 | static inline int tcg_temp_new_internal(TCGType type, int temp_local) |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 402 | { |
| 403 | TCGContext *s = &tcg_ctx; |
| 404 | TCGTemp *ts; |
bellard | 641d5fb | 2008-05-25 17:24:00 +0000 | [diff] [blame] | 405 | int idx, k; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 406 | |
bellard | 641d5fb | 2008-05-25 17:24:00 +0000 | [diff] [blame] | 407 | k = type; |
| 408 | if (temp_local) |
| 409 | k += TCG_TYPE_COUNT; |
| 410 | idx = s->first_free_temp[k]; |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 411 | if (idx != -1) { |
| 412 | /* There is already an available temp with the |
| 413 | right type */ |
| 414 | ts = &s->temps[idx]; |
bellard | 641d5fb | 2008-05-25 17:24:00 +0000 | [diff] [blame] | 415 | s->first_free_temp[k] = ts->next_free_temp; |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 416 | ts->temp_allocated = 1; |
bellard | 641d5fb | 2008-05-25 17:24:00 +0000 | [diff] [blame] | 417 | assert(ts->temp_local == temp_local); |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 418 | } else { |
| 419 | idx = s->nb_temps; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 420 | #if TCG_TARGET_REG_BITS == 32 |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 421 | if (type == TCG_TYPE_I64) { |
ths | 8df1ca4 | 2008-06-11 11:03:34 +0000 | [diff] [blame] | 422 | tcg_temp_alloc(s, s->nb_temps + 2); |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 423 | ts = &s->temps[s->nb_temps]; |
| 424 | ts->base_type = type; |
| 425 | ts->type = TCG_TYPE_I32; |
| 426 | ts->temp_allocated = 1; |
bellard | 641d5fb | 2008-05-25 17:24:00 +0000 | [diff] [blame] | 427 | ts->temp_local = temp_local; |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 428 | ts->name = NULL; |
| 429 | ts++; |
| 430 | ts->base_type = TCG_TYPE_I32; |
| 431 | ts->type = TCG_TYPE_I32; |
| 432 | ts->temp_allocated = 1; |
bellard | 641d5fb | 2008-05-25 17:24:00 +0000 | [diff] [blame] | 433 | ts->temp_local = temp_local; |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 434 | ts->name = NULL; |
| 435 | s->nb_temps += 2; |
| 436 | } else |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 437 | #endif |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 438 | { |
| 439 | tcg_temp_alloc(s, s->nb_temps + 1); |
| 440 | ts = &s->temps[s->nb_temps]; |
| 441 | ts->base_type = type; |
| 442 | ts->type = type; |
| 443 | ts->temp_allocated = 1; |
bellard | 641d5fb | 2008-05-25 17:24:00 +0000 | [diff] [blame] | 444 | ts->temp_local = temp_local; |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 445 | ts->name = NULL; |
| 446 | s->nb_temps++; |
| 447 | } |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 448 | } |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 449 | return idx; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 450 | } |
| 451 | |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 452 | TCGv_i32 tcg_temp_new_internal_i32(int temp_local) |
| 453 | { |
| 454 | int idx; |
| 455 | |
| 456 | idx = tcg_temp_new_internal(TCG_TYPE_I32, temp_local); |
| 457 | return MAKE_TCGV_I32(idx); |
| 458 | } |
| 459 | |
| 460 | TCGv_i64 tcg_temp_new_internal_i64(int temp_local) |
| 461 | { |
| 462 | int idx; |
| 463 | |
| 464 | idx = tcg_temp_new_internal(TCG_TYPE_I64, temp_local); |
| 465 | return MAKE_TCGV_I64(idx); |
| 466 | } |
| 467 | |
| 468 | static inline void tcg_temp_free_internal(int idx) |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 469 | { |
| 470 | TCGContext *s = &tcg_ctx; |
| 471 | TCGTemp *ts; |
bellard | 641d5fb | 2008-05-25 17:24:00 +0000 | [diff] [blame] | 472 | int k; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 473 | |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 474 | assert(idx >= s->nb_globals && idx < s->nb_temps); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 475 | ts = &s->temps[idx]; |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 476 | assert(ts->temp_allocated != 0); |
| 477 | ts->temp_allocated = 0; |
bellard | 641d5fb | 2008-05-25 17:24:00 +0000 | [diff] [blame] | 478 | k = ts->base_type; |
| 479 | if (ts->temp_local) |
| 480 | k += TCG_TYPE_COUNT; |
| 481 | ts->next_free_temp = s->first_free_temp[k]; |
| 482 | s->first_free_temp[k] = idx; |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 483 | } |
| 484 | |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 485 | void tcg_temp_free_i32(TCGv_i32 arg) |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 486 | { |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 487 | tcg_temp_free_internal(GET_TCGV_I32(arg)); |
| 488 | } |
| 489 | |
| 490 | void tcg_temp_free_i64(TCGv_i64 arg) |
| 491 | { |
| 492 | tcg_temp_free_internal(GET_TCGV_I64(arg)); |
| 493 | } |
| 494 | |
| 495 | TCGv_i32 tcg_const_i32(int32_t val) |
| 496 | { |
| 497 | TCGv_i32 t0; |
| 498 | t0 = tcg_temp_new_i32(); |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 499 | tcg_gen_movi_i32(t0, val); |
| 500 | return t0; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 501 | } |
| 502 | |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 503 | TCGv_i64 tcg_const_i64(int64_t val) |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 504 | { |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 505 | TCGv_i64 t0; |
| 506 | t0 = tcg_temp_new_i64(); |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 507 | tcg_gen_movi_i64(t0, val); |
| 508 | return t0; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 509 | } |
| 510 | |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 511 | TCGv_i32 tcg_const_local_i32(int32_t val) |
aurel32 | bdffd4a | 2008-10-21 11:30:45 +0000 | [diff] [blame] | 512 | { |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 513 | TCGv_i32 t0; |
| 514 | t0 = tcg_temp_local_new_i32(); |
aurel32 | bdffd4a | 2008-10-21 11:30:45 +0000 | [diff] [blame] | 515 | tcg_gen_movi_i32(t0, val); |
| 516 | return t0; |
| 517 | } |
| 518 | |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 519 | TCGv_i64 tcg_const_local_i64(int64_t val) |
aurel32 | bdffd4a | 2008-10-21 11:30:45 +0000 | [diff] [blame] | 520 | { |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 521 | TCGv_i64 t0; |
| 522 | t0 = tcg_temp_local_new_i64(); |
aurel32 | bdffd4a | 2008-10-21 11:30:45 +0000 | [diff] [blame] | 523 | tcg_gen_movi_i64(t0, val); |
| 524 | return t0; |
| 525 | } |
| 526 | |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 527 | void tcg_register_helper(void *func, const char *name) |
| 528 | { |
| 529 | TCGContext *s = &tcg_ctx; |
| 530 | int n; |
| 531 | if ((s->nb_helpers + 1) > s->allocated_helpers) { |
| 532 | n = s->allocated_helpers; |
| 533 | if (n == 0) { |
| 534 | n = 4; |
| 535 | } else { |
| 536 | n *= 2; |
| 537 | } |
| 538 | s->helpers = realloc(s->helpers, n * sizeof(TCGHelperInfo)); |
| 539 | s->allocated_helpers = n; |
| 540 | } |
bellard | 4dc81f2 | 2008-05-22 16:08:32 +0000 | [diff] [blame] | 541 | s->helpers[s->nb_helpers].func = (tcg_target_ulong)func; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 542 | s->helpers[s->nb_helpers].name = name; |
| 543 | s->nb_helpers++; |
| 544 | } |
| 545 | |
bellard | 39cf05d | 2008-05-22 14:59:57 +0000 | [diff] [blame] | 546 | /* Note: we convert the 64 bit args to 32 bit and do some alignment |
| 547 | and endian swap. Maybe it would be better to do the alignment |
| 548 | and endian swap in tcg_reg_alloc_call(). */ |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 549 | void tcg_gen_callN(TCGContext *s, TCGv_ptr func, unsigned int flags, |
| 550 | int sizemask, TCGArg ret, int nargs, TCGArg *args) |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 551 | { |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 552 | int call_type; |
| 553 | int i; |
| 554 | int real_args; |
| 555 | int nb_rets; |
| 556 | TCGArg *nparam; |
| 557 | *gen_opc_ptr++ = INDEX_op_call; |
| 558 | nparam = gen_opparam_ptr++; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 559 | call_type = (flags & TCG_CALL_TYPE_MASK); |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 560 | if (ret != TCG_CALL_DUMMY_ARG) { |
| 561 | #if TCG_TARGET_REG_BITS < 64 |
| 562 | if (sizemask & 1) { |
| 563 | #ifdef TCG_TARGET_WORDS_BIGENDIAN |
| 564 | *gen_opparam_ptr++ = ret + 1; |
| 565 | *gen_opparam_ptr++ = ret; |
| 566 | #else |
| 567 | *gen_opparam_ptr++ = ret; |
| 568 | *gen_opparam_ptr++ = ret + 1; |
| 569 | #endif |
| 570 | nb_rets = 2; |
| 571 | } else |
| 572 | #endif |
| 573 | { |
| 574 | *gen_opparam_ptr++ = ret; |
| 575 | nb_rets = 1; |
| 576 | } |
| 577 | } else { |
| 578 | nb_rets = 0; |
| 579 | } |
| 580 | real_args = 0; |
| 581 | for (i = 0; i < nargs; i++) { |
| 582 | #if TCG_TARGET_REG_BITS < 64 |
| 583 | if (sizemask & (2 << i)) { |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 584 | #ifdef TCG_TARGET_I386 |
| 585 | /* REGPARM case: if the third parameter is 64 bit, it is |
| 586 | allocated on the stack */ |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 587 | if (i == 2 && call_type == TCG_CALL_TYPE_REGPARM) { |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 588 | call_type = TCG_CALL_TYPE_REGPARM_2; |
| 589 | flags = (flags & ~TCG_CALL_TYPE_MASK) | call_type; |
| 590 | } |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 591 | #endif |
bellard | 39cf05d | 2008-05-22 14:59:57 +0000 | [diff] [blame] | 592 | #ifdef TCG_TARGET_CALL_ALIGN_ARGS |
| 593 | /* some targets want aligned 64 bit args */ |
malc | ebd486d | 2008-11-29 19:55:15 +0000 | [diff] [blame] | 594 | if (real_args & 1) { |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 595 | *gen_opparam_ptr++ = TCG_CALL_DUMMY_ARG; |
malc | ebd486d | 2008-11-29 19:55:15 +0000 | [diff] [blame] | 596 | real_args++; |
bellard | 39cf05d | 2008-05-22 14:59:57 +0000 | [diff] [blame] | 597 | } |
| 598 | #endif |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 599 | #ifdef TCG_TARGET_WORDS_BIGENDIAN |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 600 | *gen_opparam_ptr++ = args[i] + 1; |
| 601 | *gen_opparam_ptr++ = args[i]; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 602 | #else |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 603 | *gen_opparam_ptr++ = args[i]; |
| 604 | *gen_opparam_ptr++ = args[i] + 1; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 605 | #endif |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 606 | real_args += 2; |
| 607 | } else |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 608 | #endif |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 609 | { |
| 610 | *gen_opparam_ptr++ = args[i]; |
| 611 | real_args++; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 612 | } |
| 613 | } |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 614 | *gen_opparam_ptr++ = GET_TCGV_PTR(func); |
| 615 | |
| 616 | *gen_opparam_ptr++ = flags; |
| 617 | |
| 618 | *nparam = (nb_rets << 16) | (real_args + 1); |
| 619 | |
| 620 | /* total parameters, needed to go backward in the instruction stream */ |
| 621 | *gen_opparam_ptr++ = 1 + nb_rets + real_args + 3; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 622 | } |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 623 | |
pbrook | ac56dd4 | 2008-02-03 19:56:33 +0000 | [diff] [blame] | 624 | #if TCG_TARGET_REG_BITS == 32 |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 625 | void tcg_gen_shifti_i64(TCGv_i64 ret, TCGv_i64 arg1, |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 626 | int c, int right, int arith) |
| 627 | { |
bellard | cf60bce | 2008-05-22 14:39:25 +0000 | [diff] [blame] | 628 | if (c == 0) { |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 629 | tcg_gen_mov_i32(TCGV_LOW(ret), TCGV_LOW(arg1)); |
bellard | cf60bce | 2008-05-22 14:39:25 +0000 | [diff] [blame] | 630 | tcg_gen_mov_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1)); |
| 631 | } else if (c >= 32) { |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 632 | c -= 32; |
| 633 | if (right) { |
| 634 | if (arith) { |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 635 | tcg_gen_sari_i32(TCGV_LOW(ret), TCGV_HIGH(arg1), c); |
pbrook | ac56dd4 | 2008-02-03 19:56:33 +0000 | [diff] [blame] | 636 | tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), 31); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 637 | } else { |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 638 | tcg_gen_shri_i32(TCGV_LOW(ret), TCGV_HIGH(arg1), c); |
pbrook | ac56dd4 | 2008-02-03 19:56:33 +0000 | [diff] [blame] | 639 | tcg_gen_movi_i32(TCGV_HIGH(ret), 0); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 640 | } |
| 641 | } else { |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 642 | tcg_gen_shli_i32(TCGV_HIGH(ret), TCGV_LOW(arg1), c); |
| 643 | tcg_gen_movi_i32(TCGV_LOW(ret), 0); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 644 | } |
| 645 | } else { |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 646 | TCGv_i32 t0, t1; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 647 | |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 648 | t0 = tcg_temp_new_i32(); |
| 649 | t1 = tcg_temp_new_i32(); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 650 | if (right) { |
pbrook | ac56dd4 | 2008-02-03 19:56:33 +0000 | [diff] [blame] | 651 | tcg_gen_shli_i32(t0, TCGV_HIGH(arg1), 32 - c); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 652 | if (arith) |
pbrook | ac56dd4 | 2008-02-03 19:56:33 +0000 | [diff] [blame] | 653 | tcg_gen_sari_i32(t1, TCGV_HIGH(arg1), c); |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 654 | else |
pbrook | ac56dd4 | 2008-02-03 19:56:33 +0000 | [diff] [blame] | 655 | tcg_gen_shri_i32(t1, TCGV_HIGH(arg1), c); |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 656 | tcg_gen_shri_i32(TCGV_LOW(ret), TCGV_LOW(arg1), c); |
| 657 | tcg_gen_or_i32(TCGV_LOW(ret), TCGV_LOW(ret), t0); |
pbrook | ac56dd4 | 2008-02-03 19:56:33 +0000 | [diff] [blame] | 658 | tcg_gen_mov_i32(TCGV_HIGH(ret), t1); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 659 | } else { |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 660 | tcg_gen_shri_i32(t0, TCGV_LOW(arg1), 32 - c); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 661 | /* Note: ret can be the same as arg1, so we use t1 */ |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 662 | tcg_gen_shli_i32(t1, TCGV_LOW(arg1), c); |
pbrook | ac56dd4 | 2008-02-03 19:56:33 +0000 | [diff] [blame] | 663 | tcg_gen_shli_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), c); |
| 664 | tcg_gen_or_i32(TCGV_HIGH(ret), TCGV_HIGH(ret), t0); |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 665 | tcg_gen_mov_i32(TCGV_LOW(ret), t1); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 666 | } |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 667 | tcg_temp_free_i32(t0); |
| 668 | tcg_temp_free_i32(t1); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 669 | } |
| 670 | } |
pbrook | ac56dd4 | 2008-02-03 19:56:33 +0000 | [diff] [blame] | 671 | #endif |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 672 | |
blueswir1 | 8fcd369 | 2008-08-17 20:26:25 +0000 | [diff] [blame] | 673 | static void tcg_reg_alloc_start(TCGContext *s) |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 674 | { |
| 675 | int i; |
| 676 | TCGTemp *ts; |
| 677 | for(i = 0; i < s->nb_globals; i++) { |
| 678 | ts = &s->temps[i]; |
| 679 | if (ts->fixed_reg) { |
| 680 | ts->val_type = TEMP_VAL_REG; |
| 681 | } else { |
| 682 | ts->val_type = TEMP_VAL_MEM; |
| 683 | } |
| 684 | } |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 685 | for(i = s->nb_globals; i < s->nb_temps; i++) { |
| 686 | ts = &s->temps[i]; |
| 687 | ts->val_type = TEMP_VAL_DEAD; |
| 688 | ts->mem_allocated = 0; |
| 689 | ts->fixed_reg = 0; |
| 690 | } |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 691 | for(i = 0; i < TCG_TARGET_NB_REGS; i++) { |
| 692 | s->reg_to_temp[i] = -1; |
| 693 | } |
| 694 | } |
| 695 | |
pbrook | ac56dd4 | 2008-02-03 19:56:33 +0000 | [diff] [blame] | 696 | static char *tcg_get_arg_str_idx(TCGContext *s, char *buf, int buf_size, |
| 697 | int idx) |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 698 | { |
| 699 | TCGTemp *ts; |
pbrook | ac56dd4 | 2008-02-03 19:56:33 +0000 | [diff] [blame] | 700 | |
| 701 | ts = &s->temps[idx]; |
| 702 | if (idx < s->nb_globals) { |
| 703 | pstrcpy(buf, buf_size, ts->name); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 704 | } else { |
bellard | 641d5fb | 2008-05-25 17:24:00 +0000 | [diff] [blame] | 705 | if (ts->temp_local) |
| 706 | snprintf(buf, buf_size, "loc%d", idx - s->nb_globals); |
| 707 | else |
| 708 | snprintf(buf, buf_size, "tmp%d", idx - s->nb_globals); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 709 | } |
| 710 | return buf; |
| 711 | } |
| 712 | |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 713 | char *tcg_get_arg_str_i32(TCGContext *s, char *buf, int buf_size, TCGv_i32 arg) |
pbrook | ac56dd4 | 2008-02-03 19:56:33 +0000 | [diff] [blame] | 714 | { |
pbrook | a7812ae | 2008-11-17 14:43:54 +0000 | [diff] [blame] | 715 | return tcg_get_arg_str_idx(s, buf, buf_size, GET_TCGV_I32(arg)); |
| 716 | } |
| 717 | |
| 718 | char *tcg_get_arg_str_i64(TCGContext *s, char *buf, int buf_size, TCGv_i64 arg) |
| 719 | { |
blueswir1 | a810a2d | 2008-12-07 17:16:42 +0000 | [diff] [blame] | 720 | return tcg_get_arg_str_idx(s, buf, buf_size, GET_TCGV_I64(arg)); |
pbrook | ac56dd4 | 2008-02-03 19:56:33 +0000 | [diff] [blame] | 721 | } |
| 722 | |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 723 | static int helper_cmp(const void *p1, const void *p2) |
| 724 | { |
| 725 | const TCGHelperInfo *th1 = p1; |
| 726 | const TCGHelperInfo *th2 = p2; |
| 727 | if (th1->func < th2->func) |
| 728 | return -1; |
| 729 | else if (th1->func == th2->func) |
| 730 | return 0; |
| 731 | else |
| 732 | return 1; |
| 733 | } |
| 734 | |
| 735 | /* find helper definition (Note: A hash table would be better) */ |
bellard | 4dc81f2 | 2008-05-22 16:08:32 +0000 | [diff] [blame] | 736 | static TCGHelperInfo *tcg_find_helper(TCGContext *s, tcg_target_ulong val) |
| 737 | { |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 738 | int m, m_min, m_max; |
| 739 | TCGHelperInfo *th; |
| 740 | tcg_target_ulong v; |
| 741 | |
| 742 | if (unlikely(!s->helpers_sorted)) { |
| 743 | qsort(s->helpers, s->nb_helpers, sizeof(TCGHelperInfo), |
| 744 | helper_cmp); |
| 745 | s->helpers_sorted = 1; |
| 746 | } |
| 747 | |
| 748 | /* binary search */ |
| 749 | m_min = 0; |
| 750 | m_max = s->nb_helpers - 1; |
| 751 | while (m_min <= m_max) { |
| 752 | m = (m_min + m_max) >> 1; |
| 753 | th = &s->helpers[m]; |
| 754 | v = th->func; |
| 755 | if (v == val) |
| 756 | return th; |
| 757 | else if (val < v) { |
| 758 | m_max = m - 1; |
| 759 | } else { |
| 760 | m_min = m + 1; |
| 761 | } |
bellard | 4dc81f2 | 2008-05-22 16:08:32 +0000 | [diff] [blame] | 762 | } |
| 763 | return NULL; |
| 764 | } |
| 765 | |
blueswir1 | f48f3ed | 2008-09-14 07:45:17 +0000 | [diff] [blame] | 766 | static const char * const cond_name[] = |
| 767 | { |
| 768 | [TCG_COND_EQ] = "eq", |
| 769 | [TCG_COND_NE] = "ne", |
| 770 | [TCG_COND_LT] = "lt", |
| 771 | [TCG_COND_GE] = "ge", |
| 772 | [TCG_COND_LE] = "le", |
| 773 | [TCG_COND_GT] = "gt", |
| 774 | [TCG_COND_LTU] = "ltu", |
| 775 | [TCG_COND_GEU] = "geu", |
| 776 | [TCG_COND_LEU] = "leu", |
| 777 | [TCG_COND_GTU] = "gtu" |
| 778 | }; |
| 779 | |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 780 | void tcg_dump_ops(TCGContext *s, FILE *outfile) |
| 781 | { |
| 782 | const uint16_t *opc_ptr; |
| 783 | const TCGArg *args; |
| 784 | TCGArg arg; |
bellard | 7e4597d | 2008-05-22 16:56:05 +0000 | [diff] [blame] | 785 | int c, i, k, nb_oargs, nb_iargs, nb_cargs, first_insn; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 786 | const TCGOpDef *def; |
| 787 | char buf[128]; |
| 788 | |
bellard | 7e4597d | 2008-05-22 16:56:05 +0000 | [diff] [blame] | 789 | first_insn = 1; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 790 | opc_ptr = gen_opc_buf; |
| 791 | args = gen_opparam_buf; |
| 792 | while (opc_ptr < gen_opc_ptr) { |
| 793 | c = *opc_ptr++; |
| 794 | def = &tcg_op_defs[c]; |
bellard | 7e4597d | 2008-05-22 16:56:05 +0000 | [diff] [blame] | 795 | if (c == INDEX_op_debug_insn_start) { |
| 796 | uint64_t pc; |
| 797 | #if TARGET_LONG_BITS > TCG_TARGET_REG_BITS |
| 798 | pc = ((uint64_t)args[1] << 32) | args[0]; |
| 799 | #else |
| 800 | pc = args[0]; |
| 801 | #endif |
| 802 | if (!first_insn) |
| 803 | fprintf(outfile, "\n"); |
| 804 | fprintf(outfile, " ---- 0x%" PRIx64, pc); |
| 805 | first_insn = 0; |
| 806 | nb_oargs = def->nb_oargs; |
| 807 | nb_iargs = def->nb_iargs; |
| 808 | nb_cargs = def->nb_cargs; |
| 809 | } else if (c == INDEX_op_call) { |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 810 | TCGArg arg; |
bellard | 4dc81f2 | 2008-05-22 16:08:32 +0000 | [diff] [blame] | 811 | |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 812 | /* variable number of arguments */ |
| 813 | arg = *args++; |
| 814 | nb_oargs = arg >> 16; |
| 815 | nb_iargs = arg & 0xffff; |
| 816 | nb_cargs = def->nb_cargs; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 817 | |
bellard | 7e4597d | 2008-05-22 16:56:05 +0000 | [diff] [blame] | 818 | fprintf(outfile, " %s ", def->name); |
| 819 | |
bellard | b03cce8 | 2008-05-10 10:52:05 +0000 | [diff] [blame] | 820 | /* function name */ |
pbrook | ac56dd4 | 2008-02-03 19:56:33 +0000 | [diff] [blame] | 821 | fprintf(outfile, "%s", |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 822 | tcg_get_arg_str_idx(s, buf, sizeof(buf), args[nb_oargs + nb_iargs - 1])); |
bellard | b03cce8 | 2008-05-10 10:52:05 +0000 | [diff] [blame] | 823 | /* flags */ |
| 824 | fprintf(outfile, ",$0x%" TCG_PRIlx, |
| 825 | args[nb_oargs + nb_iargs]); |
| 826 | /* nb out args */ |
| 827 | fprintf(outfile, ",$%d", nb_oargs); |
| 828 | for(i = 0; i < nb_oargs; i++) { |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 829 | fprintf(outfile, ","); |
bellard | b03cce8 | 2008-05-10 10:52:05 +0000 | [diff] [blame] | 830 | fprintf(outfile, "%s", |
| 831 | tcg_get_arg_str_idx(s, buf, sizeof(buf), args[i])); |
| 832 | } |
| 833 | for(i = 0; i < (nb_iargs - 1); i++) { |
| 834 | fprintf(outfile, ","); |
bellard | 39cf05d | 2008-05-22 14:59:57 +0000 | [diff] [blame] | 835 | if (args[nb_oargs + i] == TCG_CALL_DUMMY_ARG) { |
| 836 | fprintf(outfile, "<dummy>"); |
| 837 | } else { |
| 838 | fprintf(outfile, "%s", |
| 839 | tcg_get_arg_str_idx(s, buf, sizeof(buf), args[nb_oargs + i])); |
| 840 | } |
bellard | b03cce8 | 2008-05-10 10:52:05 +0000 | [diff] [blame] | 841 | } |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 842 | } else if (c == INDEX_op_movi_i32 |
| 843 | #if TCG_TARGET_REG_BITS == 64 |
| 844 | || c == INDEX_op_movi_i64 |
| 845 | #endif |
| 846 | ) { |
| 847 | tcg_target_ulong val; |
| 848 | TCGHelperInfo *th; |
| 849 | |
| 850 | nb_oargs = def->nb_oargs; |
| 851 | nb_iargs = def->nb_iargs; |
| 852 | nb_cargs = def->nb_cargs; |
| 853 | fprintf(outfile, " %s %s,$", def->name, |
| 854 | tcg_get_arg_str_idx(s, buf, sizeof(buf), args[0])); |
| 855 | val = args[1]; |
| 856 | th = tcg_find_helper(s, val); |
| 857 | if (th) { |
aurel32 | 3e9a474 | 2008-12-14 17:29:58 +0000 | [diff] [blame] | 858 | fprintf(outfile, "%s", th->name); |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 859 | } else { |
| 860 | if (c == INDEX_op_movi_i32) |
| 861 | fprintf(outfile, "0x%x", (uint32_t)val); |
| 862 | else |
| 863 | fprintf(outfile, "0x%" PRIx64 , (uint64_t)val); |
| 864 | } |
bellard | b03cce8 | 2008-05-10 10:52:05 +0000 | [diff] [blame] | 865 | } else { |
bellard | 7e4597d | 2008-05-22 16:56:05 +0000 | [diff] [blame] | 866 | fprintf(outfile, " %s ", def->name); |
bellard | b03cce8 | 2008-05-10 10:52:05 +0000 | [diff] [blame] | 867 | if (c == INDEX_op_nopn) { |
| 868 | /* variable number of arguments */ |
| 869 | nb_cargs = *args; |
| 870 | nb_oargs = 0; |
| 871 | nb_iargs = 0; |
| 872 | } else { |
| 873 | nb_oargs = def->nb_oargs; |
| 874 | nb_iargs = def->nb_iargs; |
| 875 | nb_cargs = def->nb_cargs; |
| 876 | } |
| 877 | |
| 878 | k = 0; |
| 879 | for(i = 0; i < nb_oargs; i++) { |
| 880 | if (k != 0) |
| 881 | fprintf(outfile, ","); |
| 882 | fprintf(outfile, "%s", |
| 883 | tcg_get_arg_str_idx(s, buf, sizeof(buf), args[k++])); |
| 884 | } |
| 885 | for(i = 0; i < nb_iargs; i++) { |
| 886 | if (k != 0) |
| 887 | fprintf(outfile, ","); |
| 888 | fprintf(outfile, "%s", |
| 889 | tcg_get_arg_str_idx(s, buf, sizeof(buf), args[k++])); |
| 890 | } |
blueswir1 | f48f3ed | 2008-09-14 07:45:17 +0000 | [diff] [blame] | 891 | if (c == INDEX_op_brcond_i32 |
| 892 | #if TCG_TARGET_REG_BITS == 32 |
| 893 | || c == INDEX_op_brcond2_i32 |
| 894 | #elif TCG_TARGET_REG_BITS == 64 |
| 895 | || c == INDEX_op_brcond_i64 |
| 896 | #endif |
| 897 | ) { |
| 898 | if (args[k] < ARRAY_SIZE(cond_name) && cond_name[args[k]]) |
| 899 | fprintf(outfile, ",%s", cond_name[args[k++]]); |
| 900 | else |
| 901 | fprintf(outfile, ",$0x%" TCG_PRIlx, args[k++]); |
| 902 | i = 1; |
| 903 | } |
| 904 | else |
| 905 | i = 0; |
| 906 | for(; i < nb_cargs; i++) { |
bellard | b03cce8 | 2008-05-10 10:52:05 +0000 | [diff] [blame] | 907 | if (k != 0) |
| 908 | fprintf(outfile, ","); |
| 909 | arg = args[k++]; |
| 910 | fprintf(outfile, "$0x%" TCG_PRIlx, arg); |
| 911 | } |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 912 | } |
| 913 | fprintf(outfile, "\n"); |
| 914 | args += nb_iargs + nb_oargs + nb_cargs; |
| 915 | } |
| 916 | } |
| 917 | |
| 918 | /* we give more priority to constraints with less registers */ |
| 919 | static int get_constraint_priority(const TCGOpDef *def, int k) |
| 920 | { |
| 921 | const TCGArgConstraint *arg_ct; |
| 922 | |
| 923 | int i, n; |
| 924 | arg_ct = &def->args_ct[k]; |
| 925 | if (arg_ct->ct & TCG_CT_ALIAS) { |
| 926 | /* an alias is equivalent to a single register */ |
| 927 | n = 1; |
| 928 | } else { |
| 929 | if (!(arg_ct->ct & TCG_CT_REG)) |
| 930 | return 0; |
| 931 | n = 0; |
| 932 | for(i = 0; i < TCG_TARGET_NB_REGS; i++) { |
| 933 | if (tcg_regset_test_reg(arg_ct->u.regs, i)) |
| 934 | n++; |
| 935 | } |
| 936 | } |
| 937 | return TCG_TARGET_NB_REGS - n + 1; |
| 938 | } |
| 939 | |
| 940 | /* sort from highest priority to lowest */ |
| 941 | static void sort_constraints(TCGOpDef *def, int start, int n) |
| 942 | { |
| 943 | int i, j, p1, p2, tmp; |
| 944 | |
| 945 | for(i = 0; i < n; i++) |
| 946 | def->sorted_args[start + i] = start + i; |
| 947 | if (n <= 1) |
| 948 | return; |
| 949 | for(i = 0; i < n - 1; i++) { |
| 950 | for(j = i + 1; j < n; j++) { |
| 951 | p1 = get_constraint_priority(def, def->sorted_args[start + i]); |
| 952 | p2 = get_constraint_priority(def, def->sorted_args[start + j]); |
| 953 | if (p1 < p2) { |
| 954 | tmp = def->sorted_args[start + i]; |
| 955 | def->sorted_args[start + i] = def->sorted_args[start + j]; |
| 956 | def->sorted_args[start + j] = tmp; |
| 957 | } |
| 958 | } |
| 959 | } |
| 960 | } |
| 961 | |
| 962 | void tcg_add_target_add_op_defs(const TCGTargetOpDef *tdefs) |
| 963 | { |
| 964 | int op; |
| 965 | TCGOpDef *def; |
| 966 | const char *ct_str; |
| 967 | int i, nb_args; |
| 968 | |
| 969 | for(;;) { |
| 970 | if (tdefs->op < 0) |
| 971 | break; |
| 972 | op = tdefs->op; |
| 973 | assert(op >= 0 && op < NB_OPS); |
| 974 | def = &tcg_op_defs[op]; |
| 975 | nb_args = def->nb_iargs + def->nb_oargs; |
| 976 | for(i = 0; i < nb_args; i++) { |
| 977 | ct_str = tdefs->args_ct_str[i]; |
| 978 | tcg_regset_clear(def->args_ct[i].u.regs); |
| 979 | def->args_ct[i].ct = 0; |
| 980 | if (ct_str[0] >= '0' && ct_str[0] <= '9') { |
| 981 | int oarg; |
| 982 | oarg = ct_str[0] - '0'; |
| 983 | assert(oarg < def->nb_oargs); |
| 984 | assert(def->args_ct[oarg].ct & TCG_CT_REG); |
| 985 | /* TCG_CT_ALIAS is for the output arguments. The input |
bellard | 5ff9d6a | 2008-02-04 00:37:54 +0000 | [diff] [blame] | 986 | argument is tagged with TCG_CT_IALIAS. */ |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 987 | def->args_ct[i] = def->args_ct[oarg]; |
bellard | 5ff9d6a | 2008-02-04 00:37:54 +0000 | [diff] [blame] | 988 | def->args_ct[oarg].ct = TCG_CT_ALIAS; |
| 989 | def->args_ct[oarg].alias_index = i; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 990 | def->args_ct[i].ct |= TCG_CT_IALIAS; |
bellard | 5ff9d6a | 2008-02-04 00:37:54 +0000 | [diff] [blame] | 991 | def->args_ct[i].alias_index = oarg; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 992 | } else { |
| 993 | for(;;) { |
| 994 | if (*ct_str == '\0') |
| 995 | break; |
| 996 | switch(*ct_str) { |
| 997 | case 'i': |
| 998 | def->args_ct[i].ct |= TCG_CT_CONST; |
| 999 | ct_str++; |
| 1000 | break; |
| 1001 | default: |
| 1002 | if (target_parse_constraint(&def->args_ct[i], &ct_str) < 0) { |
| 1003 | fprintf(stderr, "Invalid constraint '%s' for arg %d of operation '%s'\n", |
| 1004 | ct_str, i, def->name); |
| 1005 | exit(1); |
| 1006 | } |
| 1007 | } |
| 1008 | } |
| 1009 | } |
| 1010 | } |
| 1011 | |
| 1012 | /* sort the constraints (XXX: this is just an heuristic) */ |
| 1013 | sort_constraints(def, 0, def->nb_oargs); |
| 1014 | sort_constraints(def, def->nb_oargs, def->nb_iargs); |
| 1015 | |
| 1016 | #if 0 |
| 1017 | { |
| 1018 | int i; |
| 1019 | |
| 1020 | printf("%s: sorted=", def->name); |
| 1021 | for(i = 0; i < def->nb_oargs + def->nb_iargs; i++) |
| 1022 | printf(" %d", def->sorted_args[i]); |
| 1023 | printf("\n"); |
| 1024 | } |
| 1025 | #endif |
| 1026 | tdefs++; |
| 1027 | } |
| 1028 | |
| 1029 | } |
| 1030 | |
| 1031 | #ifdef USE_LIVENESS_ANALYSIS |
| 1032 | |
| 1033 | /* set a nop for an operation using 'nb_args' */ |
| 1034 | static inline void tcg_set_nop(TCGContext *s, uint16_t *opc_ptr, |
| 1035 | TCGArg *args, int nb_args) |
| 1036 | { |
| 1037 | if (nb_args == 0) { |
| 1038 | *opc_ptr = INDEX_op_nop; |
| 1039 | } else { |
| 1040 | *opc_ptr = INDEX_op_nopn; |
| 1041 | args[0] = nb_args; |
| 1042 | args[nb_args - 1] = nb_args; |
| 1043 | } |
| 1044 | } |
| 1045 | |
bellard | 641d5fb | 2008-05-25 17:24:00 +0000 | [diff] [blame] | 1046 | /* liveness analysis: end of function: globals are live, temps are |
| 1047 | dead. */ |
| 1048 | /* XXX: at this stage, not used as there would be little gains because |
| 1049 | most TBs end with a conditional jump. */ |
| 1050 | static inline void tcg_la_func_end(TCGContext *s, uint8_t *dead_temps) |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1051 | { |
| 1052 | memset(dead_temps, 0, s->nb_globals); |
| 1053 | memset(dead_temps + s->nb_globals, 1, s->nb_temps - s->nb_globals); |
| 1054 | } |
| 1055 | |
bellard | 641d5fb | 2008-05-25 17:24:00 +0000 | [diff] [blame] | 1056 | /* liveness analysis: end of basic block: globals are live, temps are |
| 1057 | dead, local temps are live. */ |
| 1058 | static inline void tcg_la_bb_end(TCGContext *s, uint8_t *dead_temps) |
| 1059 | { |
| 1060 | int i; |
| 1061 | TCGTemp *ts; |
| 1062 | |
| 1063 | memset(dead_temps, 0, s->nb_globals); |
| 1064 | ts = &s->temps[s->nb_globals]; |
| 1065 | for(i = s->nb_globals; i < s->nb_temps; i++) { |
| 1066 | if (ts->temp_local) |
| 1067 | dead_temps[i] = 0; |
| 1068 | else |
| 1069 | dead_temps[i] = 1; |
| 1070 | ts++; |
| 1071 | } |
| 1072 | } |
| 1073 | |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1074 | /* Liveness analysis : update the opc_dead_iargs array to tell if a |
| 1075 | given input arguments is dead. Instructions updating dead |
| 1076 | temporaries are removed. */ |
blueswir1 | 8fcd369 | 2008-08-17 20:26:25 +0000 | [diff] [blame] | 1077 | static void tcg_liveness_analysis(TCGContext *s) |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1078 | { |
| 1079 | int i, op_index, op, nb_args, nb_iargs, nb_oargs, arg, nb_ops; |
| 1080 | TCGArg *args; |
| 1081 | const TCGOpDef *def; |
| 1082 | uint8_t *dead_temps; |
| 1083 | unsigned int dead_iargs; |
| 1084 | |
| 1085 | gen_opc_ptr++; /* skip end */ |
| 1086 | |
| 1087 | nb_ops = gen_opc_ptr - gen_opc_buf; |
| 1088 | |
Aurelien Jarno | 94f4af0 | 2009-10-04 15:30:44 +0200 | [diff] [blame] | 1089 | s->op_dead_iargs = tcg_malloc(nb_ops * sizeof(uint16_t)); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1090 | |
| 1091 | dead_temps = tcg_malloc(s->nb_temps); |
| 1092 | memset(dead_temps, 1, s->nb_temps); |
| 1093 | |
| 1094 | args = gen_opparam_ptr; |
| 1095 | op_index = nb_ops - 1; |
| 1096 | while (op_index >= 0) { |
| 1097 | op = gen_opc_buf[op_index]; |
| 1098 | def = &tcg_op_defs[op]; |
| 1099 | switch(op) { |
| 1100 | case INDEX_op_call: |
bellard | c6e113f | 2008-05-17 12:42:15 +0000 | [diff] [blame] | 1101 | { |
| 1102 | int call_flags; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1103 | |
bellard | c6e113f | 2008-05-17 12:42:15 +0000 | [diff] [blame] | 1104 | nb_args = args[-1]; |
| 1105 | args -= nb_args; |
| 1106 | nb_iargs = args[0] & 0xffff; |
| 1107 | nb_oargs = args[0] >> 16; |
| 1108 | args++; |
| 1109 | call_flags = args[nb_oargs + nb_iargs]; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1110 | |
bellard | c6e113f | 2008-05-17 12:42:15 +0000 | [diff] [blame] | 1111 | /* pure functions can be removed if their result is not |
| 1112 | used */ |
| 1113 | if (call_flags & TCG_CALL_PURE) { |
| 1114 | for(i = 0; i < nb_oargs; i++) { |
| 1115 | arg = args[i]; |
| 1116 | if (!dead_temps[arg]) |
| 1117 | goto do_not_remove_call; |
| 1118 | } |
| 1119 | tcg_set_nop(s, gen_opc_buf + op_index, |
| 1120 | args - 1, nb_args); |
| 1121 | } else { |
| 1122 | do_not_remove_call: |
| 1123 | |
| 1124 | /* output args are dead */ |
| 1125 | for(i = 0; i < nb_oargs; i++) { |
| 1126 | arg = args[i]; |
| 1127 | dead_temps[arg] = 1; |
| 1128 | } |
| 1129 | |
aurel32 | b9c18f5 | 2009-04-06 12:33:59 +0000 | [diff] [blame] | 1130 | if (!(call_flags & TCG_CALL_CONST)) { |
| 1131 | /* globals are live (they may be used by the call) */ |
| 1132 | memset(dead_temps, 0, s->nb_globals); |
| 1133 | } |
| 1134 | |
bellard | c6e113f | 2008-05-17 12:42:15 +0000 | [diff] [blame] | 1135 | /* input args are live */ |
| 1136 | dead_iargs = 0; |
| 1137 | for(i = 0; i < nb_iargs; i++) { |
| 1138 | arg = args[i + nb_oargs]; |
bellard | 39cf05d | 2008-05-22 14:59:57 +0000 | [diff] [blame] | 1139 | if (arg != TCG_CALL_DUMMY_ARG) { |
| 1140 | if (dead_temps[arg]) { |
| 1141 | dead_iargs |= (1 << i); |
| 1142 | } |
| 1143 | dead_temps[arg] = 0; |
bellard | c6e113f | 2008-05-17 12:42:15 +0000 | [diff] [blame] | 1144 | } |
bellard | c6e113f | 2008-05-17 12:42:15 +0000 | [diff] [blame] | 1145 | } |
| 1146 | s->op_dead_iargs[op_index] = dead_iargs; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1147 | } |
bellard | c6e113f | 2008-05-17 12:42:15 +0000 | [diff] [blame] | 1148 | args--; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1149 | } |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1150 | break; |
| 1151 | case INDEX_op_set_label: |
| 1152 | args--; |
| 1153 | /* mark end of basic block */ |
| 1154 | tcg_la_bb_end(s, dead_temps); |
| 1155 | break; |
bellard | 7e4597d | 2008-05-22 16:56:05 +0000 | [diff] [blame] | 1156 | case INDEX_op_debug_insn_start: |
| 1157 | args -= def->nb_args; |
| 1158 | break; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1159 | case INDEX_op_nopn: |
| 1160 | nb_args = args[-1]; |
| 1161 | args -= nb_args; |
| 1162 | break; |
bellard | 5ff9d6a | 2008-02-04 00:37:54 +0000 | [diff] [blame] | 1163 | case INDEX_op_discard: |
| 1164 | args--; |
| 1165 | /* mark the temporary as dead */ |
| 1166 | dead_temps[args[0]] = 1; |
| 1167 | break; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1168 | case INDEX_op_end: |
| 1169 | break; |
| 1170 | /* XXX: optimize by hardcoding common cases (e.g. triadic ops) */ |
| 1171 | default: |
aurel32 | 49516bc | 2008-12-07 18:15:45 +0000 | [diff] [blame] | 1172 | args -= def->nb_args; |
| 1173 | nb_iargs = def->nb_iargs; |
| 1174 | nb_oargs = def->nb_oargs; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1175 | |
aurel32 | 49516bc | 2008-12-07 18:15:45 +0000 | [diff] [blame] | 1176 | /* Test if the operation can be removed because all |
| 1177 | its outputs are dead. We assume that nb_oargs == 0 |
| 1178 | implies side effects */ |
| 1179 | if (!(def->flags & TCG_OPF_SIDE_EFFECTS) && nb_oargs != 0) { |
| 1180 | for(i = 0; i < nb_oargs; i++) { |
| 1181 | arg = args[i]; |
| 1182 | if (!dead_temps[arg]) |
| 1183 | goto do_not_remove; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1184 | } |
aurel32 | 49516bc | 2008-12-07 18:15:45 +0000 | [diff] [blame] | 1185 | tcg_set_nop(s, gen_opc_buf + op_index, args, def->nb_args); |
| 1186 | #ifdef CONFIG_PROFILER |
| 1187 | s->del_op_count++; |
| 1188 | #endif |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1189 | } else { |
aurel32 | 49516bc | 2008-12-07 18:15:45 +0000 | [diff] [blame] | 1190 | do_not_remove: |
| 1191 | |
| 1192 | /* output args are dead */ |
| 1193 | for(i = 0; i < nb_oargs; i++) { |
| 1194 | arg = args[i]; |
| 1195 | dead_temps[arg] = 1; |
| 1196 | } |
| 1197 | |
| 1198 | /* if end of basic block, update */ |
| 1199 | if (def->flags & TCG_OPF_BB_END) { |
| 1200 | tcg_la_bb_end(s, dead_temps); |
| 1201 | } else if (def->flags & TCG_OPF_CALL_CLOBBER) { |
| 1202 | /* globals are live */ |
| 1203 | memset(dead_temps, 0, s->nb_globals); |
| 1204 | } |
| 1205 | |
| 1206 | /* input args are live */ |
| 1207 | dead_iargs = 0; |
| 1208 | for(i = 0; i < nb_iargs; i++) { |
| 1209 | arg = args[i + nb_oargs]; |
| 1210 | if (dead_temps[arg]) { |
| 1211 | dead_iargs |= (1 << i); |
| 1212 | } |
| 1213 | dead_temps[arg] = 0; |
| 1214 | } |
| 1215 | s->op_dead_iargs[op_index] = dead_iargs; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1216 | } |
| 1217 | break; |
| 1218 | } |
| 1219 | op_index--; |
| 1220 | } |
| 1221 | |
| 1222 | if (args != gen_opparam_buf) |
| 1223 | tcg_abort(); |
| 1224 | } |
| 1225 | #else |
| 1226 | /* dummy liveness analysis */ |
| 1227 | void tcg_liveness_analysis(TCGContext *s) |
| 1228 | { |
| 1229 | int nb_ops; |
| 1230 | nb_ops = gen_opc_ptr - gen_opc_buf; |
| 1231 | |
| 1232 | s->op_dead_iargs = tcg_malloc(nb_ops * sizeof(uint16_t)); |
| 1233 | memset(s->op_dead_iargs, 0, nb_ops * sizeof(uint16_t)); |
| 1234 | } |
| 1235 | #endif |
| 1236 | |
| 1237 | #ifndef NDEBUG |
| 1238 | static void dump_regs(TCGContext *s) |
| 1239 | { |
| 1240 | TCGTemp *ts; |
| 1241 | int i; |
| 1242 | char buf[64]; |
| 1243 | |
| 1244 | for(i = 0; i < s->nb_temps; i++) { |
| 1245 | ts = &s->temps[i]; |
pbrook | ac56dd4 | 2008-02-03 19:56:33 +0000 | [diff] [blame] | 1246 | printf(" %10s: ", tcg_get_arg_str_idx(s, buf, sizeof(buf), i)); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1247 | switch(ts->val_type) { |
| 1248 | case TEMP_VAL_REG: |
| 1249 | printf("%s", tcg_target_reg_names[ts->reg]); |
| 1250 | break; |
| 1251 | case TEMP_VAL_MEM: |
| 1252 | printf("%d(%s)", (int)ts->mem_offset, tcg_target_reg_names[ts->mem_reg]); |
| 1253 | break; |
| 1254 | case TEMP_VAL_CONST: |
| 1255 | printf("$0x%" TCG_PRIlx, ts->val); |
| 1256 | break; |
| 1257 | case TEMP_VAL_DEAD: |
| 1258 | printf("D"); |
| 1259 | break; |
| 1260 | default: |
| 1261 | printf("???"); |
| 1262 | break; |
| 1263 | } |
| 1264 | printf("\n"); |
| 1265 | } |
| 1266 | |
| 1267 | for(i = 0; i < TCG_TARGET_NB_REGS; i++) { |
| 1268 | if (s->reg_to_temp[i] >= 0) { |
| 1269 | printf("%s: %s\n", |
| 1270 | tcg_target_reg_names[i], |
pbrook | ac56dd4 | 2008-02-03 19:56:33 +0000 | [diff] [blame] | 1271 | tcg_get_arg_str_idx(s, buf, sizeof(buf), s->reg_to_temp[i])); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1272 | } |
| 1273 | } |
| 1274 | } |
| 1275 | |
| 1276 | static void check_regs(TCGContext *s) |
| 1277 | { |
| 1278 | int reg, k; |
| 1279 | TCGTemp *ts; |
| 1280 | char buf[64]; |
| 1281 | |
| 1282 | for(reg = 0; reg < TCG_TARGET_NB_REGS; reg++) { |
| 1283 | k = s->reg_to_temp[reg]; |
| 1284 | if (k >= 0) { |
| 1285 | ts = &s->temps[k]; |
| 1286 | if (ts->val_type != TEMP_VAL_REG || |
| 1287 | ts->reg != reg) { |
| 1288 | printf("Inconsistency for register %s:\n", |
| 1289 | tcg_target_reg_names[reg]); |
bellard | b03cce8 | 2008-05-10 10:52:05 +0000 | [diff] [blame] | 1290 | goto fail; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1291 | } |
| 1292 | } |
| 1293 | } |
| 1294 | for(k = 0; k < s->nb_temps; k++) { |
| 1295 | ts = &s->temps[k]; |
| 1296 | if (ts->val_type == TEMP_VAL_REG && |
| 1297 | !ts->fixed_reg && |
| 1298 | s->reg_to_temp[ts->reg] != k) { |
| 1299 | printf("Inconsistency for temp %s:\n", |
pbrook | ac56dd4 | 2008-02-03 19:56:33 +0000 | [diff] [blame] | 1300 | tcg_get_arg_str_idx(s, buf, sizeof(buf), k)); |
bellard | b03cce8 | 2008-05-10 10:52:05 +0000 | [diff] [blame] | 1301 | fail: |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1302 | printf("reg state:\n"); |
| 1303 | dump_regs(s); |
| 1304 | tcg_abort(); |
| 1305 | } |
| 1306 | } |
| 1307 | } |
| 1308 | #endif |
| 1309 | |
| 1310 | static void temp_allocate_frame(TCGContext *s, int temp) |
| 1311 | { |
| 1312 | TCGTemp *ts; |
| 1313 | ts = &s->temps[temp]; |
| 1314 | s->current_frame_offset = (s->current_frame_offset + sizeof(tcg_target_long) - 1) & ~(sizeof(tcg_target_long) - 1); |
| 1315 | if (s->current_frame_offset + sizeof(tcg_target_long) > s->frame_end) |
bellard | 5ff9d6a | 2008-02-04 00:37:54 +0000 | [diff] [blame] | 1316 | tcg_abort(); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1317 | ts->mem_offset = s->current_frame_offset; |
| 1318 | ts->mem_reg = s->frame_reg; |
| 1319 | ts->mem_allocated = 1; |
| 1320 | s->current_frame_offset += sizeof(tcg_target_long); |
| 1321 | } |
| 1322 | |
| 1323 | /* free register 'reg' by spilling the corresponding temporary if necessary */ |
| 1324 | static void tcg_reg_free(TCGContext *s, int reg) |
| 1325 | { |
| 1326 | TCGTemp *ts; |
| 1327 | int temp; |
| 1328 | |
| 1329 | temp = s->reg_to_temp[reg]; |
| 1330 | if (temp != -1) { |
| 1331 | ts = &s->temps[temp]; |
| 1332 | assert(ts->val_type == TEMP_VAL_REG); |
| 1333 | if (!ts->mem_coherent) { |
| 1334 | if (!ts->mem_allocated) |
| 1335 | temp_allocate_frame(s, temp); |
blueswir1 | e4d5434 | 2008-03-13 17:34:19 +0000 | [diff] [blame] | 1336 | tcg_out_st(s, ts->type, reg, ts->mem_reg, ts->mem_offset); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1337 | } |
| 1338 | ts->val_type = TEMP_VAL_MEM; |
| 1339 | s->reg_to_temp[reg] = -1; |
| 1340 | } |
| 1341 | } |
| 1342 | |
| 1343 | /* Allocate a register belonging to reg1 & ~reg2 */ |
| 1344 | static int tcg_reg_alloc(TCGContext *s, TCGRegSet reg1, TCGRegSet reg2) |
| 1345 | { |
| 1346 | int i, reg; |
| 1347 | TCGRegSet reg_ct; |
| 1348 | |
| 1349 | tcg_regset_andnot(reg_ct, reg1, reg2); |
| 1350 | |
| 1351 | /* first try free registers */ |
blueswir1 | 0954d0d | 2008-03-11 21:01:02 +0000 | [diff] [blame] | 1352 | for(i = 0; i < ARRAY_SIZE(tcg_target_reg_alloc_order); i++) { |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1353 | reg = tcg_target_reg_alloc_order[i]; |
| 1354 | if (tcg_regset_test_reg(reg_ct, reg) && s->reg_to_temp[reg] == -1) |
| 1355 | return reg; |
| 1356 | } |
| 1357 | |
| 1358 | /* XXX: do better spill choice */ |
blueswir1 | 0954d0d | 2008-03-11 21:01:02 +0000 | [diff] [blame] | 1359 | for(i = 0; i < ARRAY_SIZE(tcg_target_reg_alloc_order); i++) { |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1360 | reg = tcg_target_reg_alloc_order[i]; |
| 1361 | if (tcg_regset_test_reg(reg_ct, reg)) { |
| 1362 | tcg_reg_free(s, reg); |
| 1363 | return reg; |
| 1364 | } |
| 1365 | } |
| 1366 | |
| 1367 | tcg_abort(); |
| 1368 | } |
| 1369 | |
bellard | 641d5fb | 2008-05-25 17:24:00 +0000 | [diff] [blame] | 1370 | /* save a temporary to memory. 'allocated_regs' is used in case a |
| 1371 | temporary registers needs to be allocated to store a constant. */ |
| 1372 | static void temp_save(TCGContext *s, int temp, TCGRegSet allocated_regs) |
| 1373 | { |
| 1374 | TCGTemp *ts; |
| 1375 | int reg; |
| 1376 | |
| 1377 | ts = &s->temps[temp]; |
| 1378 | if (!ts->fixed_reg) { |
| 1379 | switch(ts->val_type) { |
| 1380 | case TEMP_VAL_REG: |
| 1381 | tcg_reg_free(s, ts->reg); |
| 1382 | break; |
| 1383 | case TEMP_VAL_DEAD: |
| 1384 | ts->val_type = TEMP_VAL_MEM; |
| 1385 | break; |
| 1386 | case TEMP_VAL_CONST: |
Aurelien Jarno | d685920 | 2009-09-30 14:16:12 +0200 | [diff] [blame] | 1387 | reg = tcg_reg_alloc(s, tcg_target_available_regs[ts->type], |
| 1388 | allocated_regs); |
bellard | 641d5fb | 2008-05-25 17:24:00 +0000 | [diff] [blame] | 1389 | if (!ts->mem_allocated) |
| 1390 | temp_allocate_frame(s, temp); |
Aurelien Jarno | d685920 | 2009-09-30 14:16:12 +0200 | [diff] [blame] | 1391 | tcg_out_movi(s, ts->type, reg, ts->val); |
| 1392 | tcg_out_st(s, ts->type, reg, ts->mem_reg, ts->mem_offset); |
bellard | 641d5fb | 2008-05-25 17:24:00 +0000 | [diff] [blame] | 1393 | ts->val_type = TEMP_VAL_MEM; |
| 1394 | break; |
| 1395 | case TEMP_VAL_MEM: |
| 1396 | break; |
| 1397 | default: |
| 1398 | tcg_abort(); |
| 1399 | } |
| 1400 | } |
| 1401 | } |
| 1402 | |
bellard | e5097dc | 2008-05-21 16:24:20 +0000 | [diff] [blame] | 1403 | /* save globals to their cannonical location and assume they can be |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 1404 | modified be the following code. 'allocated_regs' is used in case a |
| 1405 | temporary registers needs to be allocated to store a constant. */ |
| 1406 | static void save_globals(TCGContext *s, TCGRegSet allocated_regs) |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1407 | { |
bellard | 641d5fb | 2008-05-25 17:24:00 +0000 | [diff] [blame] | 1408 | int i; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1409 | |
| 1410 | for(i = 0; i < s->nb_globals; i++) { |
bellard | 641d5fb | 2008-05-25 17:24:00 +0000 | [diff] [blame] | 1411 | temp_save(s, i, allocated_regs); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1412 | } |
bellard | e5097dc | 2008-05-21 16:24:20 +0000 | [diff] [blame] | 1413 | } |
| 1414 | |
| 1415 | /* at the end of a basic block, we assume all temporaries are dead and |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 1416 | all globals are stored at their canonical location. */ |
| 1417 | static void tcg_reg_alloc_bb_end(TCGContext *s, TCGRegSet allocated_regs) |
bellard | e5097dc | 2008-05-21 16:24:20 +0000 | [diff] [blame] | 1418 | { |
| 1419 | TCGTemp *ts; |
| 1420 | int i; |
| 1421 | |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1422 | for(i = s->nb_globals; i < s->nb_temps; i++) { |
| 1423 | ts = &s->temps[i]; |
bellard | 641d5fb | 2008-05-25 17:24:00 +0000 | [diff] [blame] | 1424 | if (ts->temp_local) { |
| 1425 | temp_save(s, i, allocated_regs); |
| 1426 | } else { |
| 1427 | if (ts->val_type == TEMP_VAL_REG) { |
| 1428 | s->reg_to_temp[ts->reg] = -1; |
| 1429 | } |
| 1430 | ts->val_type = TEMP_VAL_DEAD; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1431 | } |
| 1432 | } |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 1433 | |
| 1434 | save_globals(s, allocated_regs); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1435 | } |
| 1436 | |
| 1437 | #define IS_DEAD_IARG(n) ((dead_iargs >> (n)) & 1) |
| 1438 | |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 1439 | static void tcg_reg_alloc_movi(TCGContext *s, const TCGArg *args) |
| 1440 | { |
| 1441 | TCGTemp *ots; |
| 1442 | tcg_target_ulong val; |
| 1443 | |
| 1444 | ots = &s->temps[args[0]]; |
| 1445 | val = args[1]; |
| 1446 | |
| 1447 | if (ots->fixed_reg) { |
| 1448 | /* for fixed registers, we do not do any constant |
| 1449 | propagation */ |
| 1450 | tcg_out_movi(s, ots->type, ots->reg, val); |
| 1451 | } else { |
ths | 1235fc0 | 2008-06-03 19:51:57 +0000 | [diff] [blame] | 1452 | /* The movi is not explicitly generated here */ |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 1453 | if (ots->val_type == TEMP_VAL_REG) |
| 1454 | s->reg_to_temp[ots->reg] = -1; |
| 1455 | ots->val_type = TEMP_VAL_CONST; |
| 1456 | ots->val = val; |
| 1457 | } |
| 1458 | } |
| 1459 | |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1460 | static void tcg_reg_alloc_mov(TCGContext *s, const TCGOpDef *def, |
| 1461 | const TCGArg *args, |
| 1462 | unsigned int dead_iargs) |
| 1463 | { |
| 1464 | TCGTemp *ts, *ots; |
| 1465 | int reg; |
| 1466 | const TCGArgConstraint *arg_ct; |
| 1467 | |
| 1468 | ots = &s->temps[args[0]]; |
| 1469 | ts = &s->temps[args[1]]; |
| 1470 | arg_ct = &def->args_ct[0]; |
| 1471 | |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 1472 | /* XXX: always mark arg dead if IS_DEAD_IARG(0) */ |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1473 | if (ts->val_type == TEMP_VAL_REG) { |
| 1474 | if (IS_DEAD_IARG(0) && !ts->fixed_reg && !ots->fixed_reg) { |
| 1475 | /* the mov can be suppressed */ |
| 1476 | if (ots->val_type == TEMP_VAL_REG) |
| 1477 | s->reg_to_temp[ots->reg] = -1; |
| 1478 | reg = ts->reg; |
| 1479 | s->reg_to_temp[reg] = -1; |
| 1480 | ts->val_type = TEMP_VAL_DEAD; |
| 1481 | } else { |
| 1482 | if (ots->val_type == TEMP_VAL_REG) { |
| 1483 | reg = ots->reg; |
| 1484 | } else { |
| 1485 | reg = tcg_reg_alloc(s, arg_ct->u.regs, s->reserved_regs); |
| 1486 | } |
| 1487 | if (ts->reg != reg) { |
| 1488 | tcg_out_mov(s, reg, ts->reg); |
| 1489 | } |
| 1490 | } |
| 1491 | } else if (ts->val_type == TEMP_VAL_MEM) { |
| 1492 | if (ots->val_type == TEMP_VAL_REG) { |
| 1493 | reg = ots->reg; |
| 1494 | } else { |
| 1495 | reg = tcg_reg_alloc(s, arg_ct->u.regs, s->reserved_regs); |
| 1496 | } |
blueswir1 | e4d5434 | 2008-03-13 17:34:19 +0000 | [diff] [blame] | 1497 | tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1498 | } else if (ts->val_type == TEMP_VAL_CONST) { |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 1499 | if (ots->fixed_reg) { |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1500 | reg = ots->reg; |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 1501 | tcg_out_movi(s, ots->type, reg, ts->val); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1502 | } else { |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 1503 | /* propagate constant */ |
| 1504 | if (ots->val_type == TEMP_VAL_REG) |
| 1505 | s->reg_to_temp[ots->reg] = -1; |
| 1506 | ots->val_type = TEMP_VAL_CONST; |
| 1507 | ots->val = ts->val; |
| 1508 | return; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1509 | } |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1510 | } else { |
| 1511 | tcg_abort(); |
| 1512 | } |
| 1513 | s->reg_to_temp[reg] = args[0]; |
| 1514 | ots->reg = reg; |
| 1515 | ots->val_type = TEMP_VAL_REG; |
| 1516 | ots->mem_coherent = 0; |
| 1517 | } |
| 1518 | |
| 1519 | static void tcg_reg_alloc_op(TCGContext *s, |
| 1520 | const TCGOpDef *def, int opc, |
| 1521 | const TCGArg *args, |
| 1522 | unsigned int dead_iargs) |
| 1523 | { |
| 1524 | TCGRegSet allocated_regs; |
| 1525 | int i, k, nb_iargs, nb_oargs, reg; |
| 1526 | TCGArg arg; |
| 1527 | const TCGArgConstraint *arg_ct; |
| 1528 | TCGTemp *ts; |
| 1529 | TCGArg new_args[TCG_MAX_OP_ARGS]; |
| 1530 | int const_args[TCG_MAX_OP_ARGS]; |
| 1531 | |
| 1532 | nb_oargs = def->nb_oargs; |
| 1533 | nb_iargs = def->nb_iargs; |
| 1534 | |
| 1535 | /* copy constants */ |
| 1536 | memcpy(new_args + nb_oargs + nb_iargs, |
| 1537 | args + nb_oargs + nb_iargs, |
| 1538 | sizeof(TCGArg) * def->nb_cargs); |
| 1539 | |
| 1540 | /* satisfy input constraints */ |
| 1541 | tcg_regset_set(allocated_regs, s->reserved_regs); |
| 1542 | for(k = 0; k < nb_iargs; k++) { |
| 1543 | i = def->sorted_args[nb_oargs + k]; |
| 1544 | arg = args[i]; |
| 1545 | arg_ct = &def->args_ct[i]; |
| 1546 | ts = &s->temps[arg]; |
| 1547 | if (ts->val_type == TEMP_VAL_MEM) { |
| 1548 | reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs); |
blueswir1 | e4d5434 | 2008-03-13 17:34:19 +0000 | [diff] [blame] | 1549 | tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1550 | ts->val_type = TEMP_VAL_REG; |
| 1551 | ts->reg = reg; |
| 1552 | ts->mem_coherent = 1; |
| 1553 | s->reg_to_temp[reg] = arg; |
| 1554 | } else if (ts->val_type == TEMP_VAL_CONST) { |
| 1555 | if (tcg_target_const_match(ts->val, arg_ct)) { |
| 1556 | /* constant is OK for instruction */ |
| 1557 | const_args[i] = 1; |
| 1558 | new_args[i] = ts->val; |
| 1559 | goto iarg_end; |
| 1560 | } else { |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 1561 | /* need to move to a register */ |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1562 | reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs); |
| 1563 | tcg_out_movi(s, ts->type, reg, ts->val); |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 1564 | ts->val_type = TEMP_VAL_REG; |
| 1565 | ts->reg = reg; |
| 1566 | ts->mem_coherent = 0; |
| 1567 | s->reg_to_temp[reg] = arg; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1568 | } |
| 1569 | } |
| 1570 | assert(ts->val_type == TEMP_VAL_REG); |
bellard | 5ff9d6a | 2008-02-04 00:37:54 +0000 | [diff] [blame] | 1571 | if (arg_ct->ct & TCG_CT_IALIAS) { |
| 1572 | if (ts->fixed_reg) { |
| 1573 | /* if fixed register, we must allocate a new register |
| 1574 | if the alias is not the same register */ |
| 1575 | if (arg != args[arg_ct->alias_index]) |
| 1576 | goto allocate_in_reg; |
| 1577 | } else { |
| 1578 | /* if the input is aliased to an output and if it is |
| 1579 | not dead after the instruction, we must allocate |
| 1580 | a new register and move it */ |
| 1581 | if (!IS_DEAD_IARG(i - nb_oargs)) |
| 1582 | goto allocate_in_reg; |
| 1583 | } |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1584 | } |
| 1585 | reg = ts->reg; |
| 1586 | if (tcg_regset_test_reg(arg_ct->u.regs, reg)) { |
| 1587 | /* nothing to do : the constraint is satisfied */ |
| 1588 | } else { |
| 1589 | allocate_in_reg: |
| 1590 | /* allocate a new register matching the constraint |
| 1591 | and move the temporary register into it */ |
| 1592 | reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs); |
| 1593 | tcg_out_mov(s, reg, ts->reg); |
| 1594 | } |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1595 | new_args[i] = reg; |
| 1596 | const_args[i] = 0; |
| 1597 | tcg_regset_set_reg(allocated_regs, reg); |
| 1598 | iarg_end: ; |
| 1599 | } |
| 1600 | |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 1601 | if (def->flags & TCG_OPF_BB_END) { |
| 1602 | tcg_reg_alloc_bb_end(s, allocated_regs); |
| 1603 | } else { |
| 1604 | /* mark dead temporaries and free the associated registers */ |
| 1605 | for(i = 0; i < nb_iargs; i++) { |
| 1606 | arg = args[nb_oargs + i]; |
| 1607 | if (IS_DEAD_IARG(i)) { |
| 1608 | ts = &s->temps[arg]; |
| 1609 | if (!ts->fixed_reg) { |
| 1610 | if (ts->val_type == TEMP_VAL_REG) |
| 1611 | s->reg_to_temp[ts->reg] = -1; |
| 1612 | ts->val_type = TEMP_VAL_DEAD; |
| 1613 | } |
| 1614 | } |
| 1615 | } |
| 1616 | |
| 1617 | if (def->flags & TCG_OPF_CALL_CLOBBER) { |
| 1618 | /* XXX: permit generic clobber register list ? */ |
| 1619 | for(reg = 0; reg < TCG_TARGET_NB_REGS; reg++) { |
| 1620 | if (tcg_regset_test_reg(tcg_target_call_clobber_regs, reg)) { |
| 1621 | tcg_reg_free(s, reg); |
| 1622 | } |
| 1623 | } |
| 1624 | /* XXX: for load/store we could do that only for the slow path |
| 1625 | (i.e. when a memory callback is called) */ |
| 1626 | |
| 1627 | /* store globals and free associated registers (we assume the insn |
| 1628 | can modify any global. */ |
| 1629 | save_globals(s, allocated_regs); |
| 1630 | } |
| 1631 | |
| 1632 | /* satisfy the output constraints */ |
| 1633 | tcg_regset_set(allocated_regs, s->reserved_regs); |
| 1634 | for(k = 0; k < nb_oargs; k++) { |
| 1635 | i = def->sorted_args[k]; |
| 1636 | arg = args[i]; |
| 1637 | arg_ct = &def->args_ct[i]; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1638 | ts = &s->temps[arg]; |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 1639 | if (arg_ct->ct & TCG_CT_ALIAS) { |
| 1640 | reg = new_args[arg_ct->alias_index]; |
| 1641 | } else { |
| 1642 | /* if fixed register, we try to use it */ |
| 1643 | reg = ts->reg; |
| 1644 | if (ts->fixed_reg && |
| 1645 | tcg_regset_test_reg(arg_ct->u.regs, reg)) { |
| 1646 | goto oarg_end; |
| 1647 | } |
| 1648 | reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs); |
| 1649 | } |
| 1650 | tcg_regset_set_reg(allocated_regs, reg); |
| 1651 | /* if a fixed register is used, then a move will be done afterwards */ |
| 1652 | if (!ts->fixed_reg) { |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1653 | if (ts->val_type == TEMP_VAL_REG) |
| 1654 | s->reg_to_temp[ts->reg] = -1; |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 1655 | ts->val_type = TEMP_VAL_REG; |
| 1656 | ts->reg = reg; |
| 1657 | /* temp value is modified, so the value kept in memory is |
| 1658 | potentially not the same */ |
| 1659 | ts->mem_coherent = 0; |
| 1660 | s->reg_to_temp[reg] = arg; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1661 | } |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 1662 | oarg_end: |
| 1663 | new_args[i] = reg; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1664 | } |
| 1665 | } |
| 1666 | |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1667 | /* emit instruction */ |
| 1668 | tcg_out_op(s, opc, new_args, const_args); |
| 1669 | |
| 1670 | /* move the outputs in the correct register if needed */ |
| 1671 | for(i = 0; i < nb_oargs; i++) { |
| 1672 | ts = &s->temps[args[i]]; |
| 1673 | reg = new_args[i]; |
| 1674 | if (ts->fixed_reg && ts->reg != reg) { |
| 1675 | tcg_out_mov(s, ts->reg, reg); |
| 1676 | } |
| 1677 | } |
| 1678 | } |
| 1679 | |
bellard | b03cce8 | 2008-05-10 10:52:05 +0000 | [diff] [blame] | 1680 | #ifdef TCG_TARGET_STACK_GROWSUP |
| 1681 | #define STACK_DIR(x) (-(x)) |
| 1682 | #else |
| 1683 | #define STACK_DIR(x) (x) |
| 1684 | #endif |
| 1685 | |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1686 | static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def, |
| 1687 | int opc, const TCGArg *args, |
| 1688 | unsigned int dead_iargs) |
| 1689 | { |
| 1690 | int nb_iargs, nb_oargs, flags, nb_regs, i, reg, nb_params; |
| 1691 | TCGArg arg, func_arg; |
| 1692 | TCGTemp *ts; |
aurel32 | f54b3f9 | 2008-04-12 20:14:54 +0000 | [diff] [blame] | 1693 | tcg_target_long stack_offset, call_stack_size, func_addr; |
bellard | b03cce8 | 2008-05-10 10:52:05 +0000 | [diff] [blame] | 1694 | int const_func_arg, allocate_args; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1695 | TCGRegSet allocated_regs; |
| 1696 | const TCGArgConstraint *arg_ct; |
| 1697 | |
| 1698 | arg = *args++; |
| 1699 | |
| 1700 | nb_oargs = arg >> 16; |
| 1701 | nb_iargs = arg & 0xffff; |
| 1702 | nb_params = nb_iargs - 1; |
| 1703 | |
| 1704 | flags = args[nb_oargs + nb_iargs]; |
| 1705 | |
| 1706 | nb_regs = tcg_target_get_call_iarg_regs_count(flags); |
| 1707 | if (nb_regs > nb_params) |
| 1708 | nb_regs = nb_params; |
| 1709 | |
| 1710 | /* assign stack slots first */ |
| 1711 | /* XXX: preallocate call stack */ |
| 1712 | call_stack_size = (nb_params - nb_regs) * sizeof(tcg_target_long); |
| 1713 | call_stack_size = (call_stack_size + TCG_TARGET_STACK_ALIGN - 1) & |
| 1714 | ~(TCG_TARGET_STACK_ALIGN - 1); |
bellard | b03cce8 | 2008-05-10 10:52:05 +0000 | [diff] [blame] | 1715 | allocate_args = (call_stack_size > TCG_STATIC_CALL_ARGS_SIZE); |
| 1716 | if (allocate_args) { |
| 1717 | tcg_out_addi(s, TCG_REG_CALL_STACK, -STACK_DIR(call_stack_size)); |
| 1718 | } |
bellard | 39cf05d | 2008-05-22 14:59:57 +0000 | [diff] [blame] | 1719 | |
| 1720 | stack_offset = TCG_TARGET_CALL_STACK_OFFSET; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1721 | for(i = nb_regs; i < nb_params; i++) { |
| 1722 | arg = args[nb_oargs + i]; |
bellard | 39cf05d | 2008-05-22 14:59:57 +0000 | [diff] [blame] | 1723 | #ifdef TCG_TARGET_STACK_GROWSUP |
| 1724 | stack_offset -= sizeof(tcg_target_long); |
| 1725 | #endif |
| 1726 | if (arg != TCG_CALL_DUMMY_ARG) { |
| 1727 | ts = &s->temps[arg]; |
| 1728 | if (ts->val_type == TEMP_VAL_REG) { |
| 1729 | tcg_out_st(s, ts->type, ts->reg, TCG_REG_CALL_STACK, stack_offset); |
| 1730 | } else if (ts->val_type == TEMP_VAL_MEM) { |
| 1731 | reg = tcg_reg_alloc(s, tcg_target_available_regs[ts->type], |
| 1732 | s->reserved_regs); |
| 1733 | /* XXX: not correct if reading values from the stack */ |
| 1734 | tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset); |
| 1735 | tcg_out_st(s, ts->type, reg, TCG_REG_CALL_STACK, stack_offset); |
| 1736 | } else if (ts->val_type == TEMP_VAL_CONST) { |
| 1737 | reg = tcg_reg_alloc(s, tcg_target_available_regs[ts->type], |
| 1738 | s->reserved_regs); |
| 1739 | /* XXX: sign extend may be needed on some targets */ |
| 1740 | tcg_out_movi(s, ts->type, reg, ts->val); |
| 1741 | tcg_out_st(s, ts->type, reg, TCG_REG_CALL_STACK, stack_offset); |
| 1742 | } else { |
| 1743 | tcg_abort(); |
| 1744 | } |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1745 | } |
bellard | 39cf05d | 2008-05-22 14:59:57 +0000 | [diff] [blame] | 1746 | #ifndef TCG_TARGET_STACK_GROWSUP |
| 1747 | stack_offset += sizeof(tcg_target_long); |
| 1748 | #endif |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1749 | } |
| 1750 | |
| 1751 | /* assign input registers */ |
| 1752 | tcg_regset_set(allocated_regs, s->reserved_regs); |
| 1753 | for(i = 0; i < nb_regs; i++) { |
| 1754 | arg = args[nb_oargs + i]; |
bellard | 39cf05d | 2008-05-22 14:59:57 +0000 | [diff] [blame] | 1755 | if (arg != TCG_CALL_DUMMY_ARG) { |
| 1756 | ts = &s->temps[arg]; |
| 1757 | reg = tcg_target_call_iarg_regs[i]; |
| 1758 | tcg_reg_free(s, reg); |
| 1759 | if (ts->val_type == TEMP_VAL_REG) { |
| 1760 | if (ts->reg != reg) { |
| 1761 | tcg_out_mov(s, reg, ts->reg); |
| 1762 | } |
| 1763 | } else if (ts->val_type == TEMP_VAL_MEM) { |
| 1764 | tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset); |
| 1765 | } else if (ts->val_type == TEMP_VAL_CONST) { |
| 1766 | /* XXX: sign extend ? */ |
| 1767 | tcg_out_movi(s, ts->type, reg, ts->val); |
| 1768 | } else { |
| 1769 | tcg_abort(); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1770 | } |
bellard | 39cf05d | 2008-05-22 14:59:57 +0000 | [diff] [blame] | 1771 | tcg_regset_set_reg(allocated_regs, reg); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1772 | } |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1773 | } |
| 1774 | |
| 1775 | /* assign function address */ |
| 1776 | func_arg = args[nb_oargs + nb_iargs - 1]; |
| 1777 | arg_ct = &def->args_ct[0]; |
| 1778 | ts = &s->temps[func_arg]; |
aurel32 | f54b3f9 | 2008-04-12 20:14:54 +0000 | [diff] [blame] | 1779 | func_addr = ts->val; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1780 | const_func_arg = 0; |
| 1781 | if (ts->val_type == TEMP_VAL_MEM) { |
| 1782 | reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs); |
blueswir1 | e4d5434 | 2008-03-13 17:34:19 +0000 | [diff] [blame] | 1783 | tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1784 | func_arg = reg; |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 1785 | tcg_regset_set_reg(allocated_regs, reg); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1786 | } else if (ts->val_type == TEMP_VAL_REG) { |
| 1787 | reg = ts->reg; |
| 1788 | if (!tcg_regset_test_reg(arg_ct->u.regs, reg)) { |
| 1789 | reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs); |
| 1790 | tcg_out_mov(s, reg, ts->reg); |
| 1791 | } |
| 1792 | func_arg = reg; |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 1793 | tcg_regset_set_reg(allocated_regs, reg); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1794 | } else if (ts->val_type == TEMP_VAL_CONST) { |
aurel32 | f54b3f9 | 2008-04-12 20:14:54 +0000 | [diff] [blame] | 1795 | if (tcg_target_const_match(func_addr, arg_ct)) { |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1796 | const_func_arg = 1; |
aurel32 | f54b3f9 | 2008-04-12 20:14:54 +0000 | [diff] [blame] | 1797 | func_arg = func_addr; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1798 | } else { |
| 1799 | reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs); |
aurel32 | f54b3f9 | 2008-04-12 20:14:54 +0000 | [diff] [blame] | 1800 | tcg_out_movi(s, ts->type, reg, func_addr); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1801 | func_arg = reg; |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 1802 | tcg_regset_set_reg(allocated_regs, reg); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1803 | } |
| 1804 | } else { |
| 1805 | tcg_abort(); |
| 1806 | } |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 1807 | |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1808 | |
| 1809 | /* mark dead temporaries and free the associated registers */ |
bellard | c6e113f | 2008-05-17 12:42:15 +0000 | [diff] [blame] | 1810 | for(i = 0; i < nb_iargs; i++) { |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1811 | arg = args[nb_oargs + i]; |
| 1812 | if (IS_DEAD_IARG(i)) { |
| 1813 | ts = &s->temps[arg]; |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 1814 | if (!ts->fixed_reg) { |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1815 | if (ts->val_type == TEMP_VAL_REG) |
| 1816 | s->reg_to_temp[ts->reg] = -1; |
| 1817 | ts->val_type = TEMP_VAL_DEAD; |
| 1818 | } |
| 1819 | } |
| 1820 | } |
| 1821 | |
| 1822 | /* clobber call registers */ |
| 1823 | for(reg = 0; reg < TCG_TARGET_NB_REGS; reg++) { |
| 1824 | if (tcg_regset_test_reg(tcg_target_call_clobber_regs, reg)) { |
| 1825 | tcg_reg_free(s, reg); |
| 1826 | } |
| 1827 | } |
| 1828 | |
| 1829 | /* store globals and free associated registers (we assume the call |
| 1830 | can modify any global. */ |
aurel32 | b9c18f5 | 2009-04-06 12:33:59 +0000 | [diff] [blame] | 1831 | if (!(flags & TCG_CALL_CONST)) { |
| 1832 | save_globals(s, allocated_regs); |
| 1833 | } |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1834 | |
| 1835 | tcg_out_op(s, opc, &func_arg, &const_func_arg); |
| 1836 | |
bellard | b03cce8 | 2008-05-10 10:52:05 +0000 | [diff] [blame] | 1837 | if (allocate_args) { |
| 1838 | tcg_out_addi(s, TCG_REG_CALL_STACK, STACK_DIR(call_stack_size)); |
| 1839 | } |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1840 | |
| 1841 | /* assign output registers and emit moves if needed */ |
| 1842 | for(i = 0; i < nb_oargs; i++) { |
| 1843 | arg = args[i]; |
| 1844 | ts = &s->temps[arg]; |
| 1845 | reg = tcg_target_call_oarg_regs[i]; |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 1846 | assert(s->reg_to_temp[reg] == -1); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1847 | if (ts->fixed_reg) { |
| 1848 | if (ts->reg != reg) { |
| 1849 | tcg_out_mov(s, ts->reg, reg); |
| 1850 | } |
| 1851 | } else { |
| 1852 | if (ts->val_type == TEMP_VAL_REG) |
| 1853 | s->reg_to_temp[ts->reg] = -1; |
| 1854 | ts->val_type = TEMP_VAL_REG; |
| 1855 | ts->reg = reg; |
| 1856 | ts->mem_coherent = 0; |
| 1857 | s->reg_to_temp[reg] = arg; |
| 1858 | } |
| 1859 | } |
| 1860 | |
| 1861 | return nb_iargs + nb_oargs + def->nb_cargs + 1; |
| 1862 | } |
| 1863 | |
| 1864 | #ifdef CONFIG_PROFILER |
| 1865 | |
aurel32 | 54604f7 | 2008-12-07 20:35:00 +0000 | [diff] [blame] | 1866 | static int64_t tcg_table_op_count[NB_OPS]; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1867 | |
Blue Swirl | 871e6c3 | 2009-07-21 12:18:03 +0000 | [diff] [blame] | 1868 | static void dump_op_count(void) |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1869 | { |
| 1870 | int i; |
| 1871 | FILE *f; |
aurel32 | 54604f7 | 2008-12-07 20:35:00 +0000 | [diff] [blame] | 1872 | f = fopen("/tmp/op.log", "w"); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1873 | for(i = INDEX_op_end; i < NB_OPS; i++) { |
aurel32 | 54604f7 | 2008-12-07 20:35:00 +0000 | [diff] [blame] | 1874 | fprintf(f, "%s %" PRId64 "\n", tcg_op_defs[i].name, tcg_table_op_count[i]); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1875 | } |
| 1876 | fclose(f); |
| 1877 | } |
| 1878 | #endif |
| 1879 | |
| 1880 | |
| 1881 | static inline int tcg_gen_code_common(TCGContext *s, uint8_t *gen_code_buf, |
pbrook | 2ba1eeb | 2008-02-10 02:41:15 +0000 | [diff] [blame] | 1882 | long search_pc) |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1883 | { |
bellard | b314f27 | 2008-05-25 18:21:31 +0000 | [diff] [blame] | 1884 | int opc, op_index; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1885 | const TCGOpDef *def; |
| 1886 | unsigned int dead_iargs; |
| 1887 | const TCGArg *args; |
| 1888 | |
| 1889 | #ifdef DEBUG_DISAS |
aliguori | 8fec2b8 | 2009-01-15 22:36:53 +0000 | [diff] [blame] | 1890 | if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) { |
aliguori | 93fcfe3 | 2009-01-15 22:34:14 +0000 | [diff] [blame] | 1891 | qemu_log("OP:\n"); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1892 | tcg_dump_ops(s, logfile); |
aliguori | 93fcfe3 | 2009-01-15 22:34:14 +0000 | [diff] [blame] | 1893 | qemu_log("\n"); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1894 | } |
| 1895 | #endif |
| 1896 | |
bellard | a23a9ec | 2008-05-23 09:52:20 +0000 | [diff] [blame] | 1897 | #ifdef CONFIG_PROFILER |
| 1898 | s->la_time -= profile_getclock(); |
| 1899 | #endif |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1900 | tcg_liveness_analysis(s); |
bellard | a23a9ec | 2008-05-23 09:52:20 +0000 | [diff] [blame] | 1901 | #ifdef CONFIG_PROFILER |
| 1902 | s->la_time += profile_getclock(); |
| 1903 | #endif |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1904 | |
| 1905 | #ifdef DEBUG_DISAS |
aliguori | 8fec2b8 | 2009-01-15 22:36:53 +0000 | [diff] [blame] | 1906 | if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP_OPT))) { |
Aurelien Jarno | 6a95702 | 2009-10-07 07:53:41 +0200 | [diff] [blame] | 1907 | qemu_log("OP after liveness analysis:\n"); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1908 | tcg_dump_ops(s, logfile); |
aliguori | 93fcfe3 | 2009-01-15 22:34:14 +0000 | [diff] [blame] | 1909 | qemu_log("\n"); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1910 | } |
| 1911 | #endif |
| 1912 | |
| 1913 | tcg_reg_alloc_start(s); |
| 1914 | |
| 1915 | s->code_buf = gen_code_buf; |
| 1916 | s->code_ptr = gen_code_buf; |
| 1917 | |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1918 | args = gen_opparam_buf; |
| 1919 | op_index = 0; |
blueswir1 | b3db875 | 2008-03-08 13:33:42 +0000 | [diff] [blame] | 1920 | |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1921 | for(;;) { |
| 1922 | opc = gen_opc_buf[op_index]; |
| 1923 | #ifdef CONFIG_PROFILER |
aurel32 | 54604f7 | 2008-12-07 20:35:00 +0000 | [diff] [blame] | 1924 | tcg_table_op_count[opc]++; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1925 | #endif |
| 1926 | def = &tcg_op_defs[opc]; |
| 1927 | #if 0 |
| 1928 | printf("%s: %d %d %d\n", def->name, |
| 1929 | def->nb_oargs, def->nb_iargs, def->nb_cargs); |
| 1930 | // dump_regs(s); |
| 1931 | #endif |
| 1932 | switch(opc) { |
| 1933 | case INDEX_op_mov_i32: |
| 1934 | #if TCG_TARGET_REG_BITS == 64 |
| 1935 | case INDEX_op_mov_i64: |
| 1936 | #endif |
| 1937 | dead_iargs = s->op_dead_iargs[op_index]; |
| 1938 | tcg_reg_alloc_mov(s, def, args, dead_iargs); |
| 1939 | break; |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 1940 | case INDEX_op_movi_i32: |
| 1941 | #if TCG_TARGET_REG_BITS == 64 |
| 1942 | case INDEX_op_movi_i64: |
| 1943 | #endif |
| 1944 | tcg_reg_alloc_movi(s, args); |
| 1945 | break; |
bellard | 7e4597d | 2008-05-22 16:56:05 +0000 | [diff] [blame] | 1946 | case INDEX_op_debug_insn_start: |
| 1947 | /* debug instruction */ |
| 1948 | break; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1949 | case INDEX_op_nop: |
| 1950 | case INDEX_op_nop1: |
| 1951 | case INDEX_op_nop2: |
| 1952 | case INDEX_op_nop3: |
| 1953 | break; |
| 1954 | case INDEX_op_nopn: |
| 1955 | args += args[0]; |
| 1956 | goto next; |
bellard | 5ff9d6a | 2008-02-04 00:37:54 +0000 | [diff] [blame] | 1957 | case INDEX_op_discard: |
| 1958 | { |
| 1959 | TCGTemp *ts; |
| 1960 | ts = &s->temps[args[0]]; |
| 1961 | /* mark the temporary as dead */ |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 1962 | if (!ts->fixed_reg) { |
bellard | 5ff9d6a | 2008-02-04 00:37:54 +0000 | [diff] [blame] | 1963 | if (ts->val_type == TEMP_VAL_REG) |
| 1964 | s->reg_to_temp[ts->reg] = -1; |
| 1965 | ts->val_type = TEMP_VAL_DEAD; |
| 1966 | } |
| 1967 | } |
| 1968 | break; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1969 | case INDEX_op_set_label: |
bellard | e8996ee | 2008-05-23 17:33:39 +0000 | [diff] [blame] | 1970 | tcg_reg_alloc_bb_end(s, s->reserved_regs); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1971 | tcg_out_label(s, args[0], (long)s->code_ptr); |
| 1972 | break; |
| 1973 | case INDEX_op_call: |
| 1974 | dead_iargs = s->op_dead_iargs[op_index]; |
| 1975 | args += tcg_reg_alloc_call(s, def, opc, args, dead_iargs); |
| 1976 | goto next; |
| 1977 | case INDEX_op_end: |
| 1978 | goto the_end; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1979 | default: |
| 1980 | /* Note: in order to speed up the code, it would be much |
| 1981 | faster to have specialized register allocator functions for |
| 1982 | some common argument patterns */ |
| 1983 | dead_iargs = s->op_dead_iargs[op_index]; |
| 1984 | tcg_reg_alloc_op(s, def, opc, args, dead_iargs); |
| 1985 | break; |
| 1986 | } |
| 1987 | args += def->nb_args; |
ths | 8df1ca4 | 2008-06-11 11:03:34 +0000 | [diff] [blame] | 1988 | next: |
pbrook | 2ba1eeb | 2008-02-10 02:41:15 +0000 | [diff] [blame] | 1989 | if (search_pc >= 0 && search_pc < s->code_ptr - gen_code_buf) { |
bellard | b314f27 | 2008-05-25 18:21:31 +0000 | [diff] [blame] | 1990 | return op_index; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 1991 | } |
| 1992 | op_index++; |
| 1993 | #ifndef NDEBUG |
| 1994 | check_regs(s); |
| 1995 | #endif |
| 1996 | } |
| 1997 | the_end: |
| 1998 | return -1; |
| 1999 | } |
| 2000 | |
aurel32 | 54604f7 | 2008-12-07 20:35:00 +0000 | [diff] [blame] | 2001 | int tcg_gen_code(TCGContext *s, uint8_t *gen_code_buf) |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 2002 | { |
| 2003 | #ifdef CONFIG_PROFILER |
| 2004 | { |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 2005 | int n; |
| 2006 | n = (gen_opc_ptr - gen_opc_buf); |
bellard | a23a9ec | 2008-05-23 09:52:20 +0000 | [diff] [blame] | 2007 | s->op_count += n; |
| 2008 | if (n > s->op_count_max) |
| 2009 | s->op_count_max = n; |
| 2010 | |
| 2011 | s->temp_count += s->nb_temps; |
| 2012 | if (s->nb_temps > s->temp_count_max) |
| 2013 | s->temp_count_max = s->nb_temps; |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 2014 | } |
| 2015 | #endif |
| 2016 | |
pbrook | 2ba1eeb | 2008-02-10 02:41:15 +0000 | [diff] [blame] | 2017 | tcg_gen_code_common(s, gen_code_buf, -1); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 2018 | |
| 2019 | /* flush instruction cache */ |
| 2020 | flush_icache_range((unsigned long)gen_code_buf, |
| 2021 | (unsigned long)s->code_ptr); |
| 2022 | return s->code_ptr - gen_code_buf; |
| 2023 | } |
| 2024 | |
pbrook | 2ba1eeb | 2008-02-10 02:41:15 +0000 | [diff] [blame] | 2025 | /* Return the index of the micro operation such as the pc after is < |
pbrook | 623e265 | 2008-02-10 14:09:09 +0000 | [diff] [blame] | 2026 | offset bytes from the start of the TB. The contents of gen_code_buf must |
| 2027 | not be changed, though writing the same values is ok. |
| 2028 | Return -1 if not found. */ |
aurel32 | 54604f7 | 2008-12-07 20:35:00 +0000 | [diff] [blame] | 2029 | int tcg_gen_code_search_pc(TCGContext *s, uint8_t *gen_code_buf, long offset) |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 2030 | { |
pbrook | 623e265 | 2008-02-10 14:09:09 +0000 | [diff] [blame] | 2031 | return tcg_gen_code_common(s, gen_code_buf, offset); |
bellard | c896fe2 | 2008-02-01 10:05:41 +0000 | [diff] [blame] | 2032 | } |
bellard | a23a9ec | 2008-05-23 09:52:20 +0000 | [diff] [blame] | 2033 | |
| 2034 | #ifdef CONFIG_PROFILER |
| 2035 | void tcg_dump_info(FILE *f, |
| 2036 | int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) |
| 2037 | { |
| 2038 | TCGContext *s = &tcg_ctx; |
| 2039 | int64_t tot; |
| 2040 | |
| 2041 | tot = s->interm_time + s->code_time; |
| 2042 | cpu_fprintf(f, "JIT cycles %" PRId64 " (%0.3f s at 2.4 GHz)\n", |
| 2043 | tot, tot / 2.4e9); |
| 2044 | cpu_fprintf(f, "translated TBs %" PRId64 " (aborted=%" PRId64 " %0.1f%%)\n", |
| 2045 | s->tb_count, |
| 2046 | s->tb_count1 - s->tb_count, |
| 2047 | s->tb_count1 ? (double)(s->tb_count1 - s->tb_count) / s->tb_count1 * 100.0 : 0); |
| 2048 | cpu_fprintf(f, "avg ops/TB %0.1f max=%d\n", |
| 2049 | s->tb_count ? (double)s->op_count / s->tb_count : 0, s->op_count_max); |
bellard | a23a9ec | 2008-05-23 09:52:20 +0000 | [diff] [blame] | 2050 | cpu_fprintf(f, "deleted ops/TB %0.2f\n", |
| 2051 | s->tb_count ? |
| 2052 | (double)s->del_op_count / s->tb_count : 0); |
| 2053 | cpu_fprintf(f, "avg temps/TB %0.2f max=%d\n", |
| 2054 | s->tb_count ? |
| 2055 | (double)s->temp_count / s->tb_count : 0, |
| 2056 | s->temp_count_max); |
| 2057 | |
| 2058 | cpu_fprintf(f, "cycles/op %0.1f\n", |
| 2059 | s->op_count ? (double)tot / s->op_count : 0); |
| 2060 | cpu_fprintf(f, "cycles/in byte %0.1f\n", |
| 2061 | s->code_in_len ? (double)tot / s->code_in_len : 0); |
| 2062 | cpu_fprintf(f, "cycles/out byte %0.1f\n", |
| 2063 | s->code_out_len ? (double)tot / s->code_out_len : 0); |
| 2064 | if (tot == 0) |
| 2065 | tot = 1; |
| 2066 | cpu_fprintf(f, " gen_interm time %0.1f%%\n", |
| 2067 | (double)s->interm_time / tot * 100.0); |
| 2068 | cpu_fprintf(f, " gen_code time %0.1f%%\n", |
| 2069 | (double)s->code_time / tot * 100.0); |
| 2070 | cpu_fprintf(f, "liveness/code time %0.1f%%\n", |
| 2071 | (double)s->la_time / (s->code_time ? s->code_time : 1) * 100.0); |
| 2072 | cpu_fprintf(f, "cpu_restore count %" PRId64 "\n", |
| 2073 | s->restore_count); |
| 2074 | cpu_fprintf(f, " avg cycles %0.1f\n", |
| 2075 | s->restore_count ? (double)s->restore_time / s->restore_count : 0); |
Blue Swirl | 871e6c3 | 2009-07-21 12:18:03 +0000 | [diff] [blame] | 2076 | |
| 2077 | dump_op_count(); |
bellard | a23a9ec | 2008-05-23 09:52:20 +0000 | [diff] [blame] | 2078 | } |
| 2079 | #else |
bellard | 24bf7b3 | 2008-05-23 11:58:32 +0000 | [diff] [blame] | 2080 | void tcg_dump_info(FILE *f, |
bellard | a23a9ec | 2008-05-23 09:52:20 +0000 | [diff] [blame] | 2081 | int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) |
| 2082 | { |
bellard | 24bf7b3 | 2008-05-23 11:58:32 +0000 | [diff] [blame] | 2083 | cpu_fprintf(f, "[TCG profiler not compiled]\n"); |
bellard | a23a9ec | 2008-05-23 09:52:20 +0000 | [diff] [blame] | 2084 | } |
| 2085 | #endif |