OpDiv in codegen

added OpDiv to codegen if at least one operand is a double
Esse commit está contido em:
Paul Bissonnette
2013-06-28 14:30:09 -07:00
commit de Sara Golemon
commit e9a9050451
12 arquivos alterados com 198 adições e 13 exclusões
+5 -4
Ver Arquivo
@@ -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.
+29 -2
Ver Arquivo
@@ -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) {
+89 -1
Ver Arquivo
@@ -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() {
+6
Ver Arquivo
@@ -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();
+1 -1
Ver Arquivo
@@ -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) \
+25
Ver Arquivo
@@ -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
+1
Ver Arquivo
@@ -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);
+2 -1
Ver Arquivo
@@ -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.
+18 -1
Ver Arquivo
@@ -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 }},
-3
Ver Arquivo
@@ -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;
}
+5
Ver Arquivo
@@ -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.
+17
Ver Arquivo
@@ -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
)");
}
}}