mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge remote-tracking branch 'remotes/rth-gitlab/tags/pull-tcg-20210613' into staging
Clean up code_gen_buffer allocation. Add tcg_remove_ops_after. Fix tcg_constant_* documentation. Improve TB chaining documentation. Fix float32_exp2. Fix arm tcg_out_op function signature. # gpg: Signature made Mon 14 Jun 2021 02:12:35 BST # 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 * remotes/rth-gitlab/tags/pull-tcg-20210613: (34 commits) docs/devel: Explain in more detail the TB chaining mechanisms softfloat: Fix tp init in float32_exp2 tcg/arm: Fix tcg_out_op function signature tcg: Fix documentation for tcg_constant_* vs tcg_temp_free_* tcg: Introduce tcg_remove_ops_after tcg: Move tcg_init_ctx and tcg_ctx from accel/tcg/ tcg: When allocating for !splitwx, begin with PROT_NONE tcg: Merge buffer protection and guard page protection tcg: Round the tb_size default from qemu_get_host_physmem util/osdep: Add qemu_mprotect_rw tcg: Sink qemu_madvise call to common code tcg: Return the map protection from alloc_code_gen_buffer tcg: Allocate code_gen_buffer into struct tcg_region_state tcg: Move in_code_gen_buffer and tests to region.c tcg: Tidy split_cross_256mb tcg: Tidy tcg_n_regions tcg: Rename region.start to region.after_prologue tcg: Replace region.end with region.total_size tcg: Move MAX_CODE_GEN_BUFFER_SIZE to tcg-target.h tcg: Introduce tcg_max_ctxs ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
@@ -16,5 +16,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, target_ulong pc,
|
||||
int cflags);
|
||||
|
||||
void QEMU_NORETURN cpu_io_recompile(CPUState *cpu, uintptr_t retaddr);
|
||||
void page_init(void);
|
||||
void tb_htable_init(void);
|
||||
|
||||
#endif /* ACCEL_TCG_INTERNAL_H */
|
||||
|
||||
+22
-10
@@ -32,6 +32,11 @@
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/accel.h"
|
||||
#include "qapi/qapi-builtin-visit.h"
|
||||
#include "qemu/units.h"
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
#include "hw/boards.h"
|
||||
#endif
|
||||
#include "internal.h"
|
||||
|
||||
struct TCGState {
|
||||
AccelState parent_obj;
|
||||
@@ -105,22 +110,29 @@ static void tcg_accel_instance_init(Object *obj)
|
||||
|
||||
bool mttcg_enabled;
|
||||
|
||||
static int tcg_init(MachineState *ms)
|
||||
static int tcg_init_machine(MachineState *ms)
|
||||
{
|
||||
TCGState *s = TCG_STATE(current_accel());
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
unsigned max_cpus = 1;
|
||||
#else
|
||||
unsigned max_cpus = ms->smp.max_cpus;
|
||||
#endif
|
||||
|
||||
tcg_exec_init(s->tb_size * 1024 * 1024, s->splitwx_enabled);
|
||||
tcg_allowed = true;
|
||||
mttcg_enabled = s->mttcg_enabled;
|
||||
|
||||
page_init();
|
||||
tb_htable_init();
|
||||
tcg_init(s->tb_size * MiB, s->splitwx_enabled, max_cpus);
|
||||
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
/*
|
||||
* Initialize TCG regions only for softmmu.
|
||||
*
|
||||
* This needs to be done later for user mode, because the prologue
|
||||
* generation needs to be delayed so that GUEST_BASE is already set.
|
||||
* There's no guest base to take into account, so go ahead and
|
||||
* initialize the prologue now.
|
||||
*/
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
tcg_region_init();
|
||||
#endif /* !CONFIG_USER_ONLY */
|
||||
tcg_prologue_init(tcg_ctx);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -200,7 +212,7 @@ static void tcg_accel_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
AccelClass *ac = ACCEL_CLASS(oc);
|
||||
ac->name = "tcg";
|
||||
ac->init_machine = tcg_init;
|
||||
ac->init_machine = tcg_init_machine;
|
||||
ac->allowed = &tcg_allowed;
|
||||
|
||||
object_class_property_add_str(oc, "thread",
|
||||
|
||||
+2
-437
@@ -18,7 +18,6 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/units.h"
|
||||
#include "qemu-common.h"
|
||||
|
||||
#define NO_CPU_IO_DEFS
|
||||
@@ -49,7 +48,6 @@
|
||||
#include "exec/cputlb.h"
|
||||
#include "exec/translate-all.h"
|
||||
#include "qemu/bitmap.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/qemu-print.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qemu/main-loop.h"
|
||||
@@ -220,9 +218,6 @@ static int v_l2_levels;
|
||||
|
||||
static void *l1_map[V_L1_MAX_SIZE];
|
||||
|
||||
/* code generation context */
|
||||
TCGContext tcg_init_ctx;
|
||||
__thread TCGContext *tcg_ctx;
|
||||
TBContext tb_ctx;
|
||||
|
||||
static void page_table_config_init(void)
|
||||
@@ -245,11 +240,6 @@ static void page_table_config_init(void)
|
||||
assert(v_l2_levels >= 0);
|
||||
}
|
||||
|
||||
static void cpu_gen_init(void)
|
||||
{
|
||||
tcg_context_init(&tcg_init_ctx);
|
||||
}
|
||||
|
||||
/* Encode VAL as a signed leb128 sequence at P.
|
||||
Return P incremented past the encoded value. */
|
||||
static uint8_t *encode_sleb128(uint8_t *p, target_long val)
|
||||
@@ -415,7 +405,7 @@ bool cpu_restore_state(CPUState *cpu, uintptr_t host_pc, bool will_exit)
|
||||
return false;
|
||||
}
|
||||
|
||||
static void page_init(void)
|
||||
void page_init(void)
|
||||
{
|
||||
page_size_init();
|
||||
page_table_config_init();
|
||||
@@ -900,408 +890,6 @@ static void page_lock_pair(PageDesc **ret_p1, tb_page_addr_t phys1,
|
||||
}
|
||||
}
|
||||
|
||||
/* Minimum size of the code gen buffer. This number is randomly chosen,
|
||||
but not so small that we can't have a fair number of TB's live. */
|
||||
#define MIN_CODE_GEN_BUFFER_SIZE (1 * MiB)
|
||||
|
||||
/* Maximum size of the code gen buffer we'd like to use. Unless otherwise
|
||||
indicated, this is constrained by the range of direct branches on the
|
||||
host cpu, as used by the TCG implementation of goto_tb. */
|
||||
#if defined(__x86_64__)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB)
|
||||
#elif defined(__sparc__)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB)
|
||||
#elif defined(__powerpc64__)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB)
|
||||
#elif defined(__powerpc__)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (32 * MiB)
|
||||
#elif defined(__aarch64__)
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB)
|
||||
#elif defined(__s390x__)
|
||||
/* We have a +- 4GB range on the branches; leave some slop. */
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (3 * GiB)
|
||||
#elif defined(__mips__)
|
||||
/* We have a 256MB branch region, but leave room to make sure the
|
||||
main executable is also within that region. */
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (128 * MiB)
|
||||
#else
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1)
|
||||
#endif
|
||||
|
||||
#if TCG_TARGET_REG_BITS == 32
|
||||
#define DEFAULT_CODE_GEN_BUFFER_SIZE_1 (32 * MiB)
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
/*
|
||||
* For user mode on smaller 32 bit systems we may run into trouble
|
||||
* allocating big chunks of data in the right place. On these systems
|
||||
* we utilise a static code generation buffer directly in the binary.
|
||||
*/
|
||||
#define USE_STATIC_CODE_GEN_BUFFER
|
||||
#endif
|
||||
#else /* TCG_TARGET_REG_BITS == 64 */
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
/*
|
||||
* As user-mode emulation typically means running multiple instances
|
||||
* of the translator don't go too nuts with our default code gen
|
||||
* buffer lest we make things too hard for the OS.
|
||||
*/
|
||||
#define DEFAULT_CODE_GEN_BUFFER_SIZE_1 (128 * MiB)
|
||||
#else
|
||||
/*
|
||||
* We expect most system emulation to run one or two guests per host.
|
||||
* Users running large scale system emulation may want to tweak their
|
||||
* runtime setup via the tb-size control on the command line.
|
||||
*/
|
||||
#define DEFAULT_CODE_GEN_BUFFER_SIZE_1 (1 * GiB)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define DEFAULT_CODE_GEN_BUFFER_SIZE \
|
||||
(DEFAULT_CODE_GEN_BUFFER_SIZE_1 < MAX_CODE_GEN_BUFFER_SIZE \
|
||||
? DEFAULT_CODE_GEN_BUFFER_SIZE_1 : MAX_CODE_GEN_BUFFER_SIZE)
|
||||
|
||||
static size_t size_code_gen_buffer(size_t tb_size)
|
||||
{
|
||||
/* Size the buffer. */
|
||||
if (tb_size == 0) {
|
||||
size_t phys_mem = qemu_get_host_physmem();
|
||||
if (phys_mem == 0) {
|
||||
tb_size = DEFAULT_CODE_GEN_BUFFER_SIZE;
|
||||
} else {
|
||||
tb_size = MIN(DEFAULT_CODE_GEN_BUFFER_SIZE, phys_mem / 8);
|
||||
}
|
||||
}
|
||||
if (tb_size < MIN_CODE_GEN_BUFFER_SIZE) {
|
||||
tb_size = MIN_CODE_GEN_BUFFER_SIZE;
|
||||
}
|
||||
if (tb_size > MAX_CODE_GEN_BUFFER_SIZE) {
|
||||
tb_size = MAX_CODE_GEN_BUFFER_SIZE;
|
||||
}
|
||||
return tb_size;
|
||||
}
|
||||
|
||||
#ifdef __mips__
|
||||
/* In order to use J and JAL within the code_gen_buffer, we require
|
||||
that the buffer not cross a 256MB boundary. */
|
||||
static inline bool cross_256mb(void *addr, size_t size)
|
||||
{
|
||||
return ((uintptr_t)addr ^ ((uintptr_t)addr + size)) & ~0x0ffffffful;
|
||||
}
|
||||
|
||||
/* We weren't able to allocate a buffer without crossing that boundary,
|
||||
so make do with the larger portion of the buffer that doesn't cross.
|
||||
Returns the new base of the buffer, and adjusts code_gen_buffer_size. */
|
||||
static inline void *split_cross_256mb(void *buf1, size_t size1)
|
||||
{
|
||||
void *buf2 = (void *)(((uintptr_t)buf1 + size1) & ~0x0ffffffful);
|
||||
size_t size2 = buf1 + size1 - buf2;
|
||||
|
||||
size1 = buf2 - buf1;
|
||||
if (size1 < size2) {
|
||||
size1 = size2;
|
||||
buf1 = buf2;
|
||||
}
|
||||
|
||||
tcg_ctx->code_gen_buffer_size = size1;
|
||||
return buf1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_STATIC_CODE_GEN_BUFFER
|
||||
static uint8_t static_code_gen_buffer[DEFAULT_CODE_GEN_BUFFER_SIZE]
|
||||
__attribute__((aligned(CODE_GEN_ALIGN)));
|
||||
|
||||
static bool alloc_code_gen_buffer(size_t tb_size, int splitwx, Error **errp)
|
||||
{
|
||||
void *buf, *end;
|
||||
size_t size;
|
||||
|
||||
if (splitwx > 0) {
|
||||
error_setg(errp, "jit split-wx not supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* page-align the beginning and end of the buffer */
|
||||
buf = static_code_gen_buffer;
|
||||
end = static_code_gen_buffer + sizeof(static_code_gen_buffer);
|
||||
buf = QEMU_ALIGN_PTR_UP(buf, qemu_real_host_page_size);
|
||||
end = QEMU_ALIGN_PTR_DOWN(end, qemu_real_host_page_size);
|
||||
|
||||
size = end - buf;
|
||||
|
||||
/* Honor a command-line option limiting the size of the buffer. */
|
||||
if (size > tb_size) {
|
||||
size = QEMU_ALIGN_DOWN(tb_size, qemu_real_host_page_size);
|
||||
}
|
||||
tcg_ctx->code_gen_buffer_size = size;
|
||||
|
||||
#ifdef __mips__
|
||||
if (cross_256mb(buf, size)) {
|
||||
buf = split_cross_256mb(buf, size);
|
||||
size = tcg_ctx->code_gen_buffer_size;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (qemu_mprotect_rwx(buf, size)) {
|
||||
error_setg_errno(errp, errno, "mprotect of jit buffer");
|
||||
return false;
|
||||
}
|
||||
qemu_madvise(buf, size, QEMU_MADV_HUGEPAGE);
|
||||
|
||||
tcg_ctx->code_gen_buffer = buf;
|
||||
return true;
|
||||
}
|
||||
#elif defined(_WIN32)
|
||||
static bool alloc_code_gen_buffer(size_t size, int splitwx, Error **errp)
|
||||
{
|
||||
void *buf;
|
||||
|
||||
if (splitwx > 0) {
|
||||
error_setg(errp, "jit split-wx not supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
buf = VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT,
|
||||
PAGE_EXECUTE_READWRITE);
|
||||
if (buf == NULL) {
|
||||
error_setg_win32(errp, GetLastError(),
|
||||
"allocate %zu bytes for jit buffer", size);
|
||||
return false;
|
||||
}
|
||||
|
||||
tcg_ctx->code_gen_buffer = buf;
|
||||
tcg_ctx->code_gen_buffer_size = size;
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
static bool alloc_code_gen_buffer_anon(size_t size, int prot,
|
||||
int flags, Error **errp)
|
||||
{
|
||||
void *buf;
|
||||
|
||||
buf = mmap(NULL, size, prot, flags, -1, 0);
|
||||
if (buf == MAP_FAILED) {
|
||||
error_setg_errno(errp, errno,
|
||||
"allocate %zu bytes for jit buffer", size);
|
||||
return false;
|
||||
}
|
||||
tcg_ctx->code_gen_buffer_size = size;
|
||||
|
||||
#ifdef __mips__
|
||||
if (cross_256mb(buf, size)) {
|
||||
/*
|
||||
* Try again, with the original still mapped, to avoid re-acquiring
|
||||
* the same 256mb crossing.
|
||||
*/
|
||||
size_t size2;
|
||||
void *buf2 = mmap(NULL, size, prot, flags, -1, 0);
|
||||
switch ((int)(buf2 != MAP_FAILED)) {
|
||||
case 1:
|
||||
if (!cross_256mb(buf2, size)) {
|
||||
/* Success! Use the new buffer. */
|
||||
munmap(buf, size);
|
||||
break;
|
||||
}
|
||||
/* Failure. Work with what we had. */
|
||||
munmap(buf2, size);
|
||||
/* fallthru */
|
||||
default:
|
||||
/* Split the original buffer. Free the smaller half. */
|
||||
buf2 = split_cross_256mb(buf, size);
|
||||
size2 = tcg_ctx->code_gen_buffer_size;
|
||||
if (buf == buf2) {
|
||||
munmap(buf + size2, size - size2);
|
||||
} else {
|
||||
munmap(buf, size - size2);
|
||||
}
|
||||
size = size2;
|
||||
break;
|
||||
}
|
||||
buf = buf2;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Request large pages for the buffer. */
|
||||
qemu_madvise(buf, size, QEMU_MADV_HUGEPAGE);
|
||||
|
||||
tcg_ctx->code_gen_buffer = buf;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_TCG_INTERPRETER
|
||||
#ifdef CONFIG_POSIX
|
||||
#include "qemu/memfd.h"
|
||||
|
||||
static bool alloc_code_gen_buffer_splitwx_memfd(size_t size, Error **errp)
|
||||
{
|
||||
void *buf_rw = NULL, *buf_rx = MAP_FAILED;
|
||||
int fd = -1;
|
||||
|
||||
#ifdef __mips__
|
||||
/* Find space for the RX mapping, vs the 256MiB regions. */
|
||||
if (!alloc_code_gen_buffer_anon(size, PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS |
|
||||
MAP_NORESERVE, errp)) {
|
||||
return false;
|
||||
}
|
||||
/* The size of the mapping may have been adjusted. */
|
||||
size = tcg_ctx->code_gen_buffer_size;
|
||||
buf_rx = tcg_ctx->code_gen_buffer;
|
||||
#endif
|
||||
|
||||
buf_rw = qemu_memfd_alloc("tcg-jit", size, 0, &fd, errp);
|
||||
if (buf_rw == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
#ifdef __mips__
|
||||
void *tmp = mmap(buf_rx, size, PROT_READ | PROT_EXEC,
|
||||
MAP_SHARED | MAP_FIXED, fd, 0);
|
||||
if (tmp != buf_rx) {
|
||||
goto fail_rx;
|
||||
}
|
||||
#else
|
||||
buf_rx = mmap(NULL, size, PROT_READ | PROT_EXEC, MAP_SHARED, fd, 0);
|
||||
if (buf_rx == MAP_FAILED) {
|
||||
goto fail_rx;
|
||||
}
|
||||
#endif
|
||||
|
||||
close(fd);
|
||||
tcg_ctx->code_gen_buffer = buf_rw;
|
||||
tcg_ctx->code_gen_buffer_size = size;
|
||||
tcg_splitwx_diff = buf_rx - buf_rw;
|
||||
|
||||
/* Request large pages for the buffer and the splitwx. */
|
||||
qemu_madvise(buf_rw, size, QEMU_MADV_HUGEPAGE);
|
||||
qemu_madvise(buf_rx, size, QEMU_MADV_HUGEPAGE);
|
||||
return true;
|
||||
|
||||
fail_rx:
|
||||
error_setg_errno(errp, errno, "failed to map shared memory for execute");
|
||||
fail:
|
||||
if (buf_rx != MAP_FAILED) {
|
||||
munmap(buf_rx, size);
|
||||
}
|
||||
if (buf_rw) {
|
||||
munmap(buf_rw, size);
|
||||
}
|
||||
if (fd >= 0) {
|
||||
close(fd);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif /* CONFIG_POSIX */
|
||||
|
||||
#ifdef CONFIG_DARWIN
|
||||
#include <mach/mach.h>
|
||||
|
||||
extern kern_return_t mach_vm_remap(vm_map_t target_task,
|
||||
mach_vm_address_t *target_address,
|
||||
mach_vm_size_t size,
|
||||
mach_vm_offset_t mask,
|
||||
int flags,
|
||||
vm_map_t src_task,
|
||||
mach_vm_address_t src_address,
|
||||
boolean_t copy,
|
||||
vm_prot_t *cur_protection,
|
||||
vm_prot_t *max_protection,
|
||||
vm_inherit_t inheritance);
|
||||
|
||||
static bool alloc_code_gen_buffer_splitwx_vmremap(size_t size, Error **errp)
|
||||
{
|
||||
kern_return_t ret;
|
||||
mach_vm_address_t buf_rw, buf_rx;
|
||||
vm_prot_t cur_prot, max_prot;
|
||||
|
||||
/* Map the read-write portion via normal anon memory. */
|
||||
if (!alloc_code_gen_buffer_anon(size, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, errp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
buf_rw = (mach_vm_address_t)tcg_ctx->code_gen_buffer;
|
||||
buf_rx = 0;
|
||||
ret = mach_vm_remap(mach_task_self(),
|
||||
&buf_rx,
|
||||
size,
|
||||
0,
|
||||
VM_FLAGS_ANYWHERE,
|
||||
mach_task_self(),
|
||||
buf_rw,
|
||||
false,
|
||||
&cur_prot,
|
||||
&max_prot,
|
||||
VM_INHERIT_NONE);
|
||||
if (ret != KERN_SUCCESS) {
|
||||
/* TODO: Convert "ret" to a human readable error message. */
|
||||
error_setg(errp, "vm_remap for jit splitwx failed");
|
||||
munmap((void *)buf_rw, size);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mprotect((void *)buf_rx, size, PROT_READ | PROT_EXEC) != 0) {
|
||||
error_setg_errno(errp, errno, "mprotect for jit splitwx");
|
||||
munmap((void *)buf_rx, size);
|
||||
munmap((void *)buf_rw, size);
|
||||
return false;
|
||||
}
|
||||
|
||||
tcg_splitwx_diff = buf_rx - buf_rw;
|
||||
return true;
|
||||
}
|
||||
#endif /* CONFIG_DARWIN */
|
||||
#endif /* CONFIG_TCG_INTERPRETER */
|
||||
|
||||
static bool alloc_code_gen_buffer_splitwx(size_t size, Error **errp)
|
||||
{
|
||||
#ifndef CONFIG_TCG_INTERPRETER
|
||||
# ifdef CONFIG_DARWIN
|
||||
return alloc_code_gen_buffer_splitwx_vmremap(size, errp);
|
||||
# endif
|
||||
# ifdef CONFIG_POSIX
|
||||
return alloc_code_gen_buffer_splitwx_memfd(size, errp);
|
||||
# endif
|
||||
#endif
|
||||
error_setg(errp, "jit split-wx not supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool alloc_code_gen_buffer(size_t size, int splitwx, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
int prot, flags;
|
||||
|
||||
if (splitwx) {
|
||||
if (alloc_code_gen_buffer_splitwx(size, errp)) {
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
* If splitwx force-on (1), fail;
|
||||
* if splitwx default-on (-1), fall through to splitwx off.
|
||||
*/
|
||||
if (splitwx > 0) {
|
||||
return false;
|
||||
}
|
||||
error_free_or_abort(errp);
|
||||
}
|
||||
|
||||
prot = PROT_READ | PROT_WRITE | PROT_EXEC;
|
||||
flags = MAP_PRIVATE | MAP_ANONYMOUS;
|
||||
#ifdef CONFIG_TCG_INTERPRETER
|
||||
/* The tcg interpreter does not need execute permission. */
|
||||
prot = PROT_READ | PROT_WRITE;
|
||||
#elif defined(CONFIG_DARWIN)
|
||||
/* Applicable to both iOS and macOS (Apple Silicon). */
|
||||
if (!splitwx) {
|
||||
flags |= MAP_JIT;
|
||||
}
|
||||
#endif
|
||||
|
||||
return alloc_code_gen_buffer_anon(size, prot, flags, errp);
|
||||
}
|
||||
#endif /* USE_STATIC_CODE_GEN_BUFFER, WIN32, POSIX */
|
||||
|
||||
static bool tb_cmp(const void *ap, const void *bp)
|
||||
{
|
||||
const TranslationBlock *a = ap;
|
||||
@@ -1316,36 +904,13 @@ static bool tb_cmp(const void *ap, const void *bp)
|
||||
a->page_addr[1] == b->page_addr[1];
|
||||
}
|
||||
|
||||
static void tb_htable_init(void)
|
||||
void tb_htable_init(void)
|
||||
{
|
||||
unsigned int mode = QHT_MODE_AUTO_RESIZE;
|
||||
|
||||
qht_init(&tb_ctx.htable, tb_cmp, CODE_GEN_HTABLE_SIZE, mode);
|
||||
}
|
||||
|
||||
/* Must be called before using the QEMU cpus. 'tb_size' is the size
|
||||
(in bytes) allocated to the translation buffer. Zero means default
|
||||
size. */
|
||||
void tcg_exec_init(unsigned long tb_size, int splitwx)
|
||||
{
|
||||
bool ok;
|
||||
|
||||
tcg_allowed = true;
|
||||
cpu_gen_init();
|
||||
page_init();
|
||||
tb_htable_init();
|
||||
|
||||
ok = alloc_code_gen_buffer(size_code_gen_buffer(tb_size),
|
||||
splitwx, &error_fatal);
|
||||
assert(ok);
|
||||
|
||||
#if defined(CONFIG_SOFTMMU)
|
||||
/* There's no guest base to take into account, so go ahead and
|
||||
initialize the prologue now. */
|
||||
tcg_prologue_init(tcg_ctx);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* call with @p->lock held */
|
||||
static inline void invalidate_page_bitmap(PageDesc *p)
|
||||
{
|
||||
|
||||
+1
-2
@@ -813,7 +813,7 @@ int main(int argc, char **argv)
|
||||
envlist_free(envlist);
|
||||
|
||||
/*
|
||||
* Now that page sizes are configured in tcg_exec_init() we can do
|
||||
* Now that page sizes are configured we can do
|
||||
* proper page alignment for guest_base.
|
||||
*/
|
||||
guest_base = HOST_PAGE_ALIGN(guest_base);
|
||||
@@ -879,7 +879,6 @@ int main(int argc, char **argv)
|
||||
* the real value of GUEST_BASE into account.
|
||||
*/
|
||||
tcg_prologue_init(tcg_ctx);
|
||||
tcg_region_init();
|
||||
|
||||
/* build Task State */
|
||||
memset(ts, 0, sizeof(TaskState));
|
||||
|
||||
+90
-11
@@ -11,13 +11,14 @@ performances.
|
||||
QEMU's dynamic translation backend is called TCG, for "Tiny Code
|
||||
Generator". For more information, please take a look at ``tcg/README``.
|
||||
|
||||
Some notable features of QEMU's dynamic translator are:
|
||||
The following sections outline some notable features and implementation
|
||||
details of QEMU's dynamic translator.
|
||||
|
||||
CPU state optimisations
|
||||
-----------------------
|
||||
|
||||
The target CPUs have many internal states which change the way it
|
||||
evaluates instructions. In order to achieve a good speed, the
|
||||
The target CPUs have many internal states which change the way they
|
||||
evaluate instructions. In order to achieve a good speed, the
|
||||
translation phase considers that some state information of the virtual
|
||||
CPU cannot change in it. The state is recorded in the Translation
|
||||
Block (TB). If the state changes (e.g. privilege level), a new TB will
|
||||
@@ -31,17 +32,95 @@ Direct block chaining
|
||||
---------------------
|
||||
|
||||
After each translated basic block is executed, QEMU uses the simulated
|
||||
Program Counter (PC) and other cpu state information (such as the CS
|
||||
Program Counter (PC) and other CPU state information (such as the CS
|
||||
segment base value) to find the next basic block.
|
||||
|
||||
In order to accelerate the most common cases where the new simulated PC
|
||||
is known, QEMU can patch a basic block so that it jumps directly to the
|
||||
next one.
|
||||
In its simplest, less optimized form, this is done by exiting from the
|
||||
current TB, going through the TB epilogue, and then back to the
|
||||
main loop. That’s where QEMU looks for the next TB to execute,
|
||||
translating it from the guest architecture if it isn’t already available
|
||||
in memory. Then QEMU proceeds to execute this next TB, starting at the
|
||||
prologue and then moving on to the translated instructions.
|
||||
|
||||
The most portable code uses an indirect jump. An indirect jump makes
|
||||
it easier to make the jump target modification atomic. On some host
|
||||
architectures (such as x86 or PowerPC), the ``JUMP`` opcode is
|
||||
directly patched so that the block chaining has no overhead.
|
||||
Exiting from the TB this way will cause the ``cpu_exec_interrupt()``
|
||||
callback to be re-evaluated before executing additional instructions.
|
||||
It is mandatory to exit this way after any CPU state changes that may
|
||||
unmask interrupts.
|
||||
|
||||
In order to accelerate the cases where the TB for the new
|
||||
simulated PC is already available, QEMU has mechanisms that allow
|
||||
multiple TBs to be chained directly, without having to go back to the
|
||||
main loop as described above. These mechanisms are:
|
||||
|
||||
``lookup_and_goto_ptr``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Calling ``tcg_gen_lookup_and_goto_ptr()`` will emit a call to
|
||||
``helper_lookup_tb_ptr``. This helper will look for an existing TB that
|
||||
matches the current CPU state. If the destination TB is available its
|
||||
code address is returned, otherwise the address of the JIT epilogue is
|
||||
returned. The call to the helper is always followed by the tcg ``goto_ptr``
|
||||
opcode, which branches to the returned address. In this way, we either
|
||||
branch to the next TB or return to the main loop.
|
||||
|
||||
``goto_tb + exit_tb``
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The translation code usually implements branching by performing the
|
||||
following steps:
|
||||
|
||||
1. Call ``tcg_gen_goto_tb()`` passing a jump slot index (either 0 or 1)
|
||||
as a parameter.
|
||||
|
||||
2. Emit TCG instructions to update the CPU state with any information
|
||||
that has been assumed constant and is required by the main loop to
|
||||
correctly locate and execute the next TB. For most guests, this is
|
||||
just the PC of the branch destination, but others may store additional
|
||||
data. The information updated in this step must be inferable from both
|
||||
``cpu_get_tb_cpu_state()`` and ``cpu_restore_state()``.
|
||||
|
||||
3. Call ``tcg_gen_exit_tb()`` passing the address of the current TB and
|
||||
the jump slot index again.
|
||||
|
||||
Step 1, ``tcg_gen_goto_tb()``, will emit a ``goto_tb`` TCG
|
||||
instruction that later on gets translated to a jump to an address
|
||||
associated with the specified jump slot. Initially, this is the address
|
||||
of step 2's instructions, which update the CPU state information. Step 3,
|
||||
``tcg_gen_exit_tb()``, exits from the current TB returning a tagged
|
||||
pointer composed of the last executed TB’s address and the jump slot
|
||||
index.
|
||||
|
||||
The first time this whole sequence is executed, step 1 simply jumps
|
||||
to step 2. Then the CPU state information gets updated and we exit from
|
||||
the current TB. As a result, the behavior is very similar to the less
|
||||
optimized form described earlier in this section.
|
||||
|
||||
Next, the main loop looks for the next TB to execute using the
|
||||
current CPU state information (creating the TB if it wasn’t already
|
||||
available) and, before starting to execute the new TB’s instructions,
|
||||
patches the previously executed TB by associating one of its jump
|
||||
slots (the one specified in the call to ``tcg_gen_exit_tb()``) with the
|
||||
address of the new TB.
|
||||
|
||||
The next time this previous TB is executed and we get to that same
|
||||
``goto_tb`` step, it will already be patched (assuming the destination TB
|
||||
is still in memory) and will jump directly to the first instruction of
|
||||
the destination TB, without going back to the main loop.
|
||||
|
||||
For the ``goto_tb + exit_tb`` mechanism to be used, the following
|
||||
conditions need to be satisfied:
|
||||
|
||||
* The change in CPU state must be constant, e.g., a direct branch and
|
||||
not an indirect branch.
|
||||
|
||||
* The direct branch cannot cross a page boundary. Memory mappings
|
||||
may change, causing the code at the destination address to change.
|
||||
|
||||
Note that, on step 3 (``tcg_gen_exit_tb()``), in addition to the
|
||||
jump slot index, the address of the TB just executed is also returned.
|
||||
This address corresponds to the TB that will be patched; it may be
|
||||
different than the one that was directly executed from the main loop
|
||||
if the latter had already been chained to other TBs.
|
||||
|
||||
Self-modifying code and translated code invalidation
|
||||
----------------------------------------------------
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
specific_ss.add(when: 'CONFIG_TCG', if_true: files('softfloat.c'))
|
||||
+1
-1
@@ -4818,7 +4818,7 @@ float32 float32_exp2(float32 a, float_status *status)
|
||||
|
||||
float_raise(float_flag_inexact, status);
|
||||
|
||||
float64_unpack_canonical(&xnp, float64_ln2, status);
|
||||
float64_unpack_canonical(&tp, float64_ln2, status);
|
||||
xp = *parts_mul(&xp, &tp, status);
|
||||
xnp = xp;
|
||||
|
||||
|
||||
@@ -512,6 +512,7 @@ void sigaction_invoke(struct sigaction *action,
|
||||
#endif
|
||||
|
||||
int qemu_madvise(void *addr, size_t len, int advice);
|
||||
int qemu_mprotect_rw(void *addr, size_t size);
|
||||
int qemu_mprotect_rwx(void *addr, size_t size);
|
||||
int qemu_mprotect_none(void *addr, size_t size);
|
||||
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
#ifndef SYSEMU_TCG_H
|
||||
#define SYSEMU_TCG_H
|
||||
|
||||
void tcg_exec_init(unsigned long tb_size, int splitwx);
|
||||
|
||||
#ifdef CONFIG_TCG
|
||||
extern bool tcg_allowed;
|
||||
#define tcg_enabled() (tcg_allowed)
|
||||
|
||||
+14
-14
@@ -689,22 +689,12 @@ static inline bool temp_readonly(TCGTemp *ts)
|
||||
return ts->kind >= TEMP_FIXED;
|
||||
}
|
||||
|
||||
extern TCGContext tcg_init_ctx;
|
||||
extern __thread TCGContext *tcg_ctx;
|
||||
extern const void *tcg_code_gen_epilogue;
|
||||
extern uintptr_t tcg_splitwx_diff;
|
||||
extern TCGv_env cpu_env;
|
||||
|
||||
static inline bool in_code_gen_buffer(const void *p)
|
||||
{
|
||||
const TCGContext *s = &tcg_init_ctx;
|
||||
/*
|
||||
* Much like it is valid to have a pointer to the byte past the
|
||||
* end of an array (so long as you don't dereference it), allow
|
||||
* a pointer to the byte past the end of the code gen buffer.
|
||||
*/
|
||||
return (size_t)(p - s->code_gen_buffer) <= s->code_gen_buffer_size;
|
||||
}
|
||||
bool in_code_gen_buffer(const void *p);
|
||||
|
||||
#ifdef CONFIG_DEBUG_TCG
|
||||
const void *tcg_splitwx_to_rx(void *rw);
|
||||
@@ -873,7 +863,6 @@ void *tcg_malloc_internal(TCGContext *s, int size);
|
||||
void tcg_pool_reset(TCGContext *s);
|
||||
TranslationBlock *tcg_tb_alloc(TCGContext *s);
|
||||
|
||||
void tcg_region_init(void);
|
||||
void tb_destroy(TranslationBlock *tb);
|
||||
void tcg_region_reset_all(void);
|
||||
|
||||
@@ -906,7 +895,7 @@ static inline void *tcg_malloc(int size)
|
||||
}
|
||||
}
|
||||
|
||||
void tcg_context_init(TCGContext *s);
|
||||
void tcg_init(size_t tb_size, int splitwx, unsigned max_cpus);
|
||||
void tcg_register_thread(void);
|
||||
void tcg_prologue_init(TCGContext *s);
|
||||
void tcg_func_start(TCGContext *s);
|
||||
@@ -1082,6 +1071,16 @@ 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);
|
||||
|
||||
/**
|
||||
* tcg_remove_ops_after:
|
||||
* @op: target operation
|
||||
*
|
||||
* Discard any opcodes emitted since @op. Expected usage is to save
|
||||
* a starting point with tcg_last_op(), speculatively emit opcodes,
|
||||
* then decide whether or not to keep those opcodes after the fact.
|
||||
*/
|
||||
void tcg_remove_ops_after(TCGOp *op);
|
||||
|
||||
void tcg_optimize(TCGContext *s);
|
||||
|
||||
/* Allocate a new temporary and initialize it with a constant. */
|
||||
@@ -1096,7 +1095,8 @@ TCGv_vec tcg_const_ones_vec_matching(TCGv_vec);
|
||||
|
||||
/*
|
||||
* Locate or create a read-only temporary that is a constant.
|
||||
* This kind of temporary need not and should not be freed.
|
||||
* This kind of temporary need not be freed, but for convenience
|
||||
* will be silently ignored by tcg_temp_free_*.
|
||||
*/
|
||||
TCGTemp *tcg_constant_internal(TCGType type, int64_t val);
|
||||
|
||||
|
||||
@@ -868,7 +868,6 @@ int main(int argc, char **argv, char **envp)
|
||||
generating the prologue until now so that the prologue can take
|
||||
the real value of GUEST_BASE into account. */
|
||||
tcg_prologue_init(tcg_ctx);
|
||||
tcg_region_init();
|
||||
|
||||
target_cpu_copy_regs(env, regs);
|
||||
|
||||
|
||||
+2
-10
@@ -1968,16 +1968,6 @@ subdir('softmmu')
|
||||
|
||||
common_ss.add(capstone)
|
||||
specific_ss.add(files('cpu.c', 'disas.c', 'gdbstub.c'), capstone)
|
||||
specific_ss.add(when: 'CONFIG_TCG', if_true: files(
|
||||
'fpu/softfloat.c',
|
||||
'tcg/optimize.c',
|
||||
'tcg/tcg-common.c',
|
||||
'tcg/tcg-op-gvec.c',
|
||||
'tcg/tcg-op-vec.c',
|
||||
'tcg/tcg-op.c',
|
||||
'tcg/tcg.c',
|
||||
))
|
||||
specific_ss.add(when: 'CONFIG_TCG_INTERPRETER', if_true: files('tcg/tci.c'))
|
||||
|
||||
# Work around a gcc bug/misfeature wherein constant propagation looks
|
||||
# through an alias:
|
||||
@@ -2007,6 +1997,8 @@ subdir('net')
|
||||
subdir('replay')
|
||||
subdir('semihosting')
|
||||
subdir('hw')
|
||||
subdir('tcg')
|
||||
subdir('fpu')
|
||||
subdir('accel')
|
||||
subdir('plugins')
|
||||
subdir('bsd-user')
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#define TCG_TARGET_INSN_UNIT_SIZE 4
|
||||
#define TCG_TARGET_TLB_DISPLACEMENT_BITS 24
|
||||
#define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB)
|
||||
#undef TCG_TARGET_STACK_GROWSUP
|
||||
|
||||
typedef enum {
|
||||
|
||||
@@ -1984,7 +1984,8 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is64)
|
||||
static void tcg_out_epilogue(TCGContext *s);
|
||||
|
||||
static inline void tcg_out_op(TCGContext *s, TCGOpcode opc,
|
||||
const TCGArg *args, const int *const_args)
|
||||
const TCGArg args[TCG_MAX_OP_ARGS],
|
||||
const int const_args[TCG_MAX_OP_ARGS])
|
||||
{
|
||||
TCGArg a0, a1, a2, a3, a4, a5;
|
||||
int c;
|
||||
|
||||
@@ -60,6 +60,7 @@ extern int arm_arch;
|
||||
#undef TCG_TARGET_STACK_GROWSUP
|
||||
#define TCG_TARGET_INSN_UNIT_SIZE 4
|
||||
#define TCG_TARGET_TLB_DISPLACEMENT_BITS 16
|
||||
#define MAX_CODE_GEN_BUFFER_SIZE UINT32_MAX
|
||||
|
||||
typedef enum {
|
||||
TCG_REG_R0 = 0,
|
||||
|
||||
@@ -31,9 +31,11 @@
|
||||
#ifdef __x86_64__
|
||||
# define TCG_TARGET_REG_BITS 64
|
||||
# define TCG_TARGET_NB_REGS 32
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB)
|
||||
#else
|
||||
# define TCG_TARGET_REG_BITS 32
|
||||
# define TCG_TARGET_NB_REGS 24
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE UINT32_MAX
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
tcg_ss = ss.source_set()
|
||||
|
||||
tcg_ss.add(files(
|
||||
'optimize.c',
|
||||
'region.c',
|
||||
'tcg.c',
|
||||
'tcg-common.c',
|
||||
'tcg-op.c',
|
||||
'tcg-op-gvec.c',
|
||||
'tcg-op-vec.c',
|
||||
))
|
||||
tcg_ss.add(when: 'CONFIG_TCG_INTERPRETER', if_true: files('tci.c'))
|
||||
|
||||
specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_ss)
|
||||
@@ -39,6 +39,12 @@
|
||||
#define TCG_TARGET_TLB_DISPLACEMENT_BITS 16
|
||||
#define TCG_TARGET_NB_REGS 32
|
||||
|
||||
/*
|
||||
* We have a 256MB branch region, but leave room to make sure the
|
||||
* main executable is also within that region.
|
||||
*/
|
||||
#define MAX_CODE_GEN_BUFFER_SIZE (128 * MiB)
|
||||
|
||||
typedef enum {
|
||||
TCG_REG_ZERO = 0,
|
||||
TCG_REG_AT,
|
||||
|
||||
@@ -27,8 +27,10 @@
|
||||
|
||||
#ifdef _ARCH_PPC64
|
||||
# define TCG_TARGET_REG_BITS 64
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB)
|
||||
#else
|
||||
# define TCG_TARGET_REG_BITS 32
|
||||
# define MAX_CODE_GEN_BUFFER_SIZE (32 * MiB)
|
||||
#endif
|
||||
|
||||
#define TCG_TARGET_NB_REGS 64
|
||||
|
||||
+999
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user