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: target/riscv/
F: hw/riscv/ F: hw/riscv/
F: include/hw/riscv/ F: include/hw/riscv/
F: disas/riscv.c F: linux-user/host/riscv32/
F: linux-user/host/riscv64/
S390 S390
M: Richard Henderson <rth@twiddle.net> M: Richard Henderson <rth@twiddle.net>
@ -2172,6 +2173,15 @@ S: Odd Fixes
F: tcg/ppc/ F: tcg/ppc/
F: disas/ppc.c 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 S390 target
M: Richard Henderson <rth@twiddle.net> M: Richard Henderson <rth@twiddle.net>
S: Maintained 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); 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 #else
#error host CPU specific signal handler needed #error host CPU specific signal handler needed

12
configure vendored
View File

@ -710,6 +710,12 @@ elif check_define __s390__ ; then
else else
cpu="s390" cpu="s390"
fi fi
elif check_define __riscv ; then
if check_define _LP64 ; then
cpu="riscv64"
else
cpu="riscv32"
fi
elif check_define __arm__ ; then elif check_define __arm__ ; then
cpu="arm" cpu="arm"
elif check_define __aarch64__ ; then elif check_define __aarch64__ ; then
@ -722,7 +728,7 @@ ARCH=
# Normalise host CPU name and set ARCH. # Normalise host CPU name and set ARCH.
# Note that this case should only have supported host CPUs, not guests. # Note that this case should only have supported host CPUs, not guests.
case "$cpu" in case "$cpu" in
ppc|ppc64|s390|s390x|sparc64|x32) ppc|ppc64|s390|s390x|sparc64|x32|riscv32|riscv64)
cpu="$cpu" cpu="$cpu"
supported_cpu="yes" supported_cpu="yes"
eval "cross_cc_${cpu}=\$host_cc" 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" QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/i386 $QEMU_INCLUDES"
elif test "$ARCH" = "ppc64" ; then elif test "$ARCH" = "ppc64" ; then
QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/ppc $QEMU_INCLUDES" 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 else
QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/\$(ARCH) $QEMU_INCLUDES" QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/\$(ARCH) $QEMU_INCLUDES"
fi fi
@ -7433,7 +7441,7 @@ for i in $ARCH $TARGET_BASE_ARCH ; do
ppc*) ppc*)
disas_config "PPC" disas_config "PPC"
;; ;;
riscv) riscv*)
disas_config "RISCV" disas_config "RISCV"
;; ;;
s390*) s390*)

10
disas.c
View File

@ -522,8 +522,14 @@ void disas(FILE *out, void *code, unsigned long size)
# ifdef _ARCH_PPC64 # ifdef _ARCH_PPC64
s.info.cap_mode = CS_MODE_64; s.info.cap_mode = CS_MODE_64;
# endif # endif
#elif defined(__riscv__) #elif defined(__riscv) && defined(CONFIG_RISCV_DIS)
print_insn = print_insn_riscv; #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) #elif defined(__aarch64__) && defined(CONFIG_ARM_A64_DIS)
print_insn = print_insn_arm_a64; print_insn = print_insn_arm_a64;
s.info.cap_arch = CS_ARCH_ARM64; 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 */ #define REG_TLBSX 36869 /* MMU: TLB Search Index reg */
/* alternate names for gen purpose regs */ /* alternate names for gen purpose regs */
#define REG_SP 1 /* stack pointer */
#define REG_ROSDP 2 /* read-only small data pointer */ #define REG_ROSDP 2 /* read-only small data pointer */
#define REG_RWSDP 13 /* read-write 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_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */
#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ #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 { typedef struct elf32_rel {
Elf32_Addr r_offset; Elf32_Addr r_offset;
Elf32_Word r_info; Elf32_Word r_info;

View File

@ -108,6 +108,19 @@
#define dh_is_signed_env dh_is_signed_ptr #define dh_is_signed_env dh_is_signed_ptr
#define dh_is_signed(t) dh_is_signed_##t #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) \ #define dh_sizemask(t, n) \
((dh_is_64bit(t) << (n*2)) | (dh_is_signed(t) << (n*2+1))) ((dh_is_64bit(t) << (n*2)) | (dh_is_signed(t) << (n*2+1)))

View File

@ -11,36 +11,43 @@
#define str(s) #s #define str(s) #s
#define DEF_HELPER_FLAGS_0(NAME, FLAGS, ret) \ #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) }, .sizemask = dh_sizemask(ret, 0) },
#define DEF_HELPER_FLAGS_1(NAME, FLAGS, ret, t1) \ #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) }, .sizemask = dh_sizemask(ret, 0) | dh_sizemask(t1, 1) },
#define DEF_HELPER_FLAGS_2(NAME, FLAGS, ret, t1, t2) \ #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) \ .sizemask = dh_sizemask(ret, 0) | dh_sizemask(t1, 1) \
| dh_sizemask(t2, 2) }, | dh_sizemask(t2, 2) },
#define DEF_HELPER_FLAGS_3(NAME, FLAGS, ret, t1, t2, t3) \ #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) \ .sizemask = dh_sizemask(ret, 0) | dh_sizemask(t1, 1) \
| dh_sizemask(t2, 2) | dh_sizemask(t3, 3) }, | dh_sizemask(t2, 2) | dh_sizemask(t3, 3) },
#define DEF_HELPER_FLAGS_4(NAME, FLAGS, ret, t1, t2, t3, t4) \ #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) \ .sizemask = dh_sizemask(ret, 0) | dh_sizemask(t1, 1) \
| dh_sizemask(t2, 2) | dh_sizemask(t3, 3) | dh_sizemask(t4, 4) }, | 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) \ #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) \ .sizemask = dh_sizemask(ret, 0) | dh_sizemask(t1, 1) \
| dh_sizemask(t2, 2) | dh_sizemask(t3, 3) | dh_sizemask(t4, 4) \ | dh_sizemask(t2, 2) | dh_sizemask(t3, 3) | dh_sizemask(t4, 4) \
| dh_sizemask(t5, 5) }, | dh_sizemask(t5, 5) },
#define DEF_HELPER_FLAGS_6(NAME, FLAGS, ret, t1, t2, t3, t4, t5, t6) \ #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) \ .sizemask = dh_sizemask(ret, 0) | dh_sizemask(t1, 1) \
| dh_sizemask(t2, 2) | dh_sizemask(t3, 3) | dh_sizemask(t4, 4) \ | dh_sizemask(t2, 2) | dh_sizemask(t3, 3) | dh_sizemask(t4, 4) \
| dh_sizemask(t5, 5) | dh_sizemask(t6, 6) }, | dh_sizemask(t5, 5) | dh_sizemask(t6, 6) },

View File

@ -79,6 +79,7 @@
#pragma GCC poison CONFIG_MOXIE_DIS #pragma GCC poison CONFIG_MOXIE_DIS
#pragma GCC poison CONFIG_NIOS2_DIS #pragma GCC poison CONFIG_NIOS2_DIS
#pragma GCC poison CONFIG_PPC_DIS #pragma GCC poison CONFIG_PPC_DIS
#pragma GCC poison CONFIG_RISCV_DIS
#pragma GCC poison CONFIG_S390_DIS #pragma GCC poison CONFIG_S390_DIS
#pragma GCC poison CONFIG_SH4_DIS #pragma GCC poison CONFIG_SH4_DIS
#pragma GCC poison CONFIG_SPARC_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) { if (cond == TCG_COND_ALWAYS) {
tcg_gen_br(l); tcg_gen_br(l);
} else if (cond != TCG_COND_NEVER) { } else if (cond != TCG_COND_NEVER) {
l->refs++;
tcg_gen_op4ii_i32(INDEX_op_brcond_i32, arg1, arg2, cond, label_arg(l)); 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) { if (cond == TCG_COND_ALWAYS) {
tcg_gen_br(l); tcg_gen_br(l);
} else if (cond != TCG_COND_NEVER) { } else if (cond != TCG_COND_NEVER) {
l->refs++;
if (TCG_TARGET_REG_BITS == 32) { if (TCG_TARGET_REG_BITS == 32) {
tcg_gen_op6ii_i32(INDEX_op_brcond2_i32, TCGV_LOW(arg1), tcg_gen_op6ii_i32(INDEX_op_brcond2_i32, TCGV_LOW(arg1),
TCGV_HIGH(arg1), TCGV_LOW(arg2), 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) static inline void tcg_gen_br(TCGLabel *l)
{ {
l->refs++;
tcg_gen_op1(INDEX_op_br, label_arg(l)); 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 */ /* QEMU specific */
DEF(insn_start, 0, 0, TLADDR_ARGS * TARGET_INSN_START_WORDS, DEF(insn_start, 0, 0, TLADDR_ARGS * TARGET_INSN_START_WORDS,
TCG_OPF_NOT_PRESENT) TCG_OPF_NOT_PRESENT)
DEF(exit_tb, 0, 0, 1, TCG_OPF_BB_END) DEF(exit_tb, 0, 0, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END)
DEF(goto_tb, 0, 0, 1, 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_END | IMPL(TCG_TARGET_HAS_goto_ptr)) 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, DEF(qemu_ld_i32, 1, TLADDR_ARGS, 1,
TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)

556
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+", [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]; char buf[128];
TCGOp *op; TCGOp *op;
@ -1902,6 +1916,7 @@ void tcg_dump_ops(TCGContext *s)
def = &tcg_op_defs[c]; def = &tcg_op_defs[c];
if (c == INDEX_op_insn_start) { if (c == INDEX_op_insn_start) {
nb_oargs = 0;
col += qemu_log("\n ----"); col += qemu_log("\n ----");
for (i = 0; i < TARGET_INSN_START_WORDS; ++i) { 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]); 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); putc(' ', qemu_logfile);
} }
}
if (op->life) {
unsigned life = op->life;
if (life & (SYNC_ARG * 3)) { if (life & (SYNC_ARG * 3)) {
qemu_log(" sync:"); 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"); qemu_log("\n");
} }
} }
@ -2171,6 +2216,26 @@ static void process_op_defs(TCGContext *s)
void tcg_op_remove(TCGContext *s, TCGOp *op) 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_REMOVE(&s->ops, op, link);
QTAILQ_INSERT_TAIL(&s->free_ops, op, link); QTAILQ_INSERT_TAIL(&s->free_ops, op, link);
s->nb_ops--; s->nb_ops--;
@ -2219,43 +2284,181 @@ TCGOp *tcg_op_insert_after(TCGContext *s, TCGOp *old_op, TCGOpcode opc)
return new_op; 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_DEAD 1
#define TS_MEM 2 #define TS_MEM 2
#define IS_DEAD_ARG(n) (arg_life & (DEAD_ARG << (n))) #define IS_DEAD_ARG(n) (arg_life & (DEAD_ARG << (n)))
#define NEED_SYNC_ARG(n) (arg_life & (SYNC_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 /* liveness analysis: end of function: all temps are dead, and globals
should be in memory. */ 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; int i;
for (i = 0; i < ng; ++i) { for (i = 0; i < ng; ++i) {
s->temps[i].state = TS_DEAD | TS_MEM; s->temps[i].state = TS_DEAD | TS_MEM;
la_reset_pref(&s->temps[i]);
} }
for (i = ng; i < nt; ++i) { for (i = ng; i < nt; ++i) {
s->temps[i].state = TS_DEAD; s->temps[i].state = TS_DEAD;
la_reset_pref(&s->temps[i]);
} }
} }
/* liveness analysis: end of basic block: all temps are dead, globals /* liveness analysis: end of basic block: all temps are dead, globals
and local temps should be in memory. */ 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; int i;
for (i = 0; i < ng; ++i) { for (i = 0; i < ng; ++i) {
s->temps[i].state = TS_DEAD | TS_MEM; s->temps[i].state = TS_DEAD | TS_MEM;
la_reset_pref(&s->temps[i]);
} }
for (i = ng; i < nt; ++i) { for (i = ng; i < nt; ++i) {
s->temps[i].state = (s->temps[i].temp_local s->temps[i].state = (s->temps[i].temp_local
? TS_DEAD | TS_MEM ? TS_DEAD | TS_MEM
: TS_DEAD); : 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) static void liveness_pass_1(TCGContext *s)
{ {
int nb_globals = s->nb_globals; int nb_globals = s->nb_globals;
int nb_temps = s->nb_temps;
TCGOp *op, *op_prev; 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) { 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; TCGOpcode opc_new, opc_new2;
bool have_opc_new2; bool have_opc_new2;
TCGLifeData arg_life = 0; TCGLifeData arg_life = 0;
TCGTemp *arg_ts; TCGTemp *ts;
TCGOpcode opc = op->opc; TCGOpcode opc = op->opc;
const TCGOpDef *def = &tcg_op_defs[opc]; const TCGOpDef *def = &tcg_op_defs[opc];
@ -2282,6 +2494,7 @@ static void liveness_pass_1(TCGContext *s)
case INDEX_op_call: case INDEX_op_call:
{ {
int call_flags; int call_flags;
int nb_call_regs;
nb_oargs = TCGOP_CALLO(op); nb_oargs = TCGOP_CALLO(op);
nb_iargs = TCGOP_CALLI(op); nb_iargs = TCGOP_CALLI(op);
@ -2290,54 +2503,75 @@ static void liveness_pass_1(TCGContext *s)
/* pure functions can be removed if their result is unused */ /* pure functions can be removed if their result is unused */
if (call_flags & TCG_CALL_NO_SIDE_EFFECTS) { if (call_flags & TCG_CALL_NO_SIDE_EFFECTS) {
for (i = 0; i < nb_oargs; i++) { for (i = 0; i < nb_oargs; i++) {
arg_ts = arg_temp(op->args[i]); ts = arg_temp(op->args[i]);
if (arg_ts->state != TS_DEAD) { if (ts->state != TS_DEAD) {
goto do_not_remove_call; goto do_not_remove_call;
} }
} }
goto do_remove; goto do_remove;
} else { }
do_not_remove_call: do_not_remove_call:
/* output args are dead */ /* Output args are dead. */
for (i = 0; i < nb_oargs; i++) { for (i = 0; i < nb_oargs; i++) {
arg_ts = arg_temp(op->args[i]); ts = arg_temp(op->args[i]);
if (arg_ts->state & TS_DEAD) { if (ts->state & TS_DEAD) {
arg_life |= DEAD_ARG << i; arg_life |= DEAD_ARG << i;
} }
if (arg_ts->state & TS_MEM) { if (ts->state & TS_MEM) {
arg_life |= SYNC_ARG << i; arg_life |= SYNC_ARG << i;
} }
arg_ts->state = TS_DEAD; ts->state = TS_DEAD;
la_reset_pref(ts);
/* Not used -- it will be tcg_target_call_oarg_regs[i]. */
op->output_pref[i] = 0;
} }
if (!(call_flags & (TCG_CALL_NO_WRITE_GLOBALS | if (!(call_flags & (TCG_CALL_NO_WRITE_GLOBALS |
TCG_CALL_NO_READ_GLOBALS))) { TCG_CALL_NO_READ_GLOBALS))) {
/* globals should go back to memory */ la_global_kill(s, nb_globals);
for (i = 0; i < nb_globals; i++) {
s->temps[i].state = TS_DEAD | TS_MEM;
}
} else if (!(call_flags & TCG_CALL_NO_READ_GLOBALS)) { } else if (!(call_flags & TCG_CALL_NO_READ_GLOBALS)) {
/* globals should be synced to memory */ la_global_sync(s, nb_globals);
for (i = 0; i < nb_globals; i++) {
s->temps[i].state |= TS_MEM;
}
} }
/* record arguments that die in this helper */ /* Record arguments that die in this helper. */
for (i = nb_oargs; i < nb_iargs + nb_oargs; i++) { for (i = nb_oargs; i < nb_iargs + nb_oargs; i++) {
arg_ts = arg_temp(op->args[i]); ts = arg_temp(op->args[i]);
if (arg_ts && arg_ts->state & TS_DEAD) { if (ts && ts->state & TS_DEAD) {
arg_life |= DEAD_ARG << i; arg_life |= DEAD_ARG << i;
} }
} }
/* input arguments are live for preceding opcodes */
for (i = nb_oargs; i < nb_iargs + nb_oargs; i++) { /* For all live registers, remove call-clobbered prefs. */
arg_ts = arg_temp(op->args[i]); la_cross_call(s, nb_temps);
if (arg_ts) {
arg_ts->state &= ~TS_DEAD; 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]);
}
} }
} }
break; break;
@ -2345,7 +2579,9 @@ static void liveness_pass_1(TCGContext *s)
break; break;
case INDEX_op_discard: case INDEX_op_discard:
/* mark the temporary as dead */ /* 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; break;
case INDEX_op_add2_i32: case INDEX_op_add2_i32:
@ -2440,44 +2676,97 @@ static void liveness_pass_1(TCGContext *s)
goto do_not_remove; goto do_not_remove;
} }
} }
goto do_remove;
}
goto do_not_remove;
do_remove: do_remove:
tcg_op_remove(s, op); tcg_op_remove(s, op);
} else { break;
do_not_remove: do_not_remove:
/* output args are dead */
for (i = 0; i < nb_oargs; i++) { for (i = 0; i < nb_oargs; i++) {
arg_ts = arg_temp(op->args[i]); ts = arg_temp(op->args[i]);
if (arg_ts->state & TS_DEAD) {
/* 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; arg_life |= DEAD_ARG << i;
} }
if (arg_ts->state & TS_MEM) { if (ts->state & TS_MEM) {
arg_life |= SYNC_ARG << i; arg_life |= SYNC_ARG << i;
} }
arg_ts->state = TS_DEAD; ts->state = TS_DEAD;
la_reset_pref(ts);
} }
/* if end of basic block, update */ /* If end of basic block, update. */
if (def->flags & TCG_OPF_BB_END) { if (def->flags & TCG_OPF_BB_EXIT) {
tcg_la_bb_end(s); 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) { } else if (def->flags & TCG_OPF_SIDE_EFFECTS) {
/* globals should be synced to memory */ la_global_sync(s, nb_globals);
for (i = 0; i < nb_globals; i++) { if (def->flags & TCG_OPF_CALL_CLOBBER) {
s->temps[i].state |= TS_MEM; la_cross_call(s, nb_temps);
} }
} }
/* record arguments that die in this opcode */ /* Record arguments that die in this opcode. */
for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) { for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) {
arg_ts = arg_temp(op->args[i]); ts = arg_temp(op->args[i]);
if (arg_ts->state & TS_DEAD) { if (ts->state & TS_DEAD) {
arg_life |= DEAD_ARG << i; arg_life |= DEAD_ARG << i;
} }
} }
/* input arguments are live for preceding opcodes */
/* Input arguments are live for preceding opcodes. */
for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) { for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) {
arg_temp(op->args[i])->state &= ~TS_DEAD; 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++) {
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;
}
break;
}
break; break;
} }
op->life = arg_life; op->life = arg_life;
@ -2727,7 +3016,7 @@ static void temp_allocate_frame(TCGContext *s, TCGTemp *ts)
s->current_frame_offset += sizeof(tcg_target_long); 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 a temporary as free or dead. If 'free_or_dead' is negative,
mark it free; otherwise mark it dead. */ 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' 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 is non-zero, subsequently release the temporary; if it is positive, the
temp is dead; if it is negative, the temp is free. */ temp is dead; if it is negative, the temp is free. */
static void temp_sync(TCGContext *s, TCGTemp *ts, static void temp_sync(TCGContext *s, TCGTemp *ts, TCGRegSet allocated_regs,
TCGRegSet allocated_regs, int free_or_dead) TCGRegSet preferred_regs, int free_or_dead)
{ {
if (ts->fixed_reg) { if (ts->fixed_reg) {
return; return;
@ -2776,7 +3065,7 @@ static void temp_sync(TCGContext *s, TCGTemp *ts,
break; break;
} }
temp_load(s, ts, tcg_target_available_regs[ts->type], temp_load(s, ts, tcg_target_available_regs[ts->type],
allocated_regs); allocated_regs, preferred_regs);
/* fallthrough */ /* fallthrough */
case TEMP_VAL_REG: 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]; TCGTemp *ts = s->reg_to_temp[reg];
if (ts != NULL) { 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, * tcg_reg_alloc:
TCGRegSet allocated_regs, bool rev) * @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; 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; order = rev ? indirect_reg_alloc_order : tcg_target_reg_alloc_order;
/* first try free registers */ /* Try free registers, preferences first. */
for(i = 0; i < n; i++) { for (j = f; j < 2; j++) {
reg = order[i]; TCGRegSet set = reg_ct[j];
if (tcg_regset_test_reg(reg_ct, reg) && s->reg_to_temp[reg] == NULL)
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; return reg;
} }
} else {
/* XXX: do better spill choice */
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
reg = order[i]; TCGReg reg = order[i];
if (tcg_regset_test_reg(reg_ct, reg)) { if (s->reg_to_temp[reg] == NULL &&
tcg_regset_test_reg(set, reg)) {
return 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); tcg_reg_free(s, reg, allocated_regs);
return reg; 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 /* Make sure the temporary is in a register. If needed, allocate the register
from DESIRED while avoiding ALLOCATED. */ from DESIRED while avoiding ALLOCATED. */
static void temp_load(TCGContext *s, TCGTemp *ts, TCGRegSet desired_regs, static void temp_load(TCGContext *s, TCGTemp *ts, TCGRegSet desired_regs,
TCGRegSet allocated_regs) TCGRegSet allocated_regs, TCGRegSet preferred_regs)
{ {
TCGReg reg; TCGReg reg;
@ -2849,12 +3179,14 @@ static void temp_load(TCGContext *s, TCGTemp *ts, TCGRegSet desired_regs,
case TEMP_VAL_REG: case TEMP_VAL_REG:
return; return;
case TEMP_VAL_CONST: 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); tcg_out_movi(s, ts->type, reg, ts->val);
ts->mem_coherent = 0; ts->mem_coherent = 0;
break; break;
case TEMP_VAL_MEM: 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); tcg_out_ld(s, ts->type, reg, ts->mem_base->reg, ts->mem_offset);
ts->mem_coherent = 1; ts->mem_coherent = 1;
break; 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, 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) { if (ots->fixed_reg) {
/* For fixed registers, we do not do any constant propagation. */ /* 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->val = val;
ots->mem_coherent = 0; ots->mem_coherent = 0;
if (NEED_SYNC_ARG(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)) { } else if (IS_DEAD_ARG(0)) {
temp_dead(s, ots); 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]); TCGTemp *ots = arg_temp(op->args[0]);
tcg_target_ulong val = op->args[1]; 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) static void tcg_reg_alloc_mov(TCGContext *s, const TCGOp *op)
{ {
const TCGLifeData arg_life = op->life; const TCGLifeData arg_life = op->life;
TCGRegSet allocated_regs; TCGRegSet allocated_regs, preferred_regs;
TCGTemp *ts, *ots; TCGTemp *ts, *ots;
TCGType otype, itype; TCGType otype, itype;
allocated_regs = s->reserved_regs; allocated_regs = s->reserved_regs;
preferred_regs = op->output_pref[0];
ots = arg_temp(op->args[0]); ots = arg_temp(op->args[0]);
ts = arg_temp(op->args[1]); 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)) { if (IS_DEAD_ARG(1)) {
temp_dead(s, ts); 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; 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 the SOURCE value into its own register first, that way we
don't have to reload SOURCE the next time it is used. */ don't have to reload SOURCE the next time it is used. */
if (ts->val_type == TEMP_VAL_MEM) { 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); 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. */ input one. */
tcg_regset_set_reg(allocated_regs, ts->reg); tcg_regset_set_reg(allocated_regs, ts->reg);
ots->reg = tcg_reg_alloc(s, tcg_target_available_regs[otype], 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); 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; ots->mem_coherent = 0;
s->reg_to_temp[ots->reg] = ots; s->reg_to_temp[ots->reg] = ots;
if (NEED_SYNC_ARG(0)) { 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 */ /* satisfy input constraints */
for (k = 0; k < nb_iargs; k++) { for (k = 0; k < nb_iargs; k++) {
TCGRegSet i_preferred_regs, o_preferred_regs;
i = def->sorted_args[nb_oargs + k]; i = def->sorted_args[nb_oargs + k];
arg = op->args[i]; arg = op->args[i];
arg_ct = &def->args_ct[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 */ /* constant is OK for instruction */
const_args[i] = 1; const_args[i] = 1;
new_args[i] = ts->val; 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) { if (arg_ct->ct & TCG_CT_IALIAS) {
o_preferred_regs = op->output_pref[arg_ct->alias_index];
if (ts->fixed_reg) { if (ts->fixed_reg) {
/* if fixed register, we must allocate a new register /* if fixed register, we must allocate a new register
if the alias is not the same 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; goto allocate_in_reg;
}
} else { } else {
/* if the input is aliased to an output and if it is /* if the input is aliased to an output and if it is
not dead after the instruction, we must allocate 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)) { if (!IS_DEAD_ARG(i)) {
goto allocate_in_reg; goto allocate_in_reg;
} }
/* check if the current register has already been allocated /* check if the current register has already been allocated
for another input aliased to an output */ for another input aliased to an output */
if (ts->val_type == TEMP_VAL_REG) {
int k2, i2; int k2, i2;
reg = ts->reg;
for (k2 = 0 ; k2 < k ; k2++) { for (k2 = 0 ; k2 < k ; k2++) {
i2 = def->sorted_args[nb_oargs + k2]; i2 = def->sorted_args[nb_oargs + k2];
if ((def->args_ct[i2].ct & TCG_CT_IALIAS) && if ((def->args_ct[i2].ct & TCG_CT_IALIAS) &&
(new_args[i2] == ts->reg)) { reg == new_args[i2]) {
goto allocate_in_reg; 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; reg = ts->reg;
if (tcg_regset_test_reg(arg_ct->u.regs, reg)) { if (tcg_regset_test_reg(arg_ct->u.regs, reg)) {
/* nothing to do : the constraint is satisfied */ /* nothing to do : the constraint is satisfied */
} else { } else {
allocate_in_reg: allocate_in_reg:
/* allocate a new register matching the constraint /* allocate a new register matching the constraint
and move the temporary register into it */ 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, 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); tcg_out_mov(s, ts->type, reg, ts->reg);
} }
new_args[i] = reg; new_args[i] = reg;
const_args[i] = 0; const_args[i] = 0;
tcg_regset_set_reg(i_allocated_regs, reg); tcg_regset_set_reg(i_allocated_regs, reg);
iarg_end: ;
} }
/* mark dead temporaries and free the associated registers */ /* 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) { } else if (arg_ct->ct & TCG_CT_NEWREG) {
reg = tcg_reg_alloc(s, arg_ct->u.regs, reg = tcg_reg_alloc(s, arg_ct->u.regs,
i_allocated_regs | o_allocated_regs, i_allocated_regs | o_allocated_regs,
ts->indirect_base); op->output_pref[k], ts->indirect_base);
} else { } else {
/* if fixed register, we try to use it */ /* if fixed register, we try to use it */
reg = ts->reg; reg = ts->reg;
@ -3156,7 +3504,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op)
goto oarg_end; goto oarg_end;
} }
reg = tcg_reg_alloc(s, arg_ct->u.regs, o_allocated_regs, 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); tcg_regset_set_reg(o_allocated_regs, reg);
/* if a fixed register is used, then a move will be done afterwards */ /* 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); tcg_out_mov(s, ts->type, ts->reg, reg);
} }
if (NEED_SYNC_ARG(i)) { 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)) { } else if (IS_DEAD_ARG(i)) {
temp_dead(s, ts); temp_dead(s, ts);
} }
@ -3248,7 +3596,7 @@ static void tcg_reg_alloc_call(TCGContext *s, TCGOp *op)
if (arg != TCG_CALL_DUMMY_ARG) { if (arg != TCG_CALL_DUMMY_ARG) {
ts = arg_temp(arg); ts = arg_temp(arg);
temp_load(s, ts, tcg_target_available_regs[ts->type], 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); tcg_out_st(s, ts->type, ts->reg, TCG_REG_CALL_STACK, stack_offset);
} }
#ifndef TCG_TARGET_STACK_GROWSUP #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) { if (arg != TCG_CALL_DUMMY_ARG) {
ts = arg_temp(arg); ts = arg_temp(arg);
reg = tcg_target_call_iarg_regs[i]; reg = tcg_target_call_iarg_regs[i];
tcg_reg_free(s, reg, allocated_regs);
if (ts->val_type == TEMP_VAL_REG) { if (ts->val_type == TEMP_VAL_REG) {
if (ts->reg != reg) { if (ts->reg != reg) {
tcg_reg_free(s, reg, allocated_regs);
tcg_out_mov(s, ts->type, reg, ts->reg); tcg_out_mov(s, ts->type, reg, ts->reg);
} }
} else { } else {
TCGRegSet arg_set = 0; TCGRegSet arg_set = 0;
tcg_reg_free(s, reg, allocated_regs);
tcg_regset_set_reg(arg_set, reg); 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); 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; ts->mem_coherent = 0;
s->reg_to_temp[reg] = ts; s->reg_to_temp[reg] = ts;
if (NEED_SYNC_ARG(i)) { 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)) { } else if (IS_DEAD_ARG(i)) {
temp_dead(s, ts); 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_in_addr_range(tb->pc))) {
qemu_log_lock(); qemu_log_lock();
qemu_log("OP:\n"); qemu_log("OP:\n");
tcg_dump_ops(s); tcg_dump_ops(s, false);
qemu_log("\n"); qemu_log("\n");
qemu_log_unlock(); 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()); atomic_set(&prof->la_time, prof->la_time - profile_getclock());
#endif #endif
reachable_code_pass(s);
liveness_pass_1(s); liveness_pass_1(s);
if (s->nb_indirects > 0) { 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_in_addr_range(tb->pc))) {
qemu_log_lock(); qemu_log_lock();
qemu_log("OP before indirect lowering:\n"); qemu_log("OP before indirect lowering:\n");
tcg_dump_ops(s); tcg_dump_ops(s, false);
qemu_log("\n"); qemu_log("\n");
qemu_log_unlock(); qemu_log_unlock();
} }
@ -3524,7 +3874,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb)
&& qemu_log_in_addr_range(tb->pc))) { && qemu_log_in_addr_range(tb->pc))) {
qemu_log_lock(); qemu_log_lock();
qemu_log("OP after optimization and liveness analysis:\n"); qemu_log("OP after optimization and liveness analysis:\n");
tcg_dump_ops(s); tcg_dump_ops(s, true);
qemu_log("\n"); qemu_log("\n");
qemu_log_unlock(); qemu_log_unlock();
} }

View File

@ -244,7 +244,8 @@ typedef struct TCGRelocation {
typedef struct TCGLabel { typedef struct TCGLabel {
unsigned has_value : 1; unsigned has_value : 1;
unsigned id : 31; unsigned id : 15;
unsigned refs : 16;
union { union {
uintptr_t value; uintptr_t value;
tcg_insn_unit *value_ptr; tcg_insn_unit *value_ptr;
@ -462,11 +463,13 @@ typedef TCGv_ptr TCGv_env;
/* call flags */ /* call flags */
/* Helper does not read globals (either directly or through an exception). It /* Helper does not read globals (either directly or through an exception). It
implies TCG_CALL_NO_WRITE_GLOBALS. */ 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 */ /* 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. */ /* 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 */ /* convenience version of most used call flags */
#define TCG_CALL_NO_RWG TCG_CALL_NO_READ_GLOBALS #define TCG_CALL_NO_RWG TCG_CALL_NO_READ_GLOBALS
@ -616,6 +619,9 @@ typedef struct TCGOp {
/* Arguments for the opcode. */ /* Arguments for the opcode. */
TCGArg args[MAX_OPC_PARAM]; TCGArg args[MAX_OPC_PARAM];
/* Register preferences for the output(s). */
TCGRegSet output_pref[2];
} TCGOp; } TCGOp;
#define TCGOP_CALLI(X) (X)->param1 #define TCGOP_CALLI(X) (X)->param1
@ -1024,20 +1030,22 @@ typedef struct TCGArgConstraint {
/* Bits for TCGOpDef->flags, 8 bits available. */ /* Bits for TCGOpDef->flags, 8 bits available. */
enum { enum {
/* Instruction exits the translation block. */
TCG_OPF_BB_EXIT = 0x01,
/* Instruction defines the end of a basic block. */ /* 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. */ /* 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 /* Instruction has side effects: it cannot be removed if its outputs
are not used, and might trigger exceptions. */ 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). */ /* 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 /* Instruction is optional and not implemented by the host, or insn
is generic and should not be implemened by the host. */ is generic and should not be implemened by the host. */
TCG_OPF_NOT_PRESENT = 0x10, TCG_OPF_NOT_PRESENT = 0x20,
/* Instruction operands are vectors. */ /* Instruction operands are vectors. */
TCG_OPF_VECTOR = 0x20, TCG_OPF_VECTOR = 0x40,
}; };
typedef struct TCGOpDef { typedef struct TCGOpDef {
@ -1076,9 +1084,6 @@ TCGOp *tcg_op_insert_after(TCGContext *s, TCGOp *op, TCGOpcode opc);
void tcg_optimize(TCGContext *s); 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_i32 tcg_const_i32(int32_t val);
TCGv_i64 tcg_const_i64(int64_t val); TCGv_i64 tcg_const_i64(int64_t val);
TCGv_i32 tcg_const_local_i32(int32_t val); TCGv_i32 tcg_const_local_i32(int32_t val);