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 { 3684f15616SRichard Henderson /* Suppress goto_tb if requested. */ 3784f15616SRichard Henderson if (tb_cflags(db->tb) & CF_NO_GOTO_TB) { 3884f15616SRichard Henderson return false; 3984f15616SRichard Henderson } 4084f15616SRichard Henderson 41d3a2a1d8SRichard Henderson /* Check for the dest on the same page as the start of the TB. */ 42d3a2a1d8SRichard Henderson return ((db->pc_first ^ dest) & TARGET_PAGE_MASK) == 0; 43d3a2a1d8SRichard Henderson } 44d3a2a1d8SRichard Henderson 45f025692cSIlya Leoshkevich static inline void translator_page_protect(DisasContextBase *dcbase, 46f025692cSIlya Leoshkevich target_ulong pc) 47f025692cSIlya Leoshkevich { 48f025692cSIlya Leoshkevich #ifdef CONFIG_USER_ONLY 49f025692cSIlya Leoshkevich dcbase->page_protect_end = pc | ~TARGET_PAGE_MASK; 50f025692cSIlya Leoshkevich page_protect(pc); 51f025692cSIlya Leoshkevich #endif 52f025692cSIlya Leoshkevich } 53f025692cSIlya Leoshkevich 54bb2e0039SLluís Vilanova void translator_loop(const TranslatorOps *ops, DisasContextBase *db, 558b86d6d2SRichard Henderson CPUState *cpu, TranslationBlock *tb, int max_insns) 56bb2e0039SLluís Vilanova { 57d40c5c79SRichard Henderson uint32_t cflags = tb_cflags(tb); 586ba6f818SEmilio G. Cota bool plugin_enabled; 59f9f1f56eSPavel Dovgalyuk 60bb2e0039SLluís Vilanova /* Initialize DisasContext */ 61bb2e0039SLluís Vilanova db->tb = tb; 62bb2e0039SLluís Vilanova db->pc_first = tb->pc; 63bb2e0039SLluís Vilanova db->pc_next = db->pc_first; 64bb2e0039SLluís Vilanova db->is_jmp = DISAS_NEXT; 65bb2e0039SLluís Vilanova db->num_insns = 0; 668b86d6d2SRichard Henderson db->max_insns = max_insns; 67c2ffd754SRichard Henderson db->singlestep_enabled = cflags & CF_SINGLE_STEP; 68f025692cSIlya Leoshkevich translator_page_protect(db, db->pc_next); 69bb2e0039SLluís Vilanova 70b542683dSEmilio G. Cota ops->init_disas_context(db, cpu); 71bb2e0039SLluís Vilanova tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ 72bb2e0039SLluís Vilanova 73bb2e0039SLluís Vilanova /* Reset the temp count so that we can identify leaks */ 74bb2e0039SLluís Vilanova tcg_clear_temp_count(); 75bb2e0039SLluís Vilanova 76bb2e0039SLluís Vilanova /* Start translating. */ 77bb2e0039SLluís Vilanova gen_tb_start(db->tb); 78bb2e0039SLluís Vilanova ops->tb_start(db, cpu); 79bb2e0039SLluís Vilanova tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ 80bb2e0039SLluís Vilanova 81d40c5c79SRichard Henderson plugin_enabled = plugin_gen_tb_start(cpu, tb, cflags & CF_MEMI_ONLY); 826ba6f818SEmilio G. Cota 83bb2e0039SLluís Vilanova while (true) { 84bb2e0039SLluís Vilanova db->num_insns++; 85bb2e0039SLluís Vilanova ops->insn_start(db, cpu); 86bb2e0039SLluís Vilanova tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ 87bb2e0039SLluís Vilanova 886ba6f818SEmilio G. Cota if (plugin_enabled) { 896ba6f818SEmilio G. Cota plugin_gen_insn_start(cpu, db); 906ba6f818SEmilio G. Cota } 916ba6f818SEmilio G. Cota 92bb2e0039SLluís Vilanova /* Disassemble one instruction. The translate_insn hook should 93bb2e0039SLluís Vilanova update db->pc_next and db->is_jmp to indicate what should be 94bb2e0039SLluís Vilanova done next -- either exiting this loop or locate the start of 95bb2e0039SLluís Vilanova the next instruction. */ 96d40c5c79SRichard Henderson if (db->num_insns == db->max_insns && (cflags & CF_LAST_IO)) { 97bb2e0039SLluís Vilanova /* Accept I/O on the last instruction. */ 98bb2e0039SLluís Vilanova gen_io_start(); 99bb2e0039SLluís Vilanova ops->translate_insn(db, cpu); 100bb2e0039SLluís Vilanova } else { 101cfd405eaSAlex Bennée /* we should only see CF_MEMI_ONLY for io_recompile */ 102d40c5c79SRichard Henderson tcg_debug_assert(!(cflags & CF_MEMI_ONLY)); 103bb2e0039SLluís Vilanova ops->translate_insn(db, cpu); 104bb2e0039SLluís Vilanova } 105bb2e0039SLluís Vilanova 106bb2e0039SLluís Vilanova /* Stop translation if translate_insn so indicated. */ 107bb2e0039SLluís Vilanova if (db->is_jmp != DISAS_NEXT) { 108bb2e0039SLluís Vilanova break; 109bb2e0039SLluís Vilanova } 110bb2e0039SLluís Vilanova 1116ba6f818SEmilio G. Cota /* 1126ba6f818SEmilio G. Cota * We can't instrument after instructions that change control 1136ba6f818SEmilio G. Cota * flow although this only really affects post-load operations. 1146ba6f818SEmilio G. Cota */ 1156ba6f818SEmilio G. Cota if (plugin_enabled) { 1166ba6f818SEmilio G. Cota plugin_gen_insn_end(); 1176ba6f818SEmilio G. Cota } 1186ba6f818SEmilio G. Cota 119bb2e0039SLluís Vilanova /* Stop translation if the output buffer is full, 120bb2e0039SLluís Vilanova or we have executed all of the allowed instructions. */ 121b542683dSEmilio G. Cota if (tcg_op_buf_full() || db->num_insns >= db->max_insns) { 122bb2e0039SLluís Vilanova db->is_jmp = DISAS_TOO_MANY; 123bb2e0039SLluís Vilanova break; 124bb2e0039SLluís Vilanova } 125bb2e0039SLluís Vilanova } 126bb2e0039SLluís Vilanova 127bb2e0039SLluís Vilanova /* Emit code to exit the TB, as indicated by db->is_jmp. */ 128bb2e0039SLluís Vilanova ops->tb_stop(db, cpu); 12910c37828SRichard Henderson gen_tb_end(db->tb, db->num_insns); 130bb2e0039SLluís Vilanova 1316ba6f818SEmilio G. Cota if (plugin_enabled) { 1326ba6f818SEmilio G. Cota plugin_gen_tb_end(cpu); 1336ba6f818SEmilio G. Cota } 1346ba6f818SEmilio G. Cota 135bb2e0039SLluís Vilanova /* The disas_log hook may use these values rather than recompute. */ 136d9971435SRichard Henderson tb->size = db->pc_next - db->pc_first; 137d9971435SRichard Henderson tb->icount = db->num_insns; 138bb2e0039SLluís Vilanova 139bb2e0039SLluís Vilanova #ifdef DEBUG_DISAS 140bb2e0039SLluís Vilanova if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) 141bb2e0039SLluís Vilanova && qemu_log_in_addr_range(db->pc_first)) { 142*c60f599bSRichard Henderson FILE *logfile = qemu_log_trylock(); 143bb2e0039SLluís Vilanova qemu_log("----------------\n"); 144bb2e0039SLluís Vilanova ops->disas_log(db, cpu); 145bb2e0039SLluís Vilanova qemu_log("\n"); 146fc59d2d8SRobert Foley qemu_log_unlock(logfile); 147bb2e0039SLluís Vilanova } 148bb2e0039SLluís Vilanova #endif 149bb2e0039SLluís Vilanova } 150f025692cSIlya Leoshkevich 151f025692cSIlya Leoshkevich static inline void translator_maybe_page_protect(DisasContextBase *dcbase, 152f025692cSIlya Leoshkevich target_ulong pc, size_t len) 153f025692cSIlya Leoshkevich { 154f025692cSIlya Leoshkevich #ifdef CONFIG_USER_ONLY 155f025692cSIlya Leoshkevich target_ulong end = pc + len - 1; 156f025692cSIlya Leoshkevich 157f025692cSIlya Leoshkevich if (end > dcbase->page_protect_end) { 158f025692cSIlya Leoshkevich translator_page_protect(dcbase, end); 159f025692cSIlya Leoshkevich } 160f025692cSIlya Leoshkevich #endif 161f025692cSIlya Leoshkevich } 162f025692cSIlya Leoshkevich 163f025692cSIlya Leoshkevich #define GEN_TRANSLATOR_LD(fullname, type, load_fn, swap_fn) \ 164f025692cSIlya Leoshkevich type fullname ## _swap(CPUArchState *env, DisasContextBase *dcbase, \ 165f025692cSIlya Leoshkevich abi_ptr pc, bool do_swap) \ 166f025692cSIlya Leoshkevich { \ 167f025692cSIlya Leoshkevich translator_maybe_page_protect(dcbase, pc, sizeof(type)); \ 168f025692cSIlya Leoshkevich type ret = load_fn(env, pc); \ 169f025692cSIlya Leoshkevich if (do_swap) { \ 170f025692cSIlya Leoshkevich ret = swap_fn(ret); \ 171f025692cSIlya Leoshkevich } \ 172357af9beSAlex Bennée plugin_insn_append(pc, &ret, sizeof(ret)); \ 173f025692cSIlya Leoshkevich return ret; \ 174f025692cSIlya Leoshkevich } 175f025692cSIlya Leoshkevich 176f025692cSIlya Leoshkevich FOR_EACH_TRANSLATOR_LD(GEN_TRANSLATOR_LD) 177f025692cSIlya Leoshkevich 178f025692cSIlya Leoshkevich #undef GEN_TRANSLATOR_LD 179