diff --git a/js/src/ion/CodeGenerator.cpp b/js/src/ion/CodeGenerator.cpp index bb952ccf0c2..a5594d58491 100644 --- a/js/src/ion/CodeGenerator.cpp +++ b/js/src/ion/CodeGenerator.cpp @@ -2866,33 +2866,7 @@ CodeGenerator::emitCompareS(LInstruction *lir, JSOp op, Register left, Register if (!ool) return false; - Label notPointerEqual; - // Fast path for identical strings - masm.branchPtr(Assembler::NotEqual, left, right, ¬PointerEqual); - masm.move32(Imm32(op == JSOP_EQ || op == JSOP_STRICTEQ), output); - masm.jump(ool->rejoin()); - - masm.bind(¬PointerEqual); - masm.loadPtr(Address(left, JSString::offsetOfLengthAndFlags()), output); - masm.loadPtr(Address(right, JSString::offsetOfLengthAndFlags()), temp); - - Label notAtom; - // We can optimize the equality operation to a pointer compare for - // two atoms. - Imm32 atomBit(JSString::ATOM_BIT); - masm.branchTest32(Assembler::Zero, output, atomBit, ¬Atom); - masm.branchTest32(Assembler::Zero, temp, atomBit, ¬Atom); - - masm.cmpPtr(left, right); - emitSet(JSOpToCondition(op), output); - masm.jump(ool->rejoin()); - - masm.bind(¬Atom); - // Strings of different length can never be equal. - masm.rshiftPtr(Imm32(JSString::LENGTH_SHIFT), output); - masm.rshiftPtr(Imm32(JSString::LENGTH_SHIFT), temp); - masm.branchPtr(Assembler::Equal, output, temp, ool->entry()); - masm.move32(Imm32(op == JSOP_NE || op == JSOP_STRICTNE), output); + masm.compareStrings(op, left, right, output, temp, ool->entry()); masm.bind(ool->rejoin()); return true; @@ -3056,7 +3030,7 @@ CodeGenerator::visitIsNullOrLikeUndefined(LIsNullOrLikeUndefined *lir) else cond = masm.testUndefined(cond, value); - emitSet(cond, output); + masm.emitSet(cond, output); return true; } @@ -4096,7 +4070,7 @@ CodeGenerator::visitIteratorMore(LIteratorMore *lir) // Set output to true if props_cursor < props_end. masm.loadPtr(Address(output, offsetof(NativeIterator, props_end)), temp); masm.cmpPtr(Address(output, offsetof(NativeIterator, props_cursor)), temp); - emitSet(Assembler::LessThan, output); + masm.emitSet(Assembler::LessThan, output); masm.bind(ool->rejoin()); return true; diff --git a/js/src/ion/IonMacroAssembler.cpp b/js/src/ion/IonMacroAssembler.cpp index eef9e707b22..f5d31aa3276 100644 --- a/js/src/ion/IonMacroAssembler.cpp +++ b/js/src/ion/IonMacroAssembler.cpp @@ -432,6 +432,43 @@ MacroAssembler::initGCThing(const Register &obj, JSObject *templateObject) } } +void +MacroAssembler::compareStrings(JSOp op, Register left, Register right, Register result, + Register temp, Label *fail) +{ + JS_ASSERT(IsEqualityOp(op)); + + Label done; + Label notPointerEqual; + // Fast path for identical strings. + branchPtr(Assembler::NotEqual, left, right, ¬PointerEqual); + move32(Imm32(op == JSOP_EQ || op == JSOP_STRICTEQ), result); + jump(&done); + + bind(¬PointerEqual); + loadPtr(Address(left, JSString::offsetOfLengthAndFlags()), result); + loadPtr(Address(right, JSString::offsetOfLengthAndFlags()), temp); + + Label notAtom; + // Optimize the equality operation to a pointer compare for two atoms. + Imm32 atomBit(JSString::ATOM_BIT); + branchTest32(Assembler::Zero, result, atomBit, ¬Atom); + branchTest32(Assembler::Zero, temp, atomBit, ¬Atom); + + cmpPtr(left, right); + emitSet(JSOpToCondition(op), result); + jump(&done); + + bind(¬Atom); + // Strings of different length can never be equal. + rshiftPtr(Imm32(JSString::LENGTH_SHIFT), result); + rshiftPtr(Imm32(JSString::LENGTH_SHIFT), temp); + branchPtr(Assembler::Equal, result, temp, fail); + move32(Imm32(op == JSOP_NE || op == JSOP_STRICTNE), result); + + bind(&done); +} + void MacroAssembler::parCheckInterruptFlags(const Register &tempReg, Label *fail) diff --git a/js/src/ion/IonMacroAssembler.h b/js/src/ion/IonMacroAssembler.h index 850c58d041b..d57722298b4 100644 --- a/js/src/ion/IonMacroAssembler.h +++ b/js/src/ion/IonMacroAssembler.h @@ -500,6 +500,11 @@ class MacroAssembler : public MacroAssemblerSpecific Label *fail); void initGCThing(const Register &obj, JSObject *templateObject); + // Compares two strings for equality based on the JSOP. + // This checks for identical pointers, atoms and length and fails for everything else. + void compareStrings(JSOp op, Register left, Register right, Register result, + Register temp, Label *fail); + // Checks the flags that signal that parallel code may need to interrupt or // abort. Branches to fail in that case. void parCheckInterruptFlags(const Register &tempReg, diff --git a/js/src/ion/arm/CodeGenerator-arm.cpp b/js/src/ion/arm/CodeGenerator-arm.cpp index f0d741674f9..879cd31bc13 100644 --- a/js/src/ion/arm/CodeGenerator-arm.cpp +++ b/js/src/ion/arm/CodeGenerator-arm.cpp @@ -103,13 +103,6 @@ CodeGeneratorARM::visitTestIAndBranch(LTestIAndBranch *test) return true; } -void -CodeGeneratorARM::emitSet(Assembler::Condition cond, const Register &dest) -{ - masm.ma_mov(Imm32(0), dest); - masm.ma_mov(Imm32(1), dest, NoSetCond, cond); -} - bool CodeGeneratorARM::visitCompare(LCompare *comp) { @@ -1236,7 +1229,7 @@ CodeGeneratorARM::visitCompareD(LCompareD *comp) Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop()); masm.compareDouble(lhs, rhs); - emitSet(Assembler::ConditionFromDoubleCondition(cond), ToRegister(comp->output())); + masm.emitSet(Assembler::ConditionFromDoubleCondition(cond), ToRegister(comp->output())); return true; } @@ -1270,7 +1263,7 @@ CodeGeneratorARM::visitCompareB(LCompareB *lir) masm.cmp32(lhs.payloadReg(), Imm32(rhs->toConstant()->toBoolean())); else masm.cmp32(lhs.payloadReg(), ToRegister(rhs)); - emitSet(JSOpToCondition(mir->jsop()), output); + masm.emitSet(JSOpToCondition(mir->jsop()), output); masm.jump(&done); } @@ -1322,7 +1315,7 @@ CodeGeneratorARM::visitCompareV(LCompareV *lir) masm.j(Assembler::NotEqual, ¬Equal); { masm.cmp32(lhs.payloadReg(), rhs.payloadReg()); - emitSet(cond, output); + masm.emitSet(cond, output); masm.jump(&done); } masm.bind(¬Equal); @@ -1363,7 +1356,7 @@ CodeGeneratorARM::visitNotI(LNotI *ins) { // It is hard to optimize !x, so just do it the basic way for now. masm.ma_cmp(ToRegister(ins->input()), Imm32(0)); - emitSet(Assembler::Equal, ToRegister(ins->output())); + masm.emitSet(Assembler::Equal, ToRegister(ins->output())); return true; } diff --git a/js/src/ion/arm/CodeGenerator-arm.h b/js/src/ion/arm/CodeGenerator-arm.h index ae28d68d992..4b82925e284 100644 --- a/js/src/ion/arm/CodeGenerator-arm.h +++ b/js/src/ion/arm/CodeGenerator-arm.h @@ -57,9 +57,6 @@ class CodeGeneratorARM : public CodeGeneratorShared void emitRoundDouble(const FloatRegister &src, const Register &dest, Label *fail); - // Emits a conditional set. - void emitSet(Assembler::Condition cond, const Register &dest); - // Emits a branch that directs control flow to the true block if |cond| is // true, and the false block if |cond| is false. void emitBranch(Assembler::Condition cond, MBasicBlock *ifTrue, MBasicBlock *ifFalse); diff --git a/js/src/ion/arm/MacroAssembler-arm.h b/js/src/ion/arm/MacroAssembler-arm.h index 5168981875f..5016eea1e0c 100644 --- a/js/src/ion/arm/MacroAssembler-arm.h +++ b/js/src/ion/arm/MacroAssembler-arm.h @@ -1065,6 +1065,13 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM ma_lsl(imm, dest, dest); } + void + emitSet(Assembler::Condition cond, const Register &dest) + { + ma_mov(Imm32(0), dest); + ma_mov(Imm32(1), dest, NoSetCond, cond); + } + // Setup a call to C/C++ code, given the number of general arguments it // takes. Note that this only supports cdecl. // diff --git a/js/src/ion/shared/CodeGenerator-x86-shared.cpp b/js/src/ion/shared/CodeGenerator-x86-shared.cpp index 8745adba307..22a195ed3ff 100644 --- a/js/src/ion/shared/CodeGenerator-x86-shared.cpp +++ b/js/src/ion/shared/CodeGenerator-x86-shared.cpp @@ -120,42 +120,6 @@ CodeGeneratorX86Shared::visitTestDAndBranch(LTestDAndBranch *test) return true; } -void -CodeGeneratorX86Shared::emitSet(Assembler::Condition cond, const Register &dest, - Assembler::NaNCond ifNaN) -{ - if (GeneralRegisterSet(Registers::SingleByteRegs).has(dest)) { - // If the register we're defining is a single byte register, - // take advantage of the setCC instruction - masm.setCC(cond, dest); - masm.movzxbl(dest, dest); - - if (ifNaN != Assembler::NaN_Unexpected) { - Label noNaN; - masm.j(Assembler::NoParity, &noNaN); - if (ifNaN == Assembler::NaN_IsTrue) - masm.movl(Imm32(1), dest); - else - masm.xorl(dest, dest); - masm.bind(&noNaN); - } - } else { - Label end; - Label ifFalse; - - if (ifNaN == Assembler::NaN_IsFalse) - masm.j(Assembler::Parity, &ifFalse); - masm.movl(Imm32(1), dest); - masm.j(cond, &end); - if (ifNaN == Assembler::NaN_IsTrue) - masm.j(Assembler::Parity, &end); - masm.bind(&ifFalse); - masm.xorl(dest, dest); - - masm.bind(&end); - } -} - void CodeGeneratorX86Shared::emitCompare(MCompare::CompareType type, const LAllocation *left, const LAllocation *right) { @@ -176,7 +140,7 @@ bool CodeGeneratorX86Shared::visitCompare(LCompare *comp) { emitCompare(comp->mir()->compareType(), comp->left(), comp->right()); - emitSet(JSOpToCondition(comp->jsop()), ToRegister(comp->output())); + masm.emitSet(JSOpToCondition(comp->jsop()), ToRegister(comp->output())); return true; } @@ -197,7 +161,7 @@ CodeGeneratorX86Shared::visitCompareD(LCompareD *comp) Assembler::DoubleCondition cond = JSOpToDoubleCondition(comp->mir()->jsop()); masm.compareDouble(cond, lhs, rhs); - emitSet(Assembler::ConditionFromDoubleCondition(cond), ToRegister(comp->output()), + masm.emitSet(Assembler::ConditionFromDoubleCondition(cond), ToRegister(comp->output()), Assembler::NaNCondFromDoubleCondition(cond)); return true; } @@ -206,7 +170,7 @@ bool CodeGeneratorX86Shared::visitNotI(LNotI *ins) { masm.cmpl(ToRegister(ins->input()), Imm32(0)); - emitSet(Assembler::Equal, ToRegister(ins->output())); + masm.emitSet(Assembler::Equal, ToRegister(ins->output())); return true; } @@ -217,7 +181,7 @@ CodeGeneratorX86Shared::visitNotD(LNotD *ins) masm.xorpd(ScratchFloatReg, ScratchFloatReg); masm.compareDouble(Assembler::DoubleEqualOrUnordered, opd, ScratchFloatReg); - emitSet(Assembler::Equal, ToRegister(ins->output()), Assembler::NaN_IsTrue); + masm.emitSet(Assembler::Equal, ToRegister(ins->output()), Assembler::NaN_IsTrue); return true; } diff --git a/js/src/ion/shared/CodeGenerator-x86-shared.h b/js/src/ion/shared/CodeGenerator-x86-shared.h index 5b03d18d89c..a21be78c22d 100644 --- a/js/src/ion/shared/CodeGenerator-x86-shared.h +++ b/js/src/ion/shared/CodeGenerator-x86-shared.h @@ -64,11 +64,6 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared void emitCompare(MCompare::CompareType type, const LAllocation *left, const LAllocation *right); - // Emits a conditional set. - void emitSet(Assembler::Condition cond, const Register &dest, - Assembler::NaNCond ifNaN = Assembler::NaN_Unexpected); - void emitSet(Assembler::DoubleCondition cond, const Register &dest); - // Emits a branch that directs control flow to the true block if |cond| is // true, and the false block if |cond| is false. void emitBranch(Assembler::Condition cond, MBasicBlock *ifTrue, MBasicBlock *ifFalse, diff --git a/js/src/ion/shared/MacroAssembler-x86-shared.h b/js/src/ion/shared/MacroAssembler-x86-shared.h index 0148a092c9a..c70d1633ee0 100644 --- a/js/src/ion/shared/MacroAssembler-x86-shared.h +++ b/js/src/ion/shared/MacroAssembler-x86-shared.h @@ -351,7 +351,7 @@ class MacroAssemblerX86Shared : public Assembler } bool maybeInlineDouble(uint64_t u, const FloatRegister &dest) { - // This implements parts of "13.4 Generating constants" of + // This implements parts of "13.4 Generating constants" of // "2. Optimizing subroutines in assembly language" by Agner Fog. switch (u) { case 0x0000000000000000ULL: // 0.0 @@ -391,6 +391,40 @@ class MacroAssemblerX86Shared : public Assembler return true; } + void emitSet(Assembler::Condition cond, const Register &dest, + Assembler::NaNCond ifNaN = Assembler::NaN_Unexpected) { + if (GeneralRegisterSet(Registers::SingleByteRegs).has(dest)) { + // If the register we're defining is a single byte register, + // take advantage of the setCC instruction + setCC(cond, dest); + movzxbl(dest, dest); + + if (ifNaN != Assembler::NaN_Unexpected) { + Label noNaN; + j(Assembler::NoParity, &noNaN); + if (ifNaN == Assembler::NaN_IsTrue) + movl(Imm32(1), dest); + else + xorl(dest, dest); + bind(&noNaN); + } + } else { + Label end; + Label ifFalse; + + if (ifNaN == Assembler::NaN_IsFalse) + j(Assembler::Parity, &ifFalse); + movl(Imm32(1), dest); + j(cond, &end); + if (ifNaN == Assembler::NaN_IsTrue) + j(Assembler::Parity, &end); + bind(&ifFalse); + xorl(dest, dest); + + bind(&end); + } + } + // Emit a JMP that can be toggled to a CMP. See ToggleToJmp(), ToggleToCmp(). CodeOffsetLabel toggledJump(Label *label) { CodeOffsetLabel offset(size()); diff --git a/js/src/ion/x64/CodeGenerator-x64.cpp b/js/src/ion/x64/CodeGenerator-x64.cpp index 8c2e3424b77..074d9214780 100644 --- a/js/src/ion/x64/CodeGenerator-x64.cpp +++ b/js/src/ion/x64/CodeGenerator-x64.cpp @@ -347,7 +347,7 @@ CodeGeneratorX64::visitCompareB(LCompareB *lir) // Perform the comparison. masm.cmpq(lhs.valueReg(), ScratchReg); - emitSet(JSOpToCondition(mir->jsop()), output); + masm.emitSet(JSOpToCondition(mir->jsop()), output); return true; } @@ -380,11 +380,10 @@ CodeGeneratorX64::visitCompareV(LCompareV *lir) const ValueOperand rhs = ToValue(lir, LCompareV::RhsInput); const Register output = ToRegister(lir->output()); - JS_ASSERT(mir->jsop() == JSOP_EQ || mir->jsop() == JSOP_STRICTEQ || - mir->jsop() == JSOP_NE || mir->jsop() == JSOP_STRICTNE); + JS_ASSERT(IsEqualityOp(mir->jsop())); masm.cmpq(lhs.valueReg(), rhs.valueReg()); - emitSet(JSOpToCondition(mir->jsop()), output); + masm.emitSet(JSOpToCondition(mir->jsop()), output); return true; } diff --git a/js/src/ion/x86/CodeGenerator-x86.cpp b/js/src/ion/x86/CodeGenerator-x86.cpp index f23c24807e3..0c6a0576d4d 100644 --- a/js/src/ion/x86/CodeGenerator-x86.cpp +++ b/js/src/ion/x86/CodeGenerator-x86.cpp @@ -351,7 +351,7 @@ CodeGeneratorX86::visitCompareB(LCompareB *lir) masm.cmp32(lhs.payloadReg(), Imm32(rhs->toConstant()->toBoolean())); else masm.cmp32(lhs.payloadReg(), ToRegister(rhs)); - emitSet(JSOpToCondition(mir->jsop()), output); + masm.emitSet(JSOpToCondition(mir->jsop()), output); masm.jump(&done); } masm.bind(¬Boolean); @@ -394,15 +394,14 @@ CodeGeneratorX86::visitCompareV(LCompareV *lir) const ValueOperand rhs = ToValue(lir, LCompareV::RhsInput); const Register output = ToRegister(lir->output()); - JS_ASSERT(mir->jsop() == JSOP_EQ || mir->jsop() == JSOP_STRICTEQ || - mir->jsop() == JSOP_NE || mir->jsop() == JSOP_STRICTNE); + JS_ASSERT(IsEqualityOp(mir->jsop())); Label notEqual, done; masm.cmp32(lhs.typeReg(), rhs.typeReg()); masm.j(Assembler::NotEqual, ¬Equal); { masm.cmp32(lhs.payloadReg(), rhs.payloadReg()); - emitSet(cond, output); + masm.emitSet(cond, output); masm.jump(&done); } masm.bind(¬Equal); diff --git a/js/src/jsopcode.h b/js/src/jsopcode.h index 79601241cd5..376faa543eb 100644 --- a/js/src/jsopcode.h +++ b/js/src/jsopcode.h @@ -459,6 +459,12 @@ IsGlobalOp(JSOp op) return js_CodeSpec[op].format & JOF_GNAME; } +inline bool +IsEqualityOp(JSOp op) +{ + return op == JSOP_EQ || op == JSOP_NE || op == JSOP_STRICTEQ || op == JSOP_STRICTNE; +} + inline bool IsGetterPC(jsbytecode *pc) {