Skip to content

Commit

Permalink
tcg/mips: detect available host instructions at runtime
Browse files Browse the repository at this point in the history
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.

Reviewed-by: Richard Henderson <rth@twiddle.net>
Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
  • Loading branch information
aurel32 committed Sep 2, 2013
1 parent 4ff78e0 commit 988902f
Show file tree
Hide file tree
Showing 2 changed files with 171 additions and 92 deletions.
213 changes: 142 additions & 71 deletions tcg/mips/tcg-target.c
Expand Up @@ -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,
Expand Down Expand Up @@ -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]);
Expand Down Expand Up @@ -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" } },

Expand Down Expand Up @@ -1688,6 +1678,86 @@ 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 = false;
#endif

#ifndef use_mips32_instructions
bool use_mips32_instructions = false;
#endif

#ifndef use_mips32r2_instructions
bool use_mips32r2_instructions = false;
#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 if MIPS32 instructions are
available. As no subsetting is allowed by the specification,
it is only necessary to probe for one of the instructions. */
#ifndef use_mips32r2_instructions
if (use_mips32_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)
{
Expand Down Expand Up @@ -1727,6 +1797,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) |
Expand Down
50 changes: 29 additions & 21 deletions tcg/mips/tcg-target.h
Expand Up @@ -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
Expand All @@ -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 */
Expand Down

0 comments on commit 988902f

Please sign in to comment.