瀏覽代碼

Refactoring of conversion ops, part 1: add IR_CONV.

Mike Pall 14 年之前
父節點
當前提交
2ca2de7f0e
共有 5 個文件被更改,包括 142 次插入1 次删除
  1. 9 0
      lib/dump.lua
  2. 95 1
      src/lj_asm.c
  3. 11 0
      src/lj_ir.h
  4. 22 0
      src/lj_opt_fold.c
  5. 5 0
      src/lj_target_x86.h

+ 9 - 0
lib/dump.lua

@@ -236,6 +236,15 @@ local litname = {
     return s
   end}),
   ["XLOAD "] = { [0] = "", "R", "U", "RU", },
+  ["CONV  "] = setmetatable({}, { __index = function(t, mode)
+    local s = irtype[band(mode, 31)]
+    if band(mode, 0x100) ~= 0 then s = s.." trunc"
+    elseif band(mode, 0x200) ~= 0 then s = s.." sext" end
+    local c = shr(mode, 10)
+    if c == 2 then s = s.." index" elseif c == 3 then s = s.." check" end
+    t[mode] = s
+    return s
+  end}),
   ["TOINT "] = tointname,
   ["TOI64 "] = tointname,
   ["FLOAD "] = vmdef.irfield,

+ 95 - 1
src/lj_asm.c

@@ -1654,6 +1654,99 @@ static void asm_toi64(ASMState *as, IRIns *ir)
   }
 }
 
+static void asm_conv(ASMState *as, IRIns *ir)
+{
+  IRType st = (IRType)(ir->op2 & 0x1f);
+  int st64 = (st == IRT_I64 || st == IRT_U64 || (LJ_64 && st == IRT_P64));
+  int stfp = (st == IRT_NUM || st == IRT_FLOAT);
+  IRRef lref = ir->op1;
+  lua_assert(irt_type(ir->t) != st);
+  if (irt_isnum(ir->t) || irt_isfloat(ir->t)) {
+    Reg dest = ra_dest(as, ir, RSET_FPR);
+    if (stfp) {  /* FP to FP conversion. */
+      Reg left = asm_fuseload(as, lref, RSET_FPR);
+      emit_mrm(as, st == IRT_NUM ? XO_CVTSD2SS : XO_CVTSS2SD, dest, left);
+      if (left == dest) return;  /* Avoid the XO_XORPS. */
+#if LJ_32
+    } else if (st >= IRT_U32) {
+      /* NYI: 64 bit integer or uint32_t to number conversion. */
+      setintV(&as->J->errinfo, ir->o);
+      lj_trace_err_info(as->J, LJ_TRERR_NYIIR);
+      return;
+#endif
+    } else {  /* Integer to FP conversion. */
+      Reg left = (LJ_64 && st == IRT_U32) ? ra_allocref(as, lref, RSET_GPR) :
+					    asm_fuseload(as, lref, RSET_GPR);
+      emit_mrm(as, irt_isnum(ir->t) ? XO_CVTSI2SD : XO_CVTSI2SS,
+	       dest|((LJ_64 && (st64 || st == IRT_U32)) ? REX_64 : 0), left);
+    }
+    if (!(as->flags & JIT_F_SPLIT_XMM))
+      emit_rr(as, XO_XORPS, dest, dest);  /* Avoid partial register stall. */
+  } else if (stfp) {  /* FP to integer conversion. */
+    if (irt_isguard(ir->t)) {
+      lua_assert(!irt_is64(ir->t));  /* No support for checked 64 bit conv. */
+      asm_tointg(as, ir, ra_alloc1(as, lref, RSET_FPR));
+#if LJ_32
+    } else if (irt_isi64(ir->t) || irt_isu64(ir->t) || irt_isu32(ir->t)) {
+      /* NYI: number to 64 bit integer or uint32_t conversion. */
+      setintV(&as->J->errinfo, ir->o);
+      lj_trace_err_info(as->J, LJ_TRERR_NYIIR);
+#else
+    } else if (irt_isu64(ir->t)) {
+      /* NYI: number to uint64_t conversion. */
+      setintV(&as->J->errinfo, ir->o);
+      lj_trace_err_info(as->J, LJ_TRERR_NYIIR);
+#endif
+    } else {
+      Reg dest = ra_dest(as, ir, RSET_GPR);
+      Reg left = asm_fuseload(as, ir->op1, RSET_FPR);
+      x86Op op = st == IRT_NUM ?
+		 ((ir->op2 & IRCONV_TRUNC) ? XO_CVTTSD2SI : XO_CVTSD2SI) :
+		 ((ir->op2 & IRCONV_TRUNC) ? XO_CVTTSS2SI : XO_CVTSS2SI);
+      if (LJ_64 && irt_isu32(ir->t))
+	emit_rr(as, XO_MOV, dest, dest);  /* Zero upper 32 bits. */
+      emit_mrm(as, op,
+	       dest|((LJ_64 &&
+		      (irt_is64(ir->t) || irt_isu32(ir->t))) ? REX_64 : 0),
+	       left);
+    }
+  } else {  /* Integer to integer conversion. Only need 32/64 bit variants. */
+    if (irt_is64(ir->t)) {
+#if LJ_32
+      /* NYI: conversion to 64 bit integers. */
+      setintV(&as->J->errinfo, ir->o);
+      lj_trace_err_info(as->J, LJ_TRERR_NYIIR);
+#else
+      Reg dest = ra_dest(as, ir, RSET_GPR);
+      if (st64 || !(ir->op2 & IRCONV_SEXT)) {
+	/* 64/64 bit no-op (cast) or 32 to 64 bit zero extension. */
+	ra_left(as, dest, lref);  /* Do nothing, but may need to move regs. */
+      } else {  /* 32 to 64 bit sign extension. */
+	Reg left = asm_fuseload(as, lref, RSET_GPR);
+	emit_mrm(as, XO_MOVSXd, dest|REX_64, left);
+      }
+#endif
+    } else {
+      Reg dest = ra_dest(as, ir, RSET_GPR);
+      if (st64) {
+#if LJ_32
+	/* NYI: conversion from 64 bit integers. */
+	setintV(&as->J->errinfo, ir->o);
+	lj_trace_err_info(as->J, LJ_TRERR_NYIIR);
+#else
+	Reg left = asm_fuseload(as, lref, RSET_GPR);
+	/* This is either a 32 bit reg/reg mov which zeroes the hi-32 bits
+	** or a load of the lower 32 bits from a 64 bit address.
+	*/
+	emit_mrm(as, XO_MOV, dest, left);
+#endif
+      } else {  /* 32/32 bit no-op (cast). */
+	ra_left(as, dest, lref);  /* Do nothing, but may need to move regs. */
+      }
+    }
+  }
+}
+
 static void asm_strto(ASMState *as, IRIns *ir)
 {
   /* Force a spill slot for the destination register (if any). */
@@ -3666,6 +3759,7 @@ static void asm_ir(ASMState *as, IRIns *ir)
     break;
   case IR_TOBIT: asm_tobit(as, ir); break;
   case IR_TOI64: asm_toi64(as, ir); break;
+  case IR_CONV: asm_conv(as, ir); break;
   case IR_TOSTR: asm_tostr(as, ir); break;
   case IR_STRTO: asm_strto(as, ir); break;
 
@@ -3808,7 +3902,7 @@ static void asm_setup_regsp(ASMState *as, GCtrace *T)
       }
       break;
     /* Do not propagate hints across type conversions. */
-    case IR_TONUM: case IR_TOINT: case IR_TOBIT:
+    case IR_CONV: case IR_TONUM: case IR_TOINT: case IR_TOBIT:
       break;
     default:
       /* Propagate hints across likely 'op reg, imm' or 'op reg'. */

+ 11 - 0
src/lj_ir.h

@@ -118,6 +118,7 @@
   _(OBAR,	S , ref, ref) \
   \
   /* Type conversions. */ \
+  _(CONV,	N , ref, lit) \
   _(TONUM,	N , ref, ___) \
   _(TOINT,	N , ref, lit) \
   _(TOBIT,	N , ref, ref) \
@@ -218,6 +219,16 @@ IRFLDEF(FLENUM)
 #define IRTOINT_TRUNCI64	5	/* Truncate number to int64_t. */
 #define IRTOINT_TOBIT		6	/* Cache only: TOBIT conversion. */
 
+/* CONV mode, stored in op2. Lowest 8 bits is the IRType of the source. */
+#define IRCONV_TRUNC		0x100	/* Truncate number to integer. */
+#define IRCONV_SEXT		0x200	/* Sign-extend integer to integer. */
+#define IRCONV_CSH		10
+/* Number to integer conversion mode. Ordered by strength of the checks. */
+#define IRCONV_TOBIT  (0<<IRCONV_CSH)	/* None. Cache only: TOBIT conv. */
+#define IRCONV_ANY    (1<<IRCONV_CSH)	/* Any FP number is ok. */
+#define IRCONV_INDEX  (2<<IRCONV_CSH)	/* Check + special backprop rules. */
+#define IRCONV_CHECK  (3<<IRCONV_CSH)	/* Number checked for integerness. */
+
 /* C call info for CALL* instructions. */
 typedef struct CCallInfo {
   ASMFunction func;		/* Function pointer. */

+ 22 - 0
src/lj_opt_fold.c

@@ -771,6 +771,28 @@ LJFOLDF(cse_toint)
   return EMITFOLD;  /* No fallthrough to regular CSE. */
 }
 
+/* Special CSE rule for CONV. */
+LJFOLD(CONV any any)
+LJFOLDF(cse_conv)
+{
+  if (LJ_LIKELY(J->flags & JIT_F_OPT_CSE)) {
+    IRRef op1 = fins->op1, op2 = (fins->op2 & IRCONV_MODEMASK);
+    uint8_t guard = irt_isguard(fins->t);
+    IRRef ref = J->chain[IR_CONV];
+    while (ref > op1) {
+      IRIns *ir = IR(ref);
+      /* CSE also depends on the target type!
+      ** OTOH commoning with stronger checks is ok, too.
+      */
+      if (ir->op1 == op1 && irt_sametype(ir->t, fins->t) &&
+	  (ir->op2 & IRCONV_MODEMASK) == op2 && irt_isguard(ir->t) >= guard)
+	return ref;
+      ref = ir->prev;
+    }
+  }
+  return EMITFOLD;  /* No fallthrough to regular CSE. */
+}
+
 /* -- Strength reduction of widening -------------------------------------- */
 
 LJFOLD(TOI64 any 3)  /* IRTOINT_ZEXT64 */

+ 5 - 0
src/lj_target_x86.h

@@ -251,6 +251,11 @@ typedef enum {
   XO_CVTSI2SD =	XO_f20f(2a),
   XO_CVTSD2SI =	XO_f20f(2d),
   XO_CVTTSD2SI=	XO_f20f(2c),
+  XO_CVTSI2SS =	XO_f30f(2a),
+  XO_CVTSS2SI =	XO_f30f(2d),
+  XO_CVTTSS2SI=	XO_f30f(2c),
+  XO_CVTSS2SD =	XO_f30f(5a),
+  XO_CVTSD2SS =	XO_f20f(5a),
   XO_MOVD =	XO_660f(6e),
   XO_MOVDto =	XO_660f(7e),