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 45306c8721SRichard Henderson void translator_loop(CPUState *cpu, TranslationBlock *tb, int max_insns, 46306c8721SRichard Henderson target_ulong pc, void *host_pc, 47306c8721SRichard Henderson const TranslatorOps *ops, DisasContextBase *db) 48bb2e0039SLluís Vilanova { 49d40c5c79SRichard Henderson uint32_t cflags = tb_cflags(tb); 506ba6f818SEmilio G. Cota bool plugin_enabled; 51f9f1f56eSPavel Dovgalyuk 52bb2e0039SLluís Vilanova /* Initialize DisasContext */ 53bb2e0039SLluís Vilanova db->tb = tb; 54306c8721SRichard Henderson db->pc_first = pc; 55306c8721SRichard Henderson db->pc_next = pc; 56bb2e0039SLluís Vilanova db->is_jmp = DISAS_NEXT; 57bb2e0039SLluís Vilanova db->num_insns = 0; 588b86d6d2SRichard Henderson db->max_insns = max_insns; 59c2ffd754SRichard Henderson db->singlestep_enabled = cflags & CF_SINGLE_STEP; 6050627f1bSRichard Henderson db->host_addr[0] = host_pc; 6150627f1bSRichard Henderson db->host_addr[1] = NULL; 6250627f1bSRichard Henderson 6350627f1bSRichard Henderson #ifdef CONFIG_USER_ONLY 6450627f1bSRichard Henderson page_protect(pc); 6550627f1bSRichard Henderson #endif 66bb2e0039SLluís Vilanova 67b542683dSEmilio G. Cota ops->init_disas_context(db, cpu); 68bb2e0039SLluís Vilanova tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ 69bb2e0039SLluís Vilanova 70bb2e0039SLluís Vilanova /* Reset the temp count so that we can identify leaks */ 71bb2e0039SLluís Vilanova tcg_clear_temp_count(); 72bb2e0039SLluís Vilanova 73bb2e0039SLluís Vilanova /* Start translating. */ 74bb2e0039SLluís Vilanova gen_tb_start(db->tb); 75bb2e0039SLluís Vilanova ops->tb_start(db, cpu); 76bb2e0039SLluís Vilanova tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ 77bb2e0039SLluís Vilanova 78b21af662SRichard Henderson plugin_enabled = plugin_gen_tb_start(cpu, db, cflags & CF_MEMI_ONLY); 796ba6f818SEmilio G. Cota 80bb2e0039SLluís Vilanova while (true) { 81bb2e0039SLluís Vilanova db->num_insns++; 82bb2e0039SLluís Vilanova ops->insn_start(db, cpu); 83bb2e0039SLluís Vilanova tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ 84bb2e0039SLluís Vilanova 856ba6f818SEmilio G. Cota if (plugin_enabled) { 866ba6f818SEmilio G. Cota plugin_gen_insn_start(cpu, db); 876ba6f818SEmilio G. Cota } 886ba6f818SEmilio G. Cota 89bb2e0039SLluís Vilanova /* Disassemble one instruction. The translate_insn hook should 90bb2e0039SLluís Vilanova update db->pc_next and db->is_jmp to indicate what should be 91bb2e0039SLluís Vilanova done next -- either exiting this loop or locate the start of 92bb2e0039SLluís Vilanova the next instruction. */ 93d40c5c79SRichard Henderson if (db->num_insns == db->max_insns && (cflags & CF_LAST_IO)) { 94bb2e0039SLluís Vilanova /* Accept I/O on the last instruction. */ 95bb2e0039SLluís Vilanova gen_io_start(); 96bb2e0039SLluís Vilanova ops->translate_insn(db, cpu); 97bb2e0039SLluís Vilanova } else { 98cfd405eaSAlex Bennée /* we should only see CF_MEMI_ONLY for io_recompile */ 99d40c5c79SRichard Henderson tcg_debug_assert(!(cflags & CF_MEMI_ONLY)); 100bb2e0039SLluís Vilanova ops->translate_insn(db, cpu); 101bb2e0039SLluís Vilanova } 102bb2e0039SLluís Vilanova 1036ba6f818SEmilio G. Cota /* 1046ba6f818SEmilio G. Cota * We can't instrument after instructions that change control 1056ba6f818SEmilio G. Cota * flow although this only really affects post-load operations. 1060f92d94aSEmilio Cota * 1070f92d94aSEmilio Cota * Calling plugin_gen_insn_end() before we possibly stop translation 1080f92d94aSEmilio Cota * is important. Even if this ends up as dead code, plugin generation 1090f92d94aSEmilio Cota * needs to see a matching plugin_gen_insn_{start,end}() pair in order 1100f92d94aSEmilio Cota * to accurately track instrumented helpers that might access memory. 1116ba6f818SEmilio G. Cota */ 1126ba6f818SEmilio G. Cota if (plugin_enabled) { 1136ba6f818SEmilio G. Cota plugin_gen_insn_end(); 1146ba6f818SEmilio G. Cota } 1156ba6f818SEmilio G. Cota 1160f92d94aSEmilio Cota /* Stop translation if translate_insn so indicated. */ 1170f92d94aSEmilio Cota if (db->is_jmp != DISAS_NEXT) { 1180f92d94aSEmilio Cota break; 1190f92d94aSEmilio Cota } 1200f92d94aSEmilio Cota 121bb2e0039SLluís Vilanova /* Stop translation if the output buffer is full, 122bb2e0039SLluís Vilanova or we have executed all of the allowed instructions. */ 123b542683dSEmilio G. Cota if (tcg_op_buf_full() || db->num_insns >= db->max_insns) { 124bb2e0039SLluís Vilanova db->is_jmp = DISAS_TOO_MANY; 125bb2e0039SLluís Vilanova break; 126bb2e0039SLluís Vilanova } 127bb2e0039SLluís Vilanova } 128bb2e0039SLluís Vilanova 129bb2e0039SLluís Vilanova /* Emit code to exit the TB, as indicated by db->is_jmp. */ 130bb2e0039SLluís Vilanova ops->tb_stop(db, cpu); 13110c37828SRichard Henderson gen_tb_end(db->tb, db->num_insns); 132bb2e0039SLluís Vilanova 1336ba6f818SEmilio G. Cota if (plugin_enabled) { 1346ba6f818SEmilio G. Cota plugin_gen_tb_end(cpu); 1356ba6f818SEmilio G. Cota } 1366ba6f818SEmilio G. Cota 137bb2e0039SLluís Vilanova /* The disas_log hook may use these values rather than recompute. */ 138d9971435SRichard Henderson tb->size = db->pc_next - db->pc_first; 139d9971435SRichard Henderson tb->icount = db->num_insns; 140bb2e0039SLluís Vilanova 141bb2e0039SLluís Vilanova #ifdef DEBUG_DISAS 142bb2e0039SLluís Vilanova if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) 143bb2e0039SLluís Vilanova && qemu_log_in_addr_range(db->pc_first)) { 144c60f599bSRichard Henderson FILE *logfile = qemu_log_trylock(); 14578b54858SRichard Henderson if (logfile) { 14678b54858SRichard Henderson fprintf(logfile, "----------------\n"); 1478eb806a7SRichard Henderson ops->disas_log(db, cpu, logfile); 14878b54858SRichard Henderson fprintf(logfile, "\n"); 149fc59d2d8SRobert Foley qemu_log_unlock(logfile); 150bb2e0039SLluís Vilanova } 15178b54858SRichard Henderson } 152bb2e0039SLluís Vilanova #endif 153bb2e0039SLluís Vilanova } 154f025692cSIlya Leoshkevich 15550627f1bSRichard Henderson static void *translator_access(CPUArchState *env, DisasContextBase *db, 156f025692cSIlya Leoshkevich target_ulong pc, size_t len) 157f025692cSIlya Leoshkevich { 15850627f1bSRichard Henderson void *host; 15950627f1bSRichard Henderson target_ulong base, end; 16050627f1bSRichard Henderson TranslationBlock *tb; 16150627f1bSRichard Henderson 16250627f1bSRichard Henderson tb = db->tb; 16350627f1bSRichard Henderson 16450627f1bSRichard Henderson /* Use slow path if first page is MMIO. */ 16528905cfbSRichard Henderson if (unlikely(tb_page_addr0(tb) == -1)) { 16650627f1bSRichard Henderson return NULL; 16750627f1bSRichard Henderson } 16850627f1bSRichard Henderson 16950627f1bSRichard Henderson end = pc + len - 1; 17050627f1bSRichard Henderson if (likely(is_same_page(db, end))) { 17150627f1bSRichard Henderson host = db->host_addr[0]; 17250627f1bSRichard Henderson base = db->pc_first; 17350627f1bSRichard Henderson } else { 17450627f1bSRichard Henderson host = db->host_addr[1]; 17550627f1bSRichard Henderson base = TARGET_PAGE_ALIGN(db->pc_first); 17650627f1bSRichard Henderson if (host == NULL) { 17728905cfbSRichard Henderson tb_page_addr_t phys_page = 17850627f1bSRichard Henderson get_page_addr_code_hostp(env, base, &db->host_addr[1]); 179*2627e452SRichard Henderson 180*2627e452SRichard Henderson /* 181*2627e452SRichard Henderson * If the second page is MMIO, treat as if the first page 182*2627e452SRichard Henderson * was MMIO as well, so that we do not cache the TB. 183*2627e452SRichard Henderson */ 184*2627e452SRichard Henderson if (unlikely(phys_page == -1)) { 185*2627e452SRichard Henderson tb_set_page_addr0(tb, -1); 186*2627e452SRichard Henderson return NULL; 187*2627e452SRichard Henderson } 188*2627e452SRichard Henderson 18928905cfbSRichard Henderson tb_set_page_addr1(tb, phys_page); 190f025692cSIlya Leoshkevich #ifdef CONFIG_USER_ONLY 19150627f1bSRichard Henderson page_protect(end); 192f025692cSIlya Leoshkevich #endif 19350627f1bSRichard Henderson host = db->host_addr[1]; 194f025692cSIlya Leoshkevich } 195f025692cSIlya Leoshkevich 19650627f1bSRichard Henderson /* Use slow path when crossing pages. */ 19750627f1bSRichard Henderson if (is_same_page(db, pc)) { 19850627f1bSRichard Henderson return NULL; 19950627f1bSRichard Henderson } 200f025692cSIlya Leoshkevich } 201f025692cSIlya Leoshkevich 20250627f1bSRichard Henderson tcg_debug_assert(pc >= base); 20350627f1bSRichard Henderson return host + (pc - base); 20450627f1bSRichard Henderson } 205f025692cSIlya Leoshkevich 20650627f1bSRichard Henderson uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, abi_ptr pc) 20750627f1bSRichard Henderson { 20850627f1bSRichard Henderson uint8_t ret; 20950627f1bSRichard Henderson void *p = translator_access(env, db, pc, sizeof(ret)); 21050627f1bSRichard Henderson 21150627f1bSRichard Henderson if (p) { 21250627f1bSRichard Henderson plugin_insn_append(pc, p, sizeof(ret)); 21350627f1bSRichard Henderson return ldub_p(p); 21450627f1bSRichard Henderson } 21550627f1bSRichard Henderson ret = cpu_ldub_code(env, pc); 21650627f1bSRichard Henderson plugin_insn_append(pc, &ret, sizeof(ret)); 21750627f1bSRichard Henderson return ret; 21850627f1bSRichard Henderson } 21950627f1bSRichard Henderson 22050627f1bSRichard Henderson uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, abi_ptr pc) 22150627f1bSRichard Henderson { 22250627f1bSRichard Henderson uint16_t ret, plug; 22350627f1bSRichard Henderson void *p = translator_access(env, db, pc, sizeof(ret)); 22450627f1bSRichard Henderson 22550627f1bSRichard Henderson if (p) { 22650627f1bSRichard Henderson plugin_insn_append(pc, p, sizeof(ret)); 22750627f1bSRichard Henderson return lduw_p(p); 22850627f1bSRichard Henderson } 22950627f1bSRichard Henderson ret = cpu_lduw_code(env, pc); 23050627f1bSRichard Henderson plug = tswap16(ret); 23150627f1bSRichard Henderson plugin_insn_append(pc, &plug, sizeof(ret)); 23250627f1bSRichard Henderson return ret; 23350627f1bSRichard Henderson } 23450627f1bSRichard Henderson 23550627f1bSRichard Henderson uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, abi_ptr pc) 23650627f1bSRichard Henderson { 23750627f1bSRichard Henderson uint32_t ret, plug; 23850627f1bSRichard Henderson void *p = translator_access(env, db, pc, sizeof(ret)); 23950627f1bSRichard Henderson 24050627f1bSRichard Henderson if (p) { 24150627f1bSRichard Henderson plugin_insn_append(pc, p, sizeof(ret)); 24250627f1bSRichard Henderson return ldl_p(p); 24350627f1bSRichard Henderson } 24450627f1bSRichard Henderson ret = cpu_ldl_code(env, pc); 24550627f1bSRichard Henderson plug = tswap32(ret); 24650627f1bSRichard Henderson plugin_insn_append(pc, &plug, sizeof(ret)); 24750627f1bSRichard Henderson return ret; 24850627f1bSRichard Henderson } 24950627f1bSRichard Henderson 25050627f1bSRichard Henderson uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, abi_ptr pc) 25150627f1bSRichard Henderson { 25250627f1bSRichard Henderson uint64_t ret, plug; 25350627f1bSRichard Henderson void *p = translator_access(env, db, pc, sizeof(ret)); 25450627f1bSRichard Henderson 25550627f1bSRichard Henderson if (p) { 25650627f1bSRichard Henderson plugin_insn_append(pc, p, sizeof(ret)); 25750627f1bSRichard Henderson return ldq_p(p); 25850627f1bSRichard Henderson } 25950627f1bSRichard Henderson ret = cpu_ldq_code(env, pc); 26050627f1bSRichard Henderson plug = tswap64(ret); 26150627f1bSRichard Henderson plugin_insn_append(pc, &plug, sizeof(ret)); 26250627f1bSRichard Henderson return ret; 26350627f1bSRichard Henderson } 264