mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge tag 'pull-tcg-20230105' of https://gitlab.com/rth7680/qemu into staging
Fix race conditions in new user-only vma tracking. Add tcg backend paired register allocation. Cleanup tcg backend function call abi. # gpg: Signature made Fri 06 Jan 2023 03:12:17 GMT # gpg: using RSA key 7A481E78868B4DB6A85A05C064DF38E8AF7E215F # gpg: issuer "richard.henderson@linaro.org" # gpg: Good signature from "Richard Henderson <richard.henderson@linaro.org>" [full] # Primary key fingerprint: 7A48 1E78 868B 4DB6 A85A 05C0 64DF 38E8 AF7E 215F * tag 'pull-tcg-20230105' of https://gitlab.com/rth7680/qemu: (47 commits) tests/tcg/multiarch: add vma-pthread.c accel/tcg: Handle false negative lookup in page_check_range accel/tcg: Use g_free_rcu for user-exec interval trees accel/tcg: Fix tb_invalidate_phys_page_unwind tcg: Add TCGHelperInfo argument to tcg_out_call tcg/aarch64: Merge tcg_out_callr into tcg_out_call tcg: Move ffi_cif pointer into TCGHelperInfo tcg: Factor init_ffi_layouts() out of tcg_context_init() tcg: Convert typecode_to_ffi from array to function tcg: Reorg function calls tcg: Use output_pref wrapper function tcg: Vary the allocation size for TCGOp tcg: Pass number of arguments to tcg_emit_op() / tcg_op_insert_*() accel/tcg/plugin: Use copy_op in append_{udata,mem}_cb accel/tcg/plugin: Avoid duplicate copy in copy_call accel/tcg/plugin: Don't search for the function pointer index tcg: Use TCG_CALL_ARG_EVEN for TCI special case tcg: Replace TCG_TARGET_EXTEND_ARGS with TCG_TARGET_CALL_ARG_I32 tcg: Replace TCG_TARGET_CALL_ALIGN_ARGS with TCG_TARGET_CALL_ARG_I64 tcg: Introduce TCGCallReturnKind and TCGCallArgumentKind ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
+8
-17
@@ -1356,7 +1356,6 @@ static uint64_t io_readx(CPUArchState *env, CPUTLBEntryFull *full,
|
||||
MemoryRegionSection *section;
|
||||
MemoryRegion *mr;
|
||||
uint64_t val;
|
||||
bool locked = false;
|
||||
MemTxResult r;
|
||||
|
||||
section = iotlb_to_section(cpu, full->xlat_section, full->attrs);
|
||||
@@ -1367,11 +1366,11 @@ static uint64_t io_readx(CPUArchState *env, CPUTLBEntryFull *full,
|
||||
cpu_io_recompile(cpu, retaddr);
|
||||
}
|
||||
|
||||
if (!qemu_mutex_iothread_locked()) {
|
||||
qemu_mutex_lock_iothread();
|
||||
locked = true;
|
||||
{
|
||||
QEMU_IOTHREAD_LOCK_GUARD();
|
||||
r = memory_region_dispatch_read(mr, mr_offset, &val, op, full->attrs);
|
||||
}
|
||||
r = memory_region_dispatch_read(mr, mr_offset, &val, op, full->attrs);
|
||||
|
||||
if (r != MEMTX_OK) {
|
||||
hwaddr physaddr = mr_offset +
|
||||
section->offset_within_address_space -
|
||||
@@ -1380,10 +1379,6 @@ static uint64_t io_readx(CPUArchState *env, CPUTLBEntryFull *full,
|
||||
cpu_transaction_failed(cpu, physaddr, addr, memop_size(op), access_type,
|
||||
mmu_idx, full->attrs, r, retaddr);
|
||||
}
|
||||
if (locked) {
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
@@ -1410,7 +1405,6 @@ static void io_writex(CPUArchState *env, CPUTLBEntryFull *full,
|
||||
hwaddr mr_offset;
|
||||
MemoryRegionSection *section;
|
||||
MemoryRegion *mr;
|
||||
bool locked = false;
|
||||
MemTxResult r;
|
||||
|
||||
section = iotlb_to_section(cpu, full->xlat_section, full->attrs);
|
||||
@@ -1427,11 +1421,11 @@ static void io_writex(CPUArchState *env, CPUTLBEntryFull *full,
|
||||
*/
|
||||
save_iotlb_data(cpu, section, mr_offset);
|
||||
|
||||
if (!qemu_mutex_iothread_locked()) {
|
||||
qemu_mutex_lock_iothread();
|
||||
locked = true;
|
||||
{
|
||||
QEMU_IOTHREAD_LOCK_GUARD();
|
||||
r = memory_region_dispatch_write(mr, mr_offset, val, op, full->attrs);
|
||||
}
|
||||
r = memory_region_dispatch_write(mr, mr_offset, val, op, full->attrs);
|
||||
|
||||
if (r != MEMTX_OK) {
|
||||
hwaddr physaddr = mr_offset +
|
||||
section->offset_within_address_space -
|
||||
@@ -1441,9 +1435,6 @@ static void io_writex(CPUArchState *env, CPUTLBEntryFull *full,
|
||||
MMU_DATA_STORE, mmu_idx, full->attrs, r,
|
||||
retaddr);
|
||||
}
|
||||
if (locked) {
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
}
|
||||
|
||||
static inline target_ulong tlb_read_ofs(CPUTLBEntry *entry, size_t ofs)
|
||||
|
||||
+24
-30
@@ -258,10 +258,13 @@ static TCGOp *rm_ops(TCGOp *op)
|
||||
|
||||
static TCGOp *copy_op_nocheck(TCGOp **begin_op, TCGOp *op)
|
||||
{
|
||||
*begin_op = QTAILQ_NEXT(*begin_op, link);
|
||||
tcg_debug_assert(*begin_op);
|
||||
op = tcg_op_insert_after(tcg_ctx, op, (*begin_op)->opc);
|
||||
memcpy(op->args, (*begin_op)->args, sizeof(op->args));
|
||||
TCGOp *old_op = QTAILQ_NEXT(*begin_op, link);
|
||||
unsigned nargs = old_op->nargs;
|
||||
|
||||
*begin_op = old_op;
|
||||
op = tcg_op_insert_after(tcg_ctx, op, old_op->opc, nargs);
|
||||
memcpy(op->args, old_op->args, sizeof(op->args[0]) * nargs);
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
@@ -381,32 +384,23 @@ static TCGOp *copy_st_ptr(TCGOp **begin_op, TCGOp *op)
|
||||
static TCGOp *copy_call(TCGOp **begin_op, TCGOp *op, void *empty_func,
|
||||
void *func, int *cb_idx)
|
||||
{
|
||||
TCGOp *old_op;
|
||||
int func_idx;
|
||||
|
||||
/* copy all ops until the call */
|
||||
do {
|
||||
op = copy_op_nocheck(begin_op, op);
|
||||
} while (op->opc != INDEX_op_call);
|
||||
|
||||
/* fill in the op call */
|
||||
op->param1 = (*begin_op)->param1;
|
||||
op->param2 = (*begin_op)->param2;
|
||||
old_op = *begin_op;
|
||||
TCGOP_CALLI(op) = TCGOP_CALLI(old_op);
|
||||
TCGOP_CALLO(op) = TCGOP_CALLO(old_op);
|
||||
tcg_debug_assert(op->life == 0);
|
||||
if (*cb_idx == -1) {
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Instead of working out the position of the callback in args[], just
|
||||
* look for @empty_func, since it should be a unique pointer.
|
||||
*/
|
||||
for (i = 0; i < MAX_OPC_PARAM_ARGS; i++) {
|
||||
if ((uintptr_t)(*begin_op)->args[i] == (uintptr_t)empty_func) {
|
||||
*cb_idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
tcg_debug_assert(i < MAX_OPC_PARAM_ARGS);
|
||||
}
|
||||
op->args[*cb_idx] = (uintptr_t)func;
|
||||
op->args[*cb_idx + 1] = (*begin_op)->args[*cb_idx + 1];
|
||||
func_idx = TCGOP_CALLO(op) + TCGOP_CALLI(op);
|
||||
*cb_idx = func_idx;
|
||||
op->args[func_idx] = (uintptr_t)func;
|
||||
|
||||
return op;
|
||||
}
|
||||
@@ -424,11 +418,11 @@ static TCGOp *append_udata_cb(const struct qemu_plugin_dyn_cb *cb,
|
||||
op = copy_const_ptr(&begin_op, op, cb->userp);
|
||||
|
||||
/* copy the ld_i32, but note that we only have to copy it once */
|
||||
begin_op = QTAILQ_NEXT(begin_op, link);
|
||||
tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32);
|
||||
if (*cb_idx == -1) {
|
||||
op = tcg_op_insert_after(tcg_ctx, op, INDEX_op_ld_i32);
|
||||
memcpy(op->args, begin_op->args, sizeof(op->args));
|
||||
op = copy_op(&begin_op, op, INDEX_op_ld_i32);
|
||||
} else {
|
||||
begin_op = QTAILQ_NEXT(begin_op, link);
|
||||
tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32);
|
||||
}
|
||||
|
||||
/* call */
|
||||
@@ -471,11 +465,11 @@ static TCGOp *append_mem_cb(const struct qemu_plugin_dyn_cb *cb,
|
||||
op = copy_const_ptr(&begin_op, op, cb->userp);
|
||||
|
||||
/* copy the ld_i32, but note that we only have to copy it once */
|
||||
begin_op = QTAILQ_NEXT(begin_op, link);
|
||||
tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32);
|
||||
if (*cb_idx == -1) {
|
||||
op = tcg_op_insert_after(tcg_ctx, op, INDEX_op_ld_i32);
|
||||
memcpy(op->args, begin_op->args, sizeof(op->args));
|
||||
op = copy_op(&begin_op, op, INDEX_op_ld_i32);
|
||||
} else {
|
||||
begin_op = QTAILQ_NEXT(begin_op, link);
|
||||
tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32);
|
||||
}
|
||||
|
||||
/* extu_tl_i64 */
|
||||
|
||||
+40
-32
@@ -1024,43 +1024,51 @@ void tb_invalidate_phys_page(tb_page_addr_t addr)
|
||||
*/
|
||||
bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc)
|
||||
{
|
||||
assert(pc != 0);
|
||||
#ifdef TARGET_HAS_PRECISE_SMC
|
||||
assert_memory_lock();
|
||||
{
|
||||
TranslationBlock *current_tb = tcg_tb_lookup(pc);
|
||||
bool current_tb_modified = false;
|
||||
TranslationBlock *tb;
|
||||
PageForEachNext n;
|
||||
TranslationBlock *current_tb;
|
||||
bool current_tb_modified;
|
||||
TranslationBlock *tb;
|
||||
PageForEachNext n;
|
||||
|
||||
addr &= TARGET_PAGE_MASK;
|
||||
/*
|
||||
* Without precise smc semantics, or when outside of a TB,
|
||||
* we can skip to invalidate.
|
||||
*/
|
||||
#ifndef TARGET_HAS_PRECISE_SMC
|
||||
pc = 0;
|
||||
#endif
|
||||
if (!pc) {
|
||||
tb_invalidate_phys_page(addr);
|
||||
return false;
|
||||
}
|
||||
|
||||
PAGE_FOR_EACH_TB(addr, addr + TARGET_PAGE_SIZE, unused, tb, n) {
|
||||
if (current_tb == tb &&
|
||||
(tb_cflags(current_tb) & CF_COUNT_MASK) != 1) {
|
||||
/*
|
||||
* If we are modifying the current TB, we must stop its
|
||||
* execution. We could be more precise by checking that
|
||||
* the modification is after the current PC, but it would
|
||||
* require a specialized function to partially restore
|
||||
* the CPU state.
|
||||
*/
|
||||
current_tb_modified = true;
|
||||
cpu_restore_state_from_tb(current_cpu, current_tb, pc);
|
||||
}
|
||||
tb_phys_invalidate__locked(tb);
|
||||
assert_memory_lock();
|
||||
current_tb = tcg_tb_lookup(pc);
|
||||
|
||||
addr &= TARGET_PAGE_MASK;
|
||||
current_tb_modified = false;
|
||||
|
||||
PAGE_FOR_EACH_TB(addr, addr + TARGET_PAGE_SIZE, unused, tb, n) {
|
||||
if (current_tb == tb &&
|
||||
(tb_cflags(current_tb) & CF_COUNT_MASK) != 1) {
|
||||
/*
|
||||
* If we are modifying the current TB, we must stop its
|
||||
* execution. We could be more precise by checking that
|
||||
* the modification is after the current PC, but it would
|
||||
* require a specialized function to partially restore
|
||||
* the CPU state.
|
||||
*/
|
||||
current_tb_modified = true;
|
||||
cpu_restore_state_from_tb(current_cpu, current_tb, pc);
|
||||
}
|
||||
tb_phys_invalidate__locked(tb);
|
||||
}
|
||||
|
||||
if (current_tb_modified) {
|
||||
/* Force execution of one insn next time. */
|
||||
CPUState *cpu = current_cpu;
|
||||
cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(current_cpu);
|
||||
return true;
|
||||
}
|
||||
if (current_tb_modified) {
|
||||
/* Force execution of one insn next time. */
|
||||
CPUState *cpu = current_cpu;
|
||||
cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(current_cpu);
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
tb_invalidate_phys_page(addr);
|
||||
#endif /* TARGET_HAS_PRECISE_SMC */
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
|
||||
+44
-15
@@ -22,6 +22,7 @@
|
||||
#include "exec/exec-all.h"
|
||||
#include "tcg/tcg.h"
|
||||
#include "qemu/bitops.h"
|
||||
#include "qemu/rcu.h"
|
||||
#include "exec/cpu_ldst.h"
|
||||
#include "exec/translate-all.h"
|
||||
#include "exec/helper-proto.h"
|
||||
@@ -136,6 +137,7 @@ bool handle_sigsegv_accerr_write(CPUState *cpu, sigset_t *old_set,
|
||||
}
|
||||
|
||||
typedef struct PageFlagsNode {
|
||||
struct rcu_head rcu;
|
||||
IntervalTreeNode itree;
|
||||
int flags;
|
||||
} PageFlagsNode;
|
||||
@@ -266,7 +268,7 @@ static bool pageflags_unset(target_ulong start, target_ulong last)
|
||||
}
|
||||
} else if (p_last <= last) {
|
||||
/* Range completely covers node -- remove it. */
|
||||
g_free(p);
|
||||
g_free_rcu(p, rcu);
|
||||
} else {
|
||||
/* Truncate the node from the start. */
|
||||
p->itree.start = last + 1;
|
||||
@@ -311,7 +313,7 @@ static void pageflags_create_merge(target_ulong start, target_ulong last,
|
||||
if (prev) {
|
||||
if (next) {
|
||||
prev->itree.last = next->itree.last;
|
||||
g_free(next);
|
||||
g_free_rcu(next, rcu);
|
||||
} else {
|
||||
prev->itree.last = last;
|
||||
}
|
||||
@@ -376,7 +378,7 @@ static bool pageflags_set_clear(target_ulong start, target_ulong last,
|
||||
p->flags = merge_flags;
|
||||
} else {
|
||||
interval_tree_remove(&p->itree, &pageflags_root);
|
||||
g_free(p);
|
||||
g_free_rcu(p, rcu);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
@@ -421,7 +423,7 @@ static bool pageflags_set_clear(target_ulong start, target_ulong last,
|
||||
p->flags = merge_flags;
|
||||
} else {
|
||||
interval_tree_remove(&p->itree, &pageflags_root);
|
||||
g_free(p);
|
||||
g_free_rcu(p, rcu);
|
||||
}
|
||||
if (p_last < last) {
|
||||
start = p_last + 1;
|
||||
@@ -462,7 +464,7 @@ static bool pageflags_set_clear(target_ulong start, target_ulong last,
|
||||
p->itree.start = last + 1;
|
||||
interval_tree_insert(&p->itree, &pageflags_root);
|
||||
} else {
|
||||
g_free(p);
|
||||
g_free_rcu(p, rcu);
|
||||
goto restart;
|
||||
}
|
||||
if (set_flags) {
|
||||
@@ -523,6 +525,8 @@ void page_set_flags(target_ulong start, target_ulong end, int flags)
|
||||
int page_check_range(target_ulong start, target_ulong len, int flags)
|
||||
{
|
||||
target_ulong last;
|
||||
int locked; /* tri-state: =0: unlocked, +1: global, -1: local */
|
||||
int ret;
|
||||
|
||||
if (len == 0) {
|
||||
return 0; /* trivial length */
|
||||
@@ -533,42 +537,67 @@ int page_check_range(target_ulong start, target_ulong len, int flags)
|
||||
return -1; /* wrap around */
|
||||
}
|
||||
|
||||
locked = have_mmap_lock();
|
||||
while (true) {
|
||||
PageFlagsNode *p = pageflags_find(start, last);
|
||||
int missing;
|
||||
|
||||
if (!p) {
|
||||
return -1; /* entire region invalid */
|
||||
if (!locked) {
|
||||
/*
|
||||
* Lockless lookups have false negatives.
|
||||
* Retry with the lock held.
|
||||
*/
|
||||
mmap_lock();
|
||||
locked = -1;
|
||||
p = pageflags_find(start, last);
|
||||
}
|
||||
if (!p) {
|
||||
ret = -1; /* entire region invalid */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (start < p->itree.start) {
|
||||
return -1; /* initial bytes invalid */
|
||||
ret = -1; /* initial bytes invalid */
|
||||
break;
|
||||
}
|
||||
|
||||
missing = flags & ~p->flags;
|
||||
if (missing & PAGE_READ) {
|
||||
return -1; /* page not readable */
|
||||
ret = -1; /* page not readable */
|
||||
break;
|
||||
}
|
||||
if (missing & PAGE_WRITE) {
|
||||
if (!(p->flags & PAGE_WRITE_ORG)) {
|
||||
return -1; /* page not writable */
|
||||
ret = -1; /* page not writable */
|
||||
break;
|
||||
}
|
||||
/* Asking about writable, but has been protected: undo. */
|
||||
if (!page_unprotect(start, 0)) {
|
||||
return -1;
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
/* TODO: page_unprotect should take a range, not a single page. */
|
||||
if (last - start < TARGET_PAGE_SIZE) {
|
||||
return 0; /* ok */
|
||||
ret = 0; /* ok */
|
||||
break;
|
||||
}
|
||||
start += TARGET_PAGE_SIZE;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (last <= p->itree.last) {
|
||||
return 0; /* ok */
|
||||
ret = 0; /* ok */
|
||||
break;
|
||||
}
|
||||
start = p->itree.last + 1;
|
||||
}
|
||||
|
||||
/* Release the lock if acquired locally. */
|
||||
if (locked < 0) {
|
||||
mmap_unlock();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void page_protect(tb_page_addr_t address)
|
||||
@@ -779,6 +808,7 @@ tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, target_ulong addr,
|
||||
#define TBD_MASK (TARGET_PAGE_MASK * TPD_PAGES)
|
||||
|
||||
typedef struct TargetPageDataNode {
|
||||
struct rcu_head rcu;
|
||||
IntervalTreeNode itree;
|
||||
char data[TPD_PAGES][TARGET_PAGE_DATA_SIZE] __attribute__((aligned));
|
||||
} TargetPageDataNode;
|
||||
@@ -801,11 +831,11 @@ void page_reset_target_data(target_ulong start, target_ulong end)
|
||||
n = next,
|
||||
next = next ? interval_tree_iter_next(n, start, last) : NULL) {
|
||||
target_ulong n_start, n_last, p_ofs, p_len;
|
||||
TargetPageDataNode *t;
|
||||
TargetPageDataNode *t = container_of(n, TargetPageDataNode, itree);
|
||||
|
||||
if (n->start >= start && n->last <= last) {
|
||||
interval_tree_remove(n, &targetdata_root);
|
||||
g_free(n);
|
||||
g_free_rcu(t, rcu);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -819,7 +849,6 @@ void page_reset_target_data(target_ulong start, target_ulong end)
|
||||
n_last = MIN(last, n->last);
|
||||
p_len = (n_last + 1 - n_start) >> TARGET_PAGE_BITS;
|
||||
|
||||
t = container_of(n, TargetPageDataNode, itree);
|
||||
memset(t->data[p_ofs], 0, p_len * TARGET_PAGE_DATA_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
.. _atomics-ref:
|
||||
|
||||
=========================
|
||||
Atomic operations in QEMU
|
||||
=========================
|
||||
|
||||
@@ -9,6 +9,7 @@ are only implementing things for HW accelerated hypervisors.
|
||||
:maxdepth: 2
|
||||
|
||||
tcg
|
||||
tcg-ops
|
||||
decodetree
|
||||
multi-thread-tcg
|
||||
tcg-icount
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -9,7 +9,7 @@ which make it relatively easily portable and simple while achieving good
|
||||
performances.
|
||||
|
||||
QEMU's dynamic translation backend is called TCG, for "Tiny Code
|
||||
Generator". For more information, please take a look at ``tcg/README``.
|
||||
Generator". For more information, please take a look at :ref:`tcg-ops-ref`.
|
||||
|
||||
The following sections outline some notable features and implementation
|
||||
details of QEMU's dynamic translator.
|
||||
|
||||
@@ -235,6 +235,7 @@ static void cpu_common_initfn(Object *obj)
|
||||
/* the default value is changed by qemu_init_vcpu() for softmmu */
|
||||
cpu->nr_cores = 1;
|
||||
cpu->nr_threads = 1;
|
||||
cpu->cflags_next_tb = -1;
|
||||
|
||||
qemu_mutex_init(&cpu->work_mutex);
|
||||
QSIMPLEQ_INIT(&cpu->work_list);
|
||||
|
||||
+1
-10
@@ -32,17 +32,12 @@ static void cpu_mips_irq_request(void *opaque, int irq, int level)
|
||||
MIPSCPU *cpu = opaque;
|
||||
CPUMIPSState *env = &cpu->env;
|
||||
CPUState *cs = CPU(cpu);
|
||||
bool locked = false;
|
||||
|
||||
if (irq < 0 || irq > 7) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Make sure locking works even if BQL is already held by the caller */
|
||||
if (!qemu_mutex_iothread_locked()) {
|
||||
locked = true;
|
||||
qemu_mutex_lock_iothread();
|
||||
}
|
||||
QEMU_IOTHREAD_LOCK_GUARD();
|
||||
|
||||
if (level) {
|
||||
env->CP0_Cause |= 1 << (irq + CP0Ca_IP);
|
||||
@@ -59,10 +54,6 @@ static void cpu_mips_irq_request(void *opaque, int irq, int level)
|
||||
} else {
|
||||
cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
|
||||
}
|
||||
|
||||
if (locked) {
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
}
|
||||
|
||||
void cpu_mips_irq_init_cpu(MIPSCPU *cpu)
|
||||
|
||||
+1
-9
@@ -44,13 +44,9 @@ void ppc_set_irq(PowerPCCPU *cpu, int irq, int level)
|
||||
{
|
||||
CPUPPCState *env = &cpu->env;
|
||||
unsigned int old_pending;
|
||||
bool locked = false;
|
||||
|
||||
/* We may already have the BQL if coming from the reset path */
|
||||
if (!qemu_mutex_iothread_locked()) {
|
||||
locked = true;
|
||||
qemu_mutex_lock_iothread();
|
||||
}
|
||||
QEMU_IOTHREAD_LOCK_GUARD();
|
||||
|
||||
old_pending = env->pending_interrupts;
|
||||
|
||||
@@ -67,10 +63,6 @@ void ppc_set_irq(PowerPCCPU *cpu, int irq, int level)
|
||||
|
||||
trace_ppc_irq_set_exit(env, irq, level, env->pending_interrupts,
|
||||
CPU(cpu)->interrupt_request);
|
||||
|
||||
if (locked) {
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
}
|
||||
|
||||
/* PowerPC 6xx / 7xx internal IRQ controller */
|
||||
|
||||
@@ -133,6 +133,6 @@
|
||||
#define DEF_HELPER_7(name, ret, t1, t2, t3, t4, t5, t6, t7) \
|
||||
DEF_HELPER_FLAGS_7(name, 0, ret, t1, t2, t3, t4, t5, t6, t7)
|
||||
|
||||
/* MAX_OPC_PARAM_IARGS must be set to n if last entry is DEF_HELPER_FLAGS_n. */
|
||||
/* MAX_CALL_IARGS must be set to n if last entry is DEF_HELPER_FLAGS_n. */
|
||||
|
||||
#endif /* EXEC_HELPER_HEAD_H */
|
||||
|
||||
@@ -343,6 +343,35 @@ void qemu_mutex_lock_iothread_impl(const char *file, int line);
|
||||
*/
|
||||
void qemu_mutex_unlock_iothread(void);
|
||||
|
||||
/**
|
||||
* QEMU_IOTHREAD_LOCK_GUARD
|
||||
*
|
||||
* Wrap a block of code in a conditional qemu_mutex_{lock,unlock}_iothread.
|
||||
*/
|
||||
typedef struct IOThreadLockAuto IOThreadLockAuto;
|
||||
|
||||
static inline IOThreadLockAuto *qemu_iothread_auto_lock(const char *file,
|
||||
int line)
|
||||
{
|
||||
if (qemu_mutex_iothread_locked()) {
|
||||
return NULL;
|
||||
}
|
||||
qemu_mutex_lock_iothread_impl(file, line);
|
||||
/* Anything non-NULL causes the cleanup function to be called */
|
||||
return (IOThreadLockAuto *)(uintptr_t)1;
|
||||
}
|
||||
|
||||
static inline void qemu_iothread_auto_unlock(IOThreadLockAuto *l)
|
||||
{
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(IOThreadLockAuto, qemu_iothread_auto_unlock)
|
||||
|
||||
#define QEMU_IOTHREAD_LOCK_GUARD() \
|
||||
g_autoptr(IOThreadLockAuto) _iothread_lock_auto __attribute__((unused)) \
|
||||
= qemu_iothread_auto_lock(__FILE__, __LINE__)
|
||||
|
||||
/*
|
||||
* qemu_cond_wait_iothread: Wait on condition for the main loop mutex
|
||||
*
|
||||
|
||||
+6
-29
@@ -667,35 +667,12 @@ static inline void tcg_gen_mul_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
|
||||
tcg_gen_op3_i64(INDEX_op_mul_i64, ret, arg1, arg2);
|
||||
}
|
||||
#else /* TCG_TARGET_REG_BITS == 32 */
|
||||
static inline void tcg_gen_st8_i64(TCGv_i64 arg1, TCGv_ptr arg2,
|
||||
tcg_target_long offset)
|
||||
{
|
||||
tcg_gen_st8_i32(TCGV_LOW(arg1), arg2, offset);
|
||||
}
|
||||
void tcg_gen_st8_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset);
|
||||
void tcg_gen_st16_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset);
|
||||
void tcg_gen_st32_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset);
|
||||
|
||||
static inline void tcg_gen_st16_i64(TCGv_i64 arg1, TCGv_ptr arg2,
|
||||
tcg_target_long offset)
|
||||
{
|
||||
tcg_gen_st16_i32(TCGV_LOW(arg1), arg2, offset);
|
||||
}
|
||||
|
||||
static inline void tcg_gen_st32_i64(TCGv_i64 arg1, TCGv_ptr arg2,
|
||||
tcg_target_long offset)
|
||||
{
|
||||
tcg_gen_st_i32(TCGV_LOW(arg1), arg2, offset);
|
||||
}
|
||||
|
||||
static inline void tcg_gen_add_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
|
||||
{
|
||||
tcg_gen_add2_i32(TCGV_LOW(ret), TCGV_HIGH(ret), TCGV_LOW(arg1),
|
||||
TCGV_HIGH(arg1), TCGV_LOW(arg2), TCGV_HIGH(arg2));
|
||||
}
|
||||
|
||||
static inline void tcg_gen_sub_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2)
|
||||
{
|
||||
tcg_gen_sub2_i32(TCGV_LOW(ret), TCGV_HIGH(ret), TCGV_LOW(arg1),
|
||||
TCGV_HIGH(arg1), TCGV_LOW(arg2), TCGV_HIGH(arg2));
|
||||
}
|
||||
void tcg_gen_add_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2);
|
||||
void tcg_gen_sub_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2);
|
||||
|
||||
void tcg_gen_discard_i64(TCGv_i64 arg);
|
||||
void tcg_gen_mov_i64(TCGv_i64 ret, TCGv_i64 arg);
|
||||
@@ -841,7 +818,7 @@ static inline void tcg_gen_plugin_cb_start(unsigned from, unsigned type,
|
||||
|
||||
static inline void tcg_gen_plugin_cb_end(void)
|
||||
{
|
||||
tcg_emit_op(INDEX_op_plugin_cb_end);
|
||||
tcg_emit_op(INDEX_op_plugin_cb_end, 0);
|
||||
}
|
||||
|
||||
#if TARGET_LONG_BITS == 32
|
||||
|
||||
+48
-48
@@ -38,19 +38,7 @@
|
||||
/* XXX: make safe guess about sizes */
|
||||
#define MAX_OP_PER_INSTR 266
|
||||
|
||||
#if HOST_LONG_BITS == 32
|
||||
#define MAX_OPC_PARAM_PER_ARG 2
|
||||
#else
|
||||
#define MAX_OPC_PARAM_PER_ARG 1
|
||||
#endif
|
||||
#define MAX_OPC_PARAM_IARGS 7
|
||||
#define MAX_OPC_PARAM_OARGS 1
|
||||
#define MAX_OPC_PARAM_ARGS (MAX_OPC_PARAM_IARGS + MAX_OPC_PARAM_OARGS)
|
||||
|
||||
/* A Call op needs up to 4 + 2N parameters on 32-bit archs,
|
||||
* and up to 4 + N parameters on 64-bit archs
|
||||
* (N = number of input arguments + output arguments). */
|
||||
#define MAX_OPC_PARAM (4 + (MAX_OPC_PARAM_PER_ARG * MAX_OPC_PARAM_ARGS))
|
||||
#define MAX_CALL_IARGS 7
|
||||
|
||||
#define CPU_TEMP_BUF_NLONGS 128
|
||||
#define TCG_STATIC_FRAME_SIZE (CPU_TEMP_BUF_NLONGS * sizeof(long))
|
||||
@@ -294,7 +282,8 @@ typedef enum TCGType {
|
||||
TCG_TYPE_V128,
|
||||
TCG_TYPE_V256,
|
||||
|
||||
TCG_TYPE_COUNT, /* number of different types */
|
||||
/* Number of different types (integer not enum) */
|
||||
#define TCG_TYPE_COUNT (TCG_TYPE_V256 + 1)
|
||||
|
||||
/* An alias for the size of the host register. */
|
||||
#if TCG_TARGET_REG_BITS == 32
|
||||
@@ -318,6 +307,22 @@ typedef enum TCGType {
|
||||
#endif
|
||||
} TCGType;
|
||||
|
||||
/**
|
||||
* tcg_type_size
|
||||
* @t: type
|
||||
*
|
||||
* Return the size of the type in bytes.
|
||||
*/
|
||||
static inline int tcg_type_size(TCGType t)
|
||||
{
|
||||
unsigned i = t;
|
||||
if (i >= TCG_TYPE_V64) {
|
||||
tcg_debug_assert(i < TCG_TYPE_COUNT);
|
||||
i -= TCG_TYPE_V64 - 1;
|
||||
}
|
||||
return 4 << i;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_alignment_bits
|
||||
* @memop: MemOp value
|
||||
@@ -408,9 +413,6 @@ typedef TCGv_ptr TCGv_env;
|
||||
#define TCG_CALL_NO_RWG_SE (TCG_CALL_NO_RWG | TCG_CALL_NO_SE)
|
||||
#define TCG_CALL_NO_WG_SE (TCG_CALL_NO_WG | TCG_CALL_NO_SE)
|
||||
|
||||
/* Used to align parameters. See the comment before tcgv_i32_temp. */
|
||||
#define TCG_CALL_DUMMY_ARG ((TCGArg)0)
|
||||
|
||||
/*
|
||||
* Flags for the bswap opcodes.
|
||||
* If IZ, the input is zero-extended, otherwise unknown.
|
||||
@@ -456,6 +458,7 @@ typedef struct TCGTemp {
|
||||
unsigned int mem_coherent:1;
|
||||
unsigned int mem_allocated:1;
|
||||
unsigned int temp_allocated:1;
|
||||
unsigned int temp_subindex:1;
|
||||
|
||||
int64_t val;
|
||||
struct TCGTemp *mem_base;
|
||||
@@ -475,34 +478,34 @@ typedef struct TCGTempSet {
|
||||
unsigned long l[BITS_TO_LONGS(TCG_MAX_TEMPS)];
|
||||
} TCGTempSet;
|
||||
|
||||
/* While we limit helpers to 6 arguments, for 32-bit hosts, with padding,
|
||||
this imples a max of 6*2 (64-bit in) + 2 (64-bit out) = 14 operands.
|
||||
There are never more than 2 outputs, which means that we can store all
|
||||
dead + sync data within 16 bits. */
|
||||
#define DEAD_ARG 4
|
||||
#define SYNC_ARG 1
|
||||
typedef uint16_t TCGLifeData;
|
||||
/*
|
||||
* With 1 128-bit output, a 32-bit host requires 4 output parameters,
|
||||
* which leaves a maximum of 28 other slots. Which is enough for 7
|
||||
* 128-bit operands.
|
||||
*/
|
||||
#define DEAD_ARG (1 << 4)
|
||||
#define SYNC_ARG (1 << 0)
|
||||
typedef uint32_t TCGLifeData;
|
||||
|
||||
/* The layout here is designed to avoid a bitfield crossing of
|
||||
a 32-bit boundary, which would cause GCC to add extra padding. */
|
||||
typedef struct TCGOp {
|
||||
TCGOpcode opc : 8; /* 8 */
|
||||
TCGOpcode opc : 8;
|
||||
unsigned nargs : 8;
|
||||
|
||||
/* Parameters for this opcode. See below. */
|
||||
unsigned param1 : 4; /* 12 */
|
||||
unsigned param2 : 4; /* 16 */
|
||||
unsigned param1 : 8;
|
||||
unsigned param2 : 8;
|
||||
|
||||
/* Lifetime data of the operands. */
|
||||
unsigned life : 16; /* 32 */
|
||||
TCGLifeData life;
|
||||
|
||||
/* Next and previous opcodes. */
|
||||
QTAILQ_ENTRY(TCGOp) link;
|
||||
|
||||
/* Arguments for the opcode. */
|
||||
TCGArg args[MAX_OPC_PARAM];
|
||||
|
||||
/* Register preferences for the output(s). */
|
||||
TCGRegSet output_pref[2];
|
||||
|
||||
/* Arguments for the opcode. */
|
||||
TCGArg args[];
|
||||
} TCGOp;
|
||||
|
||||
#define TCGOP_CALLI(X) (X)->param1
|
||||
@@ -514,6 +517,11 @@ typedef struct TCGOp {
|
||||
/* Make sure operands fit in the bitfields above. */
|
||||
QEMU_BUILD_BUG_ON(NB_OPS > (1 << 8));
|
||||
|
||||
static inline TCGRegSet output_pref(const TCGOp *op, unsigned i)
|
||||
{
|
||||
return i < ARRAY_SIZE(op->output_pref) ? op->output_pref[i] : 0;
|
||||
}
|
||||
|
||||
typedef struct TCGProfile {
|
||||
int64_t cpu_exec_time;
|
||||
int64_t tb_count1;
|
||||
@@ -737,18 +745,6 @@ static inline TCGv_vec temp_tcgv_vec(TCGTemp *t)
|
||||
return (TCGv_vec)temp_tcgv_i32(t);
|
||||
}
|
||||
|
||||
#if TCG_TARGET_REG_BITS == 32
|
||||
static inline TCGv_i32 TCGV_LOW(TCGv_i64 t)
|
||||
{
|
||||
return temp_tcgv_i32(tcgv_i64_temp(t));
|
||||
}
|
||||
|
||||
static inline TCGv_i32 TCGV_HIGH(TCGv_i64 t)
|
||||
{
|
||||
return temp_tcgv_i32(tcgv_i64_temp(t) + 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline TCGArg tcg_get_insn_param(TCGOp *op, int arg)
|
||||
{
|
||||
return op->args[arg];
|
||||
@@ -951,6 +947,8 @@ typedef struct TCGArgConstraint {
|
||||
unsigned ct : 16;
|
||||
unsigned alias_index : 4;
|
||||
unsigned sort_index : 4;
|
||||
unsigned pair_index : 4;
|
||||
unsigned pair : 2; /* 0: none, 1: first, 2: second, 3: second alias */
|
||||
bool oalias : 1;
|
||||
bool ialias : 1;
|
||||
bool newreg : 1;
|
||||
@@ -1006,10 +1004,12 @@ bool tcg_op_supported(TCGOpcode op);
|
||||
|
||||
void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args);
|
||||
|
||||
TCGOp *tcg_emit_op(TCGOpcode opc);
|
||||
TCGOp *tcg_emit_op(TCGOpcode opc, unsigned nargs);
|
||||
void tcg_op_remove(TCGContext *s, TCGOp *op);
|
||||
TCGOp *tcg_op_insert_before(TCGContext *s, TCGOp *op, TCGOpcode opc);
|
||||
TCGOp *tcg_op_insert_after(TCGContext *s, TCGOp *op, TCGOpcode opc);
|
||||
TCGOp *tcg_op_insert_before(TCGContext *s, TCGOp *op,
|
||||
TCGOpcode opc, unsigned nargs);
|
||||
TCGOp *tcg_op_insert_after(TCGContext *s, TCGOp *op,
|
||||
TCGOpcode opc, unsigned nargs);
|
||||
|
||||
/**
|
||||
* tcg_remove_ops_after:
|
||||
|
||||
+1
-3
@@ -469,6 +469,7 @@ if get_option('tcg').allowed()
|
||||
endif
|
||||
if get_option('tcg_interpreter')
|
||||
tcg_arch = 'tci'
|
||||
config_host += { 'CONFIG_TCG_INTERPRETER': 'y' }
|
||||
elif host_arch == 'x86_64'
|
||||
tcg_arch = 'i386'
|
||||
elif host_arch == 'ppc64'
|
||||
@@ -2550,9 +2551,6 @@ foreach target : target_dirs
|
||||
if sym == 'CONFIG_TCG' or target in accelerator_targets.get(sym, [])
|
||||
config_target += { sym: 'y' }
|
||||
config_all += { sym: 'y' }
|
||||
if sym == 'CONFIG_TCG' and tcg_arch == 'tci'
|
||||
config_target += { 'CONFIG_TCG_INTERPRETER': 'y' }
|
||||
endif
|
||||
if target in modular_tcg
|
||||
config_target += { 'CONFIG_TCG_MODULAR': 'y' }
|
||||
else
|
||||
|
||||
@@ -2163,22 +2163,13 @@ static int ppc_next_unmasked_interrupt(CPUPPCState *env)
|
||||
void ppc_maybe_interrupt(CPUPPCState *env)
|
||||
{
|
||||
CPUState *cs = env_cpu(env);
|
||||
bool locked = false;
|
||||
|
||||
if (!qemu_mutex_iothread_locked()) {
|
||||
locked = true;
|
||||
qemu_mutex_lock_iothread();
|
||||
}
|
||||
QEMU_IOTHREAD_LOCK_GUARD();
|
||||
|
||||
if (ppc_next_unmasked_interrupt(env)) {
|
||||
cpu_interrupt(cs, CPU_INTERRUPT_HARD);
|
||||
} else {
|
||||
cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
|
||||
}
|
||||
|
||||
if (locked) {
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(TARGET_PPC64)
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "qemu/main-loop.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "sysemu/tcg.h"
|
||||
#include "helper_regs.h"
|
||||
#include "power8-pmu.h"
|
||||
#include "cpu-models.h"
|
||||
@@ -203,17 +204,10 @@ void cpu_interrupt_exittb(CPUState *cs)
|
||||
{
|
||||
/*
|
||||
* We don't need to worry about translation blocks
|
||||
* when running with KVM.
|
||||
* unless running with TCG.
|
||||
*/
|
||||
if (kvm_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!qemu_mutex_iothread_locked()) {
|
||||
qemu_mutex_lock_iothread();
|
||||
cpu_interrupt(cs, CPU_INTERRUPT_EXITTB);
|
||||
qemu_mutex_unlock_iothread();
|
||||
} else {
|
||||
if (tcg_enabled()) {
|
||||
QEMU_IOTHREAD_LOCK_GUARD();
|
||||
cpu_interrupt(cs, CPU_INTERRUPT_EXITTB);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -610,7 +610,6 @@ uint64_t riscv_cpu_update_mip(RISCVCPU *cpu, uint64_t mask, uint64_t value)
|
||||
CPURISCVState *env = &cpu->env;
|
||||
CPUState *cs = CPU(cpu);
|
||||
uint64_t gein, vsgein = 0, vstip = 0, old = env->mip;
|
||||
bool locked = false;
|
||||
|
||||
if (riscv_cpu_virt_enabled(env)) {
|
||||
gein = get_field(env->hstatus, HSTATUS_VGEIN);
|
||||
@@ -621,10 +620,7 @@ uint64_t riscv_cpu_update_mip(RISCVCPU *cpu, uint64_t mask, uint64_t value)
|
||||
mask = ((mask == MIP_VSTIP) && env->vstime_irq) ? 0 : mask;
|
||||
vstip = env->vstime_irq ? MIP_VSTIP : 0;
|
||||
|
||||
if (!qemu_mutex_iothread_locked()) {
|
||||
locked = true;
|
||||
qemu_mutex_lock_iothread();
|
||||
}
|
||||
QEMU_IOTHREAD_LOCK_GUARD();
|
||||
|
||||
env->mip = (env->mip & ~mask) | (value & mask);
|
||||
|
||||
@@ -634,10 +630,6 @@ uint64_t riscv_cpu_update_mip(RISCVCPU *cpu, uint64_t mask, uint64_t value)
|
||||
cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
|
||||
}
|
||||
|
||||
if (locked) {
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
|
||||
return old;
|
||||
}
|
||||
|
||||
|
||||
@@ -163,13 +163,6 @@ static inline void gen_update_fprs_dirty(DisasContext *dc, int rd)
|
||||
/* floating point registers moves */
|
||||
static TCGv_i32 gen_load_fpr_F(DisasContext *dc, unsigned int src)
|
||||
{
|
||||
#if TCG_TARGET_REG_BITS == 32
|
||||
if (src & 1) {
|
||||
return TCGV_LOW(cpu_fpr[src / 2]);
|
||||
} else {
|
||||
return TCGV_HIGH(cpu_fpr[src / 2]);
|
||||
}
|
||||
#else
|
||||
TCGv_i32 ret = get_temp_i32(dc);
|
||||
if (src & 1) {
|
||||
tcg_gen_extrl_i64_i32(ret, cpu_fpr[src / 2]);
|
||||
@@ -177,22 +170,16 @@ static TCGv_i32 gen_load_fpr_F(DisasContext *dc, unsigned int src)
|
||||
tcg_gen_extrh_i64_i32(ret, cpu_fpr[src / 2]);
|
||||
}
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void gen_store_fpr_F(DisasContext *dc, unsigned int dst, TCGv_i32 v)
|
||||
{
|
||||
#if TCG_TARGET_REG_BITS == 32
|
||||
if (dst & 1) {
|
||||
tcg_gen_mov_i32(TCGV_LOW(cpu_fpr[dst / 2]), v);
|
||||
} else {
|
||||
tcg_gen_mov_i32(TCGV_HIGH(cpu_fpr[dst / 2]), v);
|
||||
}
|
||||
#else
|
||||
TCGv_i64 t = (TCGv_i64)v;
|
||||
TCGv_i64 t = tcg_temp_new_i64();
|
||||
|
||||
tcg_gen_extu_i32_i64(t, v);
|
||||
tcg_gen_deposit_i64(cpu_fpr[dst / 2], cpu_fpr[dst / 2], t,
|
||||
(dst & 1 ? 0 : 32), 32);
|
||||
#endif
|
||||
tcg_temp_free_i64(t);
|
||||
gen_update_fprs_dirty(dc, dst);
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user