Commit 7e375e04 authored by Peter Maydell's avatar Peter Maydell
Browse files

Merge remote-tracking branch 'remotes/rth/tags/pull-tgt-20170906' into staging



tcg generic translate loop v15

# gpg: Signature made Wed 06 Sep 2017 17:02:31 BST
# gpg:                using RSA key 0x64DF38E8AF7E215F
# gpg: Good signature from "Richard Henderson <richard.henderson@linaro.org>"
# gpg: WARNING: This key is not certified with sufficiently trusted signatures!
# gpg:          It is not certain that the signature belongs to the owner.
# Primary key fingerprint: 7A48 1E78 868B 4DB6 A85A  05C0 64DF 38E8 AF7E 215F

* remotes/rth/tags/pull-tgt-20170906: (32 commits)
  target/arm: Perform per-insn cross-page check only for Thumb
  target/arm: Split out thumb_tr_translate_insn
  target/arm: Move ss check to init_disas_context
  target/arm: [a64] Move page and ss checks to init_disas_context
  target/arm: [tcg] Port to generic translation framework
  target/arm: [tcg,a64] Port to disas_log
  target/arm: [tcg] Port to disas_log
  target/arm: [tcg,a64] Port to tb_stop
  target/arm: [tcg] Port to tb_stop
  target/arm: [tcg,a64] Port to translate_insn
  target/arm: [tcg] Port to translate_insn
  target/arm: [tcg,a64] Port to breakpoint_check
  target/arm: [tcg,a64] Port to insn_start
  target/arm: [tcg] Port to insn_start
  target/arm: [tcg] Port to tb_start
  target/arm: [tcg,a64] Port to init_disas_context
  target/arm: [tcg] Port to init_disas_context
  target/arm: [tcg] Port to DisasContextBase
  target/i386: [tcg] Port to generic translation framework
  target/i386: [tcg] Port to disas_log
  ...

Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parents e6533b57 d0264d86
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
obj-$(CONFIG_SOFTMMU) += tcg-all.o
obj-$(CONFIG_SOFTMMU) += cputlb.o
obj-y += cpu-exec.o cpu-exec-common.o translate-all.o
obj-y += translator.o

accel/tcg/translator.c

0 → 100644
+138 −0
Original line number Diff line number Diff line
/*
 * 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.
 */

#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/error-report.h"
#include "cpu.h"
#include "tcg/tcg.h"
#include "tcg/tcg-op.h"
#include "exec/exec-all.h"
#include "exec/gen-icount.h"
#include "exec/log.h"
#include "exec/translator.h"

/* Pairs with tcg_clear_temp_count.
   To be called by #TranslatorOps.{translate_insn,tb_stop} if
   (1) the target is sufficiently clean to support reporting,
   (2) as and when all temporaries are known to be consumed.
   For most targets, (2) is at the end of translate_insn.  */
void translator_loop_temp_check(DisasContextBase *db)
{
    if (tcg_check_temp_count()) {
        qemu_log("warning: TCG temporary leaks before "
                 TARGET_FMT_lx "\n", db->pc_next);
    }
}

void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
                     CPUState *cpu, TranslationBlock *tb)
{
    int max_insns;

    /* Initialize DisasContext */
    db->tb = tb;
    db->pc_first = tb->pc;
    db->pc_next = db->pc_first;
    db->is_jmp = DISAS_NEXT;
    db->num_insns = 0;
    db->singlestep_enabled = cpu->singlestep_enabled;

    /* Instruction counting */
    max_insns = db->tb->cflags & CF_COUNT_MASK;
    if (max_insns == 0) {
        max_insns = CF_COUNT_MASK;
    }
    if (max_insns > TCG_MAX_INSNS) {
        max_insns = TCG_MAX_INSNS;
    }
    if (db->singlestep_enabled || singlestep) {
        max_insns = 1;
    }

    max_insns = ops->init_disas_context(db, cpu, max_insns);
    tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */

    /* Reset the temp count so that we can identify leaks */
    tcg_clear_temp_count();

    /* Start translating.  */
    gen_tb_start(db->tb);
    ops->tb_start(db, cpu);
    tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */

    while (true) {
        db->num_insns++;
        ops->insn_start(db, cpu);
        tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */

        /* Pass breakpoint hits to target for further processing */
        if (unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
            CPUBreakpoint *bp;
            QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) {
                if (bp->pc == db->pc_next) {
                    if (ops->breakpoint_check(db, cpu, bp)) {
                        break;
                    }
                }
            }
            /* The breakpoint_check hook may use DISAS_TOO_MANY to indicate
               that only one more instruction is to be executed.  Otherwise
               it should use DISAS_NORETURN when generating an exception,
               but may use a DISAS_TARGET_* value for Something Else.  */
            if (db->is_jmp > DISAS_TOO_MANY) {
                break;
            }
        }

        /* Disassemble one instruction.  The translate_insn hook should
           update db->pc_next and db->is_jmp to indicate what should be
           done next -- either exiting this loop or locate the start of
           the next instruction.  */
        if (db->num_insns == max_insns && (db->tb->cflags & CF_LAST_IO)) {
            /* Accept I/O on the last instruction.  */
            gen_io_start();
            ops->translate_insn(db, cpu);
            gen_io_end();
        } else {
            ops->translate_insn(db, cpu);
        }

        /* Stop translation if translate_insn so indicated.  */
        if (db->is_jmp != DISAS_NEXT) {
            break;
        }

        /* Stop translation if the output buffer is full,
           or we have executed all of the allowed instructions.  */
        if (tcg_op_buf_full() || db->num_insns >= max_insns) {
            db->is_jmp = DISAS_TOO_MANY;
            break;
        }
    }

    /* Emit code to exit the TB, as indicated by db->is_jmp.  */
    ops->tb_stop(db, cpu);
    gen_tb_end(db->tb, db->num_insns);

    /* The disas_log hook may use these values rather than recompute.  */
    db->tb->size = db->pc_next - db->pc_first;
    db->tb->icount = db->num_insns;

#ifdef DEBUG_DISAS
    if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
        && qemu_log_in_addr_range(db->pc_first)) {
        qemu_log_lock();
        qemu_log("----------------\n");
        ops->disas_log(db, cpu);
        qemu_log("\n");
        qemu_log_unlock();
    }
#endif
}
+0 −29
Original line number Diff line number Diff line
@@ -35,35 +35,6 @@ typedef abi_ulong tb_page_addr_t;
typedef ram_addr_t tb_page_addr_t;
#endif

/* DisasContext is_jmp field values
 *
 * is_jmp starts as DISAS_NEXT. The translator will keep processing
 * instructions until an exit condition is reached. If we reach the
 * exit condition and is_jmp is still DISAS_NEXT (because of some
 * other condition) we simply "jump" to the next address.
 * The remaining exit cases are:
 *
 *   DISAS_JUMP    - Only the PC was modified dynamically (e.g computed)
 *   DISAS_TB_JUMP - Only the PC was modified statically (e.g. branch)
 *
 * In these cases as long as the PC is updated we can chain to the
 * next TB either by exiting the loop or looking up the next TB via
 * the loookup helper.
 *
 *   DISAS_UPDATE  - CPU State was modified dynamically
 *
 * This covers any other CPU state which necessities us exiting the
 * TCG code to the main run-loop. Typically this includes anything
 * that might change the interrupt state.
 *
 * Individual translators may define additional exit cases to deal
 * with per-target special conditions.
 */
#define DISAS_NEXT    0 /* next instruction can be analyzed */
#define DISAS_JUMP    1 /* only pc was modified dynamically */
#define DISAS_TB_JUMP 2 /* only pc was modified statically */
#define DISAS_UPDATE  3 /* cpu state was modified dynamically */

#include "qemu/log.h"

void gen_intermediate_code(CPUState *cpu, struct TranslationBlock *tb);
+144 −0
Original line number Diff line number Diff line
/*
 * 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 "exec/exec-all.h"
#include "tcg/tcg.h"


/**
 * 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).
 * @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;
    unsigned int num_insns;
    bool singlestep_enabled;
} DisasContextBase;

/**
 * TranslatorOps:
 * @init_disas_context:
 *      Initialize the target-specific portions of DisasContext struct.
 *      The generic DisasContextBase has already been initialized.
 *      Return max_insns, modified as necessary by db->tb->flags.
 *
 * @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.
 *
 * @breakpoint_check:
 *      When called, the breakpoint has already been checked to match the PC,
 *      but the target may decide the breakpoint missed the address
 *      (e.g., due to conditions encoded in their flags).  Return true to
 *      indicate that the breakpoint did hit, in which case no more breakpoints
 *      are checked.  If the breakpoint did hit, emit any code required to
 *      signal the exception, and set db->is_jmp as necessary to terminate
 *      the main loop.
 *
 * @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 {
    int (*init_disas_context)(DisasContextBase *db, CPUState *cpu,
                              int max_insns);
    void (*tb_start)(DisasContextBase *db, CPUState *cpu);
    void (*insn_start)(DisasContextBase *db, CPUState *cpu);
    bool (*breakpoint_check)(DisasContextBase *db, CPUState *cpu,
                             const CPUBreakpoint *bp);
    void (*translate_insn)(DisasContextBase *db, CPUState *cpu);
    void (*tb_stop)(DisasContextBase *db, CPUState *cpu);
    void (*disas_log)(const DisasContextBase *db, CPUState *cpu);
} TranslatorOps;

/**
 * translator_loop:
 * @ops: Target-specific operations.
 * @db: Disassembly context.
 * @cpu: Target vCPU.
 * @tb: Translation block.
 *
 * 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(const TranslatorOps *ops, DisasContextBase *db,
                     CPUState *cpu, TranslationBlock *tb);

void translator_loop_temp_check(DisasContextBase *db);

#endif  /* EXEC__TRANSLATOR_H */
+147 −153
Original line number Diff line number Diff line
@@ -304,7 +304,7 @@ static void gen_exception_internal_insn(DisasContext *s, int offset, int excp)
{
    gen_a64_set_pc_im(s->pc - offset);
    gen_exception_internal(excp);
    s->is_jmp = DISAS_EXC;
    s->base.is_jmp = DISAS_NORETURN;
}

static void gen_exception_insn(DisasContext *s, int offset, int excp,
@@ -312,7 +312,7 @@ static void gen_exception_insn(DisasContext *s, int offset, int excp,
{
    gen_a64_set_pc_im(s->pc - offset);
    gen_exception(excp, syndrome, target_el);
    s->is_jmp = DISAS_EXC;
    s->base.is_jmp = DISAS_NORETURN;
}

static void gen_ss_advance(DisasContext *s)
@@ -340,7 +340,7 @@ static void gen_step_complete_exception(DisasContext *s)
    gen_ss_advance(s);
    gen_exception(EXCP_UDEF, syn_swstep(s->ss_same_el, 1, s->is_ldex),
                  default_exception_el(s));
    s->is_jmp = DISAS_EXC;
    s->base.is_jmp = DISAS_NORETURN;
}

static inline bool use_goto_tb(DisasContext *s, int n, uint64_t dest)
@@ -348,13 +348,13 @@ static inline bool use_goto_tb(DisasContext *s, int n, uint64_t dest)
    /* No direct tb linking with singlestep (either QEMU's or the ARM
     * debug architecture kind) or deterministic io
     */
    if (s->singlestep_enabled || s->ss_active || (s->tb->cflags & CF_LAST_IO)) {
    if (s->base.singlestep_enabled || s->ss_active || (s->base.tb->cflags & CF_LAST_IO)) {
        return false;
    }

#ifndef CONFIG_USER_ONLY
    /* Only link tbs from inside the same guest page */
    if ((s->tb->pc & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) {
    if ((s->base.tb->pc & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) {
        return false;
    }
#endif
@@ -366,21 +366,21 @@ static inline void gen_goto_tb(DisasContext *s, int n, uint64_t dest)
{
    TranslationBlock *tb;

    tb = s->tb;
    tb = s->base.tb;
    if (use_goto_tb(s, n, dest)) {
        tcg_gen_goto_tb(n);
        gen_a64_set_pc_im(dest);
        tcg_gen_exit_tb((intptr_t)tb + n);
        s->is_jmp = DISAS_TB_JUMP;
        s->base.is_jmp = DISAS_NORETURN;
    } else {
        gen_a64_set_pc_im(dest);
        if (s->ss_active) {
            gen_step_complete_exception(s);
        } else if (s->singlestep_enabled) {
        } else if (s->base.singlestep_enabled) {
            gen_exception_internal(EXCP_DEBUG);
        } else {
            tcg_gen_lookup_and_goto_ptr(cpu_pc);
            s->is_jmp = DISAS_TB_JUMP;
            s->base.is_jmp = DISAS_NORETURN;
        }
    }
}
@@ -1331,16 +1331,16 @@ static void handle_hint(DisasContext *s, uint32_t insn,
    case 0: /* NOP */
        return;
    case 3: /* WFI */
        s->is_jmp = DISAS_WFI;
        s->base.is_jmp = DISAS_WFI;
        return;
    case 1: /* YIELD */
        if (!parallel_cpus) {
            s->is_jmp = DISAS_YIELD;
            s->base.is_jmp = DISAS_YIELD;
        }
        return;
    case 2: /* WFE */
        if (!parallel_cpus) {
            s->is_jmp = DISAS_WFE;
            s->base.is_jmp = DISAS_WFE;
        }
        return;
    case 4: /* SEV */
@@ -1424,7 +1424,7 @@ static void handle_msr_i(DisasContext *s, uint32_t insn,
        tcg_temp_free_i32(tcg_op);
        /* For DAIFClear, exit the cpu loop to re-evaluate pending IRQs.  */
        gen_a64_set_pc_im(s->pc);
        s->is_jmp = (op == 0x1f ? DISAS_EXIT : DISAS_JUMP);
        s->base.is_jmp = (op == 0x1f ? DISAS_EXIT : DISAS_JUMP);
        break;
    }
    default:
@@ -1559,7 +1559,7 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread,
        break;
    }

    if ((s->tb->cflags & CF_USE_ICOUNT) && (ri->type & ARM_CP_IO)) {
    if ((s->base.tb->cflags & CF_USE_ICOUNT) && (ri->type & ARM_CP_IO)) {
        gen_io_start();
    }

@@ -1590,16 +1590,16 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread,
        }
    }

    if ((s->tb->cflags & CF_USE_ICOUNT) && (ri->type & ARM_CP_IO)) {
    if ((s->base.tb->cflags & CF_USE_ICOUNT) && (ri->type & ARM_CP_IO)) {
        /* I/O operations must end the TB here (whether read or write) */
        gen_io_end();
        s->is_jmp = DISAS_UPDATE;
        s->base.is_jmp = DISAS_UPDATE;
    } else if (!isread && !(ri->type & ARM_CP_SUPPRESS_TB_END)) {
        /* We default to ending the TB on a coprocessor register write,
         * but allow this to be suppressed by the register definition
         * (usually only necessary to work around guest bugs).
         */
        s->is_jmp = DISAS_UPDATE;
        s->base.is_jmp = DISAS_UPDATE;
    }
}

@@ -1789,7 +1789,7 @@ static void disas_uncond_b_reg(DisasContext *s, uint32_t insn)
        }
        gen_helper_exception_return(cpu_env);
        /* Must exit loop to check un-masked IRQs */
        s->is_jmp = DISAS_EXIT;
        s->base.is_jmp = DISAS_EXIT;
        return;
    case 5: /* DRPS */
        if (rn != 0x1f) {
@@ -1803,7 +1803,7 @@ static void disas_uncond_b_reg(DisasContext *s, uint32_t insn)
        return;
    }

    s->is_jmp = DISAS_JUMP;
    s->base.is_jmp = DISAS_JUMP;
}

/* C3.2 Branches, exception generating and system instructions */
@@ -11200,23 +11200,15 @@ static void disas_a64_insn(CPUARMState *env, DisasContext *s)
    free_tmp_a64(s);
}

void gen_intermediate_code_a64(CPUState *cs, TranslationBlock *tb)
static int aarch64_tr_init_disas_context(DisasContextBase *dcbase,
                                         CPUState *cpu, int max_insns)
{
    CPUARMState *env = cs->env_ptr;
    ARMCPU *cpu = arm_env_get_cpu(env);
    DisasContext dc1, *dc = &dc1;
    target_ulong pc_start;
    target_ulong next_page_start;
    int num_insns;
    int max_insns;
    DisasContext *dc = container_of(dcbase, DisasContext, base);
    CPUARMState *env = cpu->env_ptr;
    ARMCPU *arm_cpu = arm_env_get_cpu(env);
    int bound;

    pc_start = tb->pc;

    dc->tb = tb;

    dc->is_jmp = DISAS_NEXT;
    dc->pc = pc_start;
    dc->singlestep_enabled = cs->singlestep_enabled;
    dc->pc = dc->base.pc_first;
    dc->condjmp = 0;

    dc->aarch64 = 1;
@@ -11227,20 +11219,20 @@ void gen_intermediate_code_a64(CPUState *cs, TranslationBlock *tb)
                               !arm_el_is_aa64(env, 3);
    dc->thumb = 0;
    dc->sctlr_b = 0;
    dc->be_data = ARM_TBFLAG_BE_DATA(tb->flags) ? MO_BE : MO_LE;
    dc->be_data = ARM_TBFLAG_BE_DATA(dc->base.tb->flags) ? MO_BE : MO_LE;
    dc->condexec_mask = 0;
    dc->condexec_cond = 0;
    dc->mmu_idx = core_to_arm_mmu_idx(env, ARM_TBFLAG_MMUIDX(tb->flags));
    dc->tbi0 = ARM_TBFLAG_TBI0(tb->flags);
    dc->tbi1 = ARM_TBFLAG_TBI1(tb->flags);
    dc->mmu_idx = core_to_arm_mmu_idx(env, ARM_TBFLAG_MMUIDX(dc->base.tb->flags));
    dc->tbi0 = ARM_TBFLAG_TBI0(dc->base.tb->flags);
    dc->tbi1 = ARM_TBFLAG_TBI1(dc->base.tb->flags);
    dc->current_el = arm_mmu_idx_to_el(dc->mmu_idx);
#if !defined(CONFIG_USER_ONLY)
    dc->user = (dc->current_el == 0);
#endif
    dc->fp_excp_el = ARM_TBFLAG_FPEXC_EL(tb->flags);
    dc->fp_excp_el = ARM_TBFLAG_FPEXC_EL(dc->base.tb->flags);
    dc->vec_len = 0;
    dc->vec_stride = 0;
    dc->cp_regs = cpu->cp_regs;
    dc->cp_regs = arm_cpu->cp_regs;
    dc->features = env->features;

    /* Single step state. The code-generation logic here is:
@@ -11258,41 +11250,48 @@ void gen_intermediate_code_a64(CPUState *cs, TranslationBlock *tb)
     *   emit code to generate a software step exception
     *   end the TB
     */
    dc->ss_active = ARM_TBFLAG_SS_ACTIVE(tb->flags);
    dc->pstate_ss = ARM_TBFLAG_PSTATE_SS(tb->flags);
    dc->ss_active = ARM_TBFLAG_SS_ACTIVE(dc->base.tb->flags);
    dc->pstate_ss = ARM_TBFLAG_PSTATE_SS(dc->base.tb->flags);
    dc->is_ldex = false;
    dc->ss_same_el = (arm_debug_target_el(env) == dc->current_el);

    init_tmp_a64_array(dc);
    /* Bound the number of insns to execute to those left on the page.  */
    bound = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4;

    next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
    num_insns = 0;
    max_insns = tb->cflags & CF_COUNT_MASK;
    if (max_insns == 0) {
        max_insns = CF_COUNT_MASK;
    }
    if (max_insns > TCG_MAX_INSNS) {
        max_insns = TCG_MAX_INSNS;
    /* If architectural single step active, limit to 1.  */
    if (dc->ss_active) {
        bound = 1;
    }
    max_insns = MIN(max_insns, bound);

    gen_tb_start(tb);
    init_tmp_a64_array(dc);

    return max_insns;
}

static void aarch64_tr_tb_start(DisasContextBase *db, CPUState *cpu)
{
    tcg_clear_temp_count();
}

static void aarch64_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu)
{
    DisasContext *dc = container_of(dcbase, DisasContext, base);

    do {
    dc->insn_start_idx = tcg_op_buf_count();
    tcg_gen_insn_start(dc->pc, 0, 0);
        num_insns++;
}

static bool aarch64_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cpu,
                                        const CPUBreakpoint *bp)
{
    DisasContext *dc = container_of(dcbase, DisasContext, base);

        if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) {
            CPUBreakpoint *bp;
            QTAILQ_FOREACH(bp, &cs->breakpoints, entry) {
                if (bp->pc == dc->pc) {
    if (bp->flags & BP_CPU) {
        gen_a64_set_pc_im(dc->pc);
        gen_helper_check_breakpoints(cpu_env);
        /* End the TB early; it likely won't be executed */
                        dc->is_jmp = DISAS_UPDATE;
        dc->base.is_jmp = DISAS_TOO_MANY;
    } else {
        gen_exception_internal_insn(dc, 0, EXCP_DEBUG);
        /* The address covered by the breakpoint must be
@@ -11301,17 +11300,17 @@ void gen_intermediate_code_a64(CPUState *cs, TranslationBlock *tb)
           increment the PC here so that the logic setting
           tb->size below does the right thing.  */
        dc->pc += 4;
                        goto done_generating;
                    }
                    break;
                }
            }
        dc->base.is_jmp = DISAS_NORETURN;
    }

        if (num_insns == max_insns && (tb->cflags & CF_LAST_IO)) {
            gen_io_start();
    return true;
}

static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu)
{
    DisasContext *dc = container_of(dcbase, DisasContext, base);
    CPUARMState *env = cpu->env_ptr;

    if (dc->ss_active && !dc->pstate_ss) {
        /* Singlestep state is Active-pending.
         * If we're in this state at the start of a TB then either
@@ -11323,62 +11322,59 @@ void gen_intermediate_code_a64(CPUState *cs, TranslationBlock *tb)
         * "did not step an insn" case, and so the syndrome ISV and EX
         * bits should be zero.
         */
            assert(num_insns == 1);
        assert(dc->base.num_insns == 1);
        gen_exception(EXCP_UDEF, syn_swstep(dc->ss_same_el, 0, 0),
                      default_exception_el(dc));
            dc->is_jmp = DISAS_EXC;
            break;
        }

        dc->base.is_jmp = DISAS_NORETURN;
    } else {
        disas_a64_insn(env, dc);

        if (tcg_check_temp_count()) {
            fprintf(stderr, "TCG temporary leak before "TARGET_FMT_lx"\n",
                    dc->pc);
    }

        /* Translation stops when a conditional branch is encountered.
         * Otherwise the subsequent code could get translated several times.
         * Also stop translation when a page boundary is reached.  This
         * ensures prefetch aborts occur at the right place.
         */
    } while (!dc->is_jmp && !tcg_op_buf_full() &&
             !cs->singlestep_enabled &&
             !singlestep &&
             !dc->ss_active &&
             dc->pc < next_page_start &&
             num_insns < max_insns);

    if (tb->cflags & CF_LAST_IO) {
        gen_io_end();
    dc->base.pc_next = dc->pc;
    translator_loop_temp_check(&dc->base);
}

    if (unlikely(cs->singlestep_enabled || dc->ss_active)
        && dc->is_jmp != DISAS_EXC) {
static void aarch64_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu)
{
    DisasContext *dc = container_of(dcbase, DisasContext, base);

    if (unlikely(dc->base.singlestep_enabled || dc->ss_active)) {
        /* Note that this means single stepping WFI doesn't halt the CPU.
         * For conditional branch insns this is harmless unreachable code as
         * gen_goto_tb() has already handled emitting the debug exception
         * (and thus a tb-jump is not possible when singlestepping).
         */
        assert(dc->is_jmp != DISAS_TB_JUMP);
        if (dc->is_jmp != DISAS_JUMP) {
        switch (dc->base.is_jmp) {
        default:
            gen_a64_set_pc_im(dc->pc);
        }
        if (cs->singlestep_enabled) {
            /* fall through */
        case DISAS_JUMP:
            if (dc->base.singlestep_enabled) {
                gen_exception_internal(EXCP_DEBUG);
            } else {
                gen_step_complete_exception(dc);
            }
            break;
        case DISAS_NORETURN:
            break;
        }
    } else {
        switch (dc->is_jmp) {
        switch (dc->base.is_jmp) {
        case DISAS_NEXT:
        case DISAS_TOO_MANY:
            gen_goto_tb(dc, 1, dc->pc);
            break;
        default:
        case DISAS_UPDATE:
            gen_a64_set_pc_im(dc->pc);
            /* fall through */
        case DISAS_JUMP:
            tcg_gen_lookup_and_goto_ptr(cpu_pc);
            break;
        case DISAS_TB_JUMP:
        case DISAS_EXC:
        case DISAS_EXIT:
            tcg_gen_exit_tb(0);
            break;
        case DISAS_NORETURN:
        case DISAS_SWI:
            break;
        case DISAS_WFE:
@@ -11400,31 +11396,29 @@ void gen_intermediate_code_a64(CPUState *cs, TranslationBlock *tb)
             */
            tcg_gen_exit_tb(0);
            break;
        case DISAS_UPDATE:
            gen_a64_set_pc_im(dc->pc);
            /* fall through */
        case DISAS_EXIT:
        default:
            tcg_gen_exit_tb(0);
            break;
        }
    }

done_generating:
    gen_tb_end(tb, num_insns);
    /* Functions above can change dc->pc, so re-align db->pc_next */
    dc->base.pc_next = dc->pc;
}

static void aarch64_tr_disas_log(const DisasContextBase *dcbase,
                                      CPUState *cpu)
{
    DisasContext *dc = container_of(dcbase, DisasContext, base);

#ifdef DEBUG_DISAS
    if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) &&
        qemu_log_in_addr_range(pc_start)) {
        qemu_log_lock();
        qemu_log("----------------\n");
        qemu_log("IN: %s\n", lookup_symbol(pc_start));
        log_target_disas(cs, pc_start, dc->pc - pc_start,
    qemu_log("IN: %s\n", lookup_symbol(dc->base.pc_first));
    log_target_disas(cpu, dc->base.pc_first, dc->base.tb->size,
                     4 | (bswap_code(dc->sctlr_b) ? 2 : 0));
        qemu_log("\n");
        qemu_log_unlock();
    }
#endif
    tb->size = dc->pc - pc_start;
    tb->icount = num_insns;
}

const TranslatorOps aarch64_translator_ops = {
    .init_disas_context = aarch64_tr_init_disas_context,
    .tb_start           = aarch64_tr_tb_start,
    .insn_start         = aarch64_tr_insn_start,
    .breakpoint_check   = aarch64_tr_breakpoint_check,
    .translate_insn     = aarch64_tr_translate_insn,
    .tb_stop            = aarch64_tr_tb_stop,
    .disas_log          = aarch64_tr_disas_log,
};
Loading