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:
Peter Maydell 2019-01-03 10:42:21 +00:00
commit 1b3e80082b
19 changed files with 2948 additions and 167 deletions

View file

@ -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

View file

@ -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
View file

@ -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
View file

@ -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;

View file

@ -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 */

View file

@ -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;

View file

@ -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)))

View file

@ -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) },

View file

@ -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

View 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

View 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

View 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
View 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

File diff suppressed because it is too large Load diff

View file

@ -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),

View file

@ -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));
}

View file

@ -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
View file

@ -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();
}

View file

@ -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);