Hi, Aurelien, to nitpick, use_mips32r2_instructions may already indicate use_mips32_instructions,right? so during ISA detection, we may first do mips32r2, then mips32 detection is unnecessary, I think.
在 2013-08-28三的 00:11 +0200,Aurelien Jarno写道: > Now that TCG supports enabling and disabling ops at runtime, it's > possible to detect the available host instructions at runtime, and > enable the corresponding ops accordingly. > > Unfortunately it's not easy to probe for available instructions on > MIPS, the information is partially available in /proc/cpuinfo, and > not available in AUXV. This patch therefore probes for the instructions > by trying to execute them and by catching a possible SIGILL signal. > > Signed-off-by: Aurelien Jarno <aurel...@aurel32.net> > --- > tcg/mips/tcg-target.c | 211 > ++++++++++++++++++++++++++++++++----------------- > tcg/mips/tcg-target.h | 50 +++++++----- > 2 files changed, 169 insertions(+), 92 deletions(-) > > diff --git a/tcg/mips/tcg-target.c b/tcg/mips/tcg-target.c > index 793532e..a994b11 100644 > --- a/tcg/mips/tcg-target.c > +++ b/tcg/mips/tcg-target.c > @@ -422,83 +422,83 @@ static inline void tcg_out_movi(TCGContext *s, TCGType > type, > > static inline void tcg_out_bswap16(TCGContext *s, TCGReg ret, TCGReg arg) > { > -#if defined(__mips_isa_rev) && (__mips_isa_rev >= 2) > - tcg_out_opc_reg(s, OPC_WSBH, ret, 0, arg); > -#else > - /* ret and arg can't be register at */ > - if (ret == TCG_REG_AT || arg == TCG_REG_AT) { > - tcg_abort(); > - } > + if (use_mips32r2_instructions) { > + tcg_out_opc_reg(s, OPC_WSBH, ret, 0, arg); > + } else { > + /* ret and arg can't be register at */ > + if (ret == TCG_REG_AT || arg == TCG_REG_AT) { > + tcg_abort(); > + } > > - tcg_out_opc_sa(s, OPC_SRL, TCG_REG_AT, arg, 8); > - tcg_out_opc_sa(s, OPC_SLL, ret, arg, 8); > - tcg_out_opc_imm(s, OPC_ANDI, ret, ret, 0xff00); > - tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_REG_AT); > -#endif > + tcg_out_opc_sa(s, OPC_SRL, TCG_REG_AT, arg, 8); > + tcg_out_opc_sa(s, OPC_SLL, ret, arg, 8); > + tcg_out_opc_imm(s, OPC_ANDI, ret, ret, 0xff00); > + tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_REG_AT); > + } > } > > static inline void tcg_out_bswap16s(TCGContext *s, TCGReg ret, TCGReg arg) > { > -#if defined(__mips_isa_rev) && (__mips_isa_rev >= 2) > - tcg_out_opc_reg(s, OPC_WSBH, ret, 0, arg); > - tcg_out_opc_reg(s, OPC_SEH, ret, 0, ret); > -#else > - /* ret and arg can't be register at */ > - if (ret == TCG_REG_AT || arg == TCG_REG_AT) { > - tcg_abort(); > - } > + if (use_mips32r2_instructions) { > + tcg_out_opc_reg(s, OPC_WSBH, ret, 0, arg); > + tcg_out_opc_reg(s, OPC_SEH, ret, 0, ret); > + } else { > + /* ret and arg can't be register at */ > + if (ret == TCG_REG_AT || arg == TCG_REG_AT) { > + tcg_abort(); > + } > > - tcg_out_opc_sa(s, OPC_SRL, TCG_REG_AT, arg, 8); > - tcg_out_opc_sa(s, OPC_SLL, ret, arg, 24); > - tcg_out_opc_sa(s, OPC_SRA, ret, ret, 16); > - tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_REG_AT); > -#endif > + tcg_out_opc_sa(s, OPC_SRL, TCG_REG_AT, arg, 8); > + tcg_out_opc_sa(s, OPC_SLL, ret, arg, 24); > + tcg_out_opc_sa(s, OPC_SRA, ret, ret, 16); > + tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_REG_AT); > + } > } > > static inline void tcg_out_bswap32(TCGContext *s, TCGReg ret, TCGReg arg) > { > -#if defined(__mips_isa_rev) && (__mips_isa_rev >= 2) > - tcg_out_opc_reg(s, OPC_WSBH, ret, 0, arg); > - tcg_out_opc_sa(s, OPC_ROTR, ret, ret, 16); > -#else > - /* ret and arg must be different and can't be register at */ > - if (ret == arg || ret == TCG_REG_AT || arg == TCG_REG_AT) { > - tcg_abort(); > - } > + if (use_mips32r2_instructions) { > + tcg_out_opc_reg(s, OPC_WSBH, ret, 0, arg); > + tcg_out_opc_sa(s, OPC_ROTR, ret, ret, 16); > + } else { > + /* ret and arg must be different and can't be register at */ > + if (ret == arg || ret == TCG_REG_AT || arg == TCG_REG_AT) { > + tcg_abort(); > + } > > - tcg_out_opc_sa(s, OPC_SLL, ret, arg, 24); > + tcg_out_opc_sa(s, OPC_SLL, ret, arg, 24); > > - tcg_out_opc_sa(s, OPC_SRL, TCG_REG_AT, arg, 24); > - tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_REG_AT); > + tcg_out_opc_sa(s, OPC_SRL, TCG_REG_AT, arg, 24); > + tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_REG_AT); > > - tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_AT, arg, 0xff00); > - tcg_out_opc_sa(s, OPC_SLL, TCG_REG_AT, TCG_REG_AT, 8); > - tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_REG_AT); > + tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_AT, arg, 0xff00); > + tcg_out_opc_sa(s, OPC_SLL, TCG_REG_AT, TCG_REG_AT, 8); > + tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_REG_AT); > > - tcg_out_opc_sa(s, OPC_SRL, TCG_REG_AT, arg, 8); > - tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_AT, TCG_REG_AT, 0xff00); > - tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_REG_AT); > -#endif > + tcg_out_opc_sa(s, OPC_SRL, TCG_REG_AT, arg, 8); > + tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_AT, TCG_REG_AT, 0xff00); > + tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_REG_AT); > + } > } > > static inline void tcg_out_ext8s(TCGContext *s, TCGReg ret, TCGReg arg) > { > -#if defined(__mips_isa_rev) && (__mips_isa_rev >= 2) > - tcg_out_opc_reg(s, OPC_SEB, ret, 0, arg); > -#else > - tcg_out_opc_sa(s, OPC_SLL, ret, arg, 24); > - tcg_out_opc_sa(s, OPC_SRA, ret, ret, 24); > -#endif > + if (use_mips32r2_instructions) { > + tcg_out_opc_reg(s, OPC_SEB, ret, 0, arg); > + } else { > + tcg_out_opc_sa(s, OPC_SLL, ret, arg, 24); > + tcg_out_opc_sa(s, OPC_SRA, ret, ret, 24); > + } > } > > static inline void tcg_out_ext16s(TCGContext *s, TCGReg ret, TCGReg arg) > { > -#if defined(__mips_isa_rev) && (__mips_isa_rev >= 2) > - tcg_out_opc_reg(s, OPC_SEH, ret, 0, arg); > -#else > - tcg_out_opc_sa(s, OPC_SLL, ret, arg, 16); > - tcg_out_opc_sa(s, OPC_SRA, ret, ret, 16); > -#endif > + if (use_mips32r2_instructions) { > + tcg_out_opc_reg(s, OPC_SEH, ret, 0, arg); > + } else { > + tcg_out_opc_sa(s, OPC_SLL, ret, arg, 16); > + tcg_out_opc_sa(s, OPC_SRA, ret, ret, 16); > + } > } > > static inline void tcg_out_ldst(TCGContext *s, int opc, TCGArg arg, > @@ -1406,12 +1406,12 @@ static inline void tcg_out_op(TCGContext *s, > TCGOpcode opc, > tcg_out_mov(s, TCG_TYPE_I32, args[0], TCG_REG_AT); > break; > case INDEX_op_mul_i32: > -#if defined(__mips_isa_rev) && (__mips_isa_rev >= 1) > - tcg_out_opc_reg(s, OPC_MUL, args[0], args[1], args[2]); > -#else > - tcg_out_opc_reg(s, OPC_MULT, 0, args[1], args[2]); > - tcg_out_opc_reg(s, OPC_MFLO, args[0], 0, 0); > -#endif > + if (use_mips32_instructions) { > + tcg_out_opc_reg(s, OPC_MUL, args[0], args[1], args[2]); > + } else { > + tcg_out_opc_reg(s, OPC_MULT, 0, args[1], args[2]); > + tcg_out_opc_reg(s, OPC_MFLO, args[0], 0, 0); > + } > break; > case INDEX_op_muls2_i32: > tcg_out_opc_reg(s, OPC_MULT, 0, args[2], args[3]); > @@ -1617,29 +1617,19 @@ static const TCGTargetOpDef mips_op_defs[] = { > { INDEX_op_shl_i32, { "r", "rZ", "ri" } }, > { INDEX_op_shr_i32, { "r", "rZ", "ri" } }, > { INDEX_op_sar_i32, { "r", "rZ", "ri" } }, > -#if TCG_TARGET_HAS_rot_i32 > { INDEX_op_rotr_i32, { "r", "rZ", "ri" } }, > { INDEX_op_rotl_i32, { "r", "rZ", "ri" } }, > -#endif > > -#if TCG_TARGET_HAS_bswap16_i32 > { INDEX_op_bswap16_i32, { "r", "r" } }, > -#endif > -#if TCG_TARGET_HAS_bswap32_i32 > { INDEX_op_bswap32_i32, { "r", "r" } }, > -#endif > > { INDEX_op_ext8s_i32, { "r", "rZ" } }, > { INDEX_op_ext16s_i32, { "r", "rZ" } }, > > -#if TCG_TARGET_HAS_deposit_i32 > { INDEX_op_deposit_i32, { "r", "0", "rZ" } }, > -#endif > > { INDEX_op_brcond_i32, { "rZ", "rZ" } }, > -#if TCG_TARGET_HAS_movcond_i32 > { INDEX_op_movcond_i32, { "r", "rZ", "rZ", "rZ", "0" } }, > -#endif > { INDEX_op_setcond_i32, { "r", "rZ", "rZ" } }, > { INDEX_op_setcond2_i32, { "r", "rZ", "rZ", "rZ", "rZ" } }, > > @@ -1688,6 +1678,84 @@ static int tcg_target_callee_save_regs[] = { > TCG_REG_RA, /* should be last for ABI compliance */ > }; > > +/* The Linux kernel doesn't provide any information about the available > + instruction set. Probe it using a signal handler. */ > + > +#include <signal.h> > + > +#ifndef use_movnz_instructions > +bool use_movnz_instructions; > +#endif > + > +#ifndef use_mips32_instructions > +bool use_mips32_instructions; > +#endif > + > +#ifndef use_mips32r2_instructions > +bool use_mips32r2_instructions; > +#endif > + > +static volatile sig_atomic_t got_sigill; > + > +static void sigill_handler(int signo, siginfo_t *si, void *data) > +{ > + /* Skip the faulty instruction */ > + ucontext_t *uc = (ucontext_t *)data; > + uc->uc_mcontext.pc += 4; > + > + got_sigill = 1; > +} > + > +static void tcg_target_detect_isa(void) > +{ > + struct sigaction sa_old, sa_new; > + > + memset(&sa_new, 0, sizeof(sa_new)); > + sa_new.sa_flags = SA_SIGINFO; > + sa_new.sa_sigaction = sigill_handler; > + sigaction(SIGILL, &sa_new, &sa_old); > + > + /* Probe for movn/movz, necessary to implement movcond. */ > +#ifndef use_movnz_instructions > + got_sigill = 0; > + asm volatile(".set push\n" > + ".set mips32\n" > + "movn $zero, $zero, $zero\n" > + "movz $zero, $zero, $zero\n" > + ".set pop\n" > + : : : ); > + use_movnz_instructions = !got_sigill; > +#endif > + > + /* Probe for MIPS32 instructions. As no subsetting is allowed > + by the specification, it is only necessary to probe for one > + of the instructions. */ > +#ifndef use_mips32_instructions > + got_sigill = 0; > + asm volatile(".set push\n" > + ".set mips32\n" > + "mul $zero, $zero\n" > + ".set pop\n" > + : : : ); > + use_mips32_instructions = !got_sigill; > +#endif > + > + /* Probe for MIPS32r2 instructions. As no subsetting is allowed > + by the specification, it is only necessary to probe for one > + of the instructions. */ > +#ifndef use_mips32r2_instructions > + got_sigill = 0; > + asm volatile(".set push\n" > + ".set mips32r2\n" > + "seb $zero, $zero\n" > + ".set pop\n" > + : : : ); > + use_mips32r2_instructions = !got_sigill; > +#endif > + > + sigaction(SIGILL, &sa_old, NULL); > +} > + > /* Generate global QEMU prologue and epilogue code */ > static void tcg_target_qemu_prologue(TCGContext *s) > { > @@ -1727,6 +1795,7 @@ static void tcg_target_qemu_prologue(TCGContext *s) > > static void tcg_target_init(TCGContext *s) > { > + tcg_target_detect_isa(); > tcg_regset_set(tcg_target_available_regs[TCG_TYPE_I32], 0xffffffff); > tcg_regset_set(tcg_target_call_clobber_regs, > (1 << TCG_REG_V0) | > diff --git a/tcg/mips/tcg-target.h b/tcg/mips/tcg-target.h > index a438950..43072e3 100644 > --- a/tcg/mips/tcg-target.h > +++ b/tcg/mips/tcg-target.h > @@ -77,6 +77,29 @@ typedef enum { > #define TCG_TARGET_CALL_STACK_OFFSET 16 > #define TCG_TARGET_CALL_ALIGN_ARGS 1 > > +/* MOVN/MOVZ instructions detection */ > +#if (defined(__mips_isa_rev) && (__mips_isa_rev >= 1)) || \ > + defined(_MIPS_ARCH_LOONGSON2E) || defined(_MIPS_ARCH_LOONGSON2F) || \ > + defined(_MIPS_ARCH_MIPS4) > +#define use_movnz_instructions 1 > +#else > +extern bool use_movnz_instructions; > +#endif > + > +/* MIPS32 instruction set detection */ > +#if defined(__mips_isa_rev) && (__mips_isa_rev >= 1) > +#define use_mips32_instructions 1 > +#else > +extern bool use_mips32_instructions; > +#endif > + > +/* MIPS32R2 instruction set detection */ > +#if defined(__mips_isa_rev) && (__mips_isa_rev >= 2) > +#define use_mips32r2_instructions 1 > +#else > +extern bool use_mips32r2_instructions; > +#endif > + > /* optional instructions */ > #define TCG_TARGET_HAS_div_i32 1 > #define TCG_TARGET_HAS_rem_i32 1 > @@ -90,27 +113,12 @@ typedef enum { > #define TCG_TARGET_HAS_nand_i32 0 > #define TCG_TARGET_HAS_muls2_i32 1 > > -/* optional instructions only implemented on MIPS4, MIPS32 and Loongson 2 */ > -#if (defined(__mips_isa_rev) && (__mips_isa_rev >= 1)) || \ > - defined(_MIPS_ARCH_LOONGSON2E) || defined(_MIPS_ARCH_LOONGSON2F) || \ > - defined(_MIPS_ARCH_MIPS4) > -#define TCG_TARGET_HAS_movcond_i32 1 > -#else > -#define TCG_TARGET_HAS_movcond_i32 0 > -#endif > - > -/* optional instructions only implemented on MIPS32R2 */ > -#if defined(__mips_isa_rev) && (__mips_isa_rev >= 2) > -#define TCG_TARGET_HAS_bswap16_i32 1 > -#define TCG_TARGET_HAS_bswap32_i32 1 > -#define TCG_TARGET_HAS_rot_i32 1 > -#define TCG_TARGET_HAS_deposit_i32 1 > -#else > -#define TCG_TARGET_HAS_bswap16_i32 0 > -#define TCG_TARGET_HAS_bswap32_i32 0 > -#define TCG_TARGET_HAS_rot_i32 0 > -#define TCG_TARGET_HAS_deposit_i32 0 > -#endif > +/* optional instructions detected at runtime */ > +#define TCG_TARGET_HAS_movcond_i32 use_movnz_instructions > +#define TCG_TARGET_HAS_bswap16_i32 use_mips32r2_instructions > +#define TCG_TARGET_HAS_bswap32_i32 use_mips32r2_instructions > +#define TCG_TARGET_HAS_deposit_i32 use_mips32r2_instructions > +#define TCG_TARGET_HAS_rot_i32 use_mips32r2_instructions > > /* optional instructions automatically implemented */ > #define TCG_TARGET_HAS_neg_i32 0 /* sub rd, zero, rt */