JIT - vctsxs: Properly saturate signed integers
This commit is contained in:
@@ -670,9 +670,9 @@ static const vec128_t xmm_consts[] = {
|
||||
0x80000000u),
|
||||
/* XMMShortMinPS */ vec128f(SHRT_MIN),
|
||||
/* XMMShortMaxPS */ vec128f(SHRT_MAX),
|
||||
/* XMMIntMaxPS */ vec128f(float(INT_MAX)),
|
||||
/* XMMIntMin */ vec128i(INT_MIN),
|
||||
/* XMMIntMax */ vec128i(INT_MAX),
|
||||
/* XMMIntMaxPD */ vec128d(INT_MAX),
|
||||
/* XMMInt64MaxPD */ vec128d(double(INT64_MAX)),
|
||||
};
|
||||
|
||||
// First location to try and place constants.
|
||||
|
||||
@@ -89,9 +89,9 @@ enum XmmConst {
|
||||
XMMSignMaskF32,
|
||||
XMMShortMinPS,
|
||||
XMMShortMaxPS,
|
||||
XMMIntMaxPS,
|
||||
XMMIntMin,
|
||||
XMMIntMax,
|
||||
XMMIntMaxPD,
|
||||
XMMInt64MaxPD,
|
||||
};
|
||||
|
||||
// Unfortunately due to the design of xbyak we have to pass this to the ctor.
|
||||
|
||||
@@ -1429,8 +1429,11 @@ struct CONVERT_I32_F32
|
||||
: Sequence<CONVERT_I32_F32, I<OPCODE_CONVERT, I32Op, F32Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
// TODO(benvanik): saturation check? cvtt* (trunc?)
|
||||
e.vminss(e.xmm0, i.src1, e.GetXmmConstPtr(XMMIntMaxPS));
|
||||
e.vcvtss2si(i.dest, e.xmm0);
|
||||
if (i.instr->flags == ROUND_TO_ZERO) {
|
||||
e.vcvttss2si(i.dest, e.xmm0);
|
||||
} else {
|
||||
e.vcvtss2si(i.dest, e.xmm0);
|
||||
}
|
||||
}
|
||||
};
|
||||
struct CONVERT_I32_F64
|
||||
@@ -1440,14 +1443,22 @@ struct CONVERT_I32_F64
|
||||
// PPC saturates the value instead.
|
||||
// So, we can clamp the double value to (double)0x7FFFFFFF.
|
||||
e.vminsd(e.xmm0, i.src1, e.GetXmmConstPtr(XMMIntMaxPD));
|
||||
e.vcvttsd2si(i.dest, e.xmm0);
|
||||
if (i.instr->flags == ROUND_TO_ZERO) {
|
||||
e.vcvttsd2si(i.dest, e.xmm0);
|
||||
} else {
|
||||
e.vcvtsd2si(i.dest, e.xmm0);
|
||||
}
|
||||
}
|
||||
};
|
||||
struct CONVERT_I64_F64
|
||||
: Sequence<CONVERT_I64_F64, I<OPCODE_CONVERT, I64Op, F64Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
e.vminsd(e.xmm0, i.src1, e.GetXmmConstPtr(XMMInt64MaxPD));
|
||||
e.vcvttsd2si(i.dest, e.xmm0);
|
||||
// TODO(benvanik): saturation check? cvtt* (trunc?)
|
||||
if (i.instr->flags == ROUND_TO_ZERO) {
|
||||
e.vcvttsd2si(i.dest, e.xmm0);
|
||||
} else {
|
||||
e.vcvtsd2si(i.dest, e.xmm0);
|
||||
}
|
||||
}
|
||||
};
|
||||
struct CONVERT_F32_I32
|
||||
@@ -1568,13 +1579,28 @@ struct VECTOR_CONVERT_F2I
|
||||
: Sequence<VECTOR_CONVERT_F2I,
|
||||
I<OPCODE_VECTOR_CONVERT_F2I, V128Op, V128Op>> {
|
||||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
// flags = ARITHMETIC_UNSIGNED | ARITHMETIC_UNSIGNED
|
||||
// TODO(benvanik): are these really the same? VC++ thinks so.
|
||||
e.vcvttps2dq(i.dest, i.src1);
|
||||
if (i.instr->flags & ARITHMETIC_SATURATE) {
|
||||
// TODO(benvanik): check saturation.
|
||||
// In theory cvt throws if it saturates.
|
||||
Xmm src1 = i.src1;
|
||||
|
||||
// Copy src1 if necessary.
|
||||
bool copy_src1 = !!(i.instr->flags & ARITHMETIC_SATURATE);
|
||||
if (copy_src1 && i.dest == i.src1) {
|
||||
e.vmovdqa(e.xmm1, i.src1);
|
||||
src1 = e.xmm1;
|
||||
}
|
||||
|
||||
e.vcvttps2dq(i.dest, i.src1);
|
||||
if (i.instr->flags & ARITHMETIC_SATURATE &&
|
||||
!(i.instr->flags & ARITHMETIC_UNSIGNED)) {
|
||||
// if dest is indeterminate and i.src1 >= 0 (i.e. !(i.src1 & 0x80000000))
|
||||
// i.dest = 0x7FFFFFFF
|
||||
e.vpcmpeqd(e.xmm0, i.dest, e.GetXmmConstPtr(XMMIntMin));
|
||||
e.vpandn(e.xmm0, src1, e.xmm0);
|
||||
|
||||
// (high bit of xmm0 = is ind. && i.src1 >= 0)
|
||||
e.vblendvps(i.dest, i.dest, e.GetXmmConstPtr(XMMIntMax), e.xmm0);
|
||||
}
|
||||
|
||||
// TODO(DrChat): Unsigned saturation!
|
||||
}
|
||||
};
|
||||
EMITTER_OPCODE_TABLE(OPCODE_VECTOR_CONVERT_F2I, VECTOR_CONVERT_F2I);
|
||||
|
||||
Reference in New Issue
Block a user