1bb2e0039SLluís Vilanova /* 2bb2e0039SLluís Vilanova * Generic intermediate code generation. 3bb2e0039SLluís Vilanova * 4bb2e0039SLluís Vilanova * Copyright (C) 2016-2017 Lluís Vilanova <vilanova@ac.upc.edu> 5bb2e0039SLluís Vilanova * 6bb2e0039SLluís Vilanova * This work is licensed under the terms of the GNU GPL, version 2 or later. 7bb2e0039SLluís Vilanova * See the COPYING file in the top-level directory. 8bb2e0039SLluís Vilanova */ 9bb2e0039SLluís Vilanova 10bb2e0039SLluís Vilanova #include "qemu/osdep.h" 11bb2e0039SLluís Vilanova #include "qemu/error-report.h" 12bb2e0039SLluís Vilanova #include "tcg/tcg.h" 13bb2e0039SLluís Vilanova #include "tcg/tcg-op.h" 14bb2e0039SLluís Vilanova #include "exec/exec-all.h" 15bb2e0039SLluís Vilanova #include "exec/gen-icount.h" 16bb2e0039SLluís Vilanova #include "exec/log.h" 17bb2e0039SLluís Vilanova #include "exec/translator.h" 186ba6f818SEmilio G. Cota #include "exec/plugin-gen.h" 19fda8458bSPavel Dovgalyuk #include "sysemu/replay.h" 20bb2e0039SLluís Vilanova 21bb2e0039SLluís Vilanova /* Pairs with tcg_clear_temp_count. 22bb2e0039SLluís Vilanova To be called by #TranslatorOps.{translate_insn,tb_stop} if 23bb2e0039SLluís Vilanova (1) the target is sufficiently clean to support reporting, 24bb2e0039SLluís Vilanova (2) as and when all temporaries are known to be consumed. 25bb2e0039SLluís Vilanova For most targets, (2) is at the end of translate_insn. */ 26bb2e0039SLluís Vilanova void translator_loop_temp_check(DisasContextBase *db) 27bb2e0039SLluís Vilanova { 28bb2e0039SLluís Vilanova if (tcg_check_temp_count()) { 29bb2e0039SLluís Vilanova qemu_log("warning: TCG temporary leaks before " 30bb2e0039SLluís Vilanova TARGET_FMT_lx "\n", db->pc_next); 31bb2e0039SLluís Vilanova } 32bb2e0039SLluís Vilanova } 33bb2e0039SLluís Vilanova 34d3a2a1d8SRichard Henderson bool translator_use_goto_tb(DisasContextBase *db, target_ulong dest) 35d3a2a1d8SRichard Henderson { 36*84f15616SRichard Henderson /* Suppress goto_tb if requested. */ 37*84f15616SRichard Henderson if (tb_cflags(db->tb) & CF_NO_GOTO_TB) { 38*84f15616SRichard Henderson return false; 39*84f15616SRichard Henderson } 40*84f15616SRichard Henderson 41d3a2a1d8SRichard Henderson /* Suppress goto_tb in the case of single-steping. */ 42d3a2a1d8SRichard Henderson if (db->singlestep_enabled || singlestep) { 43d3a2a1d8SRichard Henderson return false; 44d3a2a1d8SRichard Henderson } 45d3a2a1d8SRichard Henderson 46d3a2a1d8SRichard Henderson /* Check for the dest on the same page as the start of the TB. */ 47d3a2a1d8SRichard Henderson return ((db->pc_first ^ dest) & TARGET_PAGE_MASK) == 0; 48d3a2a1d8SRichard Henderson } 49d3a2a1d8SRichard Henderson 50bb2e0039SLluís Vilanova void translator_loop(const TranslatorOps *ops, DisasContextBase *db, 518b86d6d2SRichard Henderson CPUState *cpu, TranslationBlock *tb, int max_insns) 52bb2e0039SLluís Vilanova { 53f9f1f56eSPavel Dovgalyuk int bp_insn = 0; 546ba6f818SEmilio G. Cota bool plugin_enabled; 55f9f1f56eSPavel Dovgalyuk 56bb2e0039SLluís Vilanova /* Initialize DisasContext */ 57bb2e0039SLluís Vilanova db->tb = tb; 58bb2e0039SLluís Vilanova db->pc_first = tb->pc; 59bb2e0039SLluís Vilanova db->pc_next = db->pc_first; 60bb2e0039SLluís Vilanova db->is_jmp = DISAS_NEXT; 61bb2e0039SLluís Vilanova db->num_insns = 0; 628b86d6d2SRichard Henderson db->max_insns = max_insns; 63bb2e0039SLluís Vilanova db->singlestep_enabled = cpu->singlestep_enabled; 64bb2e0039SLluís Vilanova 65b542683dSEmilio G. Cota ops->init_disas_context(db, cpu); 66bb2e0039SLluís Vilanova tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ 67bb2e0039SLluís Vilanova 68bb2e0039SLluís Vilanova /* Reset the temp count so that we can identify leaks */ 69bb2e0039SLluís Vilanova tcg_clear_temp_count(); 70bb2e0039SLluís Vilanova 71bb2e0039SLluís Vilanova /* Start translating. */ 72bb2e0039SLluís Vilanova gen_tb_start(db->tb); 73bb2e0039SLluís Vilanova ops->tb_start(db, cpu); 74bb2e0039SLluís Vilanova tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ 75bb2e0039SLluís Vilanova 76cfd405eaSAlex Bennée plugin_enabled = plugin_gen_tb_start(cpu, tb, 77cfd405eaSAlex Bennée tb_cflags(db->tb) & CF_MEMI_ONLY); 786ba6f818SEmilio G. Cota 79bb2e0039SLluís Vilanova while (true) { 80bb2e0039SLluís Vilanova db->num_insns++; 81bb2e0039SLluís Vilanova ops->insn_start(db, cpu); 82bb2e0039SLluís Vilanova tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ 83bb2e0039SLluís Vilanova 846ba6f818SEmilio G. Cota if (plugin_enabled) { 856ba6f818SEmilio G. Cota plugin_gen_insn_start(cpu, db); 866ba6f818SEmilio G. Cota } 876ba6f818SEmilio G. Cota 88bb2e0039SLluís Vilanova /* Pass breakpoint hits to target for further processing */ 89f9f1f56eSPavel Dovgalyuk if (!db->singlestep_enabled 90f9f1f56eSPavel Dovgalyuk && unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) { 91bb2e0039SLluís Vilanova CPUBreakpoint *bp; 92bb2e0039SLluís Vilanova QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) { 93bb2e0039SLluís Vilanova if (bp->pc == db->pc_next) { 94bb2e0039SLluís Vilanova if (ops->breakpoint_check(db, cpu, bp)) { 95f9f1f56eSPavel Dovgalyuk bp_insn = 1; 96bb2e0039SLluís Vilanova break; 97bb2e0039SLluís Vilanova } 98bb2e0039SLluís Vilanova } 99bb2e0039SLluís Vilanova } 100bb2e0039SLluís Vilanova /* The breakpoint_check hook may use DISAS_TOO_MANY to indicate 101bb2e0039SLluís Vilanova that only one more instruction is to be executed. Otherwise 102bb2e0039SLluís Vilanova it should use DISAS_NORETURN when generating an exception, 103bb2e0039SLluís Vilanova but may use a DISAS_TARGET_* value for Something Else. */ 104bb2e0039SLluís Vilanova if (db->is_jmp > DISAS_TOO_MANY) { 105bb2e0039SLluís Vilanova break; 106bb2e0039SLluís Vilanova } 107bb2e0039SLluís Vilanova } 108bb2e0039SLluís Vilanova 109bb2e0039SLluís Vilanova /* Disassemble one instruction. The translate_insn hook should 110bb2e0039SLluís Vilanova update db->pc_next and db->is_jmp to indicate what should be 111bb2e0039SLluís Vilanova done next -- either exiting this loop or locate the start of 112bb2e0039SLluís Vilanova the next instruction. */ 113b542683dSEmilio G. Cota if (db->num_insns == db->max_insns 114b542683dSEmilio G. Cota && (tb_cflags(db->tb) & CF_LAST_IO)) { 115bb2e0039SLluís Vilanova /* Accept I/O on the last instruction. */ 116bb2e0039SLluís Vilanova gen_io_start(); 117bb2e0039SLluís Vilanova ops->translate_insn(db, cpu); 118bb2e0039SLluís Vilanova } else { 119cfd405eaSAlex Bennée /* we should only see CF_MEMI_ONLY for io_recompile */ 120cfd405eaSAlex Bennée tcg_debug_assert(!(tb_cflags(db->tb) & CF_MEMI_ONLY)); 121bb2e0039SLluís Vilanova ops->translate_insn(db, cpu); 122bb2e0039SLluís Vilanova } 123bb2e0039SLluís Vilanova 124bb2e0039SLluís Vilanova /* Stop translation if translate_insn so indicated. */ 125bb2e0039SLluís Vilanova if (db->is_jmp != DISAS_NEXT) { 126bb2e0039SLluís Vilanova break; 127bb2e0039SLluís Vilanova } 128bb2e0039SLluís Vilanova 1296ba6f818SEmilio G. Cota /* 1306ba6f818SEmilio G. Cota * We can't instrument after instructions that change control 1316ba6f818SEmilio G. Cota * flow although this only really affects post-load operations. 1326ba6f818SEmilio G. Cota */ 1336ba6f818SEmilio G. Cota if (plugin_enabled) { 1346ba6f818SEmilio G. Cota plugin_gen_insn_end(); 1356ba6f818SEmilio G. Cota } 1366ba6f818SEmilio G. Cota 137bb2e0039SLluís Vilanova /* Stop translation if the output buffer is full, 138bb2e0039SLluís Vilanova or we have executed all of the allowed instructions. */ 139b542683dSEmilio G. Cota if (tcg_op_buf_full() || db->num_insns >= db->max_insns) { 140bb2e0039SLluís Vilanova db->is_jmp = DISAS_TOO_MANY; 141bb2e0039SLluís Vilanova break; 142bb2e0039SLluís Vilanova } 143bb2e0039SLluís Vilanova } 144bb2e0039SLluís Vilanova 145bb2e0039SLluís Vilanova /* Emit code to exit the TB, as indicated by db->is_jmp. */ 146bb2e0039SLluís Vilanova ops->tb_stop(db, cpu); 147f9f1f56eSPavel Dovgalyuk gen_tb_end(db->tb, db->num_insns - bp_insn); 148bb2e0039SLluís Vilanova 1496ba6f818SEmilio G. Cota if (plugin_enabled) { 1506ba6f818SEmilio G. Cota plugin_gen_tb_end(cpu); 1516ba6f818SEmilio G. Cota } 1526ba6f818SEmilio G. Cota 153bb2e0039SLluís Vilanova /* The disas_log hook may use these values rather than recompute. */ 154d9971435SRichard Henderson tb->size = db->pc_next - db->pc_first; 155d9971435SRichard Henderson tb->icount = db->num_insns; 156bb2e0039SLluís Vilanova 157bb2e0039SLluís Vilanova #ifdef DEBUG_DISAS 158bb2e0039SLluís Vilanova if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) 159bb2e0039SLluís Vilanova && qemu_log_in_addr_range(db->pc_first)) { 160fc59d2d8SRobert Foley FILE *logfile = qemu_log_lock(); 161bb2e0039SLluís Vilanova qemu_log("----------------\n"); 162bb2e0039SLluís Vilanova ops->disas_log(db, cpu); 163bb2e0039SLluís Vilanova qemu_log("\n"); 164fc59d2d8SRobert Foley qemu_log_unlock(logfile); 165bb2e0039SLluís Vilanova } 166bb2e0039SLluís Vilanova #endif 167bb2e0039SLluís Vilanova } 168