OpDiv in codegen
added OpDiv to codegen if at least one operand is a double
Esse commit está contido em:
@@ -465,7 +465,7 @@ D:{Int|Dbl} = OpAdd S0:{Int|Dbl} S1:{Int|Dbl}
|
||||
D:{Int|Dbl} = OpSub S0:{Int|Dbl} S1:{Int|Dbl}
|
||||
D:{Int|Dbl} = OpMul S0:{Int|Dbl} S1:{Int|Dbl}
|
||||
D:Int = OpMod S0:Int S1:Int
|
||||
D:{Int|Bool|Dbl} = OpDiv S0:{Int|Dbl} S1:{Int|Dbl}
|
||||
D:Dbl = OpDivDbl S0:Dbl S1:Dbl -> L
|
||||
D:Int = OpBitAnd S0:Int S1:Int
|
||||
D:Int = OpBitOr S0:Int S1:Int
|
||||
D:Int = OpBitXor S0:Int S1:Int
|
||||
@@ -478,9 +478,10 @@ D:Int = OpShr S0:Int S1:Int
|
||||
Undefined behavior occurs if OpMod is given a divisor of zero, or if
|
||||
the divisor is -1 and the dividend is the minimum representable integer.
|
||||
|
||||
OpDiv produces boolean false when the divisor is zero-like. Ints are
|
||||
not closed under OpDiv, as OpDiv produces doubles when there is a
|
||||
non-zero remainder.
|
||||
OpDivDbl Will branch to L when S1 is zero (signed or unsigned). When the
|
||||
result of the division is a real valued number OpDivDbl conforms to IEEE 754.
|
||||
In particular should the result of a division be zero the sign will follow
|
||||
normal sign rules for division.
|
||||
|
||||
Note that OpShr is an arithmetic right shift.
|
||||
|
||||
|
||||
@@ -1415,8 +1415,35 @@ void CodeGenerator::cgOpSub(IRInstruction* inst) {
|
||||
NonCommutative);
|
||||
}
|
||||
|
||||
void CodeGenerator::cgOpDiv(IRInstruction* inst) {
|
||||
not_implemented();
|
||||
void CodeGenerator::cgOpDivDbl(IRInstruction* inst) {
|
||||
const SSATmp* dst = inst->dst();
|
||||
const SSATmp* src1 = inst->src(0);
|
||||
const SSATmp* src2 = inst->src(1);
|
||||
Block* exit = inst->taken();
|
||||
|
||||
auto dstReg = m_regs[dst].reg();
|
||||
auto resReg = dstReg.isXMM() && dstReg != m_regs[src2].reg() ?
|
||||
dstReg : PhysReg(rCgXMM0);
|
||||
assert(resReg.isXMM());
|
||||
|
||||
// only load divisor
|
||||
PhysReg srcReg2 = prepXMMReg(src2, m_as, m_regs, rCgXMM1);
|
||||
assert(srcReg2 != rCgXMM0);
|
||||
|
||||
// divide by zero check
|
||||
m_as.pxor_xmm_xmm(rCgXMM0, rCgXMM0);
|
||||
m_as.ucomisd_xmm_xmm(rCgXMM0, srcReg2);
|
||||
unlikelyIfBlock(CC_NP, [&] (Asm& a) {
|
||||
emitFwdJcc(a, CC_E, exit);
|
||||
});
|
||||
|
||||
// now load dividend
|
||||
PhysReg srcReg1 = prepXMMReg(src1, m_as, m_regs, resReg);
|
||||
assert(srcReg1 != rCgXMM1);
|
||||
|
||||
emitMovRegReg(m_as, srcReg1, resReg);
|
||||
m_as.divsd(srcReg2, resReg);
|
||||
emitMovRegReg(m_as, resReg, dstReg);
|
||||
}
|
||||
|
||||
void CodeGenerator::cgOpBitAnd(IRInstruction* inst) {
|
||||
|
||||
@@ -3337,7 +3337,95 @@ BINOP(BitXor)
|
||||
#undef BINOP
|
||||
|
||||
void HhbcTranslator::emitDiv() {
|
||||
emitInterpOne(Type::Cell, 2);
|
||||
auto divisorType = topC(0)->type();
|
||||
auto dividendType = topC(1)->type();
|
||||
|
||||
auto isNumeric = [&] (Type type) {
|
||||
return type.subtypeOfAny(Type::Int, Type::Dbl, Type::Bool);
|
||||
};
|
||||
|
||||
// not going to bother with string division etc.
|
||||
if (!isNumeric(divisorType) || !isNumeric(dividendType)) {
|
||||
emitInterpOne(Type::Cell, 2);
|
||||
checkTypeTopOfStack(Type::Dbl, nextBcOff());
|
||||
return;
|
||||
}
|
||||
|
||||
auto divisor = topC(0);
|
||||
auto dividend = topC(1);
|
||||
|
||||
// we can't codegen this but we may be able to special case it away
|
||||
if (!divisorType.subtypeOf(Type::Dbl) && !dividendType.subtypeOf(Type::Dbl)) {
|
||||
// TODO(#2570625): support integer-integer division, move this to simlifier:
|
||||
if (divisor->isConst()) {
|
||||
int64_t divisorVal;
|
||||
if (divisor->isA(Type::Int)) {
|
||||
divisorVal = divisor->getValInt();
|
||||
} else {
|
||||
assert(divisor->isA(Type::Bool));
|
||||
divisorVal = divisor->getValBool();
|
||||
}
|
||||
|
||||
if (divisorVal == 0) {
|
||||
popC();
|
||||
popC();
|
||||
gen(RaiseWarning,
|
||||
cns(StringData::GetStaticString(Strings::DIVISION_BY_ZERO)));
|
||||
push(cns(false));
|
||||
return;
|
||||
}
|
||||
|
||||
if (dividend->isConst()) {
|
||||
int64_t dividendVal;
|
||||
if (dividend->isA(Type::Int)) {
|
||||
dividendVal = dividend->getValInt();
|
||||
} else {
|
||||
assert(dividend->isA(Type::Bool));
|
||||
dividendVal = dividend->getValBool();
|
||||
}
|
||||
popC();
|
||||
popC();
|
||||
if (dividendVal == LLONG_MIN || dividendVal % divisorVal) {
|
||||
push(cns((double)dividendVal / divisorVal));
|
||||
} else {
|
||||
push(cns(dividendVal / divisorVal));
|
||||
}
|
||||
return;
|
||||
}
|
||||
/* fall through */
|
||||
}
|
||||
emitInterpOne(Type::Cell, 2);
|
||||
|
||||
// interpOne could return an int bool or double
|
||||
checkTypeTopOfStack(Type::Dbl, nextBcOff());
|
||||
return;
|
||||
}
|
||||
|
||||
auto make_double = [&] (SSATmp* src) {
|
||||
if (src->isA(Type::Int)) {
|
||||
return gen(ConvIntToDbl, src);
|
||||
} else if (src->isA(Type::Bool)) {
|
||||
return gen(ConvBoolToDbl, src);
|
||||
}
|
||||
assert(src->isA(Type::Dbl));
|
||||
return src;
|
||||
};
|
||||
|
||||
divisor = make_double(popC());
|
||||
dividend = make_double(popC());
|
||||
|
||||
// on division by zero we spill false and exit with a warning
|
||||
auto exitSpillValues = peekSpillValues();
|
||||
exitSpillValues.push_back(cns(false));
|
||||
|
||||
auto const exit = getExitTraceWarn(
|
||||
nextBcOff(),
|
||||
exitSpillValues,
|
||||
StringData::GetStaticString(Strings::DIVISION_BY_ZERO)
|
||||
);
|
||||
|
||||
assert(divisor->isA(Type::Dbl) && dividend->isA(Type::Dbl));
|
||||
push(gen(OpDivDbl, exit, dividend, divisor));
|
||||
}
|
||||
|
||||
void HhbcTranslator::emitMod() {
|
||||
|
||||
@@ -179,6 +179,12 @@ IRTranslator::translateMod(const NormalizedInstruction& i) {
|
||||
HHIR_EMIT(Mod);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
IRTranslator::translateDiv(const NormalizedInstruction& i) {
|
||||
HHIR_EMIT(Div);
|
||||
}
|
||||
|
||||
void
|
||||
IRTranslator::translateBinaryArithOp(const NormalizedInstruction& i) {
|
||||
auto const op = i.op();
|
||||
|
||||
@@ -211,7 +211,7 @@ O(DeleteUnwinderException, ND, NA, N|E|Mem) \
|
||||
O(OpAdd, DArith, S(Int,Dbl) S(Int,Dbl), C) \
|
||||
O(OpSub, DArith, S(Int,Dbl) S(Int,Dbl), C) \
|
||||
O(OpMul, DArith, S(Int,Dbl) S(Int,Dbl), C) \
|
||||
O(OpDiv, DArith, S(Int,Dbl) S(Int,Dbl), C) \
|
||||
O(OpDivDbl, D(Dbl), S(Dbl) S(Dbl), C) \
|
||||
O(OpMod, D(Int), S(Int) S(Int), C) \
|
||||
O(OpBitAnd, D(Int), S(Int) S(Int), C) \
|
||||
O(OpBitOr, D(Int), S(Int) S(Int), C) \
|
||||
|
||||
@@ -305,6 +305,7 @@ SSATmp* Simplifier::simplify(IRInstruction* inst) {
|
||||
case OpLogicXor: return simplifyLogicXor(src1, src2);
|
||||
case OpShl: return simplifyShl(inst);
|
||||
case OpShr: return simplifyShr(inst);
|
||||
case OpDivDbl: return simplifyDivDbl(inst);
|
||||
|
||||
case OpGt:
|
||||
case OpGte:
|
||||
@@ -931,6 +932,30 @@ SSATmp* Simplifier::simplifyMod(SSATmp* src1, SSATmp* src2) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SSATmp* Simplifier::simplifyDivDbl(IRInstruction* inst) {
|
||||
auto src1 = inst->src(0);
|
||||
auto src2 = inst->src(1);
|
||||
|
||||
if (!src2->isConst()) return nullptr;
|
||||
|
||||
// not supporting integers (#2570625)
|
||||
double src2Val = src2->getValDbl();
|
||||
|
||||
// X / 0 -> bool(false)
|
||||
if (src2Val == 0.0) {
|
||||
gen(RaiseWarning,
|
||||
cns(StringData::GetStaticString(Strings::DIVISION_BY_ZERO)));
|
||||
return cns(false);
|
||||
}
|
||||
|
||||
// statically compute X / Y
|
||||
if (src1->isConst()) {
|
||||
return cns(src1->getValDbl() / src2Val);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SSATmp* Simplifier::simplifyBitAnd(SSATmp* src1, SSATmp* src2) {
|
||||
SIMPLIFY_DISTRIBUTIVE(&, |, BitAnd, BitOr);
|
||||
// X & X --> X
|
||||
|
||||
@@ -68,6 +68,7 @@ private:
|
||||
SSATmp* simplifySub(SSATmp* src1, SSATmp* src2);
|
||||
SSATmp* simplifyMul(SSATmp* src1, SSATmp* src2);
|
||||
SSATmp* simplifyMod(SSATmp* src1, SSATmp* src2);
|
||||
SSATmp* simplifyDivDbl(IRInstruction* inst);
|
||||
SSATmp* simplifyBitAnd(SSATmp* src1, SSATmp* src2);
|
||||
SSATmp* simplifyBitOr(SSATmp* src1, SSATmp* src2);
|
||||
SSATmp* simplifyBitXor(SSATmp* src1, SSATmp* src2);
|
||||
|
||||
@@ -166,7 +166,8 @@
|
||||
CASE(UnsetN) \
|
||||
CASE(DecodeCufIter) \
|
||||
CASE(Shl) \
|
||||
CASE(Shr)
|
||||
CASE(Shr) \
|
||||
CASE(Div)
|
||||
|
||||
// These are instruction-like functions which cover more than one
|
||||
// opcode.
|
||||
|
||||
@@ -609,6 +609,23 @@ predictOutputs(SrcKey startSk,
|
||||
// Integers can produce integers if there's no residue, but $i / $j in
|
||||
// general produces a double. $i / 0 produces boolean false, so we have
|
||||
// actually check the result.
|
||||
auto lhs = ni->inputs[0];
|
||||
auto rhs = ni->inputs[1];
|
||||
|
||||
if (lhs->valueType() == KindOfDouble || rhs->valueType() == KindOfDouble) {
|
||||
return KindOfDouble;
|
||||
}
|
||||
|
||||
if (rhs->isLiteral()) {
|
||||
if (ni->imm[1].u_I64A == 0) return KindOfBoolean;
|
||||
if (ni->imm[1].u_I64A == 1) return lhs->valueType();
|
||||
|
||||
if (rhs->isLiteral()) {
|
||||
return ni->imm[0].u_I64A % ni->imm[1].u_I64A ? KindOfDouble
|
||||
: KindOfInt64;
|
||||
}
|
||||
}
|
||||
|
||||
return KindOfDouble;
|
||||
}
|
||||
|
||||
@@ -1007,7 +1024,7 @@ static const struct {
|
||||
{ OpSub, {StackTop2, Stack1, OutArith, -1 }},
|
||||
{ OpMul, {StackTop2, Stack1, OutArith, -1 }},
|
||||
/* Div and mod might return boolean false. Sigh. */
|
||||
{ OpDiv, {StackTop2, Stack1, OutUnknown, -1 }},
|
||||
{ OpDiv, {StackTop2, Stack1, OutPred, -1 }},
|
||||
{ OpMod, {StackTop2, Stack1, OutPred, -1 }},
|
||||
/* Logical ops */
|
||||
{ OpXor, {StackTop2, Stack1, OutBoolean, -1 }},
|
||||
|
||||
@@ -180,9 +180,6 @@ Type stkReturn(const IRInstruction* inst, int dstId,
|
||||
}
|
||||
|
||||
Type binArithResultType(Opcode op, Type t1, Type t2) {
|
||||
if (op == OpDiv) {
|
||||
return Type::Int | Type::Dbl | Type::Bool;
|
||||
}
|
||||
if (op == OpMod) {
|
||||
return Type::Int;
|
||||
}
|
||||
|
||||
@@ -701,6 +701,7 @@ struct X64Instr {
|
||||
};
|
||||
|
||||
// 0 1 2 3 4 5 flags
|
||||
const X64Instr instr_divsd { { 0x5E,0xF1,0xF1,0x00,0xF1,0xF1 }, 0x10102 };
|
||||
const X64Instr instr_movdqa = { { 0x6F,0x7F,0xF1,0x00,0xF1,0xF1 }, 0x4103 };
|
||||
const X64Instr instr_movdqu = { { 0x6F,0x7F,0xF1,0x00,0xF1,0xF1 }, 0x8103 };
|
||||
const X64Instr instr_movsd = { { 0x11,0x10,0xF1,0x00,0xF1,0xF1 }, 0x10102 };
|
||||
@@ -2325,6 +2326,10 @@ public:
|
||||
emitRR(instr_ucomisd, rn(l), rn(r));
|
||||
}
|
||||
|
||||
void divsd(RegXMM src, RegXMM srcdest) {
|
||||
emitRR(instr_divsd, rn(srcdest), rn(src));
|
||||
}
|
||||
|
||||
private:
|
||||
bool byteRegNeedsRex(int rn) const {
|
||||
// Without a rex, 4 through 7 mean the high 8-bit byte registers.
|
||||
|
||||
@@ -724,4 +724,21 @@ sar %cl,%r8
|
||||
)");
|
||||
}
|
||||
|
||||
TEST(Asm, SSEDivision) {
|
||||
Asm a;
|
||||
a.init(10 << 24);
|
||||
a. divsd(xmm0, xmm1);
|
||||
a. divsd(xmm1, xmm2);
|
||||
a. divsd(xmm2, xmm0);
|
||||
a. divsd(xmm15, xmm0);
|
||||
a. divsd(xmm12, xmm8);
|
||||
expect_asm(a, R"(
|
||||
divsd %xmm0,%xmm1
|
||||
divsd %xmm1,%xmm2
|
||||
divsd %xmm2,%xmm0
|
||||
divsd %xmm15,%xmm0
|
||||
divsd %xmm12,%xmm8
|
||||
)");
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário