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 103bb2e0039SLluís Vilanova /* Stop translation if translate_insn so indicated. */ 104bb2e0039SLluís Vilanova if (db->is_jmp != DISAS_NEXT) { 105bb2e0039SLluís Vilanova break; 106bb2e0039SLluís Vilanova } 107bb2e0039SLluís Vilanova 1086ba6f818SEmilio G. Cota /* 1096ba6f818SEmilio G. Cota * We can't instrument after instructions that change control 1106ba6f818SEmilio G. Cota * flow although this only really affects post-load operations. 1116ba6f818SEmilio G. Cota */ 1126ba6f818SEmilio G. Cota if (plugin_enabled) { 1136ba6f818SEmilio G. Cota plugin_gen_insn_end(); 1146ba6f818SEmilio G. Cota } 1156ba6f818SEmilio G. Cota 116bb2e0039SLluís Vilanova /* Stop translation if the output buffer is full, 117bb2e0039SLluís Vilanova or we have executed all of the allowed instructions. */ 118b542683dSEmilio G. Cota if (tcg_op_buf_full() || db->num_insns >= db->max_insns) { 119bb2e0039SLluís Vilanova db->is_jmp = DISAS_TOO_MANY; 120bb2e0039SLluís Vilanova break; 121bb2e0039SLluís Vilanova } 122bb2e0039SLluís Vilanova } 123bb2e0039SLluís Vilanova 124bb2e0039SLluís Vilanova /* Emit code to exit the TB, as indicated by db->is_jmp. */ 125bb2e0039SLluís Vilanova ops->tb_stop(db, cpu); 12610c37828SRichard Henderson gen_tb_end(db->tb, db->num_insns); 127bb2e0039SLluís Vilanova 1286ba6f818SEmilio G. Cota if (plugin_enabled) { 1296ba6f818SEmilio G. Cota plugin_gen_tb_end(cpu); 1306ba6f818SEmilio G. Cota } 1316ba6f818SEmilio G. Cota 132bb2e0039SLluís Vilanova /* The disas_log hook may use these values rather than recompute. */ 133d9971435SRichard Henderson tb->size = db->pc_next - db->pc_first; 134d9971435SRichard Henderson tb->icount = db->num_insns; 135bb2e0039SLluís Vilanova 136bb2e0039SLluís Vilanova #ifdef DEBUG_DISAS 137bb2e0039SLluís Vilanova if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) 138bb2e0039SLluís Vilanova && qemu_log_in_addr_range(db->pc_first)) { 139c60f599bSRichard Henderson FILE *logfile = qemu_log_trylock(); 14078b54858SRichard Henderson if (logfile) { 14178b54858SRichard Henderson fprintf(logfile, "----------------\n"); 1428eb806a7SRichard Henderson ops->disas_log(db, cpu, logfile); 14378b54858SRichard Henderson fprintf(logfile, "\n"); 144fc59d2d8SRobert Foley qemu_log_unlock(logfile); 145bb2e0039SLluís Vilanova } 14678b54858SRichard Henderson } 147bb2e0039SLluís Vilanova #endif 148bb2e0039SLluís Vilanova } 149f025692cSIlya Leoshkevich 15050627f1bSRichard Henderson static void *translator_access(CPUArchState *env, DisasContextBase *db, 151f025692cSIlya Leoshkevich target_ulong pc, size_t len) 152f025692cSIlya Leoshkevich { 15350627f1bSRichard Henderson void *host; 15450627f1bSRichard Henderson target_ulong base, end; 15550627f1bSRichard Henderson TranslationBlock *tb; 15650627f1bSRichard Henderson 15750627f1bSRichard Henderson tb = db->tb; 15850627f1bSRichard Henderson 15950627f1bSRichard Henderson /* Use slow path if first page is MMIO. */ 160*28905cfbSRichard Henderson if (unlikely(tb_page_addr0(tb) == -1)) { 16150627f1bSRichard Henderson return NULL; 16250627f1bSRichard Henderson } 16350627f1bSRichard Henderson 16450627f1bSRichard Henderson end = pc + len - 1; 16550627f1bSRichard Henderson if (likely(is_same_page(db, end))) { 16650627f1bSRichard Henderson host = db->host_addr[0]; 16750627f1bSRichard Henderson base = db->pc_first; 16850627f1bSRichard Henderson } else { 16950627f1bSRichard Henderson host = db->host_addr[1]; 17050627f1bSRichard Henderson base = TARGET_PAGE_ALIGN(db->pc_first); 17150627f1bSRichard Henderson if (host == NULL) { 172*28905cfbSRichard Henderson tb_page_addr_t phys_page = 17350627f1bSRichard Henderson get_page_addr_code_hostp(env, base, &db->host_addr[1]); 174*28905cfbSRichard Henderson /* We cannot handle MMIO as second page. */ 175*28905cfbSRichard Henderson assert(phys_page != -1); 176*28905cfbSRichard Henderson tb_set_page_addr1(tb, phys_page); 177f025692cSIlya Leoshkevich #ifdef CONFIG_USER_ONLY 17850627f1bSRichard Henderson page_protect(end); 179f025692cSIlya Leoshkevich #endif 18050627f1bSRichard Henderson host = db->host_addr[1]; 181f025692cSIlya Leoshkevich } 182f025692cSIlya Leoshkevich 18350627f1bSRichard Henderson /* Use slow path when crossing pages. */ 18450627f1bSRichard Henderson if (is_same_page(db, pc)) { 18550627f1bSRichard Henderson return NULL; 18650627f1bSRichard Henderson } 187f025692cSIlya Leoshkevich } 188f025692cSIlya Leoshkevich 18950627f1bSRichard Henderson tcg_debug_assert(pc >= base); 19050627f1bSRichard Henderson return host + (pc - base); 19150627f1bSRichard Henderson } 192f025692cSIlya Leoshkevich 19350627f1bSRichard Henderson uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, abi_ptr pc) 19450627f1bSRichard Henderson { 19550627f1bSRichard Henderson uint8_t ret; 19650627f1bSRichard Henderson void *p = translator_access(env, db, pc, sizeof(ret)); 19750627f1bSRichard Henderson 19850627f1bSRichard Henderson if (p) { 19950627f1bSRichard Henderson plugin_insn_append(pc, p, sizeof(ret)); 20050627f1bSRichard Henderson return ldub_p(p); 20150627f1bSRichard Henderson } 20250627f1bSRichard Henderson ret = cpu_ldub_code(env, pc); 20350627f1bSRichard Henderson plugin_insn_append(pc, &ret, sizeof(ret)); 20450627f1bSRichard Henderson return ret; 20550627f1bSRichard Henderson } 20650627f1bSRichard Henderson 20750627f1bSRichard Henderson uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, abi_ptr pc) 20850627f1bSRichard Henderson { 20950627f1bSRichard Henderson uint16_t ret, plug; 21050627f1bSRichard Henderson void *p = translator_access(env, db, pc, sizeof(ret)); 21150627f1bSRichard Henderson 21250627f1bSRichard Henderson if (p) { 21350627f1bSRichard Henderson plugin_insn_append(pc, p, sizeof(ret)); 21450627f1bSRichard Henderson return lduw_p(p); 21550627f1bSRichard Henderson } 21650627f1bSRichard Henderson ret = cpu_lduw_code(env, pc); 21750627f1bSRichard Henderson plug = tswap16(ret); 21850627f1bSRichard Henderson plugin_insn_append(pc, &plug, sizeof(ret)); 21950627f1bSRichard Henderson return ret; 22050627f1bSRichard Henderson } 22150627f1bSRichard Henderson 22250627f1bSRichard Henderson uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, abi_ptr pc) 22350627f1bSRichard Henderson { 22450627f1bSRichard Henderson uint32_t ret, plug; 22550627f1bSRichard Henderson void *p = translator_access(env, db, pc, sizeof(ret)); 22650627f1bSRichard Henderson 22750627f1bSRichard Henderson if (p) { 22850627f1bSRichard Henderson plugin_insn_append(pc, p, sizeof(ret)); 22950627f1bSRichard Henderson return ldl_p(p); 23050627f1bSRichard Henderson } 23150627f1bSRichard Henderson ret = cpu_ldl_code(env, pc); 23250627f1bSRichard Henderson plug = tswap32(ret); 23350627f1bSRichard Henderson plugin_insn_append(pc, &plug, sizeof(ret)); 23450627f1bSRichard Henderson return ret; 23550627f1bSRichard Henderson } 23650627f1bSRichard Henderson 23750627f1bSRichard Henderson uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, abi_ptr pc) 23850627f1bSRichard Henderson { 23950627f1bSRichard Henderson uint64_t ret, plug; 24050627f1bSRichard Henderson void *p = translator_access(env, db, pc, sizeof(ret)); 24150627f1bSRichard Henderson 24250627f1bSRichard Henderson if (p) { 24350627f1bSRichard Henderson plugin_insn_append(pc, p, sizeof(ret)); 24450627f1bSRichard Henderson return ldq_p(p); 24550627f1bSRichard Henderson } 24650627f1bSRichard Henderson ret = cpu_ldq_code(env, pc); 24750627f1bSRichard Henderson plug = tswap64(ret); 24850627f1bSRichard Henderson plugin_insn_append(pc, &plug, sizeof(ret)); 24950627f1bSRichard Henderson return ret; 25050627f1bSRichard Henderson } 251