[JSC] Add primitive String support to compare operators
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 22 Apr 2016 05:08:28 +0000 (05:08 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 22 Apr 2016 05:08:28 +0000 (05:08 +0000)
https://bugs.webkit.org/show_bug.cgi?id=156783

Patch by Benjamin Poulain <bpoulain@apple.com> on 2016-04-21
Reviewed by Geoffrey Garen.

Just the basics.
We should eventually inline some of the simplest cases.

This is a 2% improvement on Longspider. It is unfortunately neutral
for Sunspider on my machine because most of the comparison are from
baseline.

* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compare):
(JSC::DFG::SpeculativeJIT::compileStringCompare):
(JSC::DFG::SpeculativeJIT::compileStringIdentCompare):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileCompareLess):
(JSC::FTL::DFG::LowerDFGToB3::compileCompareLessEq):
(JSC::FTL::DFG::LowerDFGToB3::compileCompareGreater):
(JSC::FTL::DFG::LowerDFGToB3::compileCompareGreaterEq):
(JSC::FTL::DFG::LowerDFGToB3::compare):
* ftl/FTLOutput.h:
(JSC::FTL::Output::callWithoutSideEffects):
* jit/JITOperations.h:
* tests/stress/string-compare.js: Added.
(makeRope):
(makeString):
(let.operator.of.operators.eval.compareStringIdent):
(let.operator.of.operators.compareStringString):
(let.operator.of.operators.compareStringIdentString):
(let.operator.of.operators.compareStringStringIdent):
(let.operator.of.operators.let.left.of.typeCases.let.right.of.typeCases.eval):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@199867 268f45cc-cd09-0410-ab3c-d52691b4dbfc

13 files changed:
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGOperations.cpp
Source/JavaScriptCore/dfg/DFGOperations.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/ftl/FTLOutput.h
Source/JavaScriptCore/jit/JITOperations.h
Source/JavaScriptCore/tests/stress/string-compare.js [new file with mode: 0644]

index c2640fe..8ec6bdc 100644 (file)
@@ -1,3 +1,51 @@
+2016-04-21  Benjamin Poulain  <bpoulain@apple.com>
+
+        [JSC] Add primitive String support to compare operators
+        https://bugs.webkit.org/show_bug.cgi?id=156783
+
+        Reviewed by Geoffrey Garen.
+
+        Just the basics.
+        We should eventually inline some of the simplest cases.
+
+        This is a 2% improvement on Longspider. It is unfortunately neutral
+        for Sunspider on my machine because most of the comparison are from
+        baseline.
+
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compare):
+        (JSC::DFG::SpeculativeJIT::compileStringCompare):
+        (JSC::DFG::SpeculativeJIT::compileStringIdentCompare):
+        * dfg/DFGSpeculativeJIT.h:
+        (JSC::DFG::SpeculativeJIT::callOperation):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileCompareLess):
+        (JSC::FTL::DFG::LowerDFGToB3::compileCompareLessEq):
+        (JSC::FTL::DFG::LowerDFGToB3::compileCompareGreater):
+        (JSC::FTL::DFG::LowerDFGToB3::compileCompareGreaterEq):
+        (JSC::FTL::DFG::LowerDFGToB3::compare):
+        * ftl/FTLOutput.h:
+        (JSC::FTL::Output::callWithoutSideEffects):
+        * jit/JITOperations.h:
+        * tests/stress/string-compare.js: Added.
+        (makeRope):
+        (makeString):
+        (let.operator.of.operators.eval.compareStringIdent):
+        (let.operator.of.operators.compareStringString):
+        (let.operator.of.operators.compareStringIdentString):
+        (let.operator.of.operators.compareStringStringIdent):
+        (let.operator.of.operators.let.left.of.typeCases.let.right.of.typeCases.eval):
+
 2016-04-21  Benjamin Poulain  <bpoulain@webkit.org>
 
         [JSC] Commute FDiv-by-constant into FMul-by-reciprocal when it is safe
index 535348e..ca7343c 100644 (file)
@@ -1314,11 +1314,24 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                 break;
             }
             
-            if (node->op() == CompareEq && leftConst.isString() && rightConst.isString()) {
+            if (leftConst.isString() && rightConst.isString()) {
                 const StringImpl* a = asString(leftConst)->tryGetValueImpl();
                 const StringImpl* b = asString(rightConst)->tryGetValueImpl();
                 if (a && b) {
-                    setConstant(node, jsBoolean(WTF::equal(a, b)));
+                    bool result;
+                    if (node->op() == CompareEq)
+                        result = WTF::equal(a, b);
+                    else if (node->op() == CompareLess)
+                        result = codePointCompare(a, b) < 0;
+                    else if (node->op() == CompareLessEq)
+                        result = codePointCompare(a, b) <= 0;
+                    else if (node->op() == CompareGreater)
+                        result = codePointCompare(a, b) > 0;
+                    else if (node->op() == CompareGreaterEq)
+                        result = codePointCompare(a, b) >= 0;
+                    else
+                        RELEASE_ASSERT_NOT_REACHED();
+                    setConstant(node, jsBoolean(result));
                     break;
                 }
             }
index b0516b4..71ef98a 100644 (file)
@@ -1145,6 +1145,11 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
     case CompareLessEq:
     case CompareGreater:
     case CompareGreaterEq:
+        if (node->isBinaryUseKind(StringUse)) {
+            read(HeapObjectCount);
+            write(HeapObjectCount);
+            return;
+        }
         if (!node->isBinaryUseKind(UntypedUse)) {
             def(PureValue(node));
             return;
index 3a2b119..870e5d5 100644 (file)
@@ -468,14 +468,6 @@ private:
                 node->clearFlags(NodeMustGenerate);
                 break;
             }
-            if (node->op() != CompareEq)
-                break;
-            if (Node::shouldSpeculateSymbol(node->child1().node(), node->child2().node())) {
-                fixEdge<SymbolUse>(node->child1());
-                fixEdge<SymbolUse>(node->child2());
-                node->clearFlags(NodeMustGenerate);
-                break;
-            }
             if (node->child1()->shouldSpeculateStringIdent() && node->child2()->shouldSpeculateStringIdent()) {
                 fixEdge<StringIdentUse>(node->child1());
                 fixEdge<StringIdentUse>(node->child2());
@@ -488,6 +480,15 @@ private:
                 node->clearFlags(NodeMustGenerate);
                 break;
             }
+
+            if (node->op() != CompareEq)
+                break;
+            if (Node::shouldSpeculateSymbol(node->child1().node(), node->child2().node())) {
+                fixEdge<SymbolUse>(node->child1());
+                fixEdge<SymbolUse>(node->child2());
+                node->clearFlags(NodeMustGenerate);
+                break;
+            }
             if (node->child1()->shouldSpeculateObject() && node->child2()->shouldSpeculateObject()) {
                 fixEdge<ObjectUse>(node->child1());
                 fixEdge<ObjectUse>(node->child2());
index 58eeb18..adb18bc 100644 (file)
@@ -1342,6 +1342,58 @@ int32_t JIT_OPERATION operationSwitchStringAndGetBranchOffset(ExecState* exec, s
     return exec->codeBlock()->stringSwitchJumpTable(tableIndex).offsetForValue(string->value(exec).impl(), std::numeric_limits<int32_t>::min());
 }
 
+uintptr_t JIT_OPERATION operationCompareStringImplLess(StringImpl* a, StringImpl* b)
+{
+    return codePointCompare(a, b) < 0;
+}
+
+uintptr_t JIT_OPERATION operationCompareStringImplLessEq(StringImpl* a, StringImpl* b)
+{
+    return codePointCompare(a, b) <= 0;
+}
+
+uintptr_t JIT_OPERATION operationCompareStringImplGreater(StringImpl* a, StringImpl* b)
+{
+    return codePointCompare(a, b) > 0;
+}
+
+uintptr_t JIT_OPERATION operationCompareStringImplGreaterEq(StringImpl* a, StringImpl* b)
+{
+    return codePointCompare(a, b) >= 0;
+}
+
+uintptr_t JIT_OPERATION operationCompareStringLess(ExecState* exec, JSString* a, JSString* b)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+
+    return codePointCompareLessThan(asString(a)->value(exec), asString(b)->value(exec));
+}
+
+uintptr_t JIT_OPERATION operationCompareStringLessEq(ExecState* exec, JSString* a, JSString* b)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+
+    return !codePointCompareLessThan(asString(b)->value(exec), asString(a)->value(exec));
+}
+
+uintptr_t JIT_OPERATION operationCompareStringGreater(ExecState* exec, JSString* a, JSString* b)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+
+    return codePointCompareLessThan(asString(b)->value(exec), asString(a)->value(exec));
+}
+
+uintptr_t JIT_OPERATION operationCompareStringGreaterEq(ExecState* exec, JSString* a, JSString* b)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+
+    return !codePointCompareLessThan(asString(a)->value(exec), asString(b)->value(exec));
+}
+
 void JIT_OPERATION operationNotifyWrite(ExecState* exec, WatchpointSet* set)
 {
     VM& vm = exec->vm();
index ddecbc9..79262ff 100644 (file)
@@ -146,6 +146,14 @@ JSCell* JIT_OPERATION operationStrCat3(ExecState*, EncodedJSValue, EncodedJSValu
 char* JIT_OPERATION operationFindSwitchImmTargetForDouble(ExecState*, EncodedJSValue, size_t tableIndex);
 char* JIT_OPERATION operationSwitchString(ExecState*, size_t tableIndex, JSString*);
 int32_t JIT_OPERATION operationSwitchStringAndGetBranchOffset(ExecState*, size_t tableIndex, JSString*);
+uintptr_t JIT_OPERATION operationCompareStringImplLess(StringImpl*, StringImpl*);
+uintptr_t JIT_OPERATION operationCompareStringImplLessEq(StringImpl*, StringImpl*);
+uintptr_t JIT_OPERATION operationCompareStringImplGreater(StringImpl*, StringImpl*);
+uintptr_t JIT_OPERATION operationCompareStringImplGreaterEq(StringImpl*, StringImpl*);
+uintptr_t JIT_OPERATION operationCompareStringLess(ExecState*, JSString*, JSString*);
+uintptr_t JIT_OPERATION operationCompareStringLessEq(ExecState*, JSString*, JSString*);
+uintptr_t JIT_OPERATION operationCompareStringGreater(ExecState*, JSString*, JSString*);
+uintptr_t JIT_OPERATION operationCompareStringGreaterEq(ExecState*, JSString*, JSString*);
 void JIT_OPERATION operationNotifyWrite(ExecState*, WatchpointSet*);
 void JIT_OPERATION operationThrowStackOverflowForVarargs(ExecState*) WTF_INTERNAL;
 int32_t JIT_OPERATION operationSizeOfVarargs(ExecState*, EncodedJSValue arguments, int32_t firstVarArgOffset);
index 17bdf8b..25cb9e0 100644 (file)
@@ -1484,13 +1484,12 @@ bool SpeculativeJIT::compilePeepHoleBranch(Node* node, MacroAssembler::Relationa
         else if (node->isBinaryUseKind(Int52RepUse))
             compilePeepHoleInt52Branch(node, branchNode, condition);
 #endif // USE(JSVALUE64)
-        else if (node->isBinaryUseKind(DoubleRepUse))
+        else if (node->isBinaryUseKind(StringUse) || node->isBinaryUseKind(StringIdentUse)) {
+            // Use non-peephole comparison, for now.
+            return false;
+        } else if (node->isBinaryUseKind(DoubleRepUse))
             compilePeepHoleDoubleBranch(node, branchNode, doubleCondition);
         else if (node->op() == CompareEq) {
-            if (node->isBinaryUseKind(StringUse) || node->isBinaryUseKind(StringIdentUse)) {
-                // Use non-peephole comparison, for now.
-                return false;
-            }
             if (node->isBinaryUseKind(BooleanUse))
                 compilePeepHoleBooleanBranch(node, branchNode, condition);
             else if (node->isBinaryUseKind(SymbolUse))
@@ -4921,20 +4920,26 @@ bool SpeculativeJIT::compare(Node* node, MacroAssembler::RelationalCondition con
         compileDoubleCompare(node, doubleCondition);
         return false;
     }
-    
-    if (node->op() == CompareEq) {
-        if (node->isBinaryUseKind(StringUse)) {
+
+    if (node->isBinaryUseKind(StringUse)) {
+        if (node->op() == CompareEq)
             compileStringEquality(node);
-            return false;
-        }
-        
-        if (node->isBinaryUseKind(BooleanUse)) {
-            compileBooleanCompare(node, condition);
-            return false;
-        }
+        else
+            compileStringCompare(node, condition);
+        return false;
+    }
 
-        if (node->isBinaryUseKind(StringIdentUse)) {
+    if (node->isBinaryUseKind(StringIdentUse)) {
+        if (node->op() == CompareEq)
             compileStringIdentEquality(node);
+        else
+            compileStringIdentCompare(node, condition);
+        return false;
+    }
+
+    if (node->op() == CompareEq) {
+        if (node->isBinaryUseKind(BooleanUse)) {
+            compileBooleanCompare(node, condition);
             return false;
         }
 
@@ -5386,6 +5391,76 @@ void SpeculativeJIT::compileStringIdentToNotStringVarEquality(
     unblessedBooleanResult(rightTempGPR, node);
 }
 
+void SpeculativeJIT::compileStringCompare(Node* node, MacroAssembler::RelationalCondition condition)
+{
+    SpeculateCellOperand left(this, node->child1());
+    SpeculateCellOperand right(this, node->child2());
+    GPRReg leftGPR = left.gpr();
+    GPRReg rightGPR = right.gpr();
+
+    speculateString(node->child1(), leftGPR);
+    speculateString(node->child2(), rightGPR);
+
+    C_JITOperation_B_EJssJss compareFunction = nullptr;
+    if (condition == MacroAssembler::LessThan)
+        compareFunction = operationCompareStringLess;
+    else if (condition == MacroAssembler::LessThanOrEqual)
+        compareFunction = operationCompareStringLessEq;
+    else if (condition == MacroAssembler::GreaterThan)
+        compareFunction = operationCompareStringGreater;
+    else if (condition == MacroAssembler::GreaterThanOrEqual)
+        compareFunction = operationCompareStringGreaterEq;
+    else
+        RELEASE_ASSERT_NOT_REACHED();
+
+    GPRFlushedCallResult result(this);
+    GPRReg resultGPR = result.gpr();
+
+    flushRegisters();
+    callOperation(compareFunction, resultGPR, leftGPR, rightGPR);
+    m_jit.exceptionCheck();
+
+    unblessedBooleanResult(resultGPR, node);
+}
+
+void SpeculativeJIT::compileStringIdentCompare(Node* node, MacroAssembler::RelationalCondition condition)
+{
+    SpeculateCellOperand left(this, node->child1());
+    SpeculateCellOperand right(this, node->child2());
+    GPRFlushedCallResult result(this);
+    GPRTemporary leftTemp(this);
+    GPRTemporary rightTemp(this);
+
+    GPRReg leftGPR = left.gpr();
+    GPRReg rightGPR = right.gpr();
+    GPRReg resultGPR = result.gpr();
+    GPRReg leftTempGPR = leftTemp.gpr();
+    GPRReg rightTempGPR = rightTemp.gpr();
+
+    speculateString(node->child1(), leftGPR);
+    speculateString(node->child2(), rightGPR);
+
+    C_JITOperation_TT compareFunction = nullptr;
+    if (condition == MacroAssembler::LessThan)
+        compareFunction = operationCompareStringImplLess;
+    else if (condition == MacroAssembler::LessThanOrEqual)
+        compareFunction = operationCompareStringImplLessEq;
+    else if (condition == MacroAssembler::GreaterThan)
+        compareFunction = operationCompareStringImplGreater;
+    else if (condition == MacroAssembler::GreaterThanOrEqual)
+        compareFunction = operationCompareStringImplGreaterEq;
+    else
+        RELEASE_ASSERT_NOT_REACHED();
+
+    speculateStringIdentAndLoadStorage(node->child1(), leftGPR, leftTempGPR);
+    speculateStringIdentAndLoadStorage(node->child2(), rightGPR, rightTempGPR);
+
+    flushRegisters();
+    callOperation(compareFunction, resultGPR, leftTempGPR, rightTempGPR);
+
+    unblessedBooleanResult(resultGPR, node);
+}
+
 void SpeculativeJIT::compileStringZeroLength(Node* node)
 {
     SpeculateCellOperand str(this, node->child1());
index 92fcaf5..57c23f6 100644 (file)
@@ -1076,6 +1076,16 @@ public:
         m_jit.setupArgumentsWithExecState(arg1, arg2);
         return appendCallSetResult(operation, result);
     }
+    JITCompiler::Call callOperation(C_JITOperation_B_EJssJss operation, GPRReg result, GPRReg arg1, GPRReg arg2)
+    {
+        m_jit.setupArgumentsWithExecState(arg1, arg2);
+        return appendCallSetResult(operation, result);
+    }
+    JITCompiler::Call callOperation(C_JITOperation_TT operation, GPRReg result, GPRReg arg1, GPRReg arg2)
+    {
+        m_jit.setupArguments(arg1, arg2);
+        return appendCallSetResult(operation, result);
+    }
     JITCompiler::Call callOperation(C_JITOperation_EJssJssJss operation, GPRReg result, GPRReg arg1, GPRReg arg2, GPRReg arg3)
     {
         m_jit.setupArgumentsWithExecState(arg1, arg2, arg3);
@@ -2402,6 +2412,8 @@ public:
     void compileInt52Compare(Node*, MacroAssembler::RelationalCondition);
     void compileBooleanCompare(Node*, MacroAssembler::RelationalCondition);
     void compileDoubleCompare(Node*, MacroAssembler::DoubleCondition);
+    void compileStringCompare(Node*, MacroAssembler::RelationalCondition);
+    void compileStringIdentCompare(Node*, MacroAssembler::RelationalCondition);
     
     bool compileStrictEq(Node*);
     
index 06295ce..ee5be8f 100644 (file)
@@ -416,6 +416,10 @@ inline CapabilityLevel canCompile(Node* node)
             break;
         if (node->isBinaryUseKind(DoubleRepUse))
             break;
+        if (node->isBinaryUseKind(StringIdentUse))
+            break;
+        if (node->isBinaryUseKind(StringUse))
+            break;
         if (node->isBinaryUseKind(UntypedUse))
             break;
         return CannotCompile;
index 7d5895d..cb03cfe 100644 (file)
@@ -4794,6 +4794,8 @@ private:
             [&] (LValue left, LValue right) {
                 return m_out.doubleLessThan(left, right);
             },
+            operationCompareStringImplLess,
+            operationCompareStringLess,
             operationCompareLess);
     }
     
@@ -4806,6 +4808,8 @@ private:
             [&] (LValue left, LValue right) {
                 return m_out.doubleLessThanOrEqual(left, right);
             },
+            operationCompareStringImplLessEq,
+            operationCompareStringLessEq,
             operationCompareLessEq);
     }
     
@@ -4818,6 +4822,8 @@ private:
             [&] (LValue left, LValue right) {
                 return m_out.doubleGreaterThan(left, right);
             },
+            operationCompareStringImplGreater,
+            operationCompareStringGreater,
             operationCompareGreater);
     }
     
@@ -4830,6 +4836,8 @@ private:
             [&] (LValue left, LValue right) {
                 return m_out.doubleGreaterThanOrEqual(left, right);
             },
+            operationCompareStringImplGreaterEq,
+            operationCompareStringGreaterEq,
             operationCompareGreaterEq);
     }
     
@@ -7531,7 +7539,9 @@ private:
     template<typename IntFunctor, typename DoubleFunctor>
     void compare(
         const IntFunctor& intFunctor, const DoubleFunctor& doubleFunctor,
-        S_JITOperation_EJJ helperFunction)
+        C_JITOperation_TT stringIdentFunction,
+        C_JITOperation_B_EJssJss stringFunction,
+        S_JITOperation_EJJ fallbackFunction)
     {
         if (m_node->isBinaryUseKind(Int32Use)) {
             LValue left = lowInt32(m_node->child1());
@@ -7554,9 +7564,29 @@ private:
             setBoolean(doubleFunctor(left, right));
             return;
         }
-        
+
+        if (m_node->isBinaryUseKind(StringIdentUse)) {
+            LValue left = lowStringIdent(m_node->child1());
+            LValue right = lowStringIdent(m_node->child2());
+            setBoolean(m_out.callWithoutSideEffects(m_out.boolean, stringIdentFunction, left, right));
+            return;
+        }
+
+        if (m_node->isBinaryUseKind(StringUse)) {
+            LValue left = lowCell(m_node->child1());
+            LValue right = lowCell(m_node->child2());
+            speculateString(m_node->child1(), left);
+            speculateString(m_node->child2(), right);
+
+            LValue result = vmCall(
+                m_out.boolean, m_out.operation(stringFunction),
+                m_callFrame, left, right);
+            setBoolean(result);
+            return;
+        }
+
         if (m_node->isBinaryUseKind(UntypedUse)) {
-            nonSpeculativeCompare(intFunctor, helperFunction);
+            nonSpeculativeCompare(intFunctor, fallbackFunction);
             return;
         }
         
index b35e157..2d74592 100644 (file)
@@ -383,6 +383,14 @@ public:
     template<typename... Args>
     LValue call(LType type, LValue function, LValue arg1, Args... args) { return m_block->appendNew<B3::CCallValue>(m_proc, type, origin(), function, arg1, args...); }
 
+    template<typename Function, typename... Args>
+    LValue callWithoutSideEffects(B3::Type type, Function function, LValue arg1, Args... args)
+    {
+        return m_block->appendNew<B3::CCallValue>(m_proc, type, origin(), B3::Effects::none(),
+            m_block->appendNew<B3::ConstPtrValue>(m_proc, origin(), bitwise_cast<void*>(function)),
+            arg1, args...);
+    }
+
     template<typename FunctionType>
     LValue operation(FunctionType function) { return constIntPtr(bitwise_cast<void*>(function)); }
 
@@ -470,15 +478,6 @@ public:
 
 private:
     OrderMaker<LBasicBlock> m_blockOrder;
-    
-    template<typename Function, typename... Args>
-    LValue callWithoutSideEffects(B3::Type type, Function function, LValue arg1, Args... args)
-    {
-        return m_block->appendNew<B3::CCallValue>(m_proc, type, origin(), B3::Effects::none(),
-            m_block->appendNew<B3::ConstPtrValue>(m_proc, origin(), bitwise_cast<void*>(function)),
-            arg1, args...);
-    }
-
 };
 
 template<typename... Params>
index abb2d7a..599bd03 100644 (file)
@@ -176,6 +176,8 @@ typedef JSCell* JIT_OPERATION (*C_JITOperation_EJJJ)(ExecState*, EncodedJSValue,
 typedef JSCell* JIT_OPERATION (*C_JITOperation_EJscZ)(ExecState*, JSScope*, int32_t);
 typedef JSCell* JIT_OPERATION (*C_JITOperation_EJssSt)(ExecState*, JSString*, Structure*);
 typedef JSCell* JIT_OPERATION (*C_JITOperation_EJssJss)(ExecState*, JSString*, JSString*);
+typedef uintptr_t JIT_OPERATION (*C_JITOperation_B_EJssJss)(ExecState*, JSString*, JSString*);
+typedef uintptr_t JIT_OPERATION (*C_JITOperation_TT)(StringImpl*, StringImpl*);
 typedef JSCell* JIT_OPERATION (*C_JITOperation_EJssJssJss)(ExecState*, JSString*, JSString*, JSString*);
 typedef JSCell* JIT_OPERATION (*C_JITOperation_EL)(ExecState*, JSLexicalEnvironment*);
 typedef JSCell* JIT_OPERATION (*C_JITOperation_EO)(ExecState*, JSObject*);
diff --git a/Source/JavaScriptCore/tests/stress/string-compare.js b/Source/JavaScriptCore/tests/stress/string-compare.js
new file mode 100644 (file)
index 0000000..253e7cc
--- /dev/null
@@ -0,0 +1,78 @@
+let typeCases = [
+    "",
+    "0",
+    "1",
+    "a",
+    "aa",
+]
+
+let operators = ["<", "<=", ">", ">=", "==", "!=", "===", "!=="];
+
+function makeRope(a)
+{
+    return a + a;
+}
+noInline(makeRope);
+
+function makeString(a)
+{
+    return makeRope(a).slice(a.length);
+}
+noInline(makeString);
+
+for (let operator of operators) {
+    eval(`
+        function compareStringIdent(a, b)
+        {
+            return a ${operator} b;
+        }
+        noInline(compareStringIdent);
+
+        function compareStringString(a, b)
+        {
+            return a ${operator} b;
+        }
+        noInline(compareStringString);
+
+        function compareStringIdentString(a, b)
+        {
+            return a ${operator} b;
+        }
+        noInline(compareStringIdentString);
+
+        function compareStringStringIdent(a, b)
+        {
+            return a ${operator} b;
+        }
+        noInline(compareStringStringIdent);
+    `);
+
+    for (let left of typeCases) {
+        for (let right of typeCases) {
+            let expected = eval("'" + left + "'" + operator + "'" + right + "'");
+            eval(`
+                 for (let i = 0; i < 1e3; ++i) {
+                     let stringIdentResult = compareStringIdent('${left}', '${right}');
+                     if (stringIdentResult !== ${expected})
+                        throw "Failed compareStringIdent('${left}', '${right}'), got " + stringIdentResult + " expected ${expected}";
+                     let resolvedLeftString = makeString('${left}');
+                     let resovledRightString = makeString('${right}');
+                     let stringStringResult = compareStringString(resolvedLeftString, resovledRightString);
+                     if (stringStringResult !== ${expected})
+                        throw "Failed compareStringString('${left}', '${right}'), got " + stringStringResult + " expected ${expected}";
+                     stringStringResult = compareStringString(makeString('${left}'), makeString('${right}'));
+                     if (stringStringResult !== ${expected})
+                        throw "Failed compareStringString('${left}', '${right}'), got " + stringStringResult + " expected ${expected}";
+
+                     if (compareStringIdentString(makeString('${left}'), '${right}') !== ${expected})
+                        throw "Failed compareStringIdentString('${left}', '${right}'), expected was ${expected}";
+                     if (compareStringStringIdent('${left}', makeString('${right}')) !== ${expected})
+                        throw "Failed compareStringStringIdent('${left}', '${right}'), expected was ${expected}";
+
+                     if (('${left}' ${operator} '${right}') !== ${expected})
+                        throw "Failed constant folding of ('${left}' ${operator} '${right}'). How do you even fail constant folding?";
+                 }
+            `)
+        }
+    }
+}
\ No newline at end of file