mirror of https://github.com/xemu-project/xemu.git
Host support for riscv64.
Dead code elimination pass. Register allocation improvements. -----BEGIN PGP SIGNATURE----- iQEcBAABAgAGBQJcIpiSAAoJEGTfOOivfiFf6C0H/1eLxRh7KSaihgAfz/jxeclv 3XhJA8ffofFJPcOKQ3tDKeJXIz/4OqKM3XedktSYphKkoJQ0/Twhv9XPm9kZtghH 6Gkgw4pdLt+1zM90X1J97+46l0iY/WfDa84HpAg8Kxe99QEAWwnsGIvekmtyU3rd 1t4Z9ZiHYWDlDwI/Oudzs/vh+QOHjmyo+Y5xqOK+OMHtG45PoVdxxGaA+C+8vY3A TOnyvNA0T79CBNyKvdgdk4ADoEZh2vFYLqPwZeSLuYdw4Yr0oarRRnqvS2Hrb+Zr 30ZL6x40L0QQYAMN8xpj5JtaLMkpbDUXetCdyhngRzEJK0SjH5EjQrE3IQEmC2c= =B/4c -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/rth/tags/pull-tcg-20181226' into staging Host support for riscv64. Dead code elimination pass. Register allocation improvements. # gpg: Signature made Tue 25 Dec 2018 20:52:34 GMT # gpg: using RSA key 64DF38E8AF7E215F # gpg: Good signature from "Richard Henderson <richard.henderson@linaro.org>" # Primary key fingerprint: 7A48 1E78 868B 4DB6 A85A 05C0 64DF 38E8 AF7E 215F * remotes/rth/tags/pull-tcg-20181226: (42 commits) tcg: Improve call argument loading tcg: Record register preferences during liveness tcg: Add TCG_OPF_BB_EXIT tcg: Split out more subroutines from liveness_pass_1 tcg: Rename and adjust liveness_pass_1 helpers tcg: Reindent parts of liveness_pass_1 tcg: Dump register preference info with liveness tcg: Improve register allocation for matching constraints tcg: Add output_pref to TCGOp tcg: Add preferred_reg argument to tcg_reg_alloc_do_movi tcg: Add preferred_reg argument to temp_sync tcg: Add preferred_reg argument to temp_load tcg: Add preferred_reg argument to tcg_reg_alloc tcg: Add reachable_code_pass tcg: Reference count labels tcg: Add TCG_CALL_NO_RETURN tcg: Renumber TCG_CALL_* flags linux-user: Add safe_syscall for riscv64 host disas/microblaze: Remove unused REG_SP macro configure: Add support for building RISC-V host ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
1b3e80082b
12
MAINTAINERS
12
MAINTAINERS
|
@ -266,7 +266,8 @@ S: Maintained
|
||||||
F: target/riscv/
|
F: 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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
10
disas.c
|
@ -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;
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)))
|
||||||
|
|
||||||
|
|
|
@ -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) },
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -240,6 +240,7 @@ void tcg_gen_brcond_i32(TCGCond cond, TCGv_i32 arg1, TCGv_i32 arg2, TCGLabel *l)
|
||||||
if (cond == TCG_COND_ALWAYS) {
|
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),
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
556
tcg/tcg.c
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
31
tcg/tcg.h
31
tcg/tcg.h
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue