Host support for riscv64.
Dead code elimination pass. Register allocation improvements. -----BEGIN PGP SIGNATURE----- iQEcBAABAgAGBQJcIpiSAAoJEGTfOOivfiFf6C0H/1eLxRh7KSaihgAfz/jxeclv 3XhJA8ffofFJPcOKQ3tDKeJXIz/4OqKM3XedktSYphKkoJQ0/Twhv9XPm9kZtghH 6Gkgw4pdLt+1zM90X1J97+46l0iY/WfDa84HpAg8Kxe99QEAWwnsGIvekmtyU3rd 1t4Z9ZiHYWDlDwI/Oudzs/vh+QOHjmyo+Y5xqOK+OMHtG45PoVdxxGaA+C+8vY3A TOnyvNA0T79CBNyKvdgdk4ADoEZh2vFYLqPwZeSLuYdw4Yr0oarRRnqvS2Hrb+Zr 30ZL6x40L0QQYAMN8xpj5JtaLMkpbDUXetCdyhngRzEJK0SjH5EjQrE3IQEmC2c= =B/4c -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/rth/tags/pull-tcg-20181226' into staging Host support for riscv64. Dead code elimination pass. Register allocation improvements. # gpg: Signature made Tue 25 Dec 2018 20:52:34 GMT # gpg: using RSA key 64DF38E8AF7E215F # gpg: Good signature from "Richard Henderson <richard.henderson@linaro.org>" # Primary key fingerprint: 7A48 1E78 868B 4DB6 A85A 05C0 64DF 38E8 AF7E 215F * remotes/rth/tags/pull-tcg-20181226: (42 commits) tcg: Improve call argument loading tcg: Record register preferences during liveness tcg: Add TCG_OPF_BB_EXIT tcg: Split out more subroutines from liveness_pass_1 tcg: Rename and adjust liveness_pass_1 helpers tcg: Reindent parts of liveness_pass_1 tcg: Dump register preference info with liveness tcg: Improve register allocation for matching constraints tcg: Add output_pref to TCGOp tcg: Add preferred_reg argument to tcg_reg_alloc_do_movi tcg: Add preferred_reg argument to temp_sync tcg: Add preferred_reg argument to temp_load tcg: Add preferred_reg argument to tcg_reg_alloc tcg: Add reachable_code_pass tcg: Reference count labels tcg: Add TCG_CALL_NO_RETURN tcg: Renumber TCG_CALL_* flags linux-user: Add safe_syscall for riscv64 host disas/microblaze: Remove unused REG_SP macro configure: Add support for building RISC-V host ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
1b3e80082b
12
MAINTAINERS
12
MAINTAINERS
|
@ -266,7 +266,8 @@ S: Maintained
|
|||
F: target/riscv/
|
||||
F: hw/riscv/
|
||||
F: include/hw/riscv/
|
||||
F: disas/riscv.c
|
||||
F: linux-user/host/riscv32/
|
||||
F: linux-user/host/riscv64/
|
||||
|
||||
S390
|
||||
M: Richard Henderson <rth@twiddle.net>
|
||||
|
@ -2172,6 +2173,15 @@ S: Odd Fixes
|
|||
F: tcg/ppc/
|
||||
F: disas/ppc.c
|
||||
|
||||
RISC-V
|
||||
M: Michael Clark <mjc@sifive.com>
|
||||
M: Palmer Dabbelt <palmer@sifive.com>
|
||||
M: Alistair Francis <Alistair.Francis@wdc.com>
|
||||
L: qemu-riscv@nongnu.org
|
||||
S: Maintained
|
||||
F: tcg/riscv/
|
||||
F: disas/riscv.c
|
||||
|
||||
S390 target
|
||||
M: Richard Henderson <rth@twiddle.net>
|
||||
S: Maintained
|
||||
|
|
|
@ -571,6 +571,81 @@ int cpu_signal_handler(int host_signum, void *pinfo,
|
|||
return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
|
||||
}
|
||||
|
||||
#elif defined(__riscv)
|
||||
|
||||
int cpu_signal_handler(int host_signum, void *pinfo,
|
||||
void *puc)
|
||||
{
|
||||
siginfo_t *info = pinfo;
|
||||
ucontext_t *uc = puc;
|
||||
greg_t pc = uc->uc_mcontext.__gregs[REG_PC];
|
||||
uint32_t insn = *(uint32_t *)pc;
|
||||
int is_write = 0;
|
||||
|
||||
/* Detect store by reading the instruction at the program
|
||||
counter. Note: we currently only generate 32-bit
|
||||
instructions so we thus only detect 32-bit stores */
|
||||
switch (((insn >> 0) & 0b11)) {
|
||||
case 3:
|
||||
switch (((insn >> 2) & 0b11111)) {
|
||||
case 8:
|
||||
switch (((insn >> 12) & 0b111)) {
|
||||
case 0: /* sb */
|
||||
case 1: /* sh */
|
||||
case 2: /* sw */
|
||||
case 3: /* sd */
|
||||
case 4: /* sq */
|
||||
is_write = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 9:
|
||||
switch (((insn >> 12) & 0b111)) {
|
||||
case 2: /* fsw */
|
||||
case 3: /* fsd */
|
||||
case 4: /* fsq */
|
||||
is_write = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for compressed instructions */
|
||||
switch (((insn >> 13) & 0b111)) {
|
||||
case 7:
|
||||
switch (insn & 0b11) {
|
||||
case 0: /*c.sd */
|
||||
case 2: /* c.sdsp */
|
||||
is_write = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
switch (insn & 0b11) {
|
||||
case 0: /* c.sw */
|
||||
case 3: /* c.swsp */
|
||||
is_write = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#error host CPU specific signal handler needed
|
||||
|
|
12
configure
vendored
12
configure
vendored
|
@ -710,6 +710,12 @@ elif check_define __s390__ ; then
|
|||
else
|
||||
cpu="s390"
|
||||
fi
|
||||
elif check_define __riscv ; then
|
||||
if check_define _LP64 ; then
|
||||
cpu="riscv64"
|
||||
else
|
||||
cpu="riscv32"
|
||||
fi
|
||||
elif check_define __arm__ ; then
|
||||
cpu="arm"
|
||||
elif check_define __aarch64__ ; then
|
||||
|
@ -722,7 +728,7 @@ ARCH=
|
|||
# Normalise host CPU name and set ARCH.
|
||||
# Note that this case should only have supported host CPUs, not guests.
|
||||
case "$cpu" in
|
||||
ppc|ppc64|s390|s390x|sparc64|x32)
|
||||
ppc|ppc64|s390|s390x|sparc64|x32|riscv32|riscv64)
|
||||
cpu="$cpu"
|
||||
supported_cpu="yes"
|
||||
eval "cross_cc_${cpu}=\$host_cc"
|
||||
|
@ -6937,6 +6943,8 @@ elif test "$ARCH" = "x86_64" -o "$ARCH" = "x32" ; then
|
|||
QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/i386 $QEMU_INCLUDES"
|
||||
elif test "$ARCH" = "ppc64" ; then
|
||||
QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/ppc $QEMU_INCLUDES"
|
||||
elif test "$ARCH" = "riscv32" -o "$ARCH" = "riscv64" ; then
|
||||
QEMU_INCLUDES="-I\$(SRC_PATH)/tcg/riscv $QEMU_INCLUDES"
|
||||
else
|
||||
QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/\$(ARCH) $QEMU_INCLUDES"
|
||||
fi
|
||||
|
@ -7433,7 +7441,7 @@ for i in $ARCH $TARGET_BASE_ARCH ; do
|
|||
ppc*)
|
||||
disas_config "PPC"
|
||||
;;
|
||||
riscv)
|
||||
riscv*)
|
||||
disas_config "RISCV"
|
||||
;;
|
||||
s390*)
|
||||
|
|
10
disas.c
10
disas.c
|
@ -522,8 +522,14 @@ void disas(FILE *out, void *code, unsigned long size)
|
|||
# ifdef _ARCH_PPC64
|
||||
s.info.cap_mode = CS_MODE_64;
|
||||
# endif
|
||||
#elif defined(__riscv__)
|
||||
print_insn = print_insn_riscv;
|
||||
#elif defined(__riscv) && defined(CONFIG_RISCV_DIS)
|
||||
#if defined(_ILP32) || (__riscv_xlen == 32)
|
||||
print_insn = print_insn_riscv32;
|
||||
#elif defined(_LP64)
|
||||
print_insn = print_insn_riscv64;
|
||||
#else
|
||||
#error unsupported RISC-V ABI
|
||||
#endif
|
||||
#elif defined(__aarch64__) && defined(CONFIG_ARM_A64_DIS)
|
||||
print_insn = print_insn_arm_a64;
|
||||
s.info.cap_arch = CS_ARCH_ARM64;
|
||||
|
|
|
@ -176,7 +176,6 @@ enum microblaze_instr_type {
|
|||
#define REG_TLBSX 36869 /* MMU: TLB Search Index reg */
|
||||
|
||||
/* alternate names for gen purpose regs */
|
||||
#define REG_SP 1 /* stack pointer */
|
||||
#define REG_ROSDP 2 /* read-only small data pointer */
|
||||
#define REG_RWSDP 13 /* read-write small data pointer */
|
||||
|
||||
|
|
|
@ -1338,6 +1338,61 @@ typedef struct {
|
|||
#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */
|
||||
#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */
|
||||
|
||||
/* RISC-V relocations. */
|
||||
#define R_RISCV_NONE 0
|
||||
#define R_RISCV_32 1
|
||||
#define R_RISCV_64 2
|
||||
#define R_RISCV_RELATIVE 3
|
||||
#define R_RISCV_COPY 4
|
||||
#define R_RISCV_JUMP_SLOT 5
|
||||
#define R_RISCV_TLS_DTPMOD32 6
|
||||
#define R_RISCV_TLS_DTPMOD64 7
|
||||
#define R_RISCV_TLS_DTPREL32 8
|
||||
#define R_RISCV_TLS_DTPREL64 9
|
||||
#define R_RISCV_TLS_TPREL32 10
|
||||
#define R_RISCV_TLS_TPREL64 11
|
||||
#define R_RISCV_BRANCH 16
|
||||
#define R_RISCV_JAL 17
|
||||
#define R_RISCV_CALL 18
|
||||
#define R_RISCV_CALL_PLT 19
|
||||
#define R_RISCV_GOT_HI20 20
|
||||
#define R_RISCV_TLS_GOT_HI20 21
|
||||
#define R_RISCV_TLS_GD_HI20 22
|
||||
#define R_RISCV_PCREL_HI20 23
|
||||
#define R_RISCV_PCREL_LO12_I 24
|
||||
#define R_RISCV_PCREL_LO12_S 25
|
||||
#define R_RISCV_HI20 26
|
||||
#define R_RISCV_LO12_I 27
|
||||
#define R_RISCV_LO12_S 28
|
||||
#define R_RISCV_TPREL_HI20 29
|
||||
#define R_RISCV_TPREL_LO12_I 30
|
||||
#define R_RISCV_TPREL_LO12_S 31
|
||||
#define R_RISCV_TPREL_ADD 32
|
||||
#define R_RISCV_ADD8 33
|
||||
#define R_RISCV_ADD16 34
|
||||
#define R_RISCV_ADD32 35
|
||||
#define R_RISCV_ADD64 36
|
||||
#define R_RISCV_SUB8 37
|
||||
#define R_RISCV_SUB16 38
|
||||
#define R_RISCV_SUB32 39
|
||||
#define R_RISCV_SUB64 40
|
||||
#define R_RISCV_GNU_VTINHERIT 41
|
||||
#define R_RISCV_GNU_VTENTRY 42
|
||||
#define R_RISCV_ALIGN 43
|
||||
#define R_RISCV_RVC_BRANCH 44
|
||||
#define R_RISCV_RVC_JUMP 45
|
||||
#define R_RISCV_RVC_LUI 46
|
||||
#define R_RISCV_GPREL_I 47
|
||||
#define R_RISCV_GPREL_S 48
|
||||
#define R_RISCV_TPREL_I 49
|
||||
#define R_RISCV_TPREL_S 50
|
||||
#define R_RISCV_RELAX 51
|
||||
#define R_RISCV_SUB6 52
|
||||
#define R_RISCV_SET6 53
|
||||
#define R_RISCV_SET8 54
|
||||
#define R_RISCV_SET16 55
|
||||
#define R_RISCV_SET32 56
|
||||
|
||||
typedef struct elf32_rel {
|
||||
Elf32_Addr r_offset;
|
||||
Elf32_Word r_info;
|
||||
|
|
|
@ -108,6 +108,19 @@
|
|||
#define dh_is_signed_env dh_is_signed_ptr
|
||||
#define dh_is_signed(t) dh_is_signed_##t
|
||||
|
||||
#define dh_callflag_i32 0
|
||||
#define dh_callflag_s32 0
|
||||
#define dh_callflag_int 0
|
||||
#define dh_callflag_i64 0
|
||||
#define dh_callflag_s64 0
|
||||
#define dh_callflag_f16 0
|
||||
#define dh_callflag_f32 0
|
||||
#define dh_callflag_f64 0
|
||||
#define dh_callflag_ptr 0
|
||||
#define dh_callflag_void 0
|
||||
#define dh_callflag_noreturn TCG_CALL_NO_RETURN
|
||||
#define dh_callflag(t) glue(dh_callflag_, dh_alias(t))
|
||||
|
||||
#define dh_sizemask(t, n) \
|
||||
((dh_is_64bit(t) << (n*2)) | (dh_is_signed(t) << (n*2+1)))
|
||||
|
||||
|
|
|
@ -11,36 +11,43 @@
|
|||
#define str(s) #s
|
||||
|
||||
#define DEF_HELPER_FLAGS_0(NAME, FLAGS, ret) \
|
||||
{ .func = HELPER(NAME), .name = str(NAME), .flags = FLAGS, \
|
||||
{ .func = HELPER(NAME), .name = str(NAME), \
|
||||
.flags = FLAGS | dh_callflag(ret), \
|
||||
.sizemask = dh_sizemask(ret, 0) },
|
||||
|
||||
#define DEF_HELPER_FLAGS_1(NAME, FLAGS, ret, t1) \
|
||||
{ .func = HELPER(NAME), .name = str(NAME), .flags = FLAGS, \
|
||||
{ .func = HELPER(NAME), .name = str(NAME), \
|
||||
.flags = FLAGS | dh_callflag(ret), \
|
||||
.sizemask = dh_sizemask(ret, 0) | dh_sizemask(t1, 1) },
|
||||
|
||||
#define DEF_HELPER_FLAGS_2(NAME, FLAGS, ret, t1, t2) \
|
||||
{ .func = HELPER(NAME), .name = str(NAME), .flags = FLAGS, \
|
||||
{ .func = HELPER(NAME), .name = str(NAME), \
|
||||
.flags = FLAGS | dh_callflag(ret), \
|
||||
.sizemask = dh_sizemask(ret, 0) | dh_sizemask(t1, 1) \
|
||||
| dh_sizemask(t2, 2) },
|
||||
|
||||
#define DEF_HELPER_FLAGS_3(NAME, FLAGS, ret, t1, t2, t3) \
|
||||
{ .func = HELPER(NAME), .name = str(NAME), .flags = FLAGS, \
|
||||
{ .func = HELPER(NAME), .name = str(NAME), \
|
||||
.flags = FLAGS | dh_callflag(ret), \
|
||||
.sizemask = dh_sizemask(ret, 0) | dh_sizemask(t1, 1) \
|
||||
| dh_sizemask(t2, 2) | dh_sizemask(t3, 3) },
|
||||
|
||||
#define DEF_HELPER_FLAGS_4(NAME, FLAGS, ret, t1, t2, t3, t4) \
|
||||
{ .func = HELPER(NAME), .name = str(NAME), .flags = FLAGS, \
|
||||
{ .func = HELPER(NAME), .name = str(NAME), \
|
||||
.flags = FLAGS | dh_callflag(ret), \
|
||||
.sizemask = dh_sizemask(ret, 0) | dh_sizemask(t1, 1) \
|
||||
| dh_sizemask(t2, 2) | dh_sizemask(t3, 3) | dh_sizemask(t4, 4) },
|
||||
|
||||
#define DEF_HELPER_FLAGS_5(NAME, FLAGS, ret, t1, t2, t3, t4, t5) \
|
||||
{ .func = HELPER(NAME), .name = str(NAME), .flags = FLAGS, \
|
||||
{ .func = HELPER(NAME), .name = str(NAME), \
|
||||
.flags = FLAGS | dh_callflag(ret), \
|
||||
.sizemask = dh_sizemask(ret, 0) | dh_sizemask(t1, 1) \
|
||||
| dh_sizemask(t2, 2) | dh_sizemask(t3, 3) | dh_sizemask(t4, 4) \
|
||||
| dh_sizemask(t5, 5) },
|
||||
|
||||
#define DEF_HELPER_FLAGS_6(NAME, FLAGS, ret, t1, t2, t3, t4, t5, t6) \
|
||||
{ .func = HELPER(NAME), .name = str(NAME), .flags = FLAGS, \
|
||||
{ .func = HELPER(NAME), .name = str(NAME), \
|
||||
.flags = FLAGS | dh_callflag(ret), \
|
||||
.sizemask = dh_sizemask(ret, 0) | dh_sizemask(t1, 1) \
|
||||
| dh_sizemask(t2, 2) | dh_sizemask(t3, 3) | dh_sizemask(t4, 4) \
|
||||
| dh_sizemask(t5, 5) | dh_sizemask(t6, 6) },
|
||||
|
|
|
@ -79,6 +79,7 @@
|
|||
#pragma GCC poison CONFIG_MOXIE_DIS
|
||||
#pragma GCC poison CONFIG_NIOS2_DIS
|
||||
#pragma GCC poison CONFIG_PPC_DIS
|
||||
#pragma GCC poison CONFIG_RISCV_DIS
|
||||
#pragma GCC poison CONFIG_S390_DIS
|
||||
#pragma GCC poison CONFIG_SH4_DIS
|
||||
#pragma GCC poison CONFIG_SPARC_DIS
|
||||
|
|
11
linux-user/host/riscv32/hostdep.h
Normal file
11
linux-user/host/riscv32/hostdep.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* hostdep.h : things which are dependent on the host architecture
|
||||
*
|
||||
* 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 RISCV32_HOSTDEP_H
|
||||
#define RISCV32_HOSTDEP_H
|
||||
|
||||
#endif
|
34
linux-user/host/riscv64/hostdep.h
Normal file
34
linux-user/host/riscv64/hostdep.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* hostdep.h : things which are dependent on the host architecture
|
||||
*
|
||||
* 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 RISCV64_HOSTDEP_H
|
||||
#define RISCV64_HOSTDEP_H
|
||||
|
||||
/* We have a safe-syscall.inc.S */
|
||||
#define HAVE_SAFE_SYSCALL
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
/* These are defined by the safe-syscall.inc.S file */
|
||||
extern char safe_syscall_start[];
|
||||
extern char safe_syscall_end[];
|
||||
|
||||
/* Adjust the signal context to rewind out of safe-syscall if we're in it */
|
||||
static inline void rewind_if_in_safe_syscall(void *puc)
|
||||
{
|
||||
ucontext_t *uc = puc;
|
||||
unsigned long *pcreg = &uc->uc_mcontext.__gregs[REG_PC];
|
||||
|
||||
if (*pcreg > (uintptr_t)safe_syscall_start
|
||||
&& *pcreg < (uintptr_t)safe_syscall_end) {
|
||||
*pcreg = (uintptr_t)safe_syscall_start;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* __ASSEMBLER__ */
|
||||
|
||||
#endif
|
77
linux-user/host/riscv64/safe-syscall.inc.S
Normal file
77
linux-user/host/riscv64/safe-syscall.inc.S
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* safe-syscall.inc.S : host-specific assembly fragment
|
||||
* to handle signals occurring at the same time as system calls.
|
||||
* This is intended to be included by linux-user/safe-syscall.S
|
||||
*
|
||||
* Written by Richard Henderson <rth@twiddle.net>
|
||||
* Copyright (C) 2018 Linaro, Inc.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
.global safe_syscall_base
|
||||
.global safe_syscall_start
|
||||
.global safe_syscall_end
|
||||
.type safe_syscall_base, @function
|
||||
.type safe_syscall_start, @function
|
||||
.type safe_syscall_end, @function
|
||||
|
||||
/*
|
||||
* This is the entry point for making a system call. The calling
|
||||
* convention here is that of a C varargs function with the
|
||||
* first argument an 'int *' to the signal_pending flag, the
|
||||
* second one the system call number (as a 'long'), and all further
|
||||
* arguments being syscall arguments (also 'long').
|
||||
* We return a long which is the syscall's return value, which
|
||||
* may be negative-errno on failure. Conversion to the
|
||||
* -1-and-errno-set convention is done by the calling wrapper.
|
||||
*/
|
||||
safe_syscall_base:
|
||||
.cfi_startproc
|
||||
/*
|
||||
* The syscall calling convention is nearly the same as C:
|
||||
* we enter with a0 == *signal_pending
|
||||
* a1 == syscall number
|
||||
* a2 ... a7 == syscall arguments
|
||||
* and return the result in a0
|
||||
* and the syscall instruction needs
|
||||
* a7 == syscall number
|
||||
* a0 ... a5 == syscall arguments
|
||||
* and returns the result in a0
|
||||
* Shuffle everything around appropriately.
|
||||
*/
|
||||
mv t0, a0 /* signal_pending pointer */
|
||||
mv t1, a1 /* syscall number */
|
||||
mv a0, a2 /* syscall arguments */
|
||||
mv a1, a3
|
||||
mv a2, a4
|
||||
mv a3, a5
|
||||
mv a4, a6
|
||||
mv a5, a7
|
||||
mv a7, t1
|
||||
|
||||
/*
|
||||
* This next sequence of code works in conjunction with the
|
||||
* rewind_if_safe_syscall_function(). If a signal is taken
|
||||
* and the interrupted PC is anywhere between 'safe_syscall_start'
|
||||
* and 'safe_syscall_end' then we rewind it to 'safe_syscall_start'.
|
||||
* The code sequence must therefore be able to cope with this, and
|
||||
* the syscall instruction must be the final one in the sequence.
|
||||
*/
|
||||
safe_syscall_start:
|
||||
/* If signal_pending is non-zero, don't do the call */
|
||||
lw t1, 0(t0)
|
||||
bnez t1, 0f
|
||||
scall
|
||||
safe_syscall_end:
|
||||
/* code path for having successfully executed the syscall */
|
||||
ret
|
||||
|
||||
0:
|
||||
/* code path when we didn't execute the syscall */
|
||||
li a0, -TARGET_ERESTARTSYS
|
||||
ret
|
||||
.cfi_endproc
|
||||
|
||||
.size safe_syscall_base, .-safe_syscall_base
|
177
tcg/riscv/tcg-target.h
Normal file
177
tcg/riscv/tcg-target.h
Normal file
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* Tiny Code Generator for QEMU
|
||||
*
|
||||
* Copyright (c) 2018 SiFive, Inc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef RISCV_TCG_TARGET_H
|
||||
#define RISCV_TCG_TARGET_H
|
||||
|
||||
#if __riscv_xlen == 32
|
||||
# define TCG_TARGET_REG_BITS 32
|
||||
#elif __riscv_xlen == 64
|
||||
# define TCG_TARGET_REG_BITS 64
|
||||
#endif
|
||||
|
||||
#define TCG_TARGET_INSN_UNIT_SIZE 4
|
||||
#define TCG_TARGET_TLB_DISPLACEMENT_BITS 20
|
||||
#define TCG_TARGET_NB_REGS 32
|
||||
|
||||
typedef enum {
|
||||
TCG_REG_ZERO,
|
||||
TCG_REG_RA,
|
||||
TCG_REG_SP,
|
||||
TCG_REG_GP,
|
||||
TCG_REG_TP,
|
||||
TCG_REG_T0,
|
||||
TCG_REG_T1,
|
||||
TCG_REG_T2,
|
||||
TCG_REG_S0,
|
||||
TCG_REG_S1,
|
||||
TCG_REG_A0,
|
||||
TCG_REG_A1,
|
||||
TCG_REG_A2,
|
||||
TCG_REG_A3,
|
||||
TCG_REG_A4,
|
||||
TCG_REG_A5,
|
||||
TCG_REG_A6,
|
||||
TCG_REG_A7,
|
||||
TCG_REG_S2,
|
||||
TCG_REG_S3,
|
||||
TCG_REG_S4,
|
||||
TCG_REG_S5,
|
||||
TCG_REG_S6,
|
||||
TCG_REG_S7,
|
||||
TCG_REG_S8,
|
||||
TCG_REG_S9,
|
||||
TCG_REG_S10,
|
||||
TCG_REG_S11,
|
||||
TCG_REG_T3,
|
||||
TCG_REG_T4,
|
||||
TCG_REG_T5,
|
||||
TCG_REG_T6,
|
||||
|
||||
/* aliases */
|
||||
TCG_AREG0 = TCG_REG_S0,
|
||||
TCG_GUEST_BASE_REG = TCG_REG_S1,
|
||||
TCG_REG_TMP0 = TCG_REG_T6,
|
||||
TCG_REG_TMP1 = TCG_REG_T5,
|
||||
TCG_REG_TMP2 = TCG_REG_T4,
|
||||
} TCGReg;
|
||||
|
||||
/* used for function call generation */
|
||||
#define TCG_REG_CALL_STACK TCG_REG_SP
|
||||
#define TCG_TARGET_STACK_ALIGN 16
|
||||
#define TCG_TARGET_CALL_ALIGN_ARGS 1
|
||||
#define TCG_TARGET_CALL_STACK_OFFSET 0
|
||||
|
||||
/* optional instructions */
|
||||
#define TCG_TARGET_HAS_goto_ptr 1
|
||||
#define TCG_TARGET_HAS_movcond_i32 0
|
||||
#define TCG_TARGET_HAS_div_i32 1
|
||||
#define TCG_TARGET_HAS_rem_i32 1
|
||||
#define TCG_TARGET_HAS_div2_i32 0
|
||||
#define TCG_TARGET_HAS_rot_i32 0
|
||||
#define TCG_TARGET_HAS_deposit_i32 0
|
||||
#define TCG_TARGET_HAS_extract_i32 0
|
||||
#define TCG_TARGET_HAS_sextract_i32 0
|
||||
#define TCG_TARGET_HAS_add2_i32 1
|
||||
#define TCG_TARGET_HAS_sub2_i32 1
|
||||
#define TCG_TARGET_HAS_mulu2_i32 0
|
||||
#define TCG_TARGET_HAS_muls2_i32 0
|
||||
#define TCG_TARGET_HAS_muluh_i32 (TCG_TARGET_REG_BITS == 32)
|
||||
#define TCG_TARGET_HAS_mulsh_i32 (TCG_TARGET_REG_BITS == 32)
|
||||
#define TCG_TARGET_HAS_ext8s_i32 1
|
||||
#define TCG_TARGET_HAS_ext16s_i32 1
|
||||
#define TCG_TARGET_HAS_ext8u_i32 1
|
||||
#define TCG_TARGET_HAS_ext16u_i32 1
|
||||
#define TCG_TARGET_HAS_bswap16_i32 0
|
||||
#define TCG_TARGET_HAS_bswap32_i32 0
|
||||
#define TCG_TARGET_HAS_not_i32 1
|
||||
#define TCG_TARGET_HAS_neg_i32 1
|
||||
#define TCG_TARGET_HAS_andc_i32 0
|
||||
#define TCG_TARGET_HAS_orc_i32 0
|
||||
#define TCG_TARGET_HAS_eqv_i32 0
|
||||
#define TCG_TARGET_HAS_nand_i32 0
|
||||
#define TCG_TARGET_HAS_nor_i32 0
|
||||
#define TCG_TARGET_HAS_clz_i32 0
|
||||
#define TCG_TARGET_HAS_ctz_i32 0
|
||||
#define TCG_TARGET_HAS_ctpop_i32 0
|
||||
#define TCG_TARGET_HAS_direct_jump 0
|
||||
#define TCG_TARGET_HAS_brcond2 1
|
||||
#define TCG_TARGET_HAS_setcond2 1
|
||||
|
||||
#if TCG_TARGET_REG_BITS == 64
|
||||
#define TCG_TARGET_HAS_movcond_i64 0
|
||||
#define TCG_TARGET_HAS_div_i64 1
|
||||
#define TCG_TARGET_HAS_rem_i64 1
|
||||
#define TCG_TARGET_HAS_div2_i64 0
|
||||
#define TCG_TARGET_HAS_rot_i64 0
|
||||
#define TCG_TARGET_HAS_deposit_i64 0
|
||||
#define TCG_TARGET_HAS_extract_i64 0
|
||||
#define TCG_TARGET_HAS_sextract_i64 0
|
||||
#define TCG_TARGET_HAS_extrl_i64_i32 1
|
||||
#define TCG_TARGET_HAS_extrh_i64_i32 1
|
||||
#define TCG_TARGET_HAS_ext8s_i64 1
|
||||
#define TCG_TARGET_HAS_ext16s_i64 1
|
||||
#define TCG_TARGET_HAS_ext32s_i64 1
|
||||
#define TCG_TARGET_HAS_ext8u_i64 1
|
||||
#define TCG_TARGET_HAS_ext16u_i64 1
|
||||
#define TCG_TARGET_HAS_ext32u_i64 1
|
||||
#define TCG_TARGET_HAS_bswap16_i64 0
|
||||
#define TCG_TARGET_HAS_bswap32_i64 0
|
||||
#define TCG_TARGET_HAS_bswap64_i64 0
|
||||
#define TCG_TARGET_HAS_not_i64 1
|
||||
#define TCG_TARGET_HAS_neg_i64 1
|
||||
#define TCG_TARGET_HAS_andc_i64 0
|
||||
#define TCG_TARGET_HAS_orc_i64 0
|
||||
#define TCG_TARGET_HAS_eqv_i64 0
|
||||
#define TCG_TARGET_HAS_nand_i64 0
|
||||
#define TCG_TARGET_HAS_nor_i64 0
|
||||
#define TCG_TARGET_HAS_clz_i64 0
|
||||
#define TCG_TARGET_HAS_ctz_i64 0
|
||||
#define TCG_TARGET_HAS_ctpop_i64 0
|
||||
#define TCG_TARGET_HAS_add2_i64 1
|
||||
#define TCG_TARGET_HAS_sub2_i64 1
|
||||
#define TCG_TARGET_HAS_mulu2_i64 0
|
||||
#define TCG_TARGET_HAS_muls2_i64 0
|
||||
#define TCG_TARGET_HAS_muluh_i64 1
|
||||
#define TCG_TARGET_HAS_mulsh_i64 1
|
||||
#endif
|
||||
|
||||
static inline void flush_icache_range(uintptr_t start, uintptr_t stop)
|
||||
{
|
||||
__builtin___clear_cache((char *)start, (char *)stop);
|
||||
}
|
||||
|
||||
/* not defined -- call should be eliminated at compile time */
|
||||
void tb_target_set_jmp_target(uintptr_t, uintptr_t, uintptr_t);
|
||||
|
||||
#define TCG_TARGET_DEFAULT_MO (0)
|
||||
|
||||
#ifdef CONFIG_SOFTMMU
|
||||
#define TCG_TARGET_NEED_LDST_LABELS
|
||||
#endif
|
||||
#define TCG_TARGET_NEED_POOL_LABELS
|
||||
|
||||
#define TCG_TARGET_HAS_MEMORY_BSWAP 0
|
||||
|
||||
#endif
|
1949
tcg/riscv/tcg-target.inc.c
Normal file
1949
tcg/riscv/tcg-target.inc.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -240,6 +240,7 @@ void tcg_gen_brcond_i32(TCGCond cond, TCGv_i32 arg1, TCGv_i32 arg2, TCGLabel *l)
|
|||
if (cond == TCG_COND_ALWAYS) {
|
||||
tcg_gen_br(l);
|
||||
} else if (cond != TCG_COND_NEVER) {
|
||||
l->refs++;
|
||||
tcg_gen_op4ii_i32(INDEX_op_brcond_i32, arg1, arg2, cond, label_arg(l));
|
||||
}
|
||||
}
|
||||
|
@ -1405,6 +1406,7 @@ void tcg_gen_brcond_i64(TCGCond cond, TCGv_i64 arg1, TCGv_i64 arg2, TCGLabel *l)
|
|||
if (cond == TCG_COND_ALWAYS) {
|
||||
tcg_gen_br(l);
|
||||
} else if (cond != TCG_COND_NEVER) {
|
||||
l->refs++;
|
||||
if (TCG_TARGET_REG_BITS == 32) {
|
||||
tcg_gen_op6ii_i32(INDEX_op_brcond2_i32, TCGV_LOW(arg1),
|
||||
TCGV_HIGH(arg1), TCGV_LOW(arg2),
|
||||
|
|
|
@ -260,6 +260,7 @@ static inline void gen_set_label(TCGLabel *l)
|
|||
|
||||
static inline void tcg_gen_br(TCGLabel *l)
|
||||
{
|
||||
l->refs++;
|
||||
tcg_gen_op1(INDEX_op_br, label_arg(l));
|
||||
}
|
||||
|
||||
|
|
|
@ -191,9 +191,10 @@ DEF(mulsh_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_mulsh_i64))
|
|||
/* QEMU specific */
|
||||
DEF(insn_start, 0, 0, TLADDR_ARGS * TARGET_INSN_START_WORDS,
|
||||
TCG_OPF_NOT_PRESENT)
|
||||
DEF(exit_tb, 0, 0, 1, TCG_OPF_BB_END)
|
||||
DEF(goto_tb, 0, 0, 1, TCG_OPF_BB_END)
|
||||
DEF(goto_ptr, 0, 1, 0, TCG_OPF_BB_END | IMPL(TCG_TARGET_HAS_goto_ptr))
|
||||
DEF(exit_tb, 0, 0, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END)
|
||||
DEF(goto_tb, 0, 0, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END)
|
||||
DEF(goto_ptr, 0, 1, 0,
|
||||
TCG_OPF_BB_EXIT | TCG_OPF_BB_END | IMPL(TCG_TARGET_HAS_goto_ptr))
|
||||
|
||||
DEF(qemu_ld_i32, 1, TLADDR_ARGS, 1,
|
||||
TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
|
||||
|
|
626
tcg/tcg.c
626
tcg/tcg.c
|
@ -1887,7 +1887,21 @@ static const char * const alignment_name[(MO_AMASK >> MO_ASHIFT) + 1] = {
|
|||
[MO_ALIGN_64 >> MO_ASHIFT] = "al64+",
|
||||
};
|
||||
|
||||
void tcg_dump_ops(TCGContext *s)
|
||||
static inline bool tcg_regset_single(TCGRegSet d)
|
||||
{
|
||||
return (d & (d - 1)) == 0;
|
||||
}
|
||||
|
||||
static inline TCGReg tcg_regset_first(TCGRegSet d)
|
||||
{
|
||||
if (TCG_TARGET_NB_REGS <= 32) {
|
||||
return ctz32(d);
|
||||
} else {
|
||||
return ctz64(d);
|
||||
}
|
||||
}
|
||||
|
||||
static void tcg_dump_ops(TCGContext *s, bool have_prefs)
|
||||
{
|
||||
char buf[128];
|
||||
TCGOp *op;
|
||||
|
@ -1902,6 +1916,7 @@ void tcg_dump_ops(TCGContext *s)
|
|||
def = &tcg_op_defs[c];
|
||||
|
||||
if (c == INDEX_op_insn_start) {
|
||||
nb_oargs = 0;
|
||||
col += qemu_log("\n ----");
|
||||
|
||||
for (i = 0; i < TARGET_INSN_START_WORDS; ++i) {
|
||||
|
@ -2021,12 +2036,15 @@ void tcg_dump_ops(TCGContext *s)
|
|||
col += qemu_log("%s$0x%" TCG_PRIlx, k ? "," : "", op->args[k]);
|
||||
}
|
||||
}
|
||||
if (op->life) {
|
||||
unsigned life = op->life;
|
||||
|
||||
for (; col < 48; ++col) {
|
||||
if (have_prefs || op->life) {
|
||||
for (; col < 40; ++col) {
|
||||
putc(' ', qemu_logfile);
|
||||
}
|
||||
}
|
||||
|
||||
if (op->life) {
|
||||
unsigned life = op->life;
|
||||
|
||||
if (life & (SYNC_ARG * 3)) {
|
||||
qemu_log(" sync:");
|
||||
|
@ -2046,6 +2064,33 @@ void tcg_dump_ops(TCGContext *s)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (have_prefs) {
|
||||
for (i = 0; i < nb_oargs; ++i) {
|
||||
TCGRegSet set = op->output_pref[i];
|
||||
|
||||
if (i == 0) {
|
||||
qemu_log(" pref=");
|
||||
} else {
|
||||
qemu_log(",");
|
||||
}
|
||||
if (set == 0) {
|
||||
qemu_log("none");
|
||||
} else if (set == MAKE_64BIT_MASK(0, TCG_TARGET_NB_REGS)) {
|
||||
qemu_log("all");
|
||||
#ifdef CONFIG_DEBUG_TCG
|
||||
} else if (tcg_regset_single(set)) {
|
||||
TCGReg reg = tcg_regset_first(set);
|
||||
qemu_log("%s", tcg_target_reg_names[reg]);
|
||||
#endif
|
||||
} else if (TCG_TARGET_NB_REGS <= 32) {
|
||||
qemu_log("%#x", (uint32_t)set);
|
||||
} else {
|
||||
qemu_log("%#" PRIx64, (uint64_t)set);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qemu_log("\n");
|
||||
}
|
||||
}
|
||||
|
@ -2171,6 +2216,26 @@ static void process_op_defs(TCGContext *s)
|
|||
|
||||
void tcg_op_remove(TCGContext *s, TCGOp *op)
|
||||
{
|
||||
TCGLabel *label;
|
||||
|
||||
switch (op->opc) {
|
||||
case INDEX_op_br:
|
||||
label = arg_label(op->args[0]);
|
||||
label->refs--;
|
||||
break;
|
||||
case INDEX_op_brcond_i32:
|
||||
case INDEX_op_brcond_i64:
|
||||
label = arg_label(op->args[3]);
|
||||
label->refs--;
|
||||
break;
|
||||
case INDEX_op_brcond2_i32:
|
||||
label = arg_label(op->args[5]);
|
||||
label->refs--;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
QTAILQ_REMOVE(&s->ops, op, link);
|
||||
QTAILQ_INSERT_TAIL(&s->free_ops, op, link);
|
||||
s->nb_ops--;
|
||||
|
@ -2219,43 +2284,181 @@ TCGOp *tcg_op_insert_after(TCGContext *s, TCGOp *old_op, TCGOpcode opc)
|
|||
return new_op;
|
||||
}
|
||||
|
||||
/* Reachable analysis : remove unreachable code. */
|
||||
static void reachable_code_pass(TCGContext *s)
|
||||
{
|
||||
TCGOp *op, *op_next;
|
||||
bool dead = false;
|
||||
|
||||
QTAILQ_FOREACH_SAFE(op, &s->ops, link, op_next) {
|
||||
bool remove = dead;
|
||||
TCGLabel *label;
|
||||
int call_flags;
|
||||
|
||||
switch (op->opc) {
|
||||
case INDEX_op_set_label:
|
||||
label = arg_label(op->args[0]);
|
||||
if (label->refs == 0) {
|
||||
/*
|
||||
* While there is an occasional backward branch, virtually
|
||||
* all branches generated by the translators are forward.
|
||||
* Which means that generally we will have already removed
|
||||
* all references to the label that will be, and there is
|
||||
* little to be gained by iterating.
|
||||
*/
|
||||
remove = true;
|
||||
} else {
|
||||
/* Once we see a label, insns become live again. */
|
||||
dead = false;
|
||||
remove = false;
|
||||
|
||||
/*
|
||||
* Optimization can fold conditional branches to unconditional.
|
||||
* If we find a label with one reference which is preceded by
|
||||
* an unconditional branch to it, remove both. This needed to
|
||||
* wait until the dead code in between them was removed.
|
||||
*/
|
||||
if (label->refs == 1) {
|
||||
TCGOp *op_prev = QTAILQ_PREV(op, TCGOpHead, link);
|
||||
if (op_prev->opc == INDEX_op_br &&
|
||||
label == arg_label(op_prev->args[0])) {
|
||||
tcg_op_remove(s, op_prev);
|
||||
remove = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case INDEX_op_br:
|
||||
case INDEX_op_exit_tb:
|
||||
case INDEX_op_goto_ptr:
|
||||
/* Unconditional branches; everything following is dead. */
|
||||
dead = true;
|
||||
break;
|
||||
|
||||
case INDEX_op_call:
|
||||
/* Notice noreturn helper calls, raising exceptions. */
|
||||
call_flags = op->args[TCGOP_CALLO(op) + TCGOP_CALLI(op) + 1];
|
||||
if (call_flags & TCG_CALL_NO_RETURN) {
|
||||
dead = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case INDEX_op_insn_start:
|
||||
/* Never remove -- we need to keep these for unwind. */
|
||||
remove = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (remove) {
|
||||
tcg_op_remove(s, op);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define TS_DEAD 1
|
||||
#define TS_MEM 2
|
||||
|
||||
#define IS_DEAD_ARG(n) (arg_life & (DEAD_ARG << (n)))
|
||||
#define NEED_SYNC_ARG(n) (arg_life & (SYNC_ARG << (n)))
|
||||
|
||||
/* For liveness_pass_1, the register preferences for a given temp. */
|
||||
static inline TCGRegSet *la_temp_pref(TCGTemp *ts)
|
||||
{
|
||||
return ts->state_ptr;
|
||||
}
|
||||
|
||||
/* For liveness_pass_1, reset the preferences for a given temp to the
|
||||
* maximal regset for its type.
|
||||
*/
|
||||
static inline void la_reset_pref(TCGTemp *ts)
|
||||
{
|
||||
*la_temp_pref(ts)
|
||||
= (ts->state == TS_DEAD ? 0 : tcg_target_available_regs[ts->type]);
|
||||
}
|
||||
|
||||
/* liveness analysis: end of function: all temps are dead, and globals
|
||||
should be in memory. */
|
||||
static void tcg_la_func_end(TCGContext *s)
|
||||
static void la_func_end(TCGContext *s, int ng, int nt)
|
||||
{
|
||||
int ng = s->nb_globals;
|
||||
int nt = s->nb_temps;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ng; ++i) {
|
||||
s->temps[i].state = TS_DEAD | TS_MEM;
|
||||
la_reset_pref(&s->temps[i]);
|
||||
}
|
||||
for (i = ng; i < nt; ++i) {
|
||||
s->temps[i].state = TS_DEAD;
|
||||
la_reset_pref(&s->temps[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* liveness analysis: end of basic block: all temps are dead, globals
|
||||
and local temps should be in memory. */
|
||||
static void tcg_la_bb_end(TCGContext *s)
|
||||
static void la_bb_end(TCGContext *s, int ng, int nt)
|
||||
{
|
||||
int ng = s->nb_globals;
|
||||
int nt = s->nb_temps;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ng; ++i) {
|
||||
s->temps[i].state = TS_DEAD | TS_MEM;
|
||||
la_reset_pref(&s->temps[i]);
|
||||
}
|
||||
for (i = ng; i < nt; ++i) {
|
||||
s->temps[i].state = (s->temps[i].temp_local
|
||||
? TS_DEAD | TS_MEM
|
||||
: TS_DEAD);
|
||||
la_reset_pref(&s->temps[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* liveness analysis: sync globals back to memory. */
|
||||
static void la_global_sync(TCGContext *s, int ng)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ng; ++i) {
|
||||
int state = s->temps[i].state;
|
||||
s->temps[i].state = state | TS_MEM;
|
||||
if (state == TS_DEAD) {
|
||||
/* If the global was previously dead, reset prefs. */
|
||||
la_reset_pref(&s->temps[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* liveness analysis: sync globals back to memory and kill. */
|
||||
static void la_global_kill(TCGContext *s, int ng)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ng; i++) {
|
||||
s->temps[i].state = TS_DEAD | TS_MEM;
|
||||
la_reset_pref(&s->temps[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* liveness analysis: note live globals crossing calls. */
|
||||
static void la_cross_call(TCGContext *s, int nt)
|
||||
{
|
||||
TCGRegSet mask = ~tcg_target_call_clobber_regs;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nt; i++) {
|
||||
TCGTemp *ts = &s->temps[i];
|
||||
if (!(ts->state & TS_DEAD)) {
|
||||
TCGRegSet *pset = la_temp_pref(ts);
|
||||
TCGRegSet set = *pset;
|
||||
|
||||
set &= mask;
|
||||
/* If the combination is not possible, restart. */
|
||||
if (set == 0) {
|
||||
set = tcg_target_available_regs[ts->type] & mask;
|
||||
}
|
||||
*pset = set;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2265,16 +2468,25 @@ static void tcg_la_bb_end(TCGContext *s)
|
|||
static void liveness_pass_1(TCGContext *s)
|
||||
{
|
||||
int nb_globals = s->nb_globals;
|
||||
int nb_temps = s->nb_temps;
|
||||
TCGOp *op, *op_prev;
|
||||
TCGRegSet *prefs;
|
||||
int i;
|
||||
|
||||
tcg_la_func_end(s);
|
||||
prefs = tcg_malloc(sizeof(TCGRegSet) * nb_temps);
|
||||
for (i = 0; i < nb_temps; ++i) {
|
||||
s->temps[i].state_ptr = prefs + i;
|
||||
}
|
||||
|
||||
/* ??? Should be redundant with the exit_tb that ends the TB. */
|
||||
la_func_end(s, nb_globals, nb_temps);
|
||||
|
||||
QTAILQ_FOREACH_REVERSE_SAFE(op, &s->ops, TCGOpHead, link, op_prev) {
|
||||
int i, nb_iargs, nb_oargs;
|
||||
int nb_iargs, nb_oargs;
|
||||
TCGOpcode opc_new, opc_new2;
|
||||
bool have_opc_new2;
|
||||
TCGLifeData arg_life = 0;
|
||||
TCGTemp *arg_ts;
|
||||
TCGTemp *ts;
|
||||
TCGOpcode opc = op->opc;
|
||||
const TCGOpDef *def = &tcg_op_defs[opc];
|
||||
|
||||
|
@ -2282,6 +2494,7 @@ static void liveness_pass_1(TCGContext *s)
|
|||
case INDEX_op_call:
|
||||
{
|
||||
int call_flags;
|
||||
int nb_call_regs;
|
||||
|
||||
nb_oargs = TCGOP_CALLO(op);
|
||||
nb_iargs = TCGOP_CALLI(op);
|
||||
|
@ -2290,53 +2503,74 @@ static void liveness_pass_1(TCGContext *s)
|
|||
/* pure functions can be removed if their result is unused */
|
||||
if (call_flags & TCG_CALL_NO_SIDE_EFFECTS) {
|
||||
for (i = 0; i < nb_oargs; i++) {
|
||||
arg_ts = arg_temp(op->args[i]);
|
||||
if (arg_ts->state != TS_DEAD) {
|
||||
ts = arg_temp(op->args[i]);
|
||||
if (ts->state != TS_DEAD) {
|
||||
goto do_not_remove_call;
|
||||
}
|
||||
}
|
||||
goto do_remove;
|
||||
} else {
|
||||
do_not_remove_call:
|
||||
}
|
||||
do_not_remove_call:
|
||||
|
||||
/* output args are dead */
|
||||
for (i = 0; i < nb_oargs; i++) {
|
||||
arg_ts = arg_temp(op->args[i]);
|
||||
if (arg_ts->state & TS_DEAD) {
|
||||
arg_life |= DEAD_ARG << i;
|
||||
}
|
||||
if (arg_ts->state & TS_MEM) {
|
||||
arg_life |= SYNC_ARG << i;
|
||||
}
|
||||
arg_ts->state = TS_DEAD;
|
||||
/* Output args are dead. */
|
||||
for (i = 0; i < nb_oargs; i++) {
|
||||
ts = arg_temp(op->args[i]);
|
||||
if (ts->state & TS_DEAD) {
|
||||
arg_life |= DEAD_ARG << i;
|
||||
}
|
||||
if (ts->state & TS_MEM) {
|
||||
arg_life |= SYNC_ARG << i;
|
||||
}
|
||||
ts->state = TS_DEAD;
|
||||
la_reset_pref(ts);
|
||||
|
||||
if (!(call_flags & (TCG_CALL_NO_WRITE_GLOBALS |
|
||||
TCG_CALL_NO_READ_GLOBALS))) {
|
||||
/* globals should go back to memory */
|
||||
for (i = 0; i < nb_globals; i++) {
|
||||
s->temps[i].state = TS_DEAD | TS_MEM;
|
||||
}
|
||||
} else if (!(call_flags & TCG_CALL_NO_READ_GLOBALS)) {
|
||||
/* globals should be synced to memory */
|
||||
for (i = 0; i < nb_globals; i++) {
|
||||
s->temps[i].state |= TS_MEM;
|
||||
}
|
||||
}
|
||||
/* Not used -- it will be tcg_target_call_oarg_regs[i]. */
|
||||
op->output_pref[i] = 0;
|
||||
}
|
||||
|
||||
/* record arguments that die in this helper */
|
||||
for (i = nb_oargs; i < nb_iargs + nb_oargs; i++) {
|
||||
arg_ts = arg_temp(op->args[i]);
|
||||
if (arg_ts && arg_ts->state & TS_DEAD) {
|
||||
arg_life |= DEAD_ARG << i;
|
||||
}
|
||||
if (!(call_flags & (TCG_CALL_NO_WRITE_GLOBALS |
|
||||
TCG_CALL_NO_READ_GLOBALS))) {
|
||||
la_global_kill(s, nb_globals);
|
||||
} else if (!(call_flags & TCG_CALL_NO_READ_GLOBALS)) {
|
||||
la_global_sync(s, nb_globals);
|
||||
}
|
||||
|
||||
/* Record arguments that die in this helper. */
|
||||
for (i = nb_oargs; i < nb_iargs + nb_oargs; i++) {
|
||||
ts = arg_temp(op->args[i]);
|
||||
if (ts && ts->state & TS_DEAD) {
|
||||
arg_life |= DEAD_ARG << i;
|
||||
}
|
||||
/* input arguments are live for preceding opcodes */
|
||||
for (i = nb_oargs; i < nb_iargs + nb_oargs; i++) {
|
||||
arg_ts = arg_temp(op->args[i]);
|
||||
if (arg_ts) {
|
||||
arg_ts->state &= ~TS_DEAD;
|
||||
}
|
||||
}
|
||||
|
||||
/* For all live registers, remove call-clobbered prefs. */
|
||||
la_cross_call(s, nb_temps);
|
||||
|
||||
nb_call_regs = ARRAY_SIZE(tcg_target_call_iarg_regs);
|
||||
|
||||
/* Input arguments are live for preceding opcodes. */
|
||||
for (i = 0; i < nb_iargs; i++) {
|
||||
ts = arg_temp(op->args[i + nb_oargs]);
|
||||
if (ts && ts->state & TS_DEAD) {
|
||||
/* For those arguments that die, and will be allocated
|
||||
* in registers, clear the register set for that arg,
|
||||
* to be filled in below. For args that will be on
|
||||
* the stack, reset to any available reg.
|
||||
*/
|
||||
*la_temp_pref(ts)
|
||||
= (i < nb_call_regs ? 0 :
|
||||
tcg_target_available_regs[ts->type]);
|
||||
ts->state &= ~TS_DEAD;
|
||||
}
|
||||
}
|
||||
|
||||
/* For each input argument, add its input register to prefs.
|
||||
If a temp is used once, this produces a single set bit. */
|
||||
for (i = 0; i < MIN(nb_call_regs, nb_iargs); i++) {
|
||||
ts = arg_temp(op->args[i + nb_oargs]);
|
||||
if (ts) {
|
||||
tcg_regset_set_reg(*la_temp_pref(ts),
|
||||
tcg_target_call_iarg_regs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2345,7 +2579,9 @@ static void liveness_pass_1(TCGContext *s)
|
|||
break;
|
||||
case INDEX_op_discard:
|
||||
/* mark the temporary as dead */
|
||||
arg_temp(op->args[0])->state = TS_DEAD;
|
||||
ts = arg_temp(op->args[0]);
|
||||
ts->state = TS_DEAD;
|
||||
la_reset_pref(ts);
|
||||
break;
|
||||
|
||||
case INDEX_op_add2_i32:
|
||||
|
@ -2440,43 +2676,96 @@ static void liveness_pass_1(TCGContext *s)
|
|||
goto do_not_remove;
|
||||
}
|
||||
}
|
||||
do_remove:
|
||||
tcg_op_remove(s, op);
|
||||
} else {
|
||||
do_not_remove:
|
||||
/* output args are dead */
|
||||
for (i = 0; i < nb_oargs; i++) {
|
||||
arg_ts = arg_temp(op->args[i]);
|
||||
if (arg_ts->state & TS_DEAD) {
|
||||
arg_life |= DEAD_ARG << i;
|
||||
}
|
||||
if (arg_ts->state & TS_MEM) {
|
||||
arg_life |= SYNC_ARG << i;
|
||||
}
|
||||
arg_ts->state = TS_DEAD;
|
||||
}
|
||||
goto do_remove;
|
||||
}
|
||||
goto do_not_remove;
|
||||
|
||||
/* if end of basic block, update */
|
||||
if (def->flags & TCG_OPF_BB_END) {
|
||||
tcg_la_bb_end(s);
|
||||
} else if (def->flags & TCG_OPF_SIDE_EFFECTS) {
|
||||
/* globals should be synced to memory */
|
||||
for (i = 0; i < nb_globals; i++) {
|
||||
s->temps[i].state |= TS_MEM;
|
||||
}
|
||||
}
|
||||
do_remove:
|
||||
tcg_op_remove(s, op);
|
||||
break;
|
||||
|
||||
/* record arguments that die in this opcode */
|
||||
do_not_remove:
|
||||
for (i = 0; i < nb_oargs; i++) {
|
||||
ts = arg_temp(op->args[i]);
|
||||
|
||||
/* Remember the preference of the uses that followed. */
|
||||
op->output_pref[i] = *la_temp_pref(ts);
|
||||
|
||||
/* Output args are dead. */
|
||||
if (ts->state & TS_DEAD) {
|
||||
arg_life |= DEAD_ARG << i;
|
||||
}
|
||||
if (ts->state & TS_MEM) {
|
||||
arg_life |= SYNC_ARG << i;
|
||||
}
|
||||
ts->state = TS_DEAD;
|
||||
la_reset_pref(ts);
|
||||
}
|
||||
|
||||
/* If end of basic block, update. */
|
||||
if (def->flags & TCG_OPF_BB_EXIT) {
|
||||
la_func_end(s, nb_globals, nb_temps);
|
||||
} else if (def->flags & TCG_OPF_BB_END) {
|
||||
la_bb_end(s, nb_globals, nb_temps);
|
||||
} else if (def->flags & TCG_OPF_SIDE_EFFECTS) {
|
||||
la_global_sync(s, nb_globals);
|
||||
if (def->flags & TCG_OPF_CALL_CLOBBER) {
|
||||
la_cross_call(s, nb_temps);
|
||||
}
|
||||
}
|
||||
|
||||
/* Record arguments that die in this opcode. */
|
||||
for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) {
|
||||
ts = arg_temp(op->args[i]);
|
||||
if (ts->state & TS_DEAD) {
|
||||
arg_life |= DEAD_ARG << i;
|
||||
}
|
||||
}
|
||||
|
||||
/* Input arguments are live for preceding opcodes. */
|
||||
for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) {
|
||||
ts = arg_temp(op->args[i]);
|
||||
if (ts->state & TS_DEAD) {
|
||||
/* For operands that were dead, initially allow
|
||||
all regs for the type. */
|
||||
*la_temp_pref(ts) = tcg_target_available_regs[ts->type];
|
||||
ts->state &= ~TS_DEAD;
|
||||
}
|
||||
}
|
||||
|
||||
/* Incorporate constraints for this operand. */
|
||||
switch (opc) {
|
||||
case INDEX_op_mov_i32:
|
||||
case INDEX_op_mov_i64:
|
||||
/* Note that these are TCG_OPF_NOT_PRESENT and do not
|
||||
have proper constraints. That said, special case
|
||||
moves to propagate preferences backward. */
|
||||
if (IS_DEAD_ARG(1)) {
|
||||
*la_temp_pref(arg_temp(op->args[0]))
|
||||
= *la_temp_pref(arg_temp(op->args[1]));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) {
|
||||
arg_ts = arg_temp(op->args[i]);
|
||||
if (arg_ts->state & TS_DEAD) {
|
||||
arg_life |= DEAD_ARG << i;
|
||||
const TCGArgConstraint *ct = &def->args_ct[i];
|
||||
TCGRegSet set, *pset;
|
||||
|
||||
ts = arg_temp(op->args[i]);
|
||||
pset = la_temp_pref(ts);
|
||||
set = *pset;
|
||||
|
||||
set &= ct->u.regs;
|
||||
if (ct->ct & TCG_CT_IALIAS) {
|
||||
set &= op->output_pref[ct->alias_index];
|
||||
}
|
||||
/* If the combination is not possible, restart. */
|
||||
if (set == 0) {
|
||||
set = ct->u.regs;
|
||||
}
|
||||
*pset = set;
|
||||
}
|
||||
/* input arguments are live for preceding opcodes */
|
||||
for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) {
|
||||
arg_temp(op->args[i])->state &= ~TS_DEAD;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -2727,7 +3016,7 @@ static void temp_allocate_frame(TCGContext *s, TCGTemp *ts)
|
|||
s->current_frame_offset += sizeof(tcg_target_long);
|
||||
}
|
||||
|
||||
static void temp_load(TCGContext *, TCGTemp *, TCGRegSet, TCGRegSet);
|
||||
static void temp_load(TCGContext *, TCGTemp *, TCGRegSet, TCGRegSet, TCGRegSet);
|
||||
|
||||
/* Mark a temporary as free or dead. If 'free_or_dead' is negative,
|
||||
mark it free; otherwise mark it dead. */
|
||||
|
@ -2755,8 +3044,8 @@ static inline void temp_dead(TCGContext *s, TCGTemp *ts)
|
|||
registers needs to be allocated to store a constant. If 'free_or_dead'
|
||||
is non-zero, subsequently release the temporary; if it is positive, the
|
||||
temp is dead; if it is negative, the temp is free. */
|
||||
static void temp_sync(TCGContext *s, TCGTemp *ts,
|
||||
TCGRegSet allocated_regs, int free_or_dead)
|
||||
static void temp_sync(TCGContext *s, TCGTemp *ts, TCGRegSet allocated_regs,
|
||||
TCGRegSet preferred_regs, int free_or_dead)
|
||||
{
|
||||
if (ts->fixed_reg) {
|
||||
return;
|
||||
|
@ -2776,7 +3065,7 @@ static void temp_sync(TCGContext *s, TCGTemp *ts,
|
|||
break;
|
||||
}
|
||||
temp_load(s, ts, tcg_target_available_regs[ts->type],
|
||||
allocated_regs);
|
||||
allocated_regs, preferred_regs);
|
||||
/* fallthrough */
|
||||
|
||||
case TEMP_VAL_REG:
|
||||
|
@ -2803,35 +3092,76 @@ static void tcg_reg_free(TCGContext *s, TCGReg reg, TCGRegSet allocated_regs)
|
|||
{
|
||||
TCGTemp *ts = s->reg_to_temp[reg];
|
||||
if (ts != NULL) {
|
||||
temp_sync(s, ts, allocated_regs, -1);
|
||||
temp_sync(s, ts, allocated_regs, 0, -1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate a register belonging to reg1 & ~reg2 */
|
||||
static TCGReg tcg_reg_alloc(TCGContext *s, TCGRegSet desired_regs,
|
||||
TCGRegSet allocated_regs, bool rev)
|
||||
/**
|
||||
* tcg_reg_alloc:
|
||||
* @required_regs: Set of registers in which we must allocate.
|
||||
* @allocated_regs: Set of registers which must be avoided.
|
||||
* @preferred_regs: Set of registers we should prefer.
|
||||
* @rev: True if we search the registers in "indirect" order.
|
||||
*
|
||||
* The allocated register must be in @required_regs & ~@allocated_regs,
|
||||
* but if we can put it in @preferred_regs we may save a move later.
|
||||
*/
|
||||
static TCGReg tcg_reg_alloc(TCGContext *s, TCGRegSet required_regs,
|
||||
TCGRegSet allocated_regs,
|
||||
TCGRegSet preferred_regs, bool rev)
|
||||
{
|
||||
int i, n = ARRAY_SIZE(tcg_target_reg_alloc_order);
|
||||
int i, j, f, n = ARRAY_SIZE(tcg_target_reg_alloc_order);
|
||||
TCGRegSet reg_ct[2];
|
||||
const int *order;
|
||||
TCGReg reg;
|
||||
TCGRegSet reg_ct;
|
||||
|
||||
reg_ct = desired_regs & ~allocated_regs;
|
||||
reg_ct[1] = required_regs & ~allocated_regs;
|
||||
tcg_debug_assert(reg_ct[1] != 0);
|
||||
reg_ct[0] = reg_ct[1] & preferred_regs;
|
||||
|
||||
/* Skip the preferred_regs option if it cannot be satisfied,
|
||||
or if the preference made no difference. */
|
||||
f = reg_ct[0] == 0 || reg_ct[0] == reg_ct[1];
|
||||
|
||||
order = rev ? indirect_reg_alloc_order : tcg_target_reg_alloc_order;
|
||||
|
||||
/* first try free registers */
|
||||
for(i = 0; i < n; i++) {
|
||||
reg = order[i];
|
||||
if (tcg_regset_test_reg(reg_ct, reg) && s->reg_to_temp[reg] == NULL)
|
||||
return reg;
|
||||
/* Try free registers, preferences first. */
|
||||
for (j = f; j < 2; j++) {
|
||||
TCGRegSet set = reg_ct[j];
|
||||
|
||||
if (tcg_regset_single(set)) {
|
||||
/* One register in the set. */
|
||||
TCGReg reg = tcg_regset_first(set);
|
||||
if (s->reg_to_temp[reg] == NULL) {
|
||||
return reg;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < n; i++) {
|
||||
TCGReg reg = order[i];
|
||||
if (s->reg_to_temp[reg] == NULL &&
|
||||
tcg_regset_test_reg(set, reg)) {
|
||||
return reg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* XXX: do better spill choice */
|
||||
for(i = 0; i < n; i++) {
|
||||
reg = order[i];
|
||||
if (tcg_regset_test_reg(reg_ct, reg)) {
|
||||
/* We must spill something. */
|
||||
for (j = f; j < 2; j++) {
|
||||
TCGRegSet set = reg_ct[j];
|
||||
|
||||
if (tcg_regset_single(set)) {
|
||||
/* One register in the set. */
|
||||
TCGReg reg = tcg_regset_first(set);
|
||||
tcg_reg_free(s, reg, allocated_regs);
|
||||
return reg;
|
||||
} else {
|
||||
for (i = 0; i < n; i++) {
|
||||
TCGReg reg = order[i];
|
||||
if (tcg_regset_test_reg(set, reg)) {
|
||||
tcg_reg_free(s, reg, allocated_regs);
|
||||
return reg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2841,7 +3171,7 @@ static TCGReg tcg_reg_alloc(TCGContext *s, TCGRegSet desired_regs,
|
|||
/* Make sure the temporary is in a register. If needed, allocate the register
|
||||
from DESIRED while avoiding ALLOCATED. */
|
||||
static void temp_load(TCGContext *s, TCGTemp *ts, TCGRegSet desired_regs,
|
||||
TCGRegSet allocated_regs)
|
||||
TCGRegSet allocated_regs, TCGRegSet preferred_regs)
|
||||
{
|
||||
TCGReg reg;
|
||||
|
||||
|
@ -2849,12 +3179,14 @@ static void temp_load(TCGContext *s, TCGTemp *ts, TCGRegSet desired_regs,
|
|||
case TEMP_VAL_REG:
|
||||
return;
|
||||
case TEMP_VAL_CONST:
|
||||
reg = tcg_reg_alloc(s, desired_regs, allocated_regs, ts->indirect_base);
|
||||
reg = tcg_reg_alloc(s, desired_regs, allocated_regs,
|
||||
preferred_regs, ts->indirect_base);
|
||||
tcg_out_movi(s, ts->type, reg, ts->val);
|
||||
ts->mem_coherent = 0;
|
||||
break;
|
||||
case TEMP_VAL_MEM:
|
||||
reg = tcg_reg_alloc(s, desired_regs, allocated_regs, ts->indirect_base);
|
||||
reg = tcg_reg_alloc(s, desired_regs, allocated_regs,
|
||||
preferred_regs, ts->indirect_base);
|
||||
tcg_out_ld(s, ts->type, reg, ts->mem_base->reg, ts->mem_offset);
|
||||
ts->mem_coherent = 1;
|
||||
break;
|
||||
|
@ -2924,7 +3256,8 @@ static void tcg_reg_alloc_bb_end(TCGContext *s, TCGRegSet allocated_regs)
|
|||
}
|
||||
|
||||
static void tcg_reg_alloc_do_movi(TCGContext *s, TCGTemp *ots,
|
||||
tcg_target_ulong val, TCGLifeData arg_life)
|
||||
tcg_target_ulong val, TCGLifeData arg_life,
|
||||
TCGRegSet preferred_regs)
|
||||
{
|
||||
if (ots->fixed_reg) {
|
||||
/* For fixed registers, we do not do any constant propagation. */
|
||||
|
@ -2940,7 +3273,7 @@ static void tcg_reg_alloc_do_movi(TCGContext *s, TCGTemp *ots,
|
|||
ots->val = val;
|
||||
ots->mem_coherent = 0;
|
||||
if (NEED_SYNC_ARG(0)) {
|
||||
temp_sync(s, ots, s->reserved_regs, IS_DEAD_ARG(0));
|
||||
temp_sync(s, ots, s->reserved_regs, preferred_regs, IS_DEAD_ARG(0));
|
||||
} else if (IS_DEAD_ARG(0)) {
|
||||
temp_dead(s, ots);
|
||||
}
|
||||
|
@ -2951,17 +3284,18 @@ static void tcg_reg_alloc_movi(TCGContext *s, const TCGOp *op)
|
|||
TCGTemp *ots = arg_temp(op->args[0]);
|
||||
tcg_target_ulong val = op->args[1];
|
||||
|
||||
tcg_reg_alloc_do_movi(s, ots, val, op->life);
|
||||
tcg_reg_alloc_do_movi(s, ots, val, op->life, op->output_pref[0]);
|
||||
}
|
||||
|
||||
static void tcg_reg_alloc_mov(TCGContext *s, const TCGOp *op)
|
||||
{
|
||||
const TCGLifeData arg_life = op->life;
|
||||
TCGRegSet allocated_regs;
|
||||
TCGRegSet allocated_regs, preferred_regs;
|
||||
TCGTemp *ts, *ots;
|
||||
TCGType otype, itype;
|
||||
|
||||
allocated_regs = s->reserved_regs;
|
||||
preferred_regs = op->output_pref[0];
|
||||
ots = arg_temp(op->args[0]);
|
||||
ts = arg_temp(op->args[1]);
|
||||
|
||||
|
@ -2975,7 +3309,7 @@ static void tcg_reg_alloc_mov(TCGContext *s, const TCGOp *op)
|
|||
if (IS_DEAD_ARG(1)) {
|
||||
temp_dead(s, ts);
|
||||
}
|
||||
tcg_reg_alloc_do_movi(s, ots, val, arg_life);
|
||||
tcg_reg_alloc_do_movi(s, ots, val, arg_life, preferred_regs);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2984,7 +3318,8 @@ static void tcg_reg_alloc_mov(TCGContext *s, const TCGOp *op)
|
|||
the SOURCE value into its own register first, that way we
|
||||
don't have to reload SOURCE the next time it is used. */
|
||||
if (ts->val_type == TEMP_VAL_MEM) {
|
||||
temp_load(s, ts, tcg_target_available_regs[itype], allocated_regs);
|
||||
temp_load(s, ts, tcg_target_available_regs[itype],
|
||||
allocated_regs, preferred_regs);
|
||||
}
|
||||
|
||||
tcg_debug_assert(ts->val_type == TEMP_VAL_REG);
|
||||
|
@ -3014,7 +3349,8 @@ static void tcg_reg_alloc_mov(TCGContext *s, const TCGOp *op)
|
|||
input one. */
|
||||
tcg_regset_set_reg(allocated_regs, ts->reg);
|
||||
ots->reg = tcg_reg_alloc(s, tcg_target_available_regs[otype],
|
||||
allocated_regs, ots->indirect_base);
|
||||
allocated_regs, preferred_regs,
|
||||
ots->indirect_base);
|
||||
}
|
||||
tcg_out_mov(s, otype, ots->reg, ts->reg);
|
||||
}
|
||||
|
@ -3022,7 +3358,7 @@ static void tcg_reg_alloc_mov(TCGContext *s, const TCGOp *op)
|
|||
ots->mem_coherent = 0;
|
||||
s->reg_to_temp[ots->reg] = ots;
|
||||
if (NEED_SYNC_ARG(0)) {
|
||||
temp_sync(s, ots, allocated_regs, 0);
|
||||
temp_sync(s, ots, allocated_regs, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3054,6 +3390,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op)
|
|||
|
||||
/* satisfy input constraints */
|
||||
for (k = 0; k < nb_iargs; k++) {
|
||||
TCGRegSet i_preferred_regs, o_preferred_regs;
|
||||
|
||||
i = def->sorted_args[nb_oargs + k];
|
||||
arg = op->args[i];
|
||||
arg_ct = &def->args_ct[i];
|
||||
|
@ -3064,17 +3402,18 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op)
|
|||
/* constant is OK for instruction */
|
||||
const_args[i] = 1;
|
||||
new_args[i] = ts->val;
|
||||
goto iarg_end;
|
||||
continue;
|
||||
}
|
||||
|
||||
temp_load(s, ts, arg_ct->u.regs, i_allocated_regs);
|
||||
|
||||
i_preferred_regs = o_preferred_regs = 0;
|
||||
if (arg_ct->ct & TCG_CT_IALIAS) {
|
||||
o_preferred_regs = op->output_pref[arg_ct->alias_index];
|
||||
if (ts->fixed_reg) {
|
||||
/* if fixed register, we must allocate a new register
|
||||
if the alias is not the same register */
|
||||
if (arg != op->args[arg_ct->alias_index])
|
||||
if (arg != op->args[arg_ct->alias_index]) {
|
||||
goto allocate_in_reg;
|
||||
}
|
||||
} else {
|
||||
/* if the input is aliased to an output and if it is
|
||||
not dead after the instruction, we must allocate
|
||||
|
@ -3082,33 +3421,42 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op)
|
|||
if (!IS_DEAD_ARG(i)) {
|
||||
goto allocate_in_reg;
|
||||
}
|
||||
|
||||
/* check if the current register has already been allocated
|
||||
for another input aliased to an output */
|
||||
int k2, i2;
|
||||
for (k2 = 0 ; k2 < k ; k2++) {
|
||||
i2 = def->sorted_args[nb_oargs + k2];
|
||||
if ((def->args_ct[i2].ct & TCG_CT_IALIAS) &&
|
||||
(new_args[i2] == ts->reg)) {
|
||||
goto allocate_in_reg;
|
||||
if (ts->val_type == TEMP_VAL_REG) {
|
||||
int k2, i2;
|
||||
reg = ts->reg;
|
||||
for (k2 = 0 ; k2 < k ; k2++) {
|
||||
i2 = def->sorted_args[nb_oargs + k2];
|
||||
if ((def->args_ct[i2].ct & TCG_CT_IALIAS) &&
|
||||
reg == new_args[i2]) {
|
||||
goto allocate_in_reg;
|
||||
}
|
||||
}
|
||||
}
|
||||
i_preferred_regs = o_preferred_regs;
|
||||
}
|
||||
}
|
||||
|
||||
temp_load(s, ts, arg_ct->u.regs, i_allocated_regs, i_preferred_regs);
|
||||
reg = ts->reg;
|
||||
|
||||
if (tcg_regset_test_reg(arg_ct->u.regs, reg)) {
|
||||
/* nothing to do : the constraint is satisfied */
|
||||
} else {
|
||||
allocate_in_reg:
|
||||
/* allocate a new register matching the constraint
|
||||
and move the temporary register into it */
|
||||
temp_load(s, ts, tcg_target_available_regs[ts->type],
|
||||
i_allocated_regs, 0);
|
||||
reg = tcg_reg_alloc(s, arg_ct->u.regs, i_allocated_regs,
|
||||
ts->indirect_base);
|
||||
o_preferred_regs, ts->indirect_base);
|
||||
tcg_out_mov(s, ts->type, reg, ts->reg);
|
||||
}
|
||||
new_args[i] = reg;
|
||||
const_args[i] = 0;
|
||||
tcg_regset_set_reg(i_allocated_regs, reg);
|
||||
iarg_end: ;
|
||||
}
|
||||
|
||||
/* mark dead temporaries and free the associated registers */
|
||||
|
@ -3147,7 +3495,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op)
|
|||
} else if (arg_ct->ct & TCG_CT_NEWREG) {
|
||||
reg = tcg_reg_alloc(s, arg_ct->u.regs,
|
||||
i_allocated_regs | o_allocated_regs,
|
||||
ts->indirect_base);
|
||||
op->output_pref[k], ts->indirect_base);
|
||||
} else {
|
||||
/* if fixed register, we try to use it */
|
||||
reg = ts->reg;
|
||||
|
@ -3156,7 +3504,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op)
|
|||
goto oarg_end;
|
||||
}
|
||||
reg = tcg_reg_alloc(s, arg_ct->u.regs, o_allocated_regs,
|
||||
ts->indirect_base);
|
||||
op->output_pref[k], ts->indirect_base);
|
||||
}
|
||||
tcg_regset_set_reg(o_allocated_regs, reg);
|
||||
/* if a fixed register is used, then a move will be done afterwards */
|
||||
|
@ -3192,7 +3540,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op)
|
|||
tcg_out_mov(s, ts->type, ts->reg, reg);
|
||||
}
|
||||
if (NEED_SYNC_ARG(i)) {
|
||||
temp_sync(s, ts, o_allocated_regs, IS_DEAD_ARG(i));
|
||||
temp_sync(s, ts, o_allocated_regs, 0, IS_DEAD_ARG(i));
|
||||
} else if (IS_DEAD_ARG(i)) {
|
||||
temp_dead(s, ts);
|
||||
}
|
||||
|
@ -3248,7 +3596,7 @@ static void tcg_reg_alloc_call(TCGContext *s, TCGOp *op)
|
|||
if (arg != TCG_CALL_DUMMY_ARG) {
|
||||
ts = arg_temp(arg);
|
||||
temp_load(s, ts, tcg_target_available_regs[ts->type],
|
||||
s->reserved_regs);
|
||||
s->reserved_regs, 0);
|
||||
tcg_out_st(s, ts->type, ts->reg, TCG_REG_CALL_STACK, stack_offset);
|
||||
}
|
||||
#ifndef TCG_TARGET_STACK_GROWSUP
|
||||
|
@ -3263,17 +3611,18 @@ static void tcg_reg_alloc_call(TCGContext *s, TCGOp *op)
|
|||
if (arg != TCG_CALL_DUMMY_ARG) {
|
||||
ts = arg_temp(arg);
|
||||
reg = tcg_target_call_iarg_regs[i];
|
||||
tcg_reg_free(s, reg, allocated_regs);
|
||||
|
||||
if (ts->val_type == TEMP_VAL_REG) {
|
||||
if (ts->reg != reg) {
|
||||
tcg_reg_free(s, reg, allocated_regs);
|
||||
tcg_out_mov(s, ts->type, reg, ts->reg);
|
||||
}
|
||||
} else {
|
||||
TCGRegSet arg_set = 0;
|
||||
|
||||
tcg_reg_free(s, reg, allocated_regs);
|
||||
tcg_regset_set_reg(arg_set, reg);
|
||||
temp_load(s, ts, arg_set, allocated_regs);
|
||||
temp_load(s, ts, arg_set, allocated_regs, 0);
|
||||
}
|
||||
|
||||
tcg_regset_set_reg(allocated_regs, reg);
|
||||
|
@ -3326,7 +3675,7 @@ static void tcg_reg_alloc_call(TCGContext *s, TCGOp *op)
|
|||
ts->mem_coherent = 0;
|
||||
s->reg_to_temp[reg] = ts;
|
||||
if (NEED_SYNC_ARG(i)) {
|
||||
temp_sync(s, ts, allocated_regs, IS_DEAD_ARG(i));
|
||||
temp_sync(s, ts, allocated_regs, 0, IS_DEAD_ARG(i));
|
||||
} else if (IS_DEAD_ARG(i)) {
|
||||
temp_dead(s, ts);
|
||||
}
|
||||
|
@ -3476,7 +3825,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb)
|
|||
&& qemu_log_in_addr_range(tb->pc))) {
|
||||
qemu_log_lock();
|
||||
qemu_log("OP:\n");
|
||||
tcg_dump_ops(s);
|
||||
tcg_dump_ops(s, false);
|
||||
qemu_log("\n");
|
||||
qemu_log_unlock();
|
||||
}
|
||||
|
@ -3495,6 +3844,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb)
|
|||
atomic_set(&prof->la_time, prof->la_time - profile_getclock());
|
||||
#endif
|
||||
|
||||
reachable_code_pass(s);
|
||||
liveness_pass_1(s);
|
||||
|
||||
if (s->nb_indirects > 0) {
|
||||
|
@ -3503,7 +3853,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb)
|
|||
&& qemu_log_in_addr_range(tb->pc))) {
|
||||
qemu_log_lock();
|
||||
qemu_log("OP before indirect lowering:\n");
|
||||
tcg_dump_ops(s);
|
||||
tcg_dump_ops(s, false);
|
||||
qemu_log("\n");
|
||||
qemu_log_unlock();
|
||||
}
|
||||
|
@ -3524,7 +3874,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb)
|
|||
&& qemu_log_in_addr_range(tb->pc))) {
|
||||
qemu_log_lock();
|
||||
qemu_log("OP after optimization and liveness analysis:\n");
|
||||
tcg_dump_ops(s);
|
||||
tcg_dump_ops(s, true);
|
||||
qemu_log("\n");
|
||||
qemu_log_unlock();
|
||||
}
|
||||
|
|
31
tcg/tcg.h
31
tcg/tcg.h
|
@ -244,7 +244,8 @@ typedef struct TCGRelocation {
|
|||
|
||||
typedef struct TCGLabel {
|
||||
unsigned has_value : 1;
|
||||
unsigned id : 31;
|
||||
unsigned id : 15;
|
||||
unsigned refs : 16;
|
||||
union {
|
||||
uintptr_t value;
|
||||
tcg_insn_unit *value_ptr;
|
||||
|
@ -462,11 +463,13 @@ typedef TCGv_ptr TCGv_env;
|
|||
/* call flags */
|
||||
/* Helper does not read globals (either directly or through an exception). It
|
||||
implies TCG_CALL_NO_WRITE_GLOBALS. */
|
||||
#define TCG_CALL_NO_READ_GLOBALS 0x0010
|
||||
#define TCG_CALL_NO_READ_GLOBALS 0x0001
|
||||
/* Helper does not write globals */
|
||||
#define TCG_CALL_NO_WRITE_GLOBALS 0x0020
|
||||
#define TCG_CALL_NO_WRITE_GLOBALS 0x0002
|
||||
/* Helper can be safely suppressed if the return value is not used. */
|
||||
#define TCG_CALL_NO_SIDE_EFFECTS 0x0040
|
||||
#define TCG_CALL_NO_SIDE_EFFECTS 0x0004
|
||||
/* Helper is QEMU_NORETURN. */
|
||||
#define TCG_CALL_NO_RETURN 0x0008
|
||||
|
||||
/* convenience version of most used call flags */
|
||||
#define TCG_CALL_NO_RWG TCG_CALL_NO_READ_GLOBALS
|
||||
|
@ -616,6 +619,9 @@ typedef struct TCGOp {
|
|||
|
||||
/* Arguments for the opcode. */
|
||||
TCGArg args[MAX_OPC_PARAM];
|
||||
|
||||
/* Register preferences for the output(s). */
|
||||
TCGRegSet output_pref[2];
|
||||
} TCGOp;
|
||||
|
||||
#define TCGOP_CALLI(X) (X)->param1
|
||||
|
@ -1024,20 +1030,22 @@ typedef struct TCGArgConstraint {
|
|||
|
||||
/* Bits for TCGOpDef->flags, 8 bits available. */
|
||||
enum {
|
||||
/* Instruction exits the translation block. */
|
||||
TCG_OPF_BB_EXIT = 0x01,
|
||||
/* Instruction defines the end of a basic block. */
|
||||
TCG_OPF_BB_END = 0x01,
|
||||
TCG_OPF_BB_END = 0x02,
|
||||
/* Instruction clobbers call registers and potentially update globals. */
|
||||
TCG_OPF_CALL_CLOBBER = 0x02,
|
||||
TCG_OPF_CALL_CLOBBER = 0x04,
|
||||
/* Instruction has side effects: it cannot be removed if its outputs
|
||||
are not used, and might trigger exceptions. */
|
||||
TCG_OPF_SIDE_EFFECTS = 0x04,
|
||||
TCG_OPF_SIDE_EFFECTS = 0x08,
|
||||
/* Instruction operands are 64-bits (otherwise 32-bits). */
|
||||
TCG_OPF_64BIT = 0x08,
|
||||
TCG_OPF_64BIT = 0x10,
|
||||
/* Instruction is optional and not implemented by the host, or insn
|
||||
is generic and should not be implemened by the host. */
|
||||
TCG_OPF_NOT_PRESENT = 0x10,
|
||||
TCG_OPF_NOT_PRESENT = 0x20,
|
||||
/* Instruction operands are vectors. */
|
||||
TCG_OPF_VECTOR = 0x20,
|
||||
TCG_OPF_VECTOR = 0x40,
|
||||
};
|
||||
|
||||
typedef struct TCGOpDef {
|
||||
|
@ -1076,9 +1084,6 @@ TCGOp *tcg_op_insert_after(TCGContext *s, TCGOp *op, TCGOpcode opc);
|
|||
|
||||
void tcg_optimize(TCGContext *s);
|
||||
|
||||
/* only used for debugging purposes */
|
||||
void tcg_dump_ops(TCGContext *s);
|
||||
|
||||
TCGv_i32 tcg_const_i32(int32_t val);
|
||||
TCGv_i64 tcg_const_i64(int64_t val);
|
||||
TCGv_i32 tcg_const_local_i32(int32_t val);
|
||||
|
|
Loading…
Reference in a new issue