diff mbox

[1/2] tcg/mips: detect available host instructions at runtime

Message ID 1376582293-1438-1-git-send-email-aurelien@aurel32.net
State New
Headers show

Commit Message

Aurelien Jarno Aug. 15, 2013, 3:58 p.m. UTC
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 <aurelien@aurel32.net>
---
 tcg/mips/tcg-target.c |  211 ++++++++++++++++++++++++++++++++-----------------
 tcg/mips/tcg-target.h |   50 +++++++-----
 2 files changed, 169 insertions(+), 92 deletions(-)

Comments

Maciej W. Rozycki Aug. 15, 2013, 4:52 p.m. UTC | #1
On Thu, 15 Aug 2013, Aurelien Jarno wrote:

> +    /* 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"
> +                 "mult $zero, $zero\n"
> +                 ".set pop\n"
> +                 : : : );
> +    use_mips32_instructions = !got_sigill;
> +#endif

 Are you sure?  MULT is an ISA I instruction.  Perhaps you meant the 
three-argument MUL?  But that might be slightly usafe as a MIPS32 ISA 
detector because that instruction was also implemented on the earlier NEC 
Vr5500 chips.  By the look at opcodes/mips-opc.c in binutils Vr5500 chips 
implement most, but not all MIPS32 ISA instructions.  So the question is 
-- how close the host has to be?

 The MIPS32 instructions missing from Vr5500 are the EJTAG stuff (DERET 
and SDBBP), JR.HB/JALR.HB (hmm, weird -- these are actually not guaranteed 
to work on all MIPS32 chips either, e.g. the 4Kc didn't support these 
encodings and trapped), SYNC, three-argument MFCx/MTCx instructions (CP0, 
CP2, CP3 register set selection) and two-argument BC2* instructions (extra 
CP2 condition bits).

 All it looks like pretty obscure stuff to me as far as QEMU is concerned, 
so perhaps checking for MUL is good enough.  But I'm not the QEMU expert 
here, so I'm just raising the issue in hope that you or someone else 
knows.

  Maciej
Aurelien Jarno Aug. 15, 2013, 5:38 p.m. UTC | #2
On Thu, Aug 15, 2013 at 05:52:55PM +0100, Maciej W. Rozycki wrote:
> On Thu, 15 Aug 2013, Aurelien Jarno wrote:
> 
> > +    /* 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"
> > +                 "mult $zero, $zero\n"
> > +                 ".set pop\n"
> > +                 : : : );
> > +    use_mips32_instructions = !got_sigill;
> > +#endif
> 
>  Are you sure?  MULT is an ISA I instruction.  Perhaps you meant the 
> three-argument MUL?  But that might be slightly usafe as a MIPS32 ISA 
> detector because that instruction was also implemented on the earlier NEC 
> Vr5500 chips.  By the look at opcodes/mips-opc.c in binutils Vr5500 chips 
> implement most, but not all MIPS32 ISA instructions.  So the question is 
> -- how close the host has to be?

It's indeed a typo. It should be MUL and no MULT. 

>  The MIPS32 instructions missing from Vr5500 are the EJTAG stuff (DERET 
> and SDBBP), JR.HB/JALR.HB (hmm, weird -- these are actually not guaranteed 
> to work on all MIPS32 chips either, e.g. the 4Kc didn't support these 
> encodings and trapped), SYNC, three-argument MFCx/MTCx instructions (CP0, 
> CP2, CP3 register set selection) and two-argument BC2* instructions (extra 
> CP2 condition bits).

So far the only use case of detecting the MIPS32 ISA is actually to use
MUL instead of MULT. All the others instructions not supported by the
Vr5500 are not used by QEMU, which uses only non-privileged instructions.
JR.HB/JALR.HB seems anyway to be supported only on MIPS32R2.

>  All it looks like pretty obscure stuff to me as far as QEMU is concerned, 
> so perhaps checking for MUL is good enough.  But I'm not the QEMU expert 
> here, so I'm just raising the issue in hope that you or someone else 
> knows.

The question is to know if there are other chips which implement MUL,
but not the other MIPS32 non-privileged (and non-FPU) instructions? For
example  MOVN and MOVZ is implemented on MIPS4 and Loongson that's why
there is another test for them.
Maciej W. Rozycki Aug. 15, 2013, 5:59 p.m. UTC | #3
On Thu, 15 Aug 2013, Aurelien Jarno wrote:

> >  The MIPS32 instructions missing from Vr5500 are the EJTAG stuff (DERET 
> > and SDBBP), JR.HB/JALR.HB (hmm, weird -- these are actually not guaranteed 
> > to work on all MIPS32 chips either, e.g. the 4Kc didn't support these 
> > encodings and trapped), SYNC, three-argument MFCx/MTCx instructions (CP0, 
> > CP2, CP3 register set selection) and two-argument BC2* instructions (extra 
> > CP2 condition bits).
> 
> So far the only use case of detecting the MIPS32 ISA is actually to use
> MUL instead of MULT. All the others instructions not supported by the
> Vr5500 are not used by QEMU, which uses only non-privileged instructions.
> JR.HB/JALR.HB seems anyway to be supported only on MIPS32R2.

 Great!

> >  All it looks like pretty obscure stuff to me as far as QEMU is concerned, 
> > so perhaps checking for MUL is good enough.  But I'm not the QEMU expert 
> > here, so I'm just raising the issue in hope that you or someone else 
> > knows.
> 
> The question is to know if there are other chips which implement MUL,
> but not the other MIPS32 non-privileged (and non-FPU) instructions? For
> example  MOVN and MOVZ is implemented on MIPS4 and Loongson that's why
> there is another test for them.

 The only other processor that supports the MIPS32 MUL instruction is the 
IDT R4650 chip, but that does not have a TLB MMU and therefore I think can 
be safely disregarded.  It won't run Linux or any such OS.

 NEC Vr5400 chips (the Vr5432 is the only variant that's been actually 
taped out) support a MUL instruction that has the same semantics but a 
different encoding.  It shouldn't be a problem though as this assembly 
piece uses .set mips32r2 and therefore the MIPS32 encoding should be 
produced instead.  You may want to double check it with -march=vr5400 and 
a small assembly source with code extracted from this piece though.

  Maciej
diff mbox

Patch

diff --git a/tcg/mips/tcg-target.c b/tcg/mips/tcg-target.c
index 793532e..4e6b712 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"
+                 "mult $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   */