Implement fstatat64() syscall (by Kirill Shutemov).
[qemu/mini2440.git] / tcg / tcg.c
blob1b7bf5ca3b8c68879e586a1fe1ae98ef5d6baa01
1 /*
2 * Tiny Code Generator for QEMU
4 * Copyright (c) 2008 Fabrice Bellard
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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.
25 /* define it to suppress various consistency checks (faster) */
26 #define NDEBUG
28 /* define it to use liveness analysis (better code) */
29 #define USE_LIVENESS_ANALYSIS
31 #include <assert.h>
32 #include <stdarg.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <inttypes.h>
37 #ifdef _WIN32
38 #include <malloc.h>
39 #endif
41 #include "config.h"
42 #include "qemu-common.h"
44 /* Note: the long term plan is to reduce the dependancies on the QEMU
45 CPU definitions. Currently they are used for qemu_ld/st
46 instructions */
47 #define NO_CPU_IO_DEFS
48 #include "cpu.h"
49 #include "exec-all.h"
51 #include "tcg-op.h"
52 #include "elf.h"
55 static void patch_reloc(uint8_t *code_ptr, int type,
56 tcg_target_long value, tcg_target_long addend);
58 TCGOpDef tcg_op_defs[] = {
59 #define DEF(s, n, copy_size) { #s, 0, 0, n, n, 0, copy_size },
60 #define DEF2(s, iargs, oargs, cargs, flags) { #s, iargs, oargs, cargs, iargs + oargs + cargs, flags, 0 },
61 #include "tcg-opc.h"
62 #undef DEF
63 #undef DEF2
66 TCGRegSet tcg_target_available_regs[2];
67 TCGRegSet tcg_target_call_clobber_regs;
69 /* XXX: move that inside the context */
70 uint16_t *gen_opc_ptr;
71 TCGArg *gen_opparam_ptr;
73 static inline void tcg_out8(TCGContext *s, uint8_t v)
75 *s->code_ptr++ = v;
78 static inline void tcg_out16(TCGContext *s, uint16_t v)
80 *(uint16_t *)s->code_ptr = v;
81 s->code_ptr += 2;
84 static inline void tcg_out32(TCGContext *s, uint32_t v)
86 *(uint32_t *)s->code_ptr = v;
87 s->code_ptr += 4;
90 /* label relocation processing */
92 void tcg_out_reloc(TCGContext *s, uint8_t *code_ptr, int type,
93 int label_index, long addend)
95 TCGLabel *l;
96 TCGRelocation *r;
98 l = &s->labels[label_index];
99 if (l->has_value) {
100 /* FIXME: This may break relocations on RISC targets that
101 modify instruction fields in place. The caller may not have
102 written the initial value. */
103 patch_reloc(code_ptr, type, l->u.value, addend);
104 } else {
105 /* add a new relocation entry */
106 r = tcg_malloc(sizeof(TCGRelocation));
107 r->type = type;
108 r->ptr = code_ptr;
109 r->addend = addend;
110 r->next = l->u.first_reloc;
111 l->u.first_reloc = r;
115 static void tcg_out_label(TCGContext *s, int label_index,
116 tcg_target_long value)
118 TCGLabel *l;
119 TCGRelocation *r;
121 l = &s->labels[label_index];
122 if (l->has_value)
123 tcg_abort();
124 r = l->u.first_reloc;
125 while (r != NULL) {
126 patch_reloc(r->ptr, r->type, value, r->addend);
127 r = r->next;
129 l->has_value = 1;
130 l->u.value = value;
133 int gen_new_label(void)
135 TCGContext *s = &tcg_ctx;
136 int idx;
137 TCGLabel *l;
139 if (s->nb_labels >= TCG_MAX_LABELS)
140 tcg_abort();
141 idx = s->nb_labels++;
142 l = &s->labels[idx];
143 l->has_value = 0;
144 l->u.first_reloc = NULL;
145 return idx;
148 #include "tcg-target.c"
150 /* pool based memory allocation */
151 void *tcg_malloc_internal(TCGContext *s, int size)
153 TCGPool *p;
154 int pool_size;
156 if (size > TCG_POOL_CHUNK_SIZE) {
157 /* big malloc: insert a new pool (XXX: could optimize) */
158 p = qemu_malloc(sizeof(TCGPool) + size);
159 p->size = size;
160 if (s->pool_current)
161 s->pool_current->next = p;
162 else
163 s->pool_first = p;
164 p->next = s->pool_current;
165 } else {
166 p = s->pool_current;
167 if (!p) {
168 p = s->pool_first;
169 if (!p)
170 goto new_pool;
171 } else {
172 if (!p->next) {
173 new_pool:
174 pool_size = TCG_POOL_CHUNK_SIZE;
175 p = qemu_malloc(sizeof(TCGPool) + pool_size);
176 p->size = pool_size;
177 p->next = NULL;
178 if (s->pool_current)
179 s->pool_current->next = p;
180 else
181 s->pool_first = p;
182 } else {
183 p = p->next;
187 s->pool_current = p;
188 s->pool_cur = p->data + size;
189 s->pool_end = p->data + p->size;
190 return p->data;
193 void tcg_pool_reset(TCGContext *s)
195 s->pool_cur = s->pool_end = NULL;
196 s->pool_current = NULL;
199 void tcg_context_init(TCGContext *s)
201 int op, total_args, n;
202 TCGOpDef *def;
203 TCGArgConstraint *args_ct;
204 int *sorted_args;
206 memset(s, 0, sizeof(*s));
207 s->temps = s->static_temps;
208 s->nb_globals = 0;
210 /* Count total number of arguments and allocate the corresponding
211 space */
212 total_args = 0;
213 for(op = 0; op < NB_OPS; op++) {
214 def = &tcg_op_defs[op];
215 n = def->nb_iargs + def->nb_oargs;
216 total_args += n;
219 args_ct = qemu_malloc(sizeof(TCGArgConstraint) * total_args);
220 sorted_args = qemu_malloc(sizeof(int) * total_args);
222 for(op = 0; op < NB_OPS; op++) {
223 def = &tcg_op_defs[op];
224 def->args_ct = args_ct;
225 def->sorted_args = sorted_args;
226 n = def->nb_iargs + def->nb_oargs;
227 sorted_args += n;
228 args_ct += n;
231 tcg_target_init(s);
233 /* init global prologue and epilogue */
234 s->code_buf = code_gen_prologue;
235 s->code_ptr = s->code_buf;
236 tcg_target_qemu_prologue(s);
237 flush_icache_range((unsigned long)s->code_buf,
238 (unsigned long)s->code_ptr);
241 void tcg_set_frame(TCGContext *s, int reg,
242 tcg_target_long start, tcg_target_long size)
244 s->frame_start = start;
245 s->frame_end = start + size;
246 s->frame_reg = reg;
249 void tcg_func_start(TCGContext *s)
251 int i;
252 tcg_pool_reset(s);
253 s->nb_temps = s->nb_globals;
254 for(i = 0; i < (TCG_TYPE_COUNT * 2); i++)
255 s->first_free_temp[i] = -1;
256 s->labels = tcg_malloc(sizeof(TCGLabel) * TCG_MAX_LABELS);
257 s->nb_labels = 0;
258 s->current_frame_offset = s->frame_start;
260 gen_opc_ptr = gen_opc_buf;
261 gen_opparam_ptr = gen_opparam_buf;
264 static inline void tcg_temp_alloc(TCGContext *s, int n)
266 if (n > TCG_MAX_TEMPS)
267 tcg_abort();
270 TCGv tcg_global_reg_new(TCGType type, int reg, const char *name)
272 TCGContext *s = &tcg_ctx;
273 TCGTemp *ts;
274 int idx;
276 #if TCG_TARGET_REG_BITS == 32
277 if (type != TCG_TYPE_I32)
278 tcg_abort();
279 #endif
280 if (tcg_regset_test_reg(s->reserved_regs, reg))
281 tcg_abort();
282 idx = s->nb_globals;
283 tcg_temp_alloc(s, s->nb_globals + 1);
284 ts = &s->temps[s->nb_globals];
285 ts->base_type = type;
286 ts->type = type;
287 ts->fixed_reg = 1;
288 ts->reg = reg;
289 ts->name = name;
290 s->nb_globals++;
291 tcg_regset_set_reg(s->reserved_regs, reg);
292 return MAKE_TCGV(idx);
295 #if TCG_TARGET_REG_BITS == 32
296 /* temporary hack to avoid register shortage for tcg_qemu_st64() */
297 TCGv tcg_global_reg2_new_hack(TCGType type, int reg1, int reg2,
298 const char *name)
300 TCGContext *s = &tcg_ctx;
301 TCGTemp *ts;
302 int idx;
303 char buf[64];
305 if (type != TCG_TYPE_I64)
306 tcg_abort();
307 idx = s->nb_globals;
308 tcg_temp_alloc(s, s->nb_globals + 2);
309 ts = &s->temps[s->nb_globals];
310 ts->base_type = type;
311 ts->type = TCG_TYPE_I32;
312 ts->fixed_reg = 1;
313 ts->reg = reg1;
314 pstrcpy(buf, sizeof(buf), name);
315 pstrcat(buf, sizeof(buf), "_0");
316 ts->name = strdup(buf);
318 ts++;
319 ts->base_type = type;
320 ts->type = TCG_TYPE_I32;
321 ts->fixed_reg = 1;
322 ts->reg = reg2;
323 pstrcpy(buf, sizeof(buf), name);
324 pstrcat(buf, sizeof(buf), "_1");
325 ts->name = strdup(buf);
327 s->nb_globals += 2;
328 return MAKE_TCGV(idx);
330 #endif
332 TCGv tcg_global_mem_new(TCGType type, int reg, tcg_target_long offset,
333 const char *name)
335 TCGContext *s = &tcg_ctx;
336 TCGTemp *ts;
337 int idx;
339 idx = s->nb_globals;
340 #if TCG_TARGET_REG_BITS == 32
341 if (type == TCG_TYPE_I64) {
342 char buf[64];
343 tcg_temp_alloc(s, s->nb_globals + 2);
344 ts = &s->temps[s->nb_globals];
345 ts->base_type = type;
346 ts->type = TCG_TYPE_I32;
347 ts->fixed_reg = 0;
348 ts->mem_allocated = 1;
349 ts->mem_reg = reg;
350 #ifdef TCG_TARGET_WORDS_BIGENDIAN
351 ts->mem_offset = offset + 4;
352 #else
353 ts->mem_offset = offset;
354 #endif
355 pstrcpy(buf, sizeof(buf), name);
356 pstrcat(buf, sizeof(buf), "_0");
357 ts->name = strdup(buf);
358 ts++;
360 ts->base_type = type;
361 ts->type = TCG_TYPE_I32;
362 ts->fixed_reg = 0;
363 ts->mem_allocated = 1;
364 ts->mem_reg = reg;
365 #ifdef TCG_TARGET_WORDS_BIGENDIAN
366 ts->mem_offset = offset;
367 #else
368 ts->mem_offset = offset + 4;
369 #endif
370 pstrcpy(buf, sizeof(buf), name);
371 pstrcat(buf, sizeof(buf), "_1");
372 ts->name = strdup(buf);
374 s->nb_globals += 2;
375 } else
376 #endif
378 tcg_temp_alloc(s, s->nb_globals + 1);
379 ts = &s->temps[s->nb_globals];
380 ts->base_type = type;
381 ts->type = type;
382 ts->fixed_reg = 0;
383 ts->mem_allocated = 1;
384 ts->mem_reg = reg;
385 ts->mem_offset = offset;
386 ts->name = name;
387 s->nb_globals++;
389 return MAKE_TCGV(idx);
392 TCGv tcg_temp_new_internal(TCGType type, int temp_local)
394 TCGContext *s = &tcg_ctx;
395 TCGTemp *ts;
396 int idx, k;
398 k = type;
399 if (temp_local)
400 k += TCG_TYPE_COUNT;
401 idx = s->first_free_temp[k];
402 if (idx != -1) {
403 /* There is already an available temp with the
404 right type */
405 ts = &s->temps[idx];
406 s->first_free_temp[k] = ts->next_free_temp;
407 ts->temp_allocated = 1;
408 assert(ts->temp_local == temp_local);
409 } else {
410 idx = s->nb_temps;
411 #if TCG_TARGET_REG_BITS == 32
412 if (type == TCG_TYPE_I64) {
413 tcg_temp_alloc(s, s->nb_temps + 2);
414 ts = &s->temps[s->nb_temps];
415 ts->base_type = type;
416 ts->type = TCG_TYPE_I32;
417 ts->temp_allocated = 1;
418 ts->temp_local = temp_local;
419 ts->name = NULL;
420 ts++;
421 ts->base_type = TCG_TYPE_I32;
422 ts->type = TCG_TYPE_I32;
423 ts->temp_allocated = 1;
424 ts->temp_local = temp_local;
425 ts->name = NULL;
426 s->nb_temps += 2;
427 } else
428 #endif
430 tcg_temp_alloc(s, s->nb_temps + 1);
431 ts = &s->temps[s->nb_temps];
432 ts->base_type = type;
433 ts->type = type;
434 ts->temp_allocated = 1;
435 ts->temp_local = temp_local;
436 ts->name = NULL;
437 s->nb_temps++;
440 return MAKE_TCGV(idx);
443 void tcg_temp_free(TCGv arg)
445 TCGContext *s = &tcg_ctx;
446 TCGTemp *ts;
447 int idx = GET_TCGV(arg);
448 int k;
450 assert(idx >= s->nb_globals && idx < s->nb_temps);
451 ts = &s->temps[idx];
452 assert(ts->temp_allocated != 0);
453 ts->temp_allocated = 0;
454 k = ts->base_type;
455 if (ts->temp_local)
456 k += TCG_TYPE_COUNT;
457 ts->next_free_temp = s->first_free_temp[k];
458 s->first_free_temp[k] = idx;
462 TCGv tcg_const_i32(int32_t val)
464 TCGv t0;
465 t0 = tcg_temp_new(TCG_TYPE_I32);
466 tcg_gen_movi_i32(t0, val);
467 return t0;
470 TCGv tcg_const_i64(int64_t val)
472 TCGv t0;
473 t0 = tcg_temp_new(TCG_TYPE_I64);
474 tcg_gen_movi_i64(t0, val);
475 return t0;
478 void tcg_register_helper(void *func, const char *name)
480 TCGContext *s = &tcg_ctx;
481 int n;
482 if ((s->nb_helpers + 1) > s->allocated_helpers) {
483 n = s->allocated_helpers;
484 if (n == 0) {
485 n = 4;
486 } else {
487 n *= 2;
489 s->helpers = realloc(s->helpers, n * sizeof(TCGHelperInfo));
490 s->allocated_helpers = n;
492 s->helpers[s->nb_helpers].func = (tcg_target_ulong)func;
493 s->helpers[s->nb_helpers].name = name;
494 s->nb_helpers++;
497 static inline TCGType tcg_get_base_type(TCGContext *s, TCGv arg)
499 return s->temps[GET_TCGV(arg)].base_type;
502 static void tcg_gen_call_internal(TCGContext *s, TCGv func,
503 unsigned int flags,
504 unsigned int nb_rets, const TCGv *rets,
505 unsigned int nb_params, const TCGv *params)
507 int i;
508 *gen_opc_ptr++ = INDEX_op_call;
509 *gen_opparam_ptr++ = (nb_rets << 16) | (nb_params + 1);
510 for(i = 0; i < nb_rets; i++) {
511 *gen_opparam_ptr++ = GET_TCGV(rets[i]);
513 for(i = 0; i < nb_params; i++) {
514 *gen_opparam_ptr++ = GET_TCGV(params[i]);
516 *gen_opparam_ptr++ = GET_TCGV(func);
518 *gen_opparam_ptr++ = flags;
519 /* total parameters, needed to go backward in the instruction stream */
520 *gen_opparam_ptr++ = 1 + nb_rets + nb_params + 3;
524 #if TCG_TARGET_REG_BITS < 64
525 /* Note: we convert the 64 bit args to 32 bit and do some alignment
526 and endian swap. Maybe it would be better to do the alignment
527 and endian swap in tcg_reg_alloc_call(). */
528 void tcg_gen_call(TCGContext *s, TCGv func, unsigned int flags,
529 unsigned int nb_rets, const TCGv *rets,
530 unsigned int nb_params, const TCGv *args1)
532 TCGv ret, *args2, rets_2[2], arg;
533 int j, i, call_type;
535 if (nb_rets == 1) {
536 ret = rets[0];
537 if (tcg_get_base_type(s, ret) == TCG_TYPE_I64) {
538 nb_rets = 2;
539 #ifdef TCG_TARGET_WORDS_BIGENDIAN
540 rets_2[0] = TCGV_HIGH(ret);
541 rets_2[1] = ret;
542 #else
543 rets_2[0] = ret;
544 rets_2[1] = TCGV_HIGH(ret);
545 #endif
546 rets = rets_2;
549 args2 = alloca((nb_params * 3) * sizeof(TCGv));
550 j = 0;
551 call_type = (flags & TCG_CALL_TYPE_MASK);
552 for(i = 0; i < nb_params; i++) {
553 arg = args1[i];
554 if (tcg_get_base_type(s, arg) == TCG_TYPE_I64) {
555 #ifdef TCG_TARGET_I386
556 /* REGPARM case: if the third parameter is 64 bit, it is
557 allocated on the stack */
558 if (j == 2 && call_type == TCG_CALL_TYPE_REGPARM) {
559 call_type = TCG_CALL_TYPE_REGPARM_2;
560 flags = (flags & ~TCG_CALL_TYPE_MASK) | call_type;
562 args2[j++] = arg;
563 args2[j++] = TCGV_HIGH(arg);
564 #else
565 #ifdef TCG_TARGET_CALL_ALIGN_ARGS
566 /* some targets want aligned 64 bit args */
567 if (j & 1) {
568 args2[j++] = TCG_CALL_DUMMY_ARG;
570 #endif
571 #ifdef TCG_TARGET_WORDS_BIGENDIAN
572 args2[j++] = TCGV_HIGH(arg);
573 args2[j++] = arg;
574 #else
575 args2[j++] = arg;
576 args2[j++] = TCGV_HIGH(arg);
577 #endif
578 #endif
579 } else {
580 args2[j++] = arg;
583 tcg_gen_call_internal(s, func, flags,
584 nb_rets, rets, j, args2);
586 #else
587 void tcg_gen_call(TCGContext *s, TCGv func, unsigned int flags,
588 unsigned int nb_rets, const TCGv *rets,
589 unsigned int nb_params, const TCGv *args1)
591 tcg_gen_call_internal(s, func, flags,
592 nb_rets, rets, nb_params, args1);
594 #endif
596 #if TCG_TARGET_REG_BITS == 32
597 void tcg_gen_shifti_i64(TCGv ret, TCGv arg1,
598 int c, int right, int arith)
600 if (c == 0) {
601 tcg_gen_mov_i32(ret, arg1);
602 tcg_gen_mov_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1));
603 } else if (c >= 32) {
604 c -= 32;
605 if (right) {
606 if (arith) {
607 tcg_gen_sari_i32(ret, TCGV_HIGH(arg1), c);
608 tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), 31);
609 } else {
610 tcg_gen_shri_i32(ret, TCGV_HIGH(arg1), c);
611 tcg_gen_movi_i32(TCGV_HIGH(ret), 0);
613 } else {
614 tcg_gen_shli_i32(TCGV_HIGH(ret), arg1, c);
615 tcg_gen_movi_i32(ret, 0);
617 } else {
618 TCGv t0, t1;
620 t0 = tcg_temp_new(TCG_TYPE_I32);
621 t1 = tcg_temp_new(TCG_TYPE_I32);
622 if (right) {
623 tcg_gen_shli_i32(t0, TCGV_HIGH(arg1), 32 - c);
624 if (arith)
625 tcg_gen_sari_i32(t1, TCGV_HIGH(arg1), c);
626 else
627 tcg_gen_shri_i32(t1, TCGV_HIGH(arg1), c);
628 tcg_gen_shri_i32(ret, arg1, c);
629 tcg_gen_or_i32(ret, ret, t0);
630 tcg_gen_mov_i32(TCGV_HIGH(ret), t1);
631 } else {
632 tcg_gen_shri_i32(t0, arg1, 32 - c);
633 /* Note: ret can be the same as arg1, so we use t1 */
634 tcg_gen_shli_i32(t1, arg1, c);
635 tcg_gen_shli_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), c);
636 tcg_gen_or_i32(TCGV_HIGH(ret), TCGV_HIGH(ret), t0);
637 tcg_gen_mov_i32(ret, t1);
639 tcg_temp_free(t0);
640 tcg_temp_free(t1);
643 #endif
645 static void tcg_reg_alloc_start(TCGContext *s)
647 int i;
648 TCGTemp *ts;
649 for(i = 0; i < s->nb_globals; i++) {
650 ts = &s->temps[i];
651 if (ts->fixed_reg) {
652 ts->val_type = TEMP_VAL_REG;
653 } else {
654 ts->val_type = TEMP_VAL_MEM;
657 for(i = s->nb_globals; i < s->nb_temps; i++) {
658 ts = &s->temps[i];
659 ts->val_type = TEMP_VAL_DEAD;
660 ts->mem_allocated = 0;
661 ts->fixed_reg = 0;
663 for(i = 0; i < TCG_TARGET_NB_REGS; i++) {
664 s->reg_to_temp[i] = -1;
668 static char *tcg_get_arg_str_idx(TCGContext *s, char *buf, int buf_size,
669 int idx)
671 TCGTemp *ts;
673 ts = &s->temps[idx];
674 if (idx < s->nb_globals) {
675 pstrcpy(buf, buf_size, ts->name);
676 } else {
677 if (ts->temp_local)
678 snprintf(buf, buf_size, "loc%d", idx - s->nb_globals);
679 else
680 snprintf(buf, buf_size, "tmp%d", idx - s->nb_globals);
682 return buf;
685 char *tcg_get_arg_str(TCGContext *s, char *buf, int buf_size, TCGv arg)
687 return tcg_get_arg_str_idx(s, buf, buf_size, GET_TCGV(arg));
690 static int helper_cmp(const void *p1, const void *p2)
692 const TCGHelperInfo *th1 = p1;
693 const TCGHelperInfo *th2 = p2;
694 if (th1->func < th2->func)
695 return -1;
696 else if (th1->func == th2->func)
697 return 0;
698 else
699 return 1;
702 /* find helper definition (Note: A hash table would be better) */
703 static TCGHelperInfo *tcg_find_helper(TCGContext *s, tcg_target_ulong val)
705 int m, m_min, m_max;
706 TCGHelperInfo *th;
707 tcg_target_ulong v;
709 if (unlikely(!s->helpers_sorted)) {
710 qsort(s->helpers, s->nb_helpers, sizeof(TCGHelperInfo),
711 helper_cmp);
712 s->helpers_sorted = 1;
715 /* binary search */
716 m_min = 0;
717 m_max = s->nb_helpers - 1;
718 while (m_min <= m_max) {
719 m = (m_min + m_max) >> 1;
720 th = &s->helpers[m];
721 v = th->func;
722 if (v == val)
723 return th;
724 else if (val < v) {
725 m_max = m - 1;
726 } else {
727 m_min = m + 1;
730 return NULL;
733 static const char * const cond_name[] =
735 [TCG_COND_EQ] = "eq",
736 [TCG_COND_NE] = "ne",
737 [TCG_COND_LT] = "lt",
738 [TCG_COND_GE] = "ge",
739 [TCG_COND_LE] = "le",
740 [TCG_COND_GT] = "gt",
741 [TCG_COND_LTU] = "ltu",
742 [TCG_COND_GEU] = "geu",
743 [TCG_COND_LEU] = "leu",
744 [TCG_COND_GTU] = "gtu"
747 void tcg_dump_ops(TCGContext *s, FILE *outfile)
749 const uint16_t *opc_ptr;
750 const TCGArg *args;
751 TCGArg arg;
752 int c, i, k, nb_oargs, nb_iargs, nb_cargs, first_insn;
753 const TCGOpDef *def;
754 char buf[128];
756 first_insn = 1;
757 opc_ptr = gen_opc_buf;
758 args = gen_opparam_buf;
759 while (opc_ptr < gen_opc_ptr) {
760 c = *opc_ptr++;
761 def = &tcg_op_defs[c];
762 if (c == INDEX_op_debug_insn_start) {
763 uint64_t pc;
764 #if TARGET_LONG_BITS > TCG_TARGET_REG_BITS
765 pc = ((uint64_t)args[1] << 32) | args[0];
766 #else
767 pc = args[0];
768 #endif
769 if (!first_insn)
770 fprintf(outfile, "\n");
771 fprintf(outfile, " ---- 0x%" PRIx64, pc);
772 first_insn = 0;
773 nb_oargs = def->nb_oargs;
774 nb_iargs = def->nb_iargs;
775 nb_cargs = def->nb_cargs;
776 } else if (c == INDEX_op_call) {
777 TCGArg arg;
779 /* variable number of arguments */
780 arg = *args++;
781 nb_oargs = arg >> 16;
782 nb_iargs = arg & 0xffff;
783 nb_cargs = def->nb_cargs;
785 fprintf(outfile, " %s ", def->name);
787 /* function name */
788 fprintf(outfile, "%s",
789 tcg_get_arg_str_idx(s, buf, sizeof(buf), args[nb_oargs + nb_iargs - 1]));
790 /* flags */
791 fprintf(outfile, ",$0x%" TCG_PRIlx,
792 args[nb_oargs + nb_iargs]);
793 /* nb out args */
794 fprintf(outfile, ",$%d", nb_oargs);
795 for(i = 0; i < nb_oargs; i++) {
796 fprintf(outfile, ",");
797 fprintf(outfile, "%s",
798 tcg_get_arg_str_idx(s, buf, sizeof(buf), args[i]));
800 for(i = 0; i < (nb_iargs - 1); i++) {
801 fprintf(outfile, ",");
802 if (args[nb_oargs + i] == TCG_CALL_DUMMY_ARG) {
803 fprintf(outfile, "<dummy>");
804 } else {
805 fprintf(outfile, "%s",
806 tcg_get_arg_str_idx(s, buf, sizeof(buf), args[nb_oargs + i]));
809 } else if (c == INDEX_op_movi_i32
810 #if TCG_TARGET_REG_BITS == 64
811 || c == INDEX_op_movi_i64
812 #endif
814 tcg_target_ulong val;
815 TCGHelperInfo *th;
817 nb_oargs = def->nb_oargs;
818 nb_iargs = def->nb_iargs;
819 nb_cargs = def->nb_cargs;
820 fprintf(outfile, " %s %s,$", def->name,
821 tcg_get_arg_str_idx(s, buf, sizeof(buf), args[0]));
822 val = args[1];
823 th = tcg_find_helper(s, val);
824 if (th) {
825 fprintf(outfile, th->name);
826 } else {
827 if (c == INDEX_op_movi_i32)
828 fprintf(outfile, "0x%x", (uint32_t)val);
829 else
830 fprintf(outfile, "0x%" PRIx64 , (uint64_t)val);
832 } else {
833 fprintf(outfile, " %s ", def->name);
834 if (c == INDEX_op_nopn) {
835 /* variable number of arguments */
836 nb_cargs = *args;
837 nb_oargs = 0;
838 nb_iargs = 0;
839 } else {
840 nb_oargs = def->nb_oargs;
841 nb_iargs = def->nb_iargs;
842 nb_cargs = def->nb_cargs;
845 k = 0;
846 for(i = 0; i < nb_oargs; i++) {
847 if (k != 0)
848 fprintf(outfile, ",");
849 fprintf(outfile, "%s",
850 tcg_get_arg_str_idx(s, buf, sizeof(buf), args[k++]));
852 for(i = 0; i < nb_iargs; i++) {
853 if (k != 0)
854 fprintf(outfile, ",");
855 fprintf(outfile, "%s",
856 tcg_get_arg_str_idx(s, buf, sizeof(buf), args[k++]));
858 if (c == INDEX_op_brcond_i32
859 #if TCG_TARGET_REG_BITS == 32
860 || c == INDEX_op_brcond2_i32
861 #elif TCG_TARGET_REG_BITS == 64
862 || c == INDEX_op_brcond_i64
863 #endif
865 if (args[k] < ARRAY_SIZE(cond_name) && cond_name[args[k]])
866 fprintf(outfile, ",%s", cond_name[args[k++]]);
867 else
868 fprintf(outfile, ",$0x%" TCG_PRIlx, args[k++]);
869 i = 1;
871 else
872 i = 0;
873 for(; i < nb_cargs; i++) {
874 if (k != 0)
875 fprintf(outfile, ",");
876 arg = args[k++];
877 fprintf(outfile, "$0x%" TCG_PRIlx, arg);
880 fprintf(outfile, "\n");
881 args += nb_iargs + nb_oargs + nb_cargs;
885 /* we give more priority to constraints with less registers */
886 static int get_constraint_priority(const TCGOpDef *def, int k)
888 const TCGArgConstraint *arg_ct;
890 int i, n;
891 arg_ct = &def->args_ct[k];
892 if (arg_ct->ct & TCG_CT_ALIAS) {
893 /* an alias is equivalent to a single register */
894 n = 1;
895 } else {
896 if (!(arg_ct->ct & TCG_CT_REG))
897 return 0;
898 n = 0;
899 for(i = 0; i < TCG_TARGET_NB_REGS; i++) {
900 if (tcg_regset_test_reg(arg_ct->u.regs, i))
901 n++;
904 return TCG_TARGET_NB_REGS - n + 1;
907 /* sort from highest priority to lowest */
908 static void sort_constraints(TCGOpDef *def, int start, int n)
910 int i, j, p1, p2, tmp;
912 for(i = 0; i < n; i++)
913 def->sorted_args[start + i] = start + i;
914 if (n <= 1)
915 return;
916 for(i = 0; i < n - 1; i++) {
917 for(j = i + 1; j < n; j++) {
918 p1 = get_constraint_priority(def, def->sorted_args[start + i]);
919 p2 = get_constraint_priority(def, def->sorted_args[start + j]);
920 if (p1 < p2) {
921 tmp = def->sorted_args[start + i];
922 def->sorted_args[start + i] = def->sorted_args[start + j];
923 def->sorted_args[start + j] = tmp;
929 void tcg_add_target_add_op_defs(const TCGTargetOpDef *tdefs)
931 int op;
932 TCGOpDef *def;
933 const char *ct_str;
934 int i, nb_args;
936 for(;;) {
937 if (tdefs->op < 0)
938 break;
939 op = tdefs->op;
940 assert(op >= 0 && op < NB_OPS);
941 def = &tcg_op_defs[op];
942 nb_args = def->nb_iargs + def->nb_oargs;
943 for(i = 0; i < nb_args; i++) {
944 ct_str = tdefs->args_ct_str[i];
945 tcg_regset_clear(def->args_ct[i].u.regs);
946 def->args_ct[i].ct = 0;
947 if (ct_str[0] >= '0' && ct_str[0] <= '9') {
948 int oarg;
949 oarg = ct_str[0] - '0';
950 assert(oarg < def->nb_oargs);
951 assert(def->args_ct[oarg].ct & TCG_CT_REG);
952 /* TCG_CT_ALIAS is for the output arguments. The input
953 argument is tagged with TCG_CT_IALIAS. */
954 def->args_ct[i] = def->args_ct[oarg];
955 def->args_ct[oarg].ct = TCG_CT_ALIAS;
956 def->args_ct[oarg].alias_index = i;
957 def->args_ct[i].ct |= TCG_CT_IALIAS;
958 def->args_ct[i].alias_index = oarg;
959 } else {
960 for(;;) {
961 if (*ct_str == '\0')
962 break;
963 switch(*ct_str) {
964 case 'i':
965 def->args_ct[i].ct |= TCG_CT_CONST;
966 ct_str++;
967 break;
968 default:
969 if (target_parse_constraint(&def->args_ct[i], &ct_str) < 0) {
970 fprintf(stderr, "Invalid constraint '%s' for arg %d of operation '%s'\n",
971 ct_str, i, def->name);
972 exit(1);
979 /* sort the constraints (XXX: this is just an heuristic) */
980 sort_constraints(def, 0, def->nb_oargs);
981 sort_constraints(def, def->nb_oargs, def->nb_iargs);
983 #if 0
985 int i;
987 printf("%s: sorted=", def->name);
988 for(i = 0; i < def->nb_oargs + def->nb_iargs; i++)
989 printf(" %d", def->sorted_args[i]);
990 printf("\n");
992 #endif
993 tdefs++;
998 #ifdef USE_LIVENESS_ANALYSIS
1000 /* set a nop for an operation using 'nb_args' */
1001 static inline void tcg_set_nop(TCGContext *s, uint16_t *opc_ptr,
1002 TCGArg *args, int nb_args)
1004 if (nb_args == 0) {
1005 *opc_ptr = INDEX_op_nop;
1006 } else {
1007 *opc_ptr = INDEX_op_nopn;
1008 args[0] = nb_args;
1009 args[nb_args - 1] = nb_args;
1013 /* liveness analysis: end of function: globals are live, temps are
1014 dead. */
1015 /* XXX: at this stage, not used as there would be little gains because
1016 most TBs end with a conditional jump. */
1017 static inline void tcg_la_func_end(TCGContext *s, uint8_t *dead_temps)
1019 memset(dead_temps, 0, s->nb_globals);
1020 memset(dead_temps + s->nb_globals, 1, s->nb_temps - s->nb_globals);
1023 /* liveness analysis: end of basic block: globals are live, temps are
1024 dead, local temps are live. */
1025 static inline void tcg_la_bb_end(TCGContext *s, uint8_t *dead_temps)
1027 int i;
1028 TCGTemp *ts;
1030 memset(dead_temps, 0, s->nb_globals);
1031 ts = &s->temps[s->nb_globals];
1032 for(i = s->nb_globals; i < s->nb_temps; i++) {
1033 if (ts->temp_local)
1034 dead_temps[i] = 0;
1035 else
1036 dead_temps[i] = 1;
1037 ts++;
1041 /* Liveness analysis : update the opc_dead_iargs array to tell if a
1042 given input arguments is dead. Instructions updating dead
1043 temporaries are removed. */
1044 static void tcg_liveness_analysis(TCGContext *s)
1046 int i, op_index, op, nb_args, nb_iargs, nb_oargs, arg, nb_ops;
1047 TCGArg *args;
1048 const TCGOpDef *def;
1049 uint8_t *dead_temps;
1050 unsigned int dead_iargs;
1052 gen_opc_ptr++; /* skip end */
1054 nb_ops = gen_opc_ptr - gen_opc_buf;
1056 /* XXX: make it really dynamic */
1057 s->op_dead_iargs = tcg_malloc(OPC_BUF_SIZE * sizeof(uint16_t));
1059 dead_temps = tcg_malloc(s->nb_temps);
1060 memset(dead_temps, 1, s->nb_temps);
1062 args = gen_opparam_ptr;
1063 op_index = nb_ops - 1;
1064 while (op_index >= 0) {
1065 op = gen_opc_buf[op_index];
1066 def = &tcg_op_defs[op];
1067 switch(op) {
1068 case INDEX_op_call:
1070 int call_flags;
1072 nb_args = args[-1];
1073 args -= nb_args;
1074 nb_iargs = args[0] & 0xffff;
1075 nb_oargs = args[0] >> 16;
1076 args++;
1077 call_flags = args[nb_oargs + nb_iargs];
1079 /* pure functions can be removed if their result is not
1080 used */
1081 if (call_flags & TCG_CALL_PURE) {
1082 for(i = 0; i < nb_oargs; i++) {
1083 arg = args[i];
1084 if (!dead_temps[arg])
1085 goto do_not_remove_call;
1087 tcg_set_nop(s, gen_opc_buf + op_index,
1088 args - 1, nb_args);
1089 } else {
1090 do_not_remove_call:
1092 /* output args are dead */
1093 for(i = 0; i < nb_oargs; i++) {
1094 arg = args[i];
1095 dead_temps[arg] = 1;
1098 /* globals are live (they may be used by the call) */
1099 memset(dead_temps, 0, s->nb_globals);
1101 /* input args are live */
1102 dead_iargs = 0;
1103 for(i = 0; i < nb_iargs; i++) {
1104 arg = args[i + nb_oargs];
1105 if (arg != TCG_CALL_DUMMY_ARG) {
1106 if (dead_temps[arg]) {
1107 dead_iargs |= (1 << i);
1109 dead_temps[arg] = 0;
1112 s->op_dead_iargs[op_index] = dead_iargs;
1114 args--;
1116 break;
1117 case INDEX_op_set_label:
1118 args--;
1119 /* mark end of basic block */
1120 tcg_la_bb_end(s, dead_temps);
1121 break;
1122 case INDEX_op_debug_insn_start:
1123 args -= def->nb_args;
1124 break;
1125 case INDEX_op_nopn:
1126 nb_args = args[-1];
1127 args -= nb_args;
1128 break;
1129 case INDEX_op_discard:
1130 args--;
1131 /* mark the temporary as dead */
1132 dead_temps[args[0]] = 1;
1133 break;
1134 case INDEX_op_end:
1135 break;
1136 /* XXX: optimize by hardcoding common cases (e.g. triadic ops) */
1137 default:
1138 if (op > INDEX_op_end) {
1139 args -= def->nb_args;
1140 nb_iargs = def->nb_iargs;
1141 nb_oargs = def->nb_oargs;
1143 /* Test if the operation can be removed because all
1144 its outputs are dead. We assume that nb_oargs == 0
1145 implies side effects */
1146 if (!(def->flags & TCG_OPF_SIDE_EFFECTS) && nb_oargs != 0) {
1147 for(i = 0; i < nb_oargs; i++) {
1148 arg = args[i];
1149 if (!dead_temps[arg])
1150 goto do_not_remove;
1152 tcg_set_nop(s, gen_opc_buf + op_index, args, def->nb_args);
1153 #ifdef CONFIG_PROFILER
1154 s->del_op_count++;
1155 #endif
1156 } else {
1157 do_not_remove:
1159 /* output args are dead */
1160 for(i = 0; i < nb_oargs; i++) {
1161 arg = args[i];
1162 dead_temps[arg] = 1;
1165 /* if end of basic block, update */
1166 if (def->flags & TCG_OPF_BB_END) {
1167 tcg_la_bb_end(s, dead_temps);
1168 } else if (def->flags & TCG_OPF_CALL_CLOBBER) {
1169 /* globals are live */
1170 memset(dead_temps, 0, s->nb_globals);
1173 /* input args are live */
1174 dead_iargs = 0;
1175 for(i = 0; i < nb_iargs; i++) {
1176 arg = args[i + nb_oargs];
1177 if (dead_temps[arg]) {
1178 dead_iargs |= (1 << i);
1180 dead_temps[arg] = 0;
1182 s->op_dead_iargs[op_index] = dead_iargs;
1184 } else {
1185 /* legacy dyngen operations */
1186 args -= def->nb_args;
1187 /* mark end of basic block */
1188 tcg_la_bb_end(s, dead_temps);
1190 break;
1192 op_index--;
1195 if (args != gen_opparam_buf)
1196 tcg_abort();
1198 #else
1199 /* dummy liveness analysis */
1200 void tcg_liveness_analysis(TCGContext *s)
1202 int nb_ops;
1203 nb_ops = gen_opc_ptr - gen_opc_buf;
1205 s->op_dead_iargs = tcg_malloc(nb_ops * sizeof(uint16_t));
1206 memset(s->op_dead_iargs, 0, nb_ops * sizeof(uint16_t));
1208 #endif
1210 #ifndef NDEBUG
1211 static void dump_regs(TCGContext *s)
1213 TCGTemp *ts;
1214 int i;
1215 char buf[64];
1217 for(i = 0; i < s->nb_temps; i++) {
1218 ts = &s->temps[i];
1219 printf(" %10s: ", tcg_get_arg_str_idx(s, buf, sizeof(buf), i));
1220 switch(ts->val_type) {
1221 case TEMP_VAL_REG:
1222 printf("%s", tcg_target_reg_names[ts->reg]);
1223 break;
1224 case TEMP_VAL_MEM:
1225 printf("%d(%s)", (int)ts->mem_offset, tcg_target_reg_names[ts->mem_reg]);
1226 break;
1227 case TEMP_VAL_CONST:
1228 printf("$0x%" TCG_PRIlx, ts->val);
1229 break;
1230 case TEMP_VAL_DEAD:
1231 printf("D");
1232 break;
1233 default:
1234 printf("???");
1235 break;
1237 printf("\n");
1240 for(i = 0; i < TCG_TARGET_NB_REGS; i++) {
1241 if (s->reg_to_temp[i] >= 0) {
1242 printf("%s: %s\n",
1243 tcg_target_reg_names[i],
1244 tcg_get_arg_str_idx(s, buf, sizeof(buf), s->reg_to_temp[i]));
1249 static void check_regs(TCGContext *s)
1251 int reg, k;
1252 TCGTemp *ts;
1253 char buf[64];
1255 for(reg = 0; reg < TCG_TARGET_NB_REGS; reg++) {
1256 k = s->reg_to_temp[reg];
1257 if (k >= 0) {
1258 ts = &s->temps[k];
1259 if (ts->val_type != TEMP_VAL_REG ||
1260 ts->reg != reg) {
1261 printf("Inconsistency for register %s:\n",
1262 tcg_target_reg_names[reg]);
1263 goto fail;
1267 for(k = 0; k < s->nb_temps; k++) {
1268 ts = &s->temps[k];
1269 if (ts->val_type == TEMP_VAL_REG &&
1270 !ts->fixed_reg &&
1271 s->reg_to_temp[ts->reg] != k) {
1272 printf("Inconsistency for temp %s:\n",
1273 tcg_get_arg_str_idx(s, buf, sizeof(buf), k));
1274 fail:
1275 printf("reg state:\n");
1276 dump_regs(s);
1277 tcg_abort();
1281 #endif
1283 static void temp_allocate_frame(TCGContext *s, int temp)
1285 TCGTemp *ts;
1286 ts = &s->temps[temp];
1287 s->current_frame_offset = (s->current_frame_offset + sizeof(tcg_target_long) - 1) & ~(sizeof(tcg_target_long) - 1);
1288 if (s->current_frame_offset + sizeof(tcg_target_long) > s->frame_end)
1289 tcg_abort();
1290 ts->mem_offset = s->current_frame_offset;
1291 ts->mem_reg = s->frame_reg;
1292 ts->mem_allocated = 1;
1293 s->current_frame_offset += sizeof(tcg_target_long);
1296 /* free register 'reg' by spilling the corresponding temporary if necessary */
1297 static void tcg_reg_free(TCGContext *s, int reg)
1299 TCGTemp *ts;
1300 int temp;
1302 temp = s->reg_to_temp[reg];
1303 if (temp != -1) {
1304 ts = &s->temps[temp];
1305 assert(ts->val_type == TEMP_VAL_REG);
1306 if (!ts->mem_coherent) {
1307 if (!ts->mem_allocated)
1308 temp_allocate_frame(s, temp);
1309 tcg_out_st(s, ts->type, reg, ts->mem_reg, ts->mem_offset);
1311 ts->val_type = TEMP_VAL_MEM;
1312 s->reg_to_temp[reg] = -1;
1316 /* Allocate a register belonging to reg1 & ~reg2 */
1317 static int tcg_reg_alloc(TCGContext *s, TCGRegSet reg1, TCGRegSet reg2)
1319 int i, reg;
1320 TCGRegSet reg_ct;
1322 tcg_regset_andnot(reg_ct, reg1, reg2);
1324 /* first try free registers */
1325 for(i = 0; i < ARRAY_SIZE(tcg_target_reg_alloc_order); i++) {
1326 reg = tcg_target_reg_alloc_order[i];
1327 if (tcg_regset_test_reg(reg_ct, reg) && s->reg_to_temp[reg] == -1)
1328 return reg;
1331 /* XXX: do better spill choice */
1332 for(i = 0; i < ARRAY_SIZE(tcg_target_reg_alloc_order); i++) {
1333 reg = tcg_target_reg_alloc_order[i];
1334 if (tcg_regset_test_reg(reg_ct, reg)) {
1335 tcg_reg_free(s, reg);
1336 return reg;
1340 tcg_abort();
1343 /* save a temporary to memory. 'allocated_regs' is used in case a
1344 temporary registers needs to be allocated to store a constant. */
1345 static void temp_save(TCGContext *s, int temp, TCGRegSet allocated_regs)
1347 TCGTemp *ts;
1348 int reg;
1350 ts = &s->temps[temp];
1351 if (!ts->fixed_reg) {
1352 switch(ts->val_type) {
1353 case TEMP_VAL_REG:
1354 tcg_reg_free(s, ts->reg);
1355 break;
1356 case TEMP_VAL_DEAD:
1357 ts->val_type = TEMP_VAL_MEM;
1358 break;
1359 case TEMP_VAL_CONST:
1360 reg = tcg_reg_alloc(s, tcg_target_available_regs[ts->type],
1361 allocated_regs);
1362 if (!ts->mem_allocated)
1363 temp_allocate_frame(s, temp);
1364 tcg_out_movi(s, ts->type, reg, ts->val);
1365 tcg_out_st(s, ts->type, reg, ts->mem_reg, ts->mem_offset);
1366 ts->val_type = TEMP_VAL_MEM;
1367 break;
1368 case TEMP_VAL_MEM:
1369 break;
1370 default:
1371 tcg_abort();
1376 /* save globals to their cannonical location and assume they can be
1377 modified be the following code. 'allocated_regs' is used in case a
1378 temporary registers needs to be allocated to store a constant. */
1379 static void save_globals(TCGContext *s, TCGRegSet allocated_regs)
1381 int i;
1383 for(i = 0; i < s->nb_globals; i++) {
1384 temp_save(s, i, allocated_regs);
1388 /* at the end of a basic block, we assume all temporaries are dead and
1389 all globals are stored at their canonical location. */
1390 static void tcg_reg_alloc_bb_end(TCGContext *s, TCGRegSet allocated_regs)
1392 TCGTemp *ts;
1393 int i;
1395 for(i = s->nb_globals; i < s->nb_temps; i++) {
1396 ts = &s->temps[i];
1397 if (ts->temp_local) {
1398 temp_save(s, i, allocated_regs);
1399 } else {
1400 if (ts->val_type == TEMP_VAL_REG) {
1401 s->reg_to_temp[ts->reg] = -1;
1403 ts->val_type = TEMP_VAL_DEAD;
1407 save_globals(s, allocated_regs);
1410 #define IS_DEAD_IARG(n) ((dead_iargs >> (n)) & 1)
1412 static void tcg_reg_alloc_movi(TCGContext *s, const TCGArg *args)
1414 TCGTemp *ots;
1415 tcg_target_ulong val;
1417 ots = &s->temps[args[0]];
1418 val = args[1];
1420 if (ots->fixed_reg) {
1421 /* for fixed registers, we do not do any constant
1422 propagation */
1423 tcg_out_movi(s, ots->type, ots->reg, val);
1424 } else {
1425 /* The movi is not explicitly generated here */
1426 if (ots->val_type == TEMP_VAL_REG)
1427 s->reg_to_temp[ots->reg] = -1;
1428 ots->val_type = TEMP_VAL_CONST;
1429 ots->val = val;
1433 static void tcg_reg_alloc_mov(TCGContext *s, const TCGOpDef *def,
1434 const TCGArg *args,
1435 unsigned int dead_iargs)
1437 TCGTemp *ts, *ots;
1438 int reg;
1439 const TCGArgConstraint *arg_ct;
1441 ots = &s->temps[args[0]];
1442 ts = &s->temps[args[1]];
1443 arg_ct = &def->args_ct[0];
1445 /* XXX: always mark arg dead if IS_DEAD_IARG(0) */
1446 if (ts->val_type == TEMP_VAL_REG) {
1447 if (IS_DEAD_IARG(0) && !ts->fixed_reg && !ots->fixed_reg) {
1448 /* the mov can be suppressed */
1449 if (ots->val_type == TEMP_VAL_REG)
1450 s->reg_to_temp[ots->reg] = -1;
1451 reg = ts->reg;
1452 s->reg_to_temp[reg] = -1;
1453 ts->val_type = TEMP_VAL_DEAD;
1454 } else {
1455 if (ots->val_type == TEMP_VAL_REG) {
1456 reg = ots->reg;
1457 } else {
1458 reg = tcg_reg_alloc(s, arg_ct->u.regs, s->reserved_regs);
1460 if (ts->reg != reg) {
1461 tcg_out_mov(s, reg, ts->reg);
1464 } else if (ts->val_type == TEMP_VAL_MEM) {
1465 if (ots->val_type == TEMP_VAL_REG) {
1466 reg = ots->reg;
1467 } else {
1468 reg = tcg_reg_alloc(s, arg_ct->u.regs, s->reserved_regs);
1470 tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset);
1471 } else if (ts->val_type == TEMP_VAL_CONST) {
1472 if (ots->fixed_reg) {
1473 reg = ots->reg;
1474 tcg_out_movi(s, ots->type, reg, ts->val);
1475 } else {
1476 /* propagate constant */
1477 if (ots->val_type == TEMP_VAL_REG)
1478 s->reg_to_temp[ots->reg] = -1;
1479 ots->val_type = TEMP_VAL_CONST;
1480 ots->val = ts->val;
1481 return;
1483 } else {
1484 tcg_abort();
1486 s->reg_to_temp[reg] = args[0];
1487 ots->reg = reg;
1488 ots->val_type = TEMP_VAL_REG;
1489 ots->mem_coherent = 0;
1492 static void tcg_reg_alloc_op(TCGContext *s,
1493 const TCGOpDef *def, int opc,
1494 const TCGArg *args,
1495 unsigned int dead_iargs)
1497 TCGRegSet allocated_regs;
1498 int i, k, nb_iargs, nb_oargs, reg;
1499 TCGArg arg;
1500 const TCGArgConstraint *arg_ct;
1501 TCGTemp *ts;
1502 TCGArg new_args[TCG_MAX_OP_ARGS];
1503 int const_args[TCG_MAX_OP_ARGS];
1505 nb_oargs = def->nb_oargs;
1506 nb_iargs = def->nb_iargs;
1508 /* copy constants */
1509 memcpy(new_args + nb_oargs + nb_iargs,
1510 args + nb_oargs + nb_iargs,
1511 sizeof(TCGArg) * def->nb_cargs);
1513 /* satisfy input constraints */
1514 tcg_regset_set(allocated_regs, s->reserved_regs);
1515 for(k = 0; k < nb_iargs; k++) {
1516 i = def->sorted_args[nb_oargs + k];
1517 arg = args[i];
1518 arg_ct = &def->args_ct[i];
1519 ts = &s->temps[arg];
1520 if (ts->val_type == TEMP_VAL_MEM) {
1521 reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs);
1522 tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset);
1523 ts->val_type = TEMP_VAL_REG;
1524 ts->reg = reg;
1525 ts->mem_coherent = 1;
1526 s->reg_to_temp[reg] = arg;
1527 } else if (ts->val_type == TEMP_VAL_CONST) {
1528 if (tcg_target_const_match(ts->val, arg_ct)) {
1529 /* constant is OK for instruction */
1530 const_args[i] = 1;
1531 new_args[i] = ts->val;
1532 goto iarg_end;
1533 } else {
1534 /* need to move to a register */
1535 reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs);
1536 tcg_out_movi(s, ts->type, reg, ts->val);
1537 ts->val_type = TEMP_VAL_REG;
1538 ts->reg = reg;
1539 ts->mem_coherent = 0;
1540 s->reg_to_temp[reg] = arg;
1543 assert(ts->val_type == TEMP_VAL_REG);
1544 if (arg_ct->ct & TCG_CT_IALIAS) {
1545 if (ts->fixed_reg) {
1546 /* if fixed register, we must allocate a new register
1547 if the alias is not the same register */
1548 if (arg != args[arg_ct->alias_index])
1549 goto allocate_in_reg;
1550 } else {
1551 /* if the input is aliased to an output and if it is
1552 not dead after the instruction, we must allocate
1553 a new register and move it */
1554 if (!IS_DEAD_IARG(i - nb_oargs))
1555 goto allocate_in_reg;
1558 reg = ts->reg;
1559 if (tcg_regset_test_reg(arg_ct->u.regs, reg)) {
1560 /* nothing to do : the constraint is satisfied */
1561 } else {
1562 allocate_in_reg:
1563 /* allocate a new register matching the constraint
1564 and move the temporary register into it */
1565 reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs);
1566 tcg_out_mov(s, reg, ts->reg);
1568 new_args[i] = reg;
1569 const_args[i] = 0;
1570 tcg_regset_set_reg(allocated_regs, reg);
1571 iarg_end: ;
1574 if (def->flags & TCG_OPF_BB_END) {
1575 tcg_reg_alloc_bb_end(s, allocated_regs);
1576 } else {
1577 /* mark dead temporaries and free the associated registers */
1578 for(i = 0; i < nb_iargs; i++) {
1579 arg = args[nb_oargs + i];
1580 if (IS_DEAD_IARG(i)) {
1581 ts = &s->temps[arg];
1582 if (!ts->fixed_reg) {
1583 if (ts->val_type == TEMP_VAL_REG)
1584 s->reg_to_temp[ts->reg] = -1;
1585 ts->val_type = TEMP_VAL_DEAD;
1590 if (def->flags & TCG_OPF_CALL_CLOBBER) {
1591 /* XXX: permit generic clobber register list ? */
1592 for(reg = 0; reg < TCG_TARGET_NB_REGS; reg++) {
1593 if (tcg_regset_test_reg(tcg_target_call_clobber_regs, reg)) {
1594 tcg_reg_free(s, reg);
1597 /* XXX: for load/store we could do that only for the slow path
1598 (i.e. when a memory callback is called) */
1600 /* store globals and free associated registers (we assume the insn
1601 can modify any global. */
1602 save_globals(s, allocated_regs);
1605 /* satisfy the output constraints */
1606 tcg_regset_set(allocated_regs, s->reserved_regs);
1607 for(k = 0; k < nb_oargs; k++) {
1608 i = def->sorted_args[k];
1609 arg = args[i];
1610 arg_ct = &def->args_ct[i];
1611 ts = &s->temps[arg];
1612 if (arg_ct->ct & TCG_CT_ALIAS) {
1613 reg = new_args[arg_ct->alias_index];
1614 } else {
1615 /* if fixed register, we try to use it */
1616 reg = ts->reg;
1617 if (ts->fixed_reg &&
1618 tcg_regset_test_reg(arg_ct->u.regs, reg)) {
1619 goto oarg_end;
1621 reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs);
1623 tcg_regset_set_reg(allocated_regs, reg);
1624 /* if a fixed register is used, then a move will be done afterwards */
1625 if (!ts->fixed_reg) {
1626 if (ts->val_type == TEMP_VAL_REG)
1627 s->reg_to_temp[ts->reg] = -1;
1628 ts->val_type = TEMP_VAL_REG;
1629 ts->reg = reg;
1630 /* temp value is modified, so the value kept in memory is
1631 potentially not the same */
1632 ts->mem_coherent = 0;
1633 s->reg_to_temp[reg] = arg;
1635 oarg_end:
1636 new_args[i] = reg;
1640 /* emit instruction */
1641 tcg_out_op(s, opc, new_args, const_args);
1643 /* move the outputs in the correct register if needed */
1644 for(i = 0; i < nb_oargs; i++) {
1645 ts = &s->temps[args[i]];
1646 reg = new_args[i];
1647 if (ts->fixed_reg && ts->reg != reg) {
1648 tcg_out_mov(s, ts->reg, reg);
1653 #ifdef TCG_TARGET_STACK_GROWSUP
1654 #define STACK_DIR(x) (-(x))
1655 #else
1656 #define STACK_DIR(x) (x)
1657 #endif
1659 static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def,
1660 int opc, const TCGArg *args,
1661 unsigned int dead_iargs)
1663 int nb_iargs, nb_oargs, flags, nb_regs, i, reg, nb_params;
1664 TCGArg arg, func_arg;
1665 TCGTemp *ts;
1666 tcg_target_long stack_offset, call_stack_size, func_addr;
1667 int const_func_arg, allocate_args;
1668 TCGRegSet allocated_regs;
1669 const TCGArgConstraint *arg_ct;
1671 arg = *args++;
1673 nb_oargs = arg >> 16;
1674 nb_iargs = arg & 0xffff;
1675 nb_params = nb_iargs - 1;
1677 flags = args[nb_oargs + nb_iargs];
1679 nb_regs = tcg_target_get_call_iarg_regs_count(flags);
1680 if (nb_regs > nb_params)
1681 nb_regs = nb_params;
1683 /* assign stack slots first */
1684 /* XXX: preallocate call stack */
1685 call_stack_size = (nb_params - nb_regs) * sizeof(tcg_target_long);
1686 call_stack_size = (call_stack_size + TCG_TARGET_STACK_ALIGN - 1) &
1687 ~(TCG_TARGET_STACK_ALIGN - 1);
1688 allocate_args = (call_stack_size > TCG_STATIC_CALL_ARGS_SIZE);
1689 if (allocate_args) {
1690 tcg_out_addi(s, TCG_REG_CALL_STACK, -STACK_DIR(call_stack_size));
1693 stack_offset = TCG_TARGET_CALL_STACK_OFFSET;
1694 for(i = nb_regs; i < nb_params; i++) {
1695 arg = args[nb_oargs + i];
1696 #ifdef TCG_TARGET_STACK_GROWSUP
1697 stack_offset -= sizeof(tcg_target_long);
1698 #endif
1699 if (arg != TCG_CALL_DUMMY_ARG) {
1700 ts = &s->temps[arg];
1701 if (ts->val_type == TEMP_VAL_REG) {
1702 tcg_out_st(s, ts->type, ts->reg, TCG_REG_CALL_STACK, stack_offset);
1703 } else if (ts->val_type == TEMP_VAL_MEM) {
1704 reg = tcg_reg_alloc(s, tcg_target_available_regs[ts->type],
1705 s->reserved_regs);
1706 /* XXX: not correct if reading values from the stack */
1707 tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset);
1708 tcg_out_st(s, ts->type, reg, TCG_REG_CALL_STACK, stack_offset);
1709 } else if (ts->val_type == TEMP_VAL_CONST) {
1710 reg = tcg_reg_alloc(s, tcg_target_available_regs[ts->type],
1711 s->reserved_regs);
1712 /* XXX: sign extend may be needed on some targets */
1713 tcg_out_movi(s, ts->type, reg, ts->val);
1714 tcg_out_st(s, ts->type, reg, TCG_REG_CALL_STACK, stack_offset);
1715 } else {
1716 tcg_abort();
1719 #ifndef TCG_TARGET_STACK_GROWSUP
1720 stack_offset += sizeof(tcg_target_long);
1721 #endif
1724 /* assign input registers */
1725 tcg_regset_set(allocated_regs, s->reserved_regs);
1726 for(i = 0; i < nb_regs; i++) {
1727 arg = args[nb_oargs + i];
1728 if (arg != TCG_CALL_DUMMY_ARG) {
1729 ts = &s->temps[arg];
1730 reg = tcg_target_call_iarg_regs[i];
1731 tcg_reg_free(s, reg);
1732 if (ts->val_type == TEMP_VAL_REG) {
1733 if (ts->reg != reg) {
1734 tcg_out_mov(s, reg, ts->reg);
1736 } else if (ts->val_type == TEMP_VAL_MEM) {
1737 tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset);
1738 } else if (ts->val_type == TEMP_VAL_CONST) {
1739 /* XXX: sign extend ? */
1740 tcg_out_movi(s, ts->type, reg, ts->val);
1741 } else {
1742 tcg_abort();
1744 tcg_regset_set_reg(allocated_regs, reg);
1748 /* assign function address */
1749 func_arg = args[nb_oargs + nb_iargs - 1];
1750 arg_ct = &def->args_ct[0];
1751 ts = &s->temps[func_arg];
1752 func_addr = ts->val;
1753 const_func_arg = 0;
1754 if (ts->val_type == TEMP_VAL_MEM) {
1755 reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs);
1756 tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset);
1757 func_arg = reg;
1758 tcg_regset_set_reg(allocated_regs, reg);
1759 } else if (ts->val_type == TEMP_VAL_REG) {
1760 reg = ts->reg;
1761 if (!tcg_regset_test_reg(arg_ct->u.regs, reg)) {
1762 reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs);
1763 tcg_out_mov(s, reg, ts->reg);
1765 func_arg = reg;
1766 tcg_regset_set_reg(allocated_regs, reg);
1767 } else if (ts->val_type == TEMP_VAL_CONST) {
1768 if (tcg_target_const_match(func_addr, arg_ct)) {
1769 const_func_arg = 1;
1770 func_arg = func_addr;
1771 } else {
1772 reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs);
1773 tcg_out_movi(s, ts->type, reg, func_addr);
1774 func_arg = reg;
1775 tcg_regset_set_reg(allocated_regs, reg);
1777 } else {
1778 tcg_abort();
1782 /* mark dead temporaries and free the associated registers */
1783 for(i = 0; i < nb_iargs; i++) {
1784 arg = args[nb_oargs + i];
1785 if (IS_DEAD_IARG(i)) {
1786 ts = &s->temps[arg];
1787 if (!ts->fixed_reg) {
1788 if (ts->val_type == TEMP_VAL_REG)
1789 s->reg_to_temp[ts->reg] = -1;
1790 ts->val_type = TEMP_VAL_DEAD;
1795 /* clobber call registers */
1796 for(reg = 0; reg < TCG_TARGET_NB_REGS; reg++) {
1797 if (tcg_regset_test_reg(tcg_target_call_clobber_regs, reg)) {
1798 tcg_reg_free(s, reg);
1802 /* store globals and free associated registers (we assume the call
1803 can modify any global. */
1804 save_globals(s, allocated_regs);
1806 tcg_out_op(s, opc, &func_arg, &const_func_arg);
1808 if (allocate_args) {
1809 tcg_out_addi(s, TCG_REG_CALL_STACK, STACK_DIR(call_stack_size));
1812 /* assign output registers and emit moves if needed */
1813 for(i = 0; i < nb_oargs; i++) {
1814 arg = args[i];
1815 ts = &s->temps[arg];
1816 reg = tcg_target_call_oarg_regs[i];
1817 assert(s->reg_to_temp[reg] == -1);
1818 if (ts->fixed_reg) {
1819 if (ts->reg != reg) {
1820 tcg_out_mov(s, ts->reg, reg);
1822 } else {
1823 if (ts->val_type == TEMP_VAL_REG)
1824 s->reg_to_temp[ts->reg] = -1;
1825 ts->val_type = TEMP_VAL_REG;
1826 ts->reg = reg;
1827 ts->mem_coherent = 0;
1828 s->reg_to_temp[reg] = arg;
1832 return nb_iargs + nb_oargs + def->nb_cargs + 1;
1835 #ifdef CONFIG_PROFILER
1837 static int64_t dyngen_table_op_count[NB_OPS];
1839 void dump_op_count(void)
1841 int i;
1842 FILE *f;
1843 f = fopen("/tmp/op1.log", "w");
1844 for(i = 0; i < INDEX_op_end; i++) {
1845 fprintf(f, "%s %" PRId64 "\n", tcg_op_defs[i].name, dyngen_table_op_count[i]);
1847 fclose(f);
1848 f = fopen("/tmp/op2.log", "w");
1849 for(i = INDEX_op_end; i < NB_OPS; i++) {
1850 fprintf(f, "%s %" PRId64 "\n", tcg_op_defs[i].name, dyngen_table_op_count[i]);
1852 fclose(f);
1854 #endif
1857 static inline int tcg_gen_code_common(TCGContext *s, uint8_t *gen_code_buf,
1858 long search_pc)
1860 int opc, op_index;
1861 const TCGOpDef *def;
1862 unsigned int dead_iargs;
1863 const TCGArg *args;
1865 #ifdef DEBUG_DISAS
1866 if (unlikely(loglevel & CPU_LOG_TB_OP)) {
1867 fprintf(logfile, "OP:\n");
1868 tcg_dump_ops(s, logfile);
1869 fprintf(logfile, "\n");
1871 #endif
1873 #ifdef CONFIG_PROFILER
1874 s->la_time -= profile_getclock();
1875 #endif
1876 tcg_liveness_analysis(s);
1877 #ifdef CONFIG_PROFILER
1878 s->la_time += profile_getclock();
1879 #endif
1881 #ifdef DEBUG_DISAS
1882 if (unlikely(loglevel & CPU_LOG_TB_OP_OPT)) {
1883 fprintf(logfile, "OP after la:\n");
1884 tcg_dump_ops(s, logfile);
1885 fprintf(logfile, "\n");
1887 #endif
1889 tcg_reg_alloc_start(s);
1891 s->code_buf = gen_code_buf;
1892 s->code_ptr = gen_code_buf;
1894 args = gen_opparam_buf;
1895 op_index = 0;
1897 for(;;) {
1898 opc = gen_opc_buf[op_index];
1899 #ifdef CONFIG_PROFILER
1900 dyngen_table_op_count[opc]++;
1901 #endif
1902 def = &tcg_op_defs[opc];
1903 #if 0
1904 printf("%s: %d %d %d\n", def->name,
1905 def->nb_oargs, def->nb_iargs, def->nb_cargs);
1906 // dump_regs(s);
1907 #endif
1908 switch(opc) {
1909 case INDEX_op_mov_i32:
1910 #if TCG_TARGET_REG_BITS == 64
1911 case INDEX_op_mov_i64:
1912 #endif
1913 dead_iargs = s->op_dead_iargs[op_index];
1914 tcg_reg_alloc_mov(s, def, args, dead_iargs);
1915 break;
1916 case INDEX_op_movi_i32:
1917 #if TCG_TARGET_REG_BITS == 64
1918 case INDEX_op_movi_i64:
1919 #endif
1920 tcg_reg_alloc_movi(s, args);
1921 break;
1922 case INDEX_op_debug_insn_start:
1923 /* debug instruction */
1924 break;
1925 case INDEX_op_nop:
1926 case INDEX_op_nop1:
1927 case INDEX_op_nop2:
1928 case INDEX_op_nop3:
1929 break;
1930 case INDEX_op_nopn:
1931 args += args[0];
1932 goto next;
1933 case INDEX_op_discard:
1935 TCGTemp *ts;
1936 ts = &s->temps[args[0]];
1937 /* mark the temporary as dead */
1938 if (!ts->fixed_reg) {
1939 if (ts->val_type == TEMP_VAL_REG)
1940 s->reg_to_temp[ts->reg] = -1;
1941 ts->val_type = TEMP_VAL_DEAD;
1944 break;
1945 case INDEX_op_set_label:
1946 tcg_reg_alloc_bb_end(s, s->reserved_regs);
1947 tcg_out_label(s, args[0], (long)s->code_ptr);
1948 break;
1949 case INDEX_op_call:
1950 dead_iargs = s->op_dead_iargs[op_index];
1951 args += tcg_reg_alloc_call(s, def, opc, args, dead_iargs);
1952 goto next;
1953 case INDEX_op_end:
1954 goto the_end;
1956 #ifdef CONFIG_DYNGEN_OP
1957 case 0 ... INDEX_op_end - 1:
1958 /* legacy dyngen ops */
1959 #ifdef CONFIG_PROFILER
1960 s->old_op_count++;
1961 #endif
1962 tcg_reg_alloc_bb_end(s, s->reserved_regs);
1963 if (search_pc >= 0) {
1964 s->code_ptr += def->copy_size;
1965 args += def->nb_args;
1966 } else {
1967 args = dyngen_op(s, opc, args);
1969 goto next;
1970 #endif
1971 default:
1972 /* Note: in order to speed up the code, it would be much
1973 faster to have specialized register allocator functions for
1974 some common argument patterns */
1975 dead_iargs = s->op_dead_iargs[op_index];
1976 tcg_reg_alloc_op(s, def, opc, args, dead_iargs);
1977 break;
1979 args += def->nb_args;
1980 next:
1981 if (search_pc >= 0 && search_pc < s->code_ptr - gen_code_buf) {
1982 return op_index;
1984 op_index++;
1985 #ifndef NDEBUG
1986 check_regs(s);
1987 #endif
1989 the_end:
1990 return -1;
1993 int dyngen_code(TCGContext *s, uint8_t *gen_code_buf)
1995 #ifdef CONFIG_PROFILER
1997 int n;
1998 n = (gen_opc_ptr - gen_opc_buf);
1999 s->op_count += n;
2000 if (n > s->op_count_max)
2001 s->op_count_max = n;
2003 s->temp_count += s->nb_temps;
2004 if (s->nb_temps > s->temp_count_max)
2005 s->temp_count_max = s->nb_temps;
2007 #endif
2009 tcg_gen_code_common(s, gen_code_buf, -1);
2011 /* flush instruction cache */
2012 flush_icache_range((unsigned long)gen_code_buf,
2013 (unsigned long)s->code_ptr);
2014 return s->code_ptr - gen_code_buf;
2017 /* Return the index of the micro operation such as the pc after is <
2018 offset bytes from the start of the TB. The contents of gen_code_buf must
2019 not be changed, though writing the same values is ok.
2020 Return -1 if not found. */
2021 int dyngen_code_search_pc(TCGContext *s, uint8_t *gen_code_buf, long offset)
2023 return tcg_gen_code_common(s, gen_code_buf, offset);
2026 #ifdef CONFIG_PROFILER
2027 void tcg_dump_info(FILE *f,
2028 int (*cpu_fprintf)(FILE *f, const char *fmt, ...))
2030 TCGContext *s = &tcg_ctx;
2031 int64_t tot;
2033 tot = s->interm_time + s->code_time;
2034 cpu_fprintf(f, "JIT cycles %" PRId64 " (%0.3f s at 2.4 GHz)\n",
2035 tot, tot / 2.4e9);
2036 cpu_fprintf(f, "translated TBs %" PRId64 " (aborted=%" PRId64 " %0.1f%%)\n",
2037 s->tb_count,
2038 s->tb_count1 - s->tb_count,
2039 s->tb_count1 ? (double)(s->tb_count1 - s->tb_count) / s->tb_count1 * 100.0 : 0);
2040 cpu_fprintf(f, "avg ops/TB %0.1f max=%d\n",
2041 s->tb_count ? (double)s->op_count / s->tb_count : 0, s->op_count_max);
2042 cpu_fprintf(f, "old ops/total ops %0.1f%%\n",
2043 s->op_count ? (double)s->old_op_count / s->op_count * 100.0 : 0);
2044 cpu_fprintf(f, "deleted ops/TB %0.2f\n",
2045 s->tb_count ?
2046 (double)s->del_op_count / s->tb_count : 0);
2047 cpu_fprintf(f, "avg temps/TB %0.2f max=%d\n",
2048 s->tb_count ?
2049 (double)s->temp_count / s->tb_count : 0,
2050 s->temp_count_max);
2052 cpu_fprintf(f, "cycles/op %0.1f\n",
2053 s->op_count ? (double)tot / s->op_count : 0);
2054 cpu_fprintf(f, "cycles/in byte %0.1f\n",
2055 s->code_in_len ? (double)tot / s->code_in_len : 0);
2056 cpu_fprintf(f, "cycles/out byte %0.1f\n",
2057 s->code_out_len ? (double)tot / s->code_out_len : 0);
2058 if (tot == 0)
2059 tot = 1;
2060 cpu_fprintf(f, " gen_interm time %0.1f%%\n",
2061 (double)s->interm_time / tot * 100.0);
2062 cpu_fprintf(f, " gen_code time %0.1f%%\n",
2063 (double)s->code_time / tot * 100.0);
2064 cpu_fprintf(f, "liveness/code time %0.1f%%\n",
2065 (double)s->la_time / (s->code_time ? s->code_time : 1) * 100.0);
2066 cpu_fprintf(f, "cpu_restore count %" PRId64 "\n",
2067 s->restore_count);
2068 cpu_fprintf(f, " avg cycles %0.1f\n",
2069 s->restore_count ? (double)s->restore_time / s->restore_count : 0);
2071 extern void dump_op_count(void);
2072 dump_op_count();
2075 #else
2076 void tcg_dump_info(FILE *f,
2077 int (*cpu_fprintf)(FILE *f, const char *fmt, ...))
2079 cpu_fprintf(f, "[TCG profiler not compiled]\n");
2081 #endif