Bug 865259 - Make comparisons work in Par Exec mode regardless of TI results r=bhackett
authorNicholas D. Matsakis <nmatsakis@mozilla.com>
Thu, 25 Apr 2013 08:47:16 -0400
changeset 141065 adef5d952d721c5d04be26c5c412f34d45a77330
parent 141064 e463630560f7d1d461283830b3f8979eab309b1a
child 141066 b1007963b1ca758984c67501d41dfb5059f5b6db
push id2579
push userakeybl@mozilla.com
push dateMon, 24 Jun 2013 18:52:47 +0000
treeherdermozilla-beta@b69b7de8a05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbhackett
bugs865259
milestone23.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 865259 - Make comparisons work in Par Exec mode regardless of TI results r=bhackett
js/src/ion/CodeGenerator.cpp
js/src/ion/CodeGenerator.h
js/src/ion/LIR-Common.h
js/src/ion/LOpcodes.h
js/src/ion/Lowering.cpp
js/src/ion/ParallelArrayAnalysis.cpp
js/src/ion/ParallelFunctions.cpp
js/src/ion/ParallelFunctions.h
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -3171,62 +3171,54 @@ CodeGenerator::visitBinaryV(LBinaryV *li
         return callVM(UrshInfo, lir);
 
       default:
         JS_NOT_REACHED("Unexpected binary op");
         return false;
     }
 }
 
-bool
-CodeGenerator::visitParCompareS(LParCompareS *lir)
-{
-    JSOp op = lir->mir()->jsop();
-    Register left = ToRegister(lir->left());
-    Register right = ToRegister(lir->right());
-
-    JS_ASSERT((op == JSOP_EQ || op == JSOP_STRICTEQ) ||
-              (op == JSOP_NE || op == JSOP_STRICTNE));
-
-    masm.setupUnalignedABICall(2, CallTempReg2);
-    masm.passABIArg(left);
-    masm.passABIArg(right);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ParCompareStrings));
-    masm.and32(Imm32(0xF), ReturnReg); // The C functions return an enum whose size is undef
-
-    // Check for cases that we do not currently handle in par exec
-    Label *bail;
-    if (!ensureOutOfLineParallelAbort(&bail))
-        return false;
-    masm.branch32(Assembler::Equal, ReturnReg, Imm32(ParCompareUnknown), bail);
-
-    if (op == JSOP_NE || op == JSOP_STRICTNE)
-        masm.xor32(Imm32(1), ReturnReg);
-
-    return true;
-}
-
 typedef bool (*StringCompareFn)(JSContext *, HandleString, HandleString, JSBool *);
 static const VMFunction stringsEqualInfo =
     FunctionInfo<StringCompareFn>(ion::StringsEqual<true>);
 static const VMFunction stringsNotEqualInfo =
     FunctionInfo<StringCompareFn>(ion::StringsEqual<false>);
 
+typedef ParallelResult (*ParStringCompareFn)(ForkJoinSlice *, HandleString, HandleString, JSBool *);
+static const VMFunction parStringsEqualInfo =
+    FunctionInfo<ParStringCompareFn>(ion::ParStringsEqual);
+static const VMFunction parStringsNotEqualInfo =
+    FunctionInfo<ParStringCompareFn>(ion::ParStringsUnequal);
+
 bool
 CodeGenerator::emitCompareS(LInstruction *lir, JSOp op, Register left, Register right,
                             Register output, Register temp)
 {
     JS_ASSERT(lir->isCompareS() || lir->isCompareStrictS());
 
     OutOfLineCode *ool = NULL;
-    if (op == JSOP_EQ || op == JSOP_STRICTEQ) {
-        ool = oolCallVM(stringsEqualInfo, lir, (ArgList(), left, right),  StoreRegisterTo(output));
-    } else {
-        JS_ASSERT(op == JSOP_NE || op == JSOP_STRICTNE);
-        ool = oolCallVM(stringsNotEqualInfo, lir, (ArgList(), left, right), StoreRegisterTo(output));
+
+    switch (gen->info().executionMode()) {
+      case SequentialExecution:
+        if (op == JSOP_EQ || op == JSOP_STRICTEQ) {
+            ool = oolCallVM(stringsEqualInfo, lir, (ArgList(), left, right),  StoreRegisterTo(output));
+        } else {
+            JS_ASSERT(op == JSOP_NE || op == JSOP_STRICTNE);
+            ool = oolCallVM(stringsNotEqualInfo, lir, (ArgList(), left, right), StoreRegisterTo(output));
+        }
+        break;
+
+      case ParallelExecution:
+        if (op == JSOP_EQ || op == JSOP_STRICTEQ) {
+            ool = oolCallVM(parStringsEqualInfo, lir, (ArgList(), left, right),  StoreRegisterTo(output));
+        } else {
+            JS_ASSERT(op == JSOP_NE || op == JSOP_STRICTNE);
+            ool = oolCallVM(parStringsNotEqualInfo, lir, (ArgList(), left, right), StoreRegisterTo(output));
+        }
+        break;
     }
 
     if (!ool)
         return false;
 
     masm.compareStrings(op, left, right, output, temp, ool->entry());
 
     masm.bind(ool->rejoin());
@@ -3277,51 +3269,97 @@ static const VMFunction EqInfo = Functio
 static const VMFunction NeInfo = FunctionInfo<CompareFn>(ion::LooselyEqual<false>);
 static const VMFunction StrictEqInfo = FunctionInfo<CompareFn>(ion::StrictlyEqual<true>);
 static const VMFunction StrictNeInfo = FunctionInfo<CompareFn>(ion::StrictlyEqual<false>);
 static const VMFunction LtInfo = FunctionInfo<CompareFn>(ion::LessThan);
 static const VMFunction LeInfo = FunctionInfo<CompareFn>(ion::LessThanOrEqual);
 static const VMFunction GtInfo = FunctionInfo<CompareFn>(ion::GreaterThan);
 static const VMFunction GeInfo = FunctionInfo<CompareFn>(ion::GreaterThanOrEqual);
 
+typedef ParallelResult (*ParCompareFn)(ForkJoinSlice *, MutableHandleValue, MutableHandleValue, JSBool *);
+static const VMFunction ParLooselyEqInfo = FunctionInfo<ParCompareFn>(ion::ParLooselyEqual);
+static const VMFunction ParStrictlyEqInfo = FunctionInfo<ParCompareFn>(ion::ParStrictlyEqual);
+static const VMFunction ParLooselyNeInfo = FunctionInfo<ParCompareFn>(ion::ParLooselyUnequal);
+static const VMFunction ParStrictlyNeInfo = FunctionInfo<ParCompareFn>(ion::ParStrictlyUnequal);
+static const VMFunction ParLtInfo = FunctionInfo<ParCompareFn>(ion::ParLessThan);
+static const VMFunction ParLeInfo = FunctionInfo<ParCompareFn>(ion::ParLessThanOrEqual);
+static const VMFunction ParGtInfo = FunctionInfo<ParCompareFn>(ion::ParGreaterThan);
+static const VMFunction ParGeInfo = FunctionInfo<ParCompareFn>(ion::ParGreaterThanOrEqual);
+
 bool
 CodeGenerator::visitCompareVM(LCompareVM *lir)
 {
     pushArg(ToValue(lir, LBinaryV::RhsInput));
     pushArg(ToValue(lir, LBinaryV::LhsInput));
 
-    switch (lir->mir()->jsop()) {
-      case JSOP_EQ:
-        return callVM(EqInfo, lir);
-
-      case JSOP_NE:
-        return callVM(NeInfo, lir);
-
-      case JSOP_STRICTEQ:
-        return callVM(StrictEqInfo, lir);
-
-      case JSOP_STRICTNE:
-        return callVM(StrictNeInfo, lir);
-
-      case JSOP_LT:
-        return callVM(LtInfo, lir);
-
-      case JSOP_LE:
-        return callVM(LeInfo, lir);
-
-      case JSOP_GT:
-        return callVM(GtInfo, lir);
-
-      case JSOP_GE:
-        return callVM(GeInfo, lir);
-
-      default:
-        JS_NOT_REACHED("Unexpected compare op");
-        return false;
+    switch (gen->info().executionMode()) {
+      case SequentialExecution:
+        switch (lir->mir()->jsop()) {
+          case JSOP_EQ:
+            return callVM(EqInfo, lir);
+
+          case JSOP_NE:
+            return callVM(NeInfo, lir);
+
+          case JSOP_STRICTEQ:
+            return callVM(StrictEqInfo, lir);
+
+          case JSOP_STRICTNE:
+            return callVM(StrictNeInfo, lir);
+
+          case JSOP_LT:
+            return callVM(LtInfo, lir);
+
+          case JSOP_LE:
+            return callVM(LeInfo, lir);
+
+          case JSOP_GT:
+            return callVM(GtInfo, lir);
+
+          case JSOP_GE:
+            return callVM(GeInfo, lir);
+
+          default:
+            JS_NOT_REACHED("Unexpected compare op");
+            return false;
+        }
+
+      case ParallelExecution:
+        switch (lir->mir()->jsop()) {
+          case JSOP_EQ:
+            return callVM(ParLooselyEqInfo, lir);
+
+          case JSOP_STRICTEQ:
+            return callVM(ParStrictlyEqInfo, lir);
+
+          case JSOP_NE:
+            return callVM(ParLooselyNeInfo, lir);
+
+          case JSOP_STRICTNE:
+            return callVM(ParStrictlyNeInfo, lir);
+
+          case JSOP_LT:
+            return callVM(ParLtInfo, lir);
+
+          case JSOP_LE:
+            return callVM(ParLeInfo, lir);
+
+          case JSOP_GT:
+            return callVM(ParGtInfo, lir);
+
+          case JSOP_GE:
+            return callVM(ParGeInfo, lir);
+
+          default:
+            JS_NOT_REACHED("Unexpected compare op");
+            return false;
+        }
     }
+
+    JS_NOT_REACHED("Unexpected exec mode");
 }
 
 bool
 CodeGenerator::visitIsNullOrLikeUndefined(LIsNullOrLikeUndefined *lir)
 {
     JSOp op = lir->mir()->jsop();
     MCompare::CompareType compareType = lir->mir()->compareType();
     JS_ASSERT(compareType == MCompare::Compare_Undefined ||
--- a/js/src/ion/CodeGenerator.h
+++ b/js/src/ion/CodeGenerator.h
@@ -148,17 +148,16 @@ class CodeGenerator : public CodeGenerat
     bool visitMathFunctionD(LMathFunctionD *ins);
     bool visitModD(LModD *ins);
     bool visitMinMaxI(LMinMaxI *lir);
     bool visitBinaryV(LBinaryV *lir);
     bool emitCompareS(LInstruction *lir, JSOp op, Register left, Register right,
                       Register output, Register temp);
     bool visitCompareS(LCompareS *lir);
     bool visitCompareStrictS(LCompareStrictS *lir);
-    bool visitParCompareS(LParCompareS *lir);
     bool visitCompareVM(LCompareVM *lir);
     bool visitIsNullOrLikeUndefined(LIsNullOrLikeUndefined *lir);
     bool visitIsNullOrLikeUndefinedAndBranch(LIsNullOrLikeUndefinedAndBranch *lir);
     bool visitEmulatesUndefined(LEmulatesUndefined *lir);
     bool visitEmulatesUndefinedAndBranch(LEmulatesUndefinedAndBranch *lir);
     bool visitConcat(LConcat *lir);
     bool visitCharCodeAt(LCharCodeAt *lir);
     bool visitFromCharCode(LFromCharCode *lir);
--- a/js/src/ion/LIR-Common.h
+++ b/js/src/ion/LIR-Common.h
@@ -1553,37 +1553,16 @@ class LCompareStrictS : public LInstruct
     const LDefinition *temp1() {
         return getTemp(1);
     }
     MCompare *mir() {
         return mir_->toCompare();
     }
 };
 
-class LParCompareS : public LCallInstructionHelper<1, 2, 0>
-{
-  public:
-    LIR_HEADER(ParCompareS);
-
-    LParCompareS(const LAllocation &left, const LAllocation &right) {
-        setOperand(0, left);
-        setOperand(1, right);
-    }
-
-    const LAllocation *left() {
-        return getOperand(0);
-    }
-    const LAllocation *right() {
-        return getOperand(1);
-    }
-    MCompare *mir() {
-        return mir_->toCompare();
-    }
-};
-
 // Used for strict-equality comparisons where one side is a boolean
 // and the other is a value. Note that CompareI is used to compare
 // two booleans.
 class LCompareB : public LInstructionHelper<1, BOX_PIECES + 1, 0>
 {
   public:
     LIR_HEADER(CompareB)
 
--- a/js/src/ion/LOpcodes.h
+++ b/js/src/ion/LOpcodes.h
@@ -71,17 +71,16 @@
     _(TypeObjectDispatch)           \
     _(PolyInlineDispatch)           \
     _(Compare)                      \
     _(CompareAndBranch)             \
     _(CompareD)                     \
     _(CompareDAndBranch)            \
     _(CompareS)                     \
     _(CompareStrictS)               \
-    _(ParCompareS)                  \
     _(CompareB)                     \
     _(CompareBAndBranch)            \
     _(CompareV)                     \
     _(CompareVAndBranch)            \
     _(CompareVM)                    \
     _(IsNullOrLikeUndefined)        \
     _(IsNullOrLikeUndefinedAndBranch)\
     _(EmulatesUndefined)            \
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -738,34 +738,20 @@ LIRGenerator::visitCompare(MCompare *com
     bool result;
     if (comp->tryFold(&result))
         return define(new LInteger(result), comp);
 
     // Move below the emitAtUses call if we ever implement
     // LCompareSAndBranch. Doing this now wouldn't be wrong, but doesn't
     // make sense and avoids confusion.
     if (comp->compareType() == MCompare::Compare_String) {
-        switch (comp->block()->info().executionMode()) {
-          case SequentialExecution:
-          {
-              LCompareS *lir = new LCompareS(useRegister(left), useRegister(right), temp());
-              if (!define(lir, comp))
-                  return false;
-              return assignSafepoint(lir, comp);
-          }
-
-          case ParallelExecution:
-          {
-              LParCompareS *lir = new LParCompareS(useFixed(left, CallTempReg0),
-                                                   useFixed(right, CallTempReg1));
-              return defineReturn(lir, comp);
-          }
-        }
-
-        JS_NOT_REACHED("Unexpected execution mode");
+        LCompareS *lir = new LCompareS(useRegister(left), useRegister(right), temp());
+        if (!define(lir, comp))
+            return false;
+        return assignSafepoint(lir, comp);
     }
 
     // Strict compare between value and string
     if (comp->compareType() == MCompare::Compare_StrictString) {
         JS_ASSERT(left->type() == MIRType_Value);
         JS_ASSERT(right->type() == MIRType_String);
 
         LCompareStrictS *lir = new LCompareStrictS(useRegister(right), temp(), temp());
--- a/js/src/ion/ParallelArrayAnalysis.cpp
+++ b/js/src/ion/ParallelArrayAnalysis.cpp
@@ -488,23 +488,36 @@ ParallelArrayVisitor::visitTest(MTest *)
 {
     return true;
 }
 
 bool
 ParallelArrayVisitor::visitCompare(MCompare *compare)
 {
     MCompare::CompareType type = compare->compareType();
-    if (type != MCompare::Compare_Int32 &&
-        type != MCompare::Compare_Double &&
-        type != MCompare::Compare_String)
-    {
+    MBox *lhsBox, *rhsBox;
+    MBasicBlock *block;
+
+    switch (type) {
+      case MCompare::Compare_Int32:
+      case MCompare::Compare_Double:
+      case MCompare::Compare_Null:
+      case MCompare::Compare_Undefined:
+      case MCompare::Compare_Boolean:
+      case MCompare::Compare_Object:
+      case MCompare::Compare_Value:
+      case MCompare::Compare_Unknown:
+      case MCompare::Compare_String:
+        // These paths through compare are ok in any mode.
+        return true;
+
+      default:
+        SpewMIR(compare, "unsafe compareType=%d\n", type);
         return markUnsafe();
     }
-    return true;
 }
 
 bool
 ParallelArrayVisitor::convertToBailout(MBasicBlock *block, MInstruction *ins)
 {
     JS_ASSERT(unsafe()); // `block` must have contained unsafe items
     JS_ASSERT(block->isMarked()); // `block` must have been reachable to get here
 
--- a/js/src/ion/ParallelFunctions.cpp
+++ b/js/src/ion/ParallelFunctions.cpp
@@ -12,16 +12,17 @@
 #include "jscompartmentinlines.h"
 
 #include "vm/ParallelDo.h"
 
 using namespace js;
 using namespace ion;
 
 using parallel::Spew;
+using parallel::SpewOps;
 using parallel::SpewBailouts;
 using parallel::SpewBailoutIR;
 
 // Load the current thread context.
 ForkJoinSlice *
 ion::ParForkJoinSlice()
 {
     return ForkJoinSlice::Current();
@@ -178,30 +179,193 @@ ion::ParExtendArray(ForkJoinSlice *slice
 {
     JSObject::EnsureDenseResult res =
         array->parExtendDenseElements(slice->allocator, NULL, length);
     if (res != JSObject::ED_OK)
         return NULL;
     return array;
 }
 
-ParCompareResult
-ion::ParCompareStrings(JSString *str1, JSString *str2)
+#define PAR_RELATIONAL_OP(OP, EXPECTED)                                         \
+do {                                                                            \
+    /* Optimize for two int-tagged operands (typical loop control). */          \
+    if (lhs.isInt32() && rhs.isInt32()) {                                       \
+        *res = (lhs.toInt32() OP rhs.toInt32()) == EXPECTED;                    \
+    } else if (lhs.isNumber() && rhs.isNumber()) {                              \
+        double l = lhs.toNumber(), r = rhs.toNumber();                          \
+        *res = (l OP r) == EXPECTED;                                            \
+    } else if (lhs.isBoolean() && rhs.isBoolean()) {                            \
+        bool l = lhs.toBoolean();                                               \
+        bool r = rhs.toBoolean();                                               \
+        *res = (l OP r) == EXPECTED;                                            \
+    } else if (lhs.isBoolean() && rhs.isNumber()) {                             \
+        bool l = lhs.toBoolean();                                               \
+        double r = rhs.toNumber();                                              \
+        *res = (l OP r) == EXPECTED;                                            \
+    } else if (lhs.isNumber() && rhs.isBoolean()) {                             \
+        double l = lhs.toNumber();                                              \
+        bool r = rhs.toBoolean();                                               \
+        *res = (l OP r) == EXPECTED;                                            \
+    } else {                                                                    \
+        int32_t vsZero;                                                         \
+        ParallelResult ret = ParCompareMaybeStrings(slice, lhs, rhs, &vsZero);  \
+        if (ret != TP_SUCCESS)                                                  \
+            return ret;                                                         \
+        *res = (vsZero OP 0) == EXPECTED;                                       \
+    }                                                                           \
+    return TP_SUCCESS;                                                          \
+} while(0)
+
+static ParallelResult
+ParCompareStrings(ForkJoinSlice *slice, HandleString str1,
+                  HandleString str2, int32_t *res)
 {
-    // NYI---the rope case
     if (!str1->isLinear())
-        return ParCompareUnknown;
+        return TP_RETRY_SEQUENTIALLY;
     if (!str2->isLinear())
-        return ParCompareUnknown;
-
+        return TP_RETRY_SEQUENTIALLY;
     JSLinearString &linearStr1 = str1->asLinear();
     JSLinearString &linearStr2 = str2->asLinear();
-    if (EqualStrings(&linearStr1, &linearStr2))
-        return ParCompareEq;
-    return ParCompareNe;
+    if (!CompareChars(linearStr1.chars(), linearStr1.length(),
+                      linearStr2.chars(), linearStr2.length(),
+                      res))
+        return TP_FATAL;
+
+    return TP_SUCCESS;
+}
+
+static ParallelResult
+ParCompareMaybeStrings(ForkJoinSlice *slice,
+                       HandleValue v1,
+                       HandleValue v2,
+                       int32_t *res)
+{
+    if (!v1.isString())
+        return TP_RETRY_SEQUENTIALLY;
+    if (!v2.isString())
+        return TP_RETRY_SEQUENTIALLY;
+    RootedString str1(slice->perThreadData, v1.toString());
+    RootedString str2(slice->perThreadData, v2.toString());
+    return ParCompareStrings(slice, str1, str2, res);
+}
+
+template<bool Equal>
+ParallelResult
+ParLooselyEqualImpl(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
+{
+    PAR_RELATIONAL_OP(==, Equal);
+}
+
+ParallelResult
+js::ion::ParLooselyEqual(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
+{
+    return ParLooselyEqualImpl<true>(slice, lhs, rhs, res);
+}
+
+ParallelResult
+js::ion::ParLooselyUnequal(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
+{
+    return ParLooselyEqualImpl<false>(slice, lhs, rhs, res);
+}
+
+template<bool Equal>
+ParallelResult
+ParStrictlyEqualImpl(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
+{
+    if (lhs.isNumber()) {
+        if (rhs.isNumber()) {
+            *res = (lhs.toNumber() == rhs.toNumber()) == Equal;
+            return TP_SUCCESS;
+        }
+    } else if (lhs.isBoolean()) {
+        if (rhs.isBoolean()) {
+            *res = (lhs.toBoolean() == rhs.toBoolean()) == Equal;
+            return TP_SUCCESS;
+        }
+    } else if (lhs.isNull()) {
+        if (rhs.isNull()) {
+            *res = Equal;
+            return TP_SUCCESS;
+        }
+    } else if (lhs.isUndefined()) {
+        if (rhs.isUndefined()) {
+            *res = Equal;
+            return TP_SUCCESS;
+        }
+    } else if (lhs.isObject()) {
+        if (rhs.isObject()) {
+            *res = (lhs.toObjectOrNull() == rhs.toObjectOrNull()) == Equal;
+            return TP_SUCCESS;
+        }
+    } else if (lhs.isString()) {
+        if (rhs.isString())
+            return ParLooselyEqualImpl<Equal>(slice, lhs, rhs, res);
+    }
+
+    return TP_RETRY_SEQUENTIALLY;
+}
+
+ParallelResult
+js::ion::ParStrictlyEqual(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
+{
+    return ParStrictlyEqualImpl<true>(slice, lhs, rhs, res);
+}
+
+ParallelResult
+js::ion::ParStrictlyUnequal(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
+{
+    return ParStrictlyEqualImpl<false>(slice, lhs, rhs, res);
+}
+
+ParallelResult
+js::ion::ParLessThan(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
+{
+    PAR_RELATIONAL_OP(<, true);
+}
+
+ParallelResult
+js::ion::ParLessThanOrEqual(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
+{
+    PAR_RELATIONAL_OP(<=, true);
+}
+
+ParallelResult
+js::ion::ParGreaterThan(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
+{
+    PAR_RELATIONAL_OP(>, true);
+}
+
+ParallelResult
+js::ion::ParGreaterThanOrEqual(ForkJoinSlice *slice, MutableHandleValue lhs, MutableHandleValue rhs, JSBool *res)
+{
+    PAR_RELATIONAL_OP(>=, true);
+}
+
+template<bool Equal>
+ParallelResult
+ParStringsEqualImpl(ForkJoinSlice *slice, HandleString lhs, HandleString rhs, JSBool *res)
+{
+    int32_t vsZero;
+    ParallelResult ret = ParCompareStrings(slice, lhs, rhs, &vsZero);
+    if (ret != TP_SUCCESS)
+        return ret;
+    *res = (vsZero == 0) == Equal;
+    return TP_SUCCESS;
+}
+
+ParallelResult
+js::ion::ParStringsEqual(ForkJoinSlice *slice, HandleString v1, HandleString v2, JSBool *res)
+{
+    return ParStringsEqualImpl<true>(slice, v1, v2, res);
+}
+
+ParallelResult
+js::ion::ParStringsUnequal(ForkJoinSlice *slice, HandleString v1, HandleString v2, JSBool *res)
+{
+    return ParStringsEqualImpl<false>(slice, v1, v2, res);
 }
 
 void
 ion::ParallelAbort(JSScript *script)
 {
     JS_ASSERT(InParallelSection());
 
     ForkJoinSlice *slice = ForkJoinSlice::Current();
--- a/js/src/ion/ParallelFunctions.h
+++ b/js/src/ion/ParallelFunctions.h
@@ -36,22 +36,29 @@ struct ParPushArgs {
 // during code generation.
 JSObject* ParPush(ParPushArgs *args);
 
 // Extends the given array with `length` new holes.  Returns NULL on
 // failure or else `array`, which is convenient during code
 // generation.
 JSObject *ParExtendArray(ForkJoinSlice *slice, JSObject *array, uint32_t length);
 
-enum ParCompareResult {
-    ParCompareNe = false,
-    ParCompareEq = true,
-    ParCompareUnknown = 2
-};
-ParCompareResult ParCompareStrings(JSString *str1, JSString *str2);
+// These parallel operations fail if they would be required to convert
+// to a string etc etc.
+ParallelResult ParStrictlyEqual(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, JSBool *);
+ParallelResult ParStrictlyUnequal(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, JSBool *);
+ParallelResult ParLooselyEqual(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, JSBool *);
+ParallelResult ParLooselyUnequal(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, JSBool *);
+ParallelResult ParLessThan(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, JSBool *);
+ParallelResult ParLessThanOrEqual(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, JSBool *);
+ParallelResult ParGreaterThan(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, JSBool *);
+ParallelResult ParGreaterThanOrEqual(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, JSBool *);
+
+ParallelResult ParStringsEqual(ForkJoinSlice *slice, HandleString v1, HandleString v2, JSBool *);
+ParallelResult ParStringsUnequal(ForkJoinSlice *slice, HandleString v1, HandleString v2, JSBool *);
 
 void ParallelAbort(JSScript *script);
 
 void TraceLIR(uint32_t bblock, uint32_t lir, uint32_t execModeInt,
               const char *lirOpName, const char *mirOpName,
               JSScript *script, jsbytecode *pc);
 
 void ParCallToUncompiledScript(JSFunction *func);