50627f1b7b
Cache the translation from guest to host address, so we may use direct loads when we hit on the primary translation page. Look up the second translation page only once, during translation. This obviates another lookup of the second page within tb_gen_code after translation. Fixes a bug in that plugin_insn_append should be passed the bytes in the original memory order, not bswapped by pieces. Acked-by: Ilya Leoshkevich <iii@linux.ibm.com> Tested-by: Ilya Leoshkevich <iii@linux.ibm.com> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
225 lines
6.8 KiB
C
225 lines
6.8 KiB
C
/*
|
|
* Generic intermediate code generation.
|
|
*
|
|
* Copyright (C) 2016-2017 Lluís Vilanova <vilanova@ac.upc.edu>
|
|
*
|
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
|
* See the COPYING file in the top-level directory.
|
|
*/
|
|
|
|
#ifndef EXEC__TRANSLATOR_H
|
|
#define EXEC__TRANSLATOR_H
|
|
|
|
/*
|
|
* Include this header from a target-specific file, and add a
|
|
*
|
|
* DisasContextBase base;
|
|
*
|
|
* member in your target-specific DisasContext.
|
|
*/
|
|
|
|
|
|
#include "qemu/bswap.h"
|
|
#include "exec/exec-all.h"
|
|
#include "exec/cpu_ldst.h"
|
|
#include "exec/plugin-gen.h"
|
|
#include "exec/translate-all.h"
|
|
#include "tcg/tcg.h"
|
|
|
|
/**
|
|
* gen_intermediate_code
|
|
* @cpu: cpu context
|
|
* @tb: translation block
|
|
* @max_insns: max number of instructions to translate
|
|
* @pc: guest virtual program counter address
|
|
* @host_pc: host physical program counter address
|
|
*
|
|
* This function must be provided by the target, which should create
|
|
* the target-specific DisasContext, and then invoke translator_loop.
|
|
*/
|
|
void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int max_insns,
|
|
target_ulong pc, void *host_pc);
|
|
|
|
/**
|
|
* DisasJumpType:
|
|
* @DISAS_NEXT: Next instruction in program order.
|
|
* @DISAS_TOO_MANY: Too many instructions translated.
|
|
* @DISAS_NORETURN: Following code is dead.
|
|
* @DISAS_TARGET_*: Start of target-specific conditions.
|
|
*
|
|
* What instruction to disassemble next.
|
|
*/
|
|
typedef enum DisasJumpType {
|
|
DISAS_NEXT,
|
|
DISAS_TOO_MANY,
|
|
DISAS_NORETURN,
|
|
DISAS_TARGET_0,
|
|
DISAS_TARGET_1,
|
|
DISAS_TARGET_2,
|
|
DISAS_TARGET_3,
|
|
DISAS_TARGET_4,
|
|
DISAS_TARGET_5,
|
|
DISAS_TARGET_6,
|
|
DISAS_TARGET_7,
|
|
DISAS_TARGET_8,
|
|
DISAS_TARGET_9,
|
|
DISAS_TARGET_10,
|
|
DISAS_TARGET_11,
|
|
} DisasJumpType;
|
|
|
|
/**
|
|
* DisasContextBase:
|
|
* @tb: Translation block for this disassembly.
|
|
* @pc_first: Address of first guest instruction in this TB.
|
|
* @pc_next: Address of next guest instruction in this TB (current during
|
|
* disassembly).
|
|
* @is_jmp: What instruction to disassemble next.
|
|
* @num_insns: Number of translated instructions (including current).
|
|
* @max_insns: Maximum number of instructions to be translated in this TB.
|
|
* @singlestep_enabled: "Hardware" single stepping enabled.
|
|
*
|
|
* Architecture-agnostic disassembly context.
|
|
*/
|
|
typedef struct DisasContextBase {
|
|
TranslationBlock *tb;
|
|
target_ulong pc_first;
|
|
target_ulong pc_next;
|
|
DisasJumpType is_jmp;
|
|
int num_insns;
|
|
int max_insns;
|
|
bool singlestep_enabled;
|
|
void *host_addr[2];
|
|
} DisasContextBase;
|
|
|
|
/**
|
|
* TranslatorOps:
|
|
* @init_disas_context:
|
|
* Initialize the target-specific portions of DisasContext struct.
|
|
* The generic DisasContextBase has already been initialized.
|
|
*
|
|
* @tb_start:
|
|
* Emit any code required before the start of the main loop,
|
|
* after the generic gen_tb_start().
|
|
*
|
|
* @insn_start:
|
|
* Emit the tcg_gen_insn_start opcode.
|
|
*
|
|
* @translate_insn:
|
|
* Disassemble one instruction and set db->pc_next for the start
|
|
* of the following instruction. Set db->is_jmp as necessary to
|
|
* terminate the main loop.
|
|
*
|
|
* @tb_stop:
|
|
* Emit any opcodes required to exit the TB, based on db->is_jmp.
|
|
*
|
|
* @disas_log:
|
|
* Print instruction disassembly to log.
|
|
*/
|
|
typedef struct TranslatorOps {
|
|
void (*init_disas_context)(DisasContextBase *db, CPUState *cpu);
|
|
void (*tb_start)(DisasContextBase *db, CPUState *cpu);
|
|
void (*insn_start)(DisasContextBase *db, CPUState *cpu);
|
|
void (*translate_insn)(DisasContextBase *db, CPUState *cpu);
|
|
void (*tb_stop)(DisasContextBase *db, CPUState *cpu);
|
|
void (*disas_log)(const DisasContextBase *db, CPUState *cpu, FILE *f);
|
|
} TranslatorOps;
|
|
|
|
/**
|
|
* translator_loop:
|
|
* @cpu: Target vCPU.
|
|
* @tb: Translation block.
|
|
* @max_insns: Maximum number of insns to translate.
|
|
* @pc: guest virtual program counter address
|
|
* @host_pc: host physical program counter address
|
|
* @ops: Target-specific operations.
|
|
* @db: Disassembly context.
|
|
*
|
|
* Generic translator loop.
|
|
*
|
|
* Translation will stop in the following cases (in order):
|
|
* - When is_jmp set by #TranslatorOps::breakpoint_check.
|
|
* - set to DISAS_TOO_MANY exits after translating one more insn
|
|
* - set to any other value than DISAS_NEXT exits immediately.
|
|
* - When is_jmp set by #TranslatorOps::translate_insn.
|
|
* - set to any value other than DISAS_NEXT exits immediately.
|
|
* - When the TCG operation buffer is full.
|
|
* - When single-stepping is enabled (system-wide or on the current vCPU).
|
|
* - When too many instructions have been translated.
|
|
*/
|
|
void translator_loop(CPUState *cpu, TranslationBlock *tb, int max_insns,
|
|
target_ulong pc, void *host_pc,
|
|
const TranslatorOps *ops, DisasContextBase *db);
|
|
|
|
void translator_loop_temp_check(DisasContextBase *db);
|
|
|
|
/**
|
|
* translator_use_goto_tb
|
|
* @db: Disassembly context
|
|
* @dest: target pc of the goto
|
|
*
|
|
* Return true if goto_tb is allowed between the current TB
|
|
* and the destination PC.
|
|
*/
|
|
bool translator_use_goto_tb(DisasContextBase *db, target_ulong dest);
|
|
|
|
/*
|
|
* Translator Load Functions
|
|
*
|
|
* These are intended to replace the direct usage of the cpu_ld*_code
|
|
* functions and are mandatory for front-ends that have been migrated
|
|
* to the common translator_loop. These functions are only intended
|
|
* to be called from the translation stage and should not be called
|
|
* from helper functions. Those functions should be converted to encode
|
|
* the relevant information at translation time.
|
|
*/
|
|
|
|
uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, abi_ptr pc);
|
|
uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, abi_ptr pc);
|
|
uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, abi_ptr pc);
|
|
uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, abi_ptr pc);
|
|
|
|
static inline uint16_t
|
|
translator_lduw_swap(CPUArchState *env, DisasContextBase *db,
|
|
abi_ptr pc, bool do_swap)
|
|
{
|
|
uint16_t ret = translator_lduw(env, db, pc);
|
|
if (do_swap) {
|
|
ret = bswap16(ret);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static inline uint32_t
|
|
translator_ldl_swap(CPUArchState *env, DisasContextBase *db,
|
|
abi_ptr pc, bool do_swap)
|
|
{
|
|
uint32_t ret = translator_ldl(env, db, pc);
|
|
if (do_swap) {
|
|
ret = bswap32(ret);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static inline uint64_t
|
|
translator_ldq_swap(CPUArchState *env, DisasContextBase *db,
|
|
abi_ptr pc, bool do_swap)
|
|
{
|
|
uint64_t ret = translator_ldq(env, db, pc);
|
|
if (do_swap) {
|
|
ret = bswap64(ret);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Return whether addr is on the same page as where disassembly started.
|
|
* Translators can use this to enforce the rule that only single-insn
|
|
* translation blocks are allowed to cross page boundaries.
|
|
*/
|
|
static inline bool is_same_page(const DisasContextBase *db, target_ulong addr)
|
|
{
|
|
return ((addr ^ db->pc_first) & TARGET_PAGE_MASK) == 0;
|
|
}
|
|
|
|
#endif /* EXEC__TRANSLATOR_H */
|