return True;
}
+ /* ----------- V{MAX,MIN}NM{.F64 d_d_d, .F32 s_s_s} ----------- */
+ /* 31 27 22 21 19 15 11 8 7 6 5 4 3
+ 1111 11101 D 00 Vn Vd 101 1 N op M 0 Vm V{MIN,MAX}NM.F64 Dd, Dn, Dm
+ 1111 11101 D 00 Vn Vd 101 0 N op M 0 Vm V{MIN,MAX}NM.F32 Sd, Sn, Sm
+
+ ARM encoding is in NV space.
+ In Thumb mode, we must not be in an IT block.
+ */
+ if (INSN(31,23) == BITS9(1,1,1,1,1,1,1,0,1) && INSN(21,20) == BITS2(0,0)
+ && INSN(11,9) == BITS3(1,0,1) && INSN(4,4) == 0) {
+ UInt bit_D = INSN(22,22);
+ UInt fld_Vn = INSN(19,16);
+ UInt fld_Vd = INSN(15,12);
+ Bool isF64 = INSN(8,8) == 1;
+ UInt bit_N = INSN(7,7);
+ Bool isMAX = INSN(6,6) == 0;
+ UInt bit_M = INSN(5,5);
+ UInt fld_Vm = INSN(3,0);
+
+ UInt dd = isF64 ? ((bit_D << 4) | fld_Vd) : ((fld_Vd << 1) | bit_D);
+ UInt nn = isF64 ? ((bit_N << 4) | fld_Vn) : ((fld_Vn << 1) | bit_N);
+ UInt mm = isF64 ? ((bit_M << 4) | fld_Vm) : ((fld_Vm << 1) | bit_M);
+
+ if (isT) {
+ gen_SIGILL_T_if_in_ITBlock(old_itstate, new_itstate);
+ }
+ /* In ARM mode, this is statically unconditional. In Thumb mode,
+ this must be dynamically unconditional, and we've SIGILLd if not.
+ In either case we can create unconditional IR. */
+
+ IROp op = isF64 ? (isMAX ? Iop_MaxNumF64 : Iop_MinNumF64)
+ : (isMAX ? Iop_MaxNumF32 : Iop_MinNumF32);
+ IRExpr* srcN = (isF64 ? llGetDReg : llGetFReg)(nn);
+ IRExpr* srcM = (isF64 ? llGetDReg : llGetFReg)(mm);
+ IRExpr* res = binop(op, srcN, srcM);
+ (isF64 ? llPutDReg : llPutFReg)(dd, res);
+
+ UChar rch = isF64 ? 'd' : 'f';
+ DIP("v%snm.%s %c%u, %c%u, %c%u\n",
+ isMAX ? "max" : "min", isF64 ? "f64" : "f32",
+ rch, dd, rch, nn, rch, mm);
+ return True;
+ }
+
/* ---------- Doesn't match anything. ---------- */
return False;
i->ARMin.VRIntR.src = src;
return i;
}
+ARMInstr* ARMInstr_VMinMaxNum ( Bool isF64, Bool isMax,
+ HReg dst, HReg srcL, HReg srcR )
+{
+ ARMInstr* i = LibVEX_Alloc_inline(sizeof(ARMInstr));
+ i->tag = ARMin_VMinMaxNum;
+ i->ARMin.VMinMaxNum.isF64 = isF64;
+ i->ARMin.VMinMaxNum.isMax = isMax;
+ i->ARMin.VMinMaxNum.dst = dst ;
+ i->ARMin.VMinMaxNum.srcL = srcL;
+ i->ARMin.VMinMaxNum.srcR = srcR;
+ return i;
+}
ARMInstr* ARMInstr_FPSCR ( Bool toFPSCR, HReg iReg ) {
ARMInstr* i = LibVEX_Alloc_inline(sizeof(ARMInstr));
i->tag = ARMin_FPSCR;
ppHRegARM(i->ARMin.VRIntR.src);
return;
}
+ case ARMin_VMinMaxNum: {
+ const HChar* sz = i->ARMin.VMinMaxNum.isF64 ? "f64" : "f32";
+ const HChar* nm = i->ARMin.VMinMaxNum.isMax ? "vmaxnm" : "vminnm";
+ vex_printf("%s.%s ", nm, sz);
+ ppHRegARM(i->ARMin.VMinMaxNum.dst);
+ vex_printf(", ");
+ ppHRegARM(i->ARMin.VMinMaxNum.srcL);
+ vex_printf(", ");
+ ppHRegARM(i->ARMin.VMinMaxNum.srcR);
+ return;
+ }
case ARMin_FPSCR:
if (i->ARMin.FPSCR.toFPSCR) {
vex_printf("fmxr fpscr, ");
addHRegUse(u, HRmWrite, i->ARMin.VRIntR.dst);
addHRegUse(u, HRmRead, i->ARMin.VRIntR.src);
return;
+ case ARMin_VMinMaxNum:
+ addHRegUse(u, HRmWrite, i->ARMin.VMinMaxNum.dst);
+ addHRegUse(u, HRmRead, i->ARMin.VMinMaxNum.srcL);
+ addHRegUse(u, HRmRead, i->ARMin.VMinMaxNum.srcR);
+ return;
case ARMin_FPSCR:
if (i->ARMin.FPSCR.toFPSCR)
addHRegUse(u, HRmRead, i->ARMin.FPSCR.iReg);
i->ARMin.VRIntR.dst = lookupHRegRemap(m, i->ARMin.VRIntR.dst);
i->ARMin.VRIntR.src = lookupHRegRemap(m, i->ARMin.VRIntR.src);
return;
+ case ARMin_VMinMaxNum:
+ i->ARMin.VMinMaxNum.dst
+ = lookupHRegRemap(m, i->ARMin.VMinMaxNum.dst);
+ i->ARMin.VMinMaxNum.srcL
+ = lookupHRegRemap(m, i->ARMin.VMinMaxNum.srcL);
+ i->ARMin.VMinMaxNum.srcR
+ = lookupHRegRemap(m, i->ARMin.VMinMaxNum.srcR);
+ return;
case ARMin_FPSCR:
i->ARMin.FPSCR.iReg = lookupHRegRemap(m, i->ARMin.FPSCR.iReg);
return;
isF64 ? X1011 : X1010, X0100 | (M << 1), Vm);
goto done;
}
+ case ARMin_VMinMaxNum: {
+ Bool isF64 = i->ARMin.VMinMaxNum.isF64;
+ Bool isMax = i->ARMin.VMinMaxNum.isMax;
+ UInt rDst = (isF64 ? dregEnc : fregEnc)(i->ARMin.VMinMaxNum.dst);
+ UInt rSrcL = (isF64 ? dregEnc : fregEnc)(i->ARMin.VMinMaxNum.srcL);
+ UInt rSrcR = (isF64 ? dregEnc : fregEnc)(i->ARMin.VMinMaxNum.srcR);
+ /* The encoding of registers here differs strangely for the
+ F32 and F64 cases. */
+ UInt D, Vd, N, Vn, M, Vm;
+ if (isF64) {
+ D = (rDst >> 4) & 1;
+ Vd = rDst & 0xF;
+ N = (rSrcL >> 4) & 1;
+ Vn = rSrcL & 0xF;
+ M = (rSrcR >> 4) & 1;
+ Vm = rSrcR & 0xF;
+ } else {
+ Vd = (rDst >> 1) & 0xF;
+ D = rDst & 1;
+ Vn = (rSrcL >> 1) & 0xF;
+ N = rSrcL & 1;
+ Vm = (rSrcR >> 1) & 0xF;
+ M = rSrcR & 1;
+ }
+ vassert(D <= 1 && Vd <= 15 && M <= 1 && Vm <= 15 && N <= 1
+ && Vn <= 15);
+ *p++ = XXXXXXXX(X1111,X1110, X1000 | (D << 2), Vn, Vd,
+ X1010 | (isF64 ? 1 : 0),
+ (N << 3) | ((isMax ? 0 : 1) << 2) | (M << 1) | 0,
+ Vm);
+ goto done;
+ }
case ARMin_FPSCR: {
Bool toFPSCR = i->ARMin.FPSCR.toFPSCR;
UInt iReg = iregEnc(i->ARMin.FPSCR.iReg);
ARMin_VXferS,
ARMin_VCvtID,
ARMin_VRIntR,
+ ARMin_VMinMaxNum,
ARMin_FPSCR,
ARMin_MFence,
ARMin_CLREX,
HReg dst;
HReg src;
} VRIntR;
+ /* Do Min/Max of F32 or F64 values, propagating the numerical arg
+ if the other is a qNaN. For ARM >= V8 hosts only. */
+ struct {
+ Bool isF64;
+ Bool isMax;
+ HReg dst;
+ HReg srcL;
+ HReg srcR;
+ } VMinMaxNum;
/* Move a 32-bit value to/from the FPSCR (FMXR, FMRX) */
struct {
Bool toFPSCR;
extern ARMInstr* ARMInstr_VCvtID ( Bool iToD, Bool syned,
HReg dst, HReg src );
extern ARMInstr* ARMInstr_VRIntR ( Bool isF64, HReg dst, HReg src );
+extern ARMInstr* ARMInstr_VMinMaxNum ( Bool isF64, Bool isMax,
+ HReg dst, HReg srcL, HReg srcR );
extern ARMInstr* ARMInstr_FPSCR ( Bool toFPSCR, HReg iReg );
extern ARMInstr* ARMInstr_MFence ( void );
extern ARMInstr* ARMInstr_CLREX ( void );
/* not a V8 target, so we can't select insns for this. */
break;
}
+ case Iop_MaxNumF64:
+ case Iop_MinNumF64: {
+ /* Same comments regarding V8 support as for Iop_RoundF64toInt. */
+ if (VEX_ARM_ARCHLEVEL(env->hwcaps) >= 8) {
+ HReg srcL = iselDblExpr(env, e->Iex.Binop.arg1);
+ HReg srcR = iselDblExpr(env, e->Iex.Binop.arg2);
+ HReg dst = newVRegD(env);
+ Bool isMax = e->Iex.Binop.op == Iop_MaxNumF64;
+ addInstr(env, ARMInstr_VMinMaxNum(
+ True/*isF64*/, isMax, dst, srcL, srcR));
+ return dst;
+ }
+ /* not a V8 target, so we can't select insns for this. */
+ break;
+ }
default:
break;
}
/* not a V8 target, so we can't select insns for this. */
break;
}
+ case Iop_MaxNumF32:
+ case Iop_MinNumF32: {
+ /* Same comments regarding V8 support as for Iop_RoundF32toInt. */
+ if (VEX_ARM_ARCHLEVEL(env->hwcaps) >= 8) {
+ HReg srcL = iselFltExpr(env, e->Iex.Binop.arg1);
+ HReg srcR = iselFltExpr(env, e->Iex.Binop.arg2);
+ HReg dst = newVRegF(env);
+ Bool isMax = e->Iex.Binop.op == Iop_MaxNumF32;
+ addInstr(env, ARMInstr_VMinMaxNum(
+ False/*!isF64*/, isMax, dst, srcL, srcR));
+ return dst;
+ }
+ /* not a V8 target, so we can't select insns for this. */
+ break;
+ }
default:
break;
}
case Iop_RecpExpF64: vex_printf("RecpExpF64"); return;
case Iop_RecpExpF32: vex_printf("RecpExpF32"); return;
+ case Iop_MaxNumF64: vex_printf("MaxNumF64"); return;
+ case Iop_MinNumF64: vex_printf("MinNumF64"); return;
+ case Iop_MaxNumF32: vex_printf("MaxNumF32"); return;
+ case Iop_MinNumF32: vex_printf("MinNumF32"); return;
+
case Iop_F16toF64: vex_printf("F16toF64"); return;
case Iop_F64toF16: vex_printf("F64toF16"); return;
case Iop_F16toF32: vex_printf("F16toF32"); return;
case Iop_RecpExpF32:
BINARY(ity_RMode,Ity_F32, Ity_F32);
- case Iop_CmpF32:
+ case Iop_MaxNumF64: case Iop_MinNumF64:
+ BINARY(Ity_F64,Ity_F64, Ity_F64);
+
+ case Iop_MaxNumF32: case Iop_MinNumF32:
+ BINARY(Ity_F32,Ity_F32, Ity_F32);
+
+ case Iop_CmpF32:
BINARY(Ity_F32,Ity_F32, Ity_I32);
case Iop_CmpF64:
Iop_RecpExpF64, /* FRECPX d :: IRRoundingMode(I32) x F64 -> F64 */
Iop_RecpExpF32, /* FRECPX s :: IRRoundingMode(I32) x F32 -> F32 */
+ /* --------- Possibly required by IEEE 754-2008. --------- */
+
+ Iop_MaxNumF64, /* max, F64, numerical operand if other is a qNaN */
+ Iop_MinNumF64, /* min, F64, ditto */
+ Iop_MaxNumF32, /* max, F32, ditto */
+ Iop_MinNumF32, /* min, F32, ditto */
+
/* ------------------ 16-bit scalar FP ------------------ */
Iop_F16toF64, /* F16 -> F64 */