tcg-sparc: Implement division properly.
authorRichard Henderson <rth@twiddle.net>
Tue, 12 Jan 2010 19:59:33 +0000 (19:59 +0000)
committerBlue Swirl <blauwirbel@gmail.com>
Tue, 12 Jan 2010 19:59:33 +0000 (19:59 +0000)
The {div,divu}2 opcodes are intended for systems for which the
division instruction produces both quotient and remainder.  Sparc
is not such a system.  Indeed, the remainder must be computed as

  quot = a / b
  rem = a - (quot * b)

Split out a tcg_out_div32 function that properly initializes Y
with the extension of the input to 64-bits.  Discard the code
that used the 64-bit DIVX on sparc9/sparcv8plus without extending
the inputs to 64-bits.  Implement remainders in terms of division
followed by multiplication.

Signed-off-by: Richard Henderson <rth@twiddle.net>
[blauwirbel@gmail.com: applied rth's typo fix in tcg_out_div32]
Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
tcg/sparc/tcg-target.c
tcg/sparc/tcg-target.h

index 8675fcee11f557d26994d908fb1b01abbef1ac81..f6262d0f30dcbf382928ec5fb52fc462d3b0cb87 100644 (file)
@@ -243,7 +243,7 @@ static inline int tcg_target_const_match(tcg_target_long val,
 #define SHIFT_SRAX (INSN_OP(2) | INSN_OP3(0x27) | (1 << 12))
 
 #define RDY        (INSN_OP(2) | INSN_OP3(0x28) | INSN_RS1(0))
-#define WRY        (INSN_OP(2) | INSN_OP3(0x30))
+#define WRY        (INSN_OP(2) | INSN_OP3(0x30) | INSN_RD(0))
 #define JMPL       (INSN_OP(2) | INSN_OP3(0x38))
 #define SAVE       (INSN_OP(2) | INSN_OP3(0x3c))
 #define RESTORE    (INSN_OP(2) | INSN_OP3(0x3d))
@@ -407,12 +407,9 @@ static inline void tcg_out_st(TCGContext *s, TCGType type, int arg,
         tcg_out_ldst(s, arg, arg1, arg2, STX);
 }
 
-static inline void tcg_out_sety(TCGContext *s, tcg_target_long val)
+static inline void tcg_out_sety(TCGContext *s, int rs)
 {
-    if (val == 0 || val == -1)
-        tcg_out32(s, WRY | INSN_IMM13(val));
-    else
-        fprintf(stderr, "unimplemented sety %ld\n", (long)val);
+    tcg_out32(s, WRY | INSN_RS1(TCG_REG_G0) | INSN_RS2(rs));
 }
 
 static inline void tcg_out_rdy(TCGContext *s, int rd)
@@ -444,6 +441,21 @@ static inline void tcg_out_andi(TCGContext *s, int reg, tcg_target_long val)
     }
 }
 
+static void tcg_out_div32(TCGContext *s, int rd, int rs1,
+                          int val2, int val2const, int uns)
+{
+    /* Load Y with the sign/zero extension of RS1 to 64-bits.  */
+    if (uns) {
+        tcg_out_sety(s, TCG_REG_G0);
+    } else {
+        tcg_out_arithi(s, TCG_REG_I5, rs1, 31, SHIFT_SRA);
+        tcg_out_sety(s, TCG_REG_I5);
+    }
+
+    tcg_out_arithc(s, rd, rs1, val2, val2const,
+                   uns ? ARITH_UDIV : ARITH_SDIV);
+}
+
 static inline void tcg_out_nop(TCGContext *s)
 {
     tcg_out_sethi(s, TCG_REG_G0, 0);
@@ -1113,24 +1125,22 @@ static inline void tcg_out_op(TCGContext *s, int opc, const TCGArg *args,
     case INDEX_op_mul_i32:
         c = ARITH_UMUL;
         goto gen_arith;
-    case INDEX_op_div2_i32:
-#if defined(__sparc_v9__) || defined(__sparc_v8plus__)
-        c = ARITH_SDIVX;
-        goto gen_arith;
-#else
-        tcg_out_sety(s, 0);
-        c = ARITH_SDIV;
-        goto gen_arith;
-#endif
-    case INDEX_op_divu2_i32:
-#if defined(__sparc_v9__) || defined(__sparc_v8plus__)
-        c = ARITH_UDIVX;
-        goto gen_arith;
-#else
-        tcg_out_sety(s, 0);
-        c = ARITH_UDIV;
-        goto gen_arith;
-#endif
+
+    case INDEX_op_div_i32:
+        tcg_out_div32(s, args[0], args[1], args[2], const_args[2], 0);
+        break;
+    case INDEX_op_divu_i32:
+        tcg_out_div32(s, args[0], args[1], args[2], const_args[2], 1);
+        break;
+
+    case INDEX_op_rem_i32:
+    case INDEX_op_remu_i32:
+        tcg_out_div32(s, TCG_REG_I5, args[1], args[2], const_args[2],
+                      opc == INDEX_op_remu_i32);
+        tcg_out_arithc(s, TCG_REG_I5, TCG_REG_I5, args[2], const_args[2],
+                       ARITH_UMUL);
+        tcg_out_arith(s, args[0], args[1], TCG_REG_I5, ARITH_SUB);
+        break;
 
     case INDEX_op_brcond_i32:
         tcg_out_brcond_i32(s, args[2], args[0], args[1], const_args[1],
@@ -1214,12 +1224,20 @@ static inline void tcg_out_op(TCGContext *s, int opc, const TCGArg *args,
     case INDEX_op_mul_i64:
         c = ARITH_MULX;
         goto gen_arith;
-    case INDEX_op_div2_i64:
+    case INDEX_op_div_i64:
         c = ARITH_SDIVX;
         goto gen_arith;
-    case INDEX_op_divu2_i64:
+    case INDEX_op_divu_i64:
         c = ARITH_UDIVX;
         goto gen_arith;
+    case INDEX_op_rem_i64:
+    case INDEX_op_remu_i64:
+        tcg_out_arithc(s, TCG_REG_I5, args[1], args[2], const_args[2],
+                       opc == INDEX_op_rem_i64 ? ARITH_SDIVX : ARITH_UDIVX);
+        tcg_out_arithc(s, TCG_REG_I5, TCG_REG_I5, args[2], const_args[2],
+                       ARITH_MULX);
+        tcg_out_arith(s, args[0], args[1], TCG_REG_I5, ARITH_SUB);
+        break;
 
     case INDEX_op_brcond_i64:
         tcg_out_brcond_i64(s, args[2], args[0], args[1], const_args[1],
@@ -1263,8 +1281,10 @@ static const TCGTargetOpDef sparc_op_defs[] = {
 
     { INDEX_op_add_i32, { "r", "r", "rJ" } },
     { INDEX_op_mul_i32, { "r", "r", "rJ" } },
-    { INDEX_op_div2_i32, { "r", "r", "0", "1", "r" } },
-    { INDEX_op_divu2_i32, { "r", "r", "0", "1", "r" } },
+    { INDEX_op_div_i32, { "r", "r", "rJ" } },
+    { INDEX_op_divu_i32, { "r", "r", "rJ" } },
+    { INDEX_op_rem_i32, { "r", "r", "rJ" } },
+    { INDEX_op_remu_i32, { "r", "r", "rJ" } },
     { INDEX_op_sub_i32, { "r", "r", "rJ" } },
     { INDEX_op_and_i32, { "r", "r", "rJ" } },
     { INDEX_op_or_i32, { "r", "r", "rJ" } },
@@ -1312,8 +1332,10 @@ static const TCGTargetOpDef sparc_op_defs[] = {
 
     { INDEX_op_add_i64, { "r", "r", "rJ" } },
     { INDEX_op_mul_i64, { "r", "r", "rJ" } },
-    { INDEX_op_div2_i64, { "r", "r", "0", "1", "r" } },
-    { INDEX_op_divu2_i64, { "r", "r", "0", "1", "r" } },
+    { INDEX_op_div_i64, { "r", "r", "rJ" } },
+    { INDEX_op_divu_i64, { "r", "r", "rJ" } },
+    { INDEX_op_rem_i64, { "r", "r", "rJ" } },
+    { INDEX_op_remu_i64, { "r", "r", "rJ" } },
     { INDEX_op_sub_i64, { "r", "r", "rJ" } },
     { INDEX_op_and_i64, { "r", "r", "rJ" } },
     { INDEX_op_or_i64, { "r", "r", "rJ" } },
index e8f8f65ee1853501b4b9e5157f0310b31c2f5496..e00707b27392b687e3cacef9eac164110e817f5e 100644 (file)
@@ -88,6 +88,9 @@ enum {
 #endif
 
 /* optional instructions */
+#define TCG_TARGET_HAS_div_i32
+#define TCG_TARGET_HAS_div_i64
+
 //#define TCG_TARGET_HAS_bswap32_i32
 //#define TCG_TARGET_HAS_bswap64_i64
 //#define TCG_TARGET_HAS_neg_i32