Push modular division down to codegen

InterpOne sucks. Let's see if we can do this right.
Esse commit está contido em:
Eric Caruso
2013-06-28 23:45:30 -07:00
commit de Sara Golemon
commit d6fd9de236
8 arquivos alterados com 94 adições e 25 exclusões
+7 -4
Ver Arquivo
@@ -446,7 +446,7 @@ D:CountedStr = AssertNonNull S0:{Nullptr|CountedStr}
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|Bool} = OpMod 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:Int = OpBitAnd S0:Int S1:Int
D:Int = OpBitOr S0:Int S1:Int
@@ -455,9 +455,12 @@ D:Int = OpBitXor S0:Int S1:Int
Integer/boolean arithmetic. Performs the operation described by the
opcode name on S0 and S1, and puts the result in D.
Note that OpMod and OpDiv produce 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.
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.
D:Bool = OpLogicXor S0:Bool S1:Bool
+43 -1
Ver Arquivo
@@ -418,7 +418,6 @@ CALL_OPCODE(RaiseWarning)
CALL_OPCODE(IncStatGrouped)
CALL_OPCODE(StaticLocInit)
CALL_OPCODE(StaticLocInitCached)
CALL_OPCODE(OpMod)
CALL_OPCODE(ArrayIdx)
// Vector instruction helpers
@@ -1486,6 +1485,49 @@ void CodeGenerator::cgOpMul(IRInstruction* inst) {
Commutative);
}
void CodeGenerator::cgOpMod(IRInstruction* inst) {
auto const src0 = inst->src(0);
auto const src1 = inst->src(1);
auto const dstReg = m_regs[inst->dst()].reg();
auto& a = m_as;
// spill rax and/or rdx
bool spillRax = dstReg != reg::rax && m_rScratch != reg::rax;
bool spillRdx = dstReg != reg::rdx && m_rScratch != reg::rdx;
if (spillRax) {
a. push (reg::rax);
}
if (spillRdx) {
a. push (reg::rdx);
}
// put divisor in rAsm
if (src1->isConst()) {
a. movq (src1->getValInt(), rAsm);
} else {
a. movq (m_regs[src1].reg(), rAsm);
}
// put dividend in rax
if (src0->isConst()) {
a. movq (src0->getValInt(), reg::rax);
} else if (m_regs[src0].reg() != reg::rax) {
a. movq (m_regs[src0].reg(), reg::rax);
}
// sign-extend rax to rdx:rax
a. cqo ();
// divide
a. idiv (rAsm);
if (dstReg != reg::rdx) {
a. movq (reg::rdx, dstReg);
}
// restore rax and/or rdx
if (spillRdx) {
a. pop (reg::rdx);
}
if (spillRax) {
a. pop (reg::rax);
}
}
void CodeGenerator::cgOpNot(IRInstruction* inst) {
auto const src = inst->src(0);
auto const dstReg = m_regs[inst->dst()].reg();
+39 -16
Ver Arquivo
@@ -3171,20 +3171,13 @@ void HhbcTranslator::emitDiv() {
}
void HhbcTranslator::emitMod() {
// XXX: Disabled until t2299606 is fixed
PUNT(emitMod);
auto tl = topC(1)->type();
auto tr = topC(0)->type();
auto isInty = [&](Type t) {
return t.subtypeOf(Type::Null | Type::Bool | Type::Int);
};
if (!(isInty(tl) && isInty(tr))) {
emitInterpOne(Type::Cell, 2);
return;
}
SSATmp* r = popC();
SSATmp* l = popC();
IRTrace* catchTrace = getCatchTrace();
SSATmp* btr = popC();
SSATmp* btl = popC();
SSATmp* tr = gen(ConvCellToInt, catchTrace, btr);
SSATmp* tl = gen(ConvCellToInt, catchTrace, btl);
gen(DecRef, btr);
gen(DecRef, btl);
// Exit path spills an additional false
auto exitSpillValues = peekSpillValues();
exitSpillValues.push_back(cns(false));
@@ -3197,8 +3190,38 @@ void HhbcTranslator::emitMod() {
exitSpillValues,
StringData::GetStaticString(Strings::DIVISION_BY_ZERO)
);
gen(JmpZero, exit, r);
push(gen(OpMod, l, r));
gen(JmpZero, exit, tr);
// We unfortunately need to special-case r = -1 here. In two's
// complement, trying to divide INT_MIN by -1 will cause an integer
// overflow.
if (tr->isConst()) {
// This crap only exists so m_tb->cond doesn't get mad when one
// of the branches gets optimized out due to constant folding.
if (tr->getValInt() == -1LL) {
push(cns(0));
} else {
push(gen(OpMod, tl, tr));
}
return;
}
// check for -1 (dynamic version)
SSATmp *res = m_tb->cond(
curFunc(),
[&] (Block* taken) {
SSATmp* negone = gen(OpEq, tr, cns(-1LL));
gen(JmpNZero, taken, negone);
},
[&] {
return gen(OpMod, tl, tr);
},
[&] {
m_tb->hint(Block::Hint::Unlikely);
return cns(0);
}
);
push(res);
}
void HhbcTranslator::emitBitNot() {
+1 -1
Ver Arquivo
@@ -211,7 +211,7 @@ 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(OpMod, DArith, S(Int,Dbl) S(Int,Dbl), C|N) \
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) \
O(OpBitXor, D(Int), S(Int) S(Int), C) \
-1
Ver Arquivo
@@ -145,7 +145,6 @@ static CallMap s_callMap({
{VerifyParamFail, (TCA)VerifyParamTypeFail, DNone, SSync, {{SSA, 0}}},
{RaiseUninitLoc, (TCA)raiseUndefVariable, DNone, SSync, {{SSA, 0}}},
{RaiseWarning, (TCA)raiseWarning, DNone, SSync, {{SSA, 0}}},
{OpMod, (TCA)modHelper, DSSA, SNone, {{SSA, 0}, {SSA, 1}}},
{WarnNonObjProp, (TCA)raisePropertyOnNonObject, DNone, SSync, {}},
{ThrowNonObjProp, (TCA)throw_null_object_prop, DNone, SSync, {}},
{RaiseUndefProp, (TCA)raiseUndefProp, DNone, SSync,
+1 -1
Ver Arquivo
@@ -1008,7 +1008,7 @@ static const struct {
{ OpMul, {StackTop2, Stack1, OutArith, -1 }},
/* Div and mod might return boolean false. Sigh. */
{ OpDiv, {StackTop2, Stack1, OutUnknown, -1 }},
{ OpMod, {StackTop2, Stack1, OutUnknown, -1 }},
{ OpMod, {StackTop2, Stack1, OutPred, -1 }},
/* Logical ops */
{ OpXor, {StackTop2, Stack1, OutBoolean, -1 }},
{ OpNot, {Stack1, Stack1, OutBoolean, 0 }},
+1 -1
Ver Arquivo
@@ -174,7 +174,7 @@ Type binArithResultType(Opcode op, Type t1, Type t2) {
return Type::Int | Type::Dbl | Type::Bool;
}
if (op == OpMod) {
return Type::Int | Type::Bool;
return Type::Int;
}
assert(op == OpAdd || op == OpSub || op == OpMul);
if (t1.subtypeOf(Type::Dbl) || t2.subtypeOf(Type::Dbl)) {
+2
Ver Arquivo
@@ -758,6 +758,7 @@ const X64Instr instr_movsbx = { { 0xBE,0xF1,0xF1,0x00,0xF1,0xF1 }, 0x2003 };
const X64Instr instr_movzwx = { { 0xB7,0xF1,0xF1,0x00,0xF1,0xF1 }, 0x0003 };
const X64Instr instr_movzbx = { { 0xB6,0xF1,0xF1,0x00,0xF1,0xF1 }, 0x2003 };
const X64Instr instr_cwde = { { 0xF1,0xF1,0xF1,0x00,0xF1,0x98 }, 0x0400 };
const X64Instr instr_cqo = { { 0xF1,0xF1,0xF1,0x00,0xF1,0x99 }, 0x0000 };
const X64Instr instr_rol = { { 0xD3,0xF1,0xC1,0x00,0xF1,0xF1 }, 0x0020 };
const X64Instr instr_ror = { { 0xD3,0xF1,0xC1,0x01,0xF1,0xF1 }, 0x0020 };
const X64Instr instr_rcl = { { 0xD3,0xF1,0xC1,0x02,0xF1,0xF1 }, 0x0020 };
@@ -1098,6 +1099,7 @@ struct X64Assembler {
void negb(Reg8 r) { instrR(instr_negb, r); }
void ret() { emit(instr_ret); }
void ret(Immed i) { emitI(instr_ret, i.w(), sz::word); }
void cqo() { emit(instr_cqo); }
void nop() { emit(instr_nop); }
void int3() { emit(instr_int3); }
void ud2() { byte(0x0f); byte(0x0b); }