Title: [188825] trunk/Source/_javascript_Core
Revision
188825
Author
[email protected]
Date
2015-08-22 11:35:47 -0700 (Sat, 22 Aug 2015)

Log Message

DFG string concatenation shouldn't be playing fast and loose with effects and OSR exit
https://bugs.webkit.org/show_bug.cgi?id=148338

Reviewed by Michael Saboff and Saam Barati.

Prior to this change, DFG string concatenation appeared to have various different ways of
creating an OSR exit right after a side effect. That's bad, because the exit will cause
us to reexecute the side effect. The code appears to have some hacks for avoiding this,
but some cases are basically unavoidable, like the OOM case of string concatenation: in
trunk that could cause two executions of the toString operation.

This changes the string concatenation code to either be speculative or effectful but
never both. It's already the case that when this code needs to be effectful, it also
needs to be slow (it does int->string conversions, calls JS functions, etc). So, this is
a small price to pay for sanity.

The biggest part of this change is the introduction of StrCat, which is like MakeRope but
does toString conversions on its own instead of relying on separate nodes. StrCat can
take either 2 or 3 children. It's the effectful but not speculative version of MakeRope.

* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGBackwardsPropagationPhase.cpp:
(JSC::DFG::BackwardsPropagationPhase::propagate):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
(JSC::DFG::FixupPhase::convertStringAddUse):
(JSC::DFG::FixupPhase::fixupToStringOrCallStringConstructor):
(JSC::DFG::FixupPhase::attemptToMakeFastStringAdd):
(JSC::DFG::FixupPhase::canOptimizeStringObjectAccess):
* dfg/DFGNodeType.h:
* dfg/DFGOperations.cpp:
* dfg/DFGOperations.h:
* dfg/DFGPredictionPropagationPhase.cpp:
(JSC::DFG::PredictionPropagationPhase::propagate):
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
(JSC::DFG::JSValueOperand::JSValueOperand):
(JSC::DFG::JSValueOperand::~JSValueOperand):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGValidate.cpp:
(JSC::DFG::Validate::validate):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLIntrinsicRepository.h:
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::DFG::LowerDFGToLLVM::compileNode):
(JSC::FTL::DFG::LowerDFGToLLVM::compileValueAdd):
(JSC::FTL::DFG::LowerDFGToLLVM::compileStrCat):
(JSC::FTL::DFG::LowerDFGToLLVM::compileArithAddOrSub):
* jit/JITOperations.h:
* tests/stress/exception-effect-strcat.js: Added. This test previously failed.
* tests/stress/exception-in-strcat-string-overflow.js: Added. An earlier version of this patch made this fail.
* tests/stress/exception-in-strcat.js: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (188824 => 188825)


--- trunk/Source/_javascript_Core/ChangeLog	2015-08-22 18:05:50 UTC (rev 188824)
+++ trunk/Source/_javascript_Core/ChangeLog	2015-08-22 18:35:47 UTC (rev 188825)
@@ -1,3 +1,71 @@
+2015-08-22  Filip Pizlo  <[email protected]>
+
+        DFG string concatenation shouldn't be playing fast and loose with effects and OSR exit
+        https://bugs.webkit.org/show_bug.cgi?id=148338
+
+        Reviewed by Michael Saboff and Saam Barati.
+
+        Prior to this change, DFG string concatenation appeared to have various different ways of
+        creating an OSR exit right after a side effect. That's bad, because the exit will cause
+        us to reexecute the side effect. The code appears to have some hacks for avoiding this,
+        but some cases are basically unavoidable, like the OOM case of string concatenation: in
+        trunk that could cause two executions of the toString operation.
+
+        This changes the string concatenation code to either be speculative or effectful but
+        never both. It's already the case that when this code needs to be effectful, it also
+        needs to be slow (it does int->string conversions, calls JS functions, etc). So, this is
+        a small price to pay for sanity.
+
+        The biggest part of this change is the introduction of StrCat, which is like MakeRope but
+        does toString conversions on its own instead of relying on separate nodes. StrCat can
+        take either 2 or 3 children. It's the effectful but not speculative version of MakeRope.
+
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGBackwardsPropagationPhase.cpp:
+        (JSC::DFG::BackwardsPropagationPhase::propagate):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        (JSC::DFG::FixupPhase::convertStringAddUse):
+        (JSC::DFG::FixupPhase::fixupToStringOrCallStringConstructor):
+        (JSC::DFG::FixupPhase::attemptToMakeFastStringAdd):
+        (JSC::DFG::FixupPhase::canOptimizeStringObjectAccess):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOperations.cpp:
+        * dfg/DFGOperations.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        (JSC::DFG::PredictionPropagationPhase::propagate):
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.h:
+        (JSC::DFG::SpeculativeJIT::callOperation):
+        (JSC::DFG::JSValueOperand::JSValueOperand):
+        (JSC::DFG::JSValueOperand::~JSValueOperand):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGValidate.cpp:
+        (JSC::DFG::Validate::validate):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLIntrinsicRepository.h:
+        * ftl/FTLLowerDFGToLLVM.cpp:
+        (JSC::FTL::DFG::LowerDFGToLLVM::compileNode):
+        (JSC::FTL::DFG::LowerDFGToLLVM::compileValueAdd):
+        (JSC::FTL::DFG::LowerDFGToLLVM::compileStrCat):
+        (JSC::FTL::DFG::LowerDFGToLLVM::compileArithAddOrSub):
+        * jit/JITOperations.h:
+        * tests/stress/exception-effect-strcat.js: Added. This test previously failed.
+        * tests/stress/exception-in-strcat-string-overflow.js: Added. An earlier version of this patch made this fail.
+        * tests/stress/exception-in-strcat.js: Added.
+
 2015-08-22  Andreas Kling  <[email protected]>
 
         [JSC] Static hash tables should be 100% compile-time constant.

Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (188824 => 188825)


--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2015-08-22 18:05:50 UTC (rev 188824)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h	2015-08-22 18:35:47 UTC (rev 188825)
@@ -418,6 +418,12 @@
         forNode(node).setType(m_graph, SpecString | SpecBytecodeNumber);
         break;
     }
+
+    case StrCat: {
+        clobberWorld(node->origin.semantic, clobberLimit);
+        forNode(node).setType(m_graph, SpecString);
+        break;
+    }
         
     case ArithAdd: {
         JSValue left = forNode(node->child1()).value();

Modified: trunk/Source/_javascript_Core/dfg/DFGBackwardsPropagationPhase.cpp (188824 => 188825)


--- trunk/Source/_javascript_Core/dfg/DFGBackwardsPropagationPhase.cpp	2015-08-22 18:05:50 UTC (rev 188824)
+++ trunk/Source/_javascript_Core/dfg/DFGBackwardsPropagationPhase.cpp	2015-08-22 18:35:47 UTC (rev 188825)
@@ -246,7 +246,7 @@
             node->child2()->mergeFlags(flags);
             break;
         }
-            
+
         case ArithAdd: {
             if (isNotNegZero(node->child1().node()) || isNotNegZero(node->child2().node()))
                 flags &= ~NodeBytecodeNeedsNegZero;

Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (188824 => 188825)


--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2015-08-22 18:05:50 UTC (rev 188824)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp	2015-08-22 18:35:47 UTC (rev 188825)
@@ -3289,37 +3289,31 @@
             int startOperand = currentInstruction[2].u.operand;
             int numOperands = currentInstruction[3].u.operand;
 #if CPU(X86)
-            // X86 doesn't have enough registers to compile MakeRope with three arguments.
-            // Rather than try to be clever, we just make MakeRope dumber on this processor.
-            const unsigned maxRopeArguments = 2;
+            // X86 doesn't have enough registers to compile MakeRope with three arguments. The
+            // StrCat we emit here may be turned into a MakeRope. Rather than try to be clever,
+            // we just make StrCat dumber on this processor.
+            const unsigned maxArguments = 2;
 #else
-            const unsigned maxRopeArguments = 3;
+            const unsigned maxArguments = 3;
 #endif
-            auto toStringNodes = std::make_unique<Node*[]>(numOperands);
-            for (int i = 0; i < numOperands; i++)
-                toStringNodes[i] = addToGraph(ToString, get(VirtualRegister(startOperand - i)));
-
-            for (int i = 0; i < numOperands; i++)
-                addToGraph(Phantom, toStringNodes[i]);
-
             Node* operands[AdjacencyList::Size];
             unsigned indexInOperands = 0;
             for (unsigned i = 0; i < AdjacencyList::Size; ++i)
                 operands[i] = 0;
             for (int operandIdx = 0; operandIdx < numOperands; ++operandIdx) {
-                if (indexInOperands == maxRopeArguments) {
-                    operands[0] = addToGraph(MakeRope, operands[0], operands[1], operands[2]);
+                if (indexInOperands == maxArguments) {
+                    operands[0] = addToGraph(StrCat, operands[0], operands[1], operands[2]);
                     for (unsigned i = 1; i < AdjacencyList::Size; ++i)
                         operands[i] = 0;
                     indexInOperands = 1;
                 }
                 
                 ASSERT(indexInOperands < AdjacencyList::Size);
-                ASSERT(indexInOperands < maxRopeArguments);
-                operands[indexInOperands++] = toStringNodes[operandIdx];
+                ASSERT(indexInOperands < maxArguments);
+                operands[indexInOperands++] = get(VirtualRegister(startOperand - operandIdx));
             }
             set(VirtualRegister(currentInstruction[1].u.operand),
-                addToGraph(MakeRope, operands[0], operands[1], operands[2]));
+                addToGraph(StrCat, operands[0], operands[1], operands[2]));
             NEXT_OPCODE(op_strcat);
         }
 

Modified: trunk/Source/_javascript_Core/dfg/DFGClobberize.h (188824 => 188825)


--- trunk/Source/_javascript_Core/dfg/DFGClobberize.h	2015-08-22 18:05:50 UTC (rev 188824)
+++ trunk/Source/_javascript_Core/dfg/DFGClobberize.h	2015-08-22 18:35:47 UTC (rev 188825)
@@ -392,6 +392,14 @@
         write(Heap);
         return;
         
+    case StrCat:
+        // This is pretty weird. In fact, StrCat has very limited effectfulness because we only
+        // pass it primitive values. But, right now, the compiler isn't smart enough to know this
+        // and that's probably OK.
+        read(World);
+        write(Heap);
+        return;
+
     case GetGetter:
         read(GetterSetter_getter);
         def(HeapLocation(GetterLoc, GetterSetter_getter, node->child1()), LazyNode(node));

Modified: trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp (188824 => 188825)


--- trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp	2015-08-22 18:05:50 UTC (rev 188824)
+++ trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp	2015-08-22 18:35:47 UTC (rev 188825)
@@ -239,6 +239,7 @@
     case ToIndexString:
     case MaterializeNewObject:
     case MaterializeCreateActivation:
+    case StrCat:
         return true;
         
     case MultiPutByOffset:

Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (188824 => 188825)


--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2015-08-22 18:05:50 UTC (rev 188824)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp	2015-08-22 18:35:47 UTC (rev 188825)
@@ -151,31 +151,18 @@
                 break;
             }
             
-            // FIXME: Optimize for the case where one of the operands is the
-            // empty string. Also consider optimizing for the case where we don't
-            // believe either side is the emtpy string. Both of these things should
-            // be easy.
-            
-            if (node->child1()->shouldSpeculateString()
-                && attemptToMakeFastStringAdd<StringUse>(node, node->child1(), node->child2()))
+            if (attemptToMakeFastStringAdd(node))
                 break;
-            if (node->child2()->shouldSpeculateString()
-                && attemptToMakeFastStringAdd<StringUse>(node, node->child2(), node->child1()))
-                break;
-            if (node->child1()->shouldSpeculateStringObject()
-                && attemptToMakeFastStringAdd<StringObjectUse>(node, node->child1(), node->child2()))
-                break;
-            if (node->child2()->shouldSpeculateStringObject()
-                && attemptToMakeFastStringAdd<StringObjectUse>(node, node->child2(), node->child1()))
-                break;
-            if (node->child1()->shouldSpeculateStringOrStringObject()
-                && attemptToMakeFastStringAdd<StringOrStringObjectUse>(node, node->child1(), node->child2()))
-                break;
-            if (node->child2()->shouldSpeculateStringOrStringObject()
-                && attemptToMakeFastStringAdd<StringOrStringObjectUse>(node, node->child2(), node->child1()))
-                break;
+
+            // We could attempt to turn this into a StrCat here. But for now, that wouldn't
+            // significantly reduce the number of branches required.
             break;
         }
+
+        case StrCat: {
+            attemptToMakeFastStringAdd(node);
+            break;
+        }
             
         case MakeRope: {
             fixupMakeRope(node);
@@ -1432,8 +1419,6 @@
             return;
         }
         
-        // FIXME: We ought to be able to have a ToPrimitiveToString node.
-        
         observeUseKindOnNode<useKind>(edge.node());
         createToString<useKind>(node, edge);
     }
@@ -1524,47 +1509,44 @@
             return;
         }
     }
-    
-    template<UseKind leftUseKind>
-    bool attemptToMakeFastStringAdd(Node* node, Edge& left, Edge& right)
+
+    bool attemptToMakeFastStringAdd(Node* node)
     {
-        ASSERT(leftUseKind == StringUse || leftUseKind == StringObjectUse || leftUseKind == StringOrStringObjectUse);
-        
-        if (isStringObjectUse<leftUseKind>() && !canOptimizeStringObjectAccess(node->origin.semantic))
+        bool goodToGo = true;
+        m_graph.doToChildren(
+            node,
+            [&] (Edge& edge) {
+                if (edge->shouldSpeculateString())
+                    return;
+                if (canOptimizeStringObjectAccess(node->origin.semantic)) {
+                    if (edge->shouldSpeculateStringObject())
+                        return;
+                    if (edge->shouldSpeculateStringOrStringObject())
+                        return;
+                }
+                goodToGo = false;
+            });
+        if (!goodToGo)
             return false;
-        
-        convertStringAddUse<leftUseKind>(node, left);
-        
-        if (right->shouldSpeculateString())
-            convertStringAddUse<StringUse>(node, right);
-        else if (right->shouldSpeculateStringObject() && canOptimizeStringObjectAccess(node->origin.semantic))
-            convertStringAddUse<StringObjectUse>(node, right);
-        else if (right->shouldSpeculateStringOrStringObject() && canOptimizeStringObjectAccess(node->origin.semantic))
-            convertStringAddUse<StringOrStringObjectUse>(node, right);
-        else {
-            // At this point we know that the other operand is something weird. The semantically correct
-            // way of dealing with this is:
-            //
-            // MakeRope(@left, ToString(ToPrimitive(@right)))
-            //
-            // So that's what we emit. NB, we need to do all relevant type checks on @left before we do
-            // anything to @right, since ToPrimitive may be effectful.
-            
-            Node* toPrimitive = m_insertionSet.insertNode(
-                m_indexInBlock, resultOfToPrimitive(right->prediction()), ToPrimitive,
-                node->origin, Edge(right.node()));
-            Node* toString = m_insertionSet.insertNode(
-                m_indexInBlock, SpecString, ToString, node->origin, Edge(toPrimitive));
-            
-            fixupToPrimitive(toPrimitive);
 
-            // Don't fix up ToString. ToString and ToPrimitive are originated from the same bytecode and
-            // ToPrimitive may have an observable side effect. ToString should not be converted into Check
-            // with speculative type check because OSR exit reproduce an observable side effect done in
-            // ToPrimitive.
-
-            right.setNode(toString);
-        }
+        m_graph.doToChildren(
+            node,
+            [&] (Edge& edge) {
+                if (edge->shouldSpeculateString()) {
+                    convertStringAddUse<StringUse>(node, edge);
+                    return;
+                }
+                ASSERT(canOptimizeStringObjectAccess(node->origin.semantic));
+                if (edge->shouldSpeculateStringObject()) {
+                    convertStringAddUse<StringObjectUse>(node, edge);
+                    return;
+                }
+                if (edge->shouldSpeculateStringOrStringObject()) {
+                    convertStringAddUse<StringOrStringObjectUse>(node, edge);
+                    return;
+                }
+                RELEASE_ASSERT_NOT_REACHED();
+            });
         
         convertToMakeRope(node);
         return true;
@@ -1600,11 +1582,15 @@
             return false;
         
         Structure* stringObjectStructure = m_graph.globalObjectFor(codeOrigin)->stringObjectStructure();
+        m_graph.registerStructure(stringObjectStructure);
         ASSERT(stringObjectStructure->storedPrototype().isObject());
         ASSERT(stringObjectStructure->storedPrototype().asCell()->classInfo() == StringPrototype::info());
-        
-        JSObject* stringPrototypeObject = asObject(stringObjectStructure->storedPrototype());
-        Structure* stringPrototypeStructure = stringPrototypeObject->structure();
+
+        FrozenValue* stringPrototypeObjectValue =
+            m_graph.freeze(stringObjectStructure->storedPrototype());
+        StringPrototype* stringPrototypeObject =
+            stringPrototypeObjectValue->dynamicCast<StringPrototype*>();
+        Structure* stringPrototypeStructure = stringPrototypeObjectValue->structure();
         if (m_graph.registerStructure(stringPrototypeStructure) != StructureRegisteredAndWatched)
             return false;
         

Modified: trunk/Source/_javascript_Core/dfg/DFGNodeType.h (188824 => 188825)


--- trunk/Source/_javascript_Core/dfg/DFGNodeType.h	2015-08-22 18:05:50 UTC (rev 188824)
+++ trunk/Source/_javascript_Core/dfg/DFGNodeType.h	2015-08-22 18:35:47 UTC (rev 188825)
@@ -160,6 +160,9 @@
     /* Add of values may either be arithmetic, or result in string concatenation. */\
     macro(ValueAdd, NodeResultJS | NodeMustGenerate) \
     \
+    /* Add of values that always convers its inputs to strings. May have two or three kids. */\
+    macro(StrCat, NodeResultJS | NodeMustGenerate) \
+    \
     /* Property access. */\
     /* PutByValAlias indicates a 'put' aliases a prior write to the same property. */\
     /* Since a put to 'length' may invalidate optimizations here, */\

Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.cpp (188824 => 188825)


--- trunk/Source/_javascript_Core/dfg/DFGOperations.cpp	2015-08-22 18:05:50 UTC (rev 188824)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.cpp	2015-08-22 18:35:47 UTC (rev 188825)
@@ -1108,6 +1108,49 @@
     return JSRopeString::create(vm, a, b, c);
 }
 
+JSCell* JIT_OPERATION operationStrCat2(ExecState* exec, EncodedJSValue a, EncodedJSValue b)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+
+    JSString* str1 = JSValue::decode(a).toString(exec);
+    if (exec->hadException())
+        return nullptr;
+    JSString* str2 = JSValue::decode(b).toString(exec);
+    if (exec->hadException())
+        return nullptr;
+
+    if (sumOverflows<int32_t>(str1->length(), str2->length())) {
+        throwOutOfMemoryError(exec);
+        return nullptr;
+    }
+
+    return JSRopeString::create(vm, str1, str2);
+}
+    
+JSCell* JIT_OPERATION operationStrCat3(ExecState* exec, EncodedJSValue a, EncodedJSValue b, EncodedJSValue c)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+
+    JSString* str1 = JSValue::decode(a).toString(exec);
+    if (exec->hadException())
+        return nullptr;
+    JSString* str2 = JSValue::decode(b).toString(exec);
+    if (exec->hadException())
+        return nullptr;
+    JSString* str3 = JSValue::decode(c).toString(exec);
+    if (exec->hadException())
+        return nullptr;
+
+    if (sumOverflows<int32_t>(str1->length(), str2->length(), str3->length())) {
+        throwOutOfMemoryError(exec);
+        return nullptr;
+    }
+
+    return JSRopeString::create(vm, str1, str2, str3);
+}
+
 char* JIT_OPERATION operationFindSwitchImmTargetForDouble(
     ExecState* exec, EncodedJSValue encodedValue, size_t tableIndex)
 {

Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.h (188824 => 188825)


--- trunk/Source/_javascript_Core/dfg/DFGOperations.h	2015-08-22 18:05:50 UTC (rev 188824)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.h	2015-08-22 18:35:47 UTC (rev 188825)
@@ -125,6 +125,8 @@
 JSCell* JIT_OPERATION operationCallStringConstructor(ExecState*, EncodedJSValue);
 JSCell* JIT_OPERATION operationMakeRope2(ExecState*, JSString*, JSString*);
 JSCell* JIT_OPERATION operationMakeRope3(ExecState*, JSString*, JSString*, JSString*);
+JSCell* JIT_OPERATION operationStrCat2(ExecState*, EncodedJSValue, EncodedJSValue);
+JSCell* JIT_OPERATION operationStrCat3(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue);
 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*);

Modified: trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (188824 => 188825)


--- trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp	2015-08-22 18:05:50 UTC (rev 188824)
+++ trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp	2015-08-22 18:35:47 UTC (rev 188825)
@@ -495,7 +495,8 @@
         case StringCharAt:
         case CallStringConstructor:
         case ToString:
-        case MakeRope: {
+        case MakeRope:
+        case StrCat: {
             changed |= setPrediction(SpecString);
             break;
         }

Modified: trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h (188824 => 188825)


--- trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h	2015-08-22 18:05:50 UTC (rev 188824)
+++ trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h	2015-08-22 18:35:47 UTC (rev 188825)
@@ -234,6 +234,7 @@
     case LogicalNot:
     case ToPrimitive:
     case ToString:
+    case StrCat:
     case CallStringConstructor:
     case NewStringObject:
     case MakeRope:

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h (188824 => 188825)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h	2015-08-22 18:05:50 UTC (rev 188824)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT.h	2015-08-22 18:35:47 UTC (rev 188825)
@@ -1374,11 +1374,21 @@
         m_jit.setupArgumentsWithExecState(arg1);
         return appendCallSetResult(operation, result);
     }
+    JITCompiler::Call callOperation(C_JITOperation_EJJ operation, GPRReg result, GPRReg arg1, GPRReg arg2)
+    {
+        m_jit.setupArgumentsWithExecState(arg1, arg2);
+        return appendCallSetResult(operation, result);
+    }
     JITCompiler::Call callOperation(C_JITOperation_EJJC operation, GPRReg result, GPRReg arg1, GPRReg arg2, GPRReg arg3)
     {
         m_jit.setupArgumentsWithExecState(arg1, arg2, arg3);
         return appendCallSetResult(operation, result);
     }
+    JITCompiler::Call callOperation(C_JITOperation_EJJJ operation, GPRReg result, GPRReg arg1, GPRReg arg2, GPRReg arg3)
+    {
+        m_jit.setupArgumentsWithExecState(arg1, arg2, arg3);
+        return appendCallSetResult(operation, result);
+    }
     JITCompiler::Call callOperation(C_JITOperation_EJZ operation, GPRReg result, GPRReg arg1, GPRReg arg2)
     {
         m_jit.setupArgumentsWithExecState(arg1, arg2);
@@ -1705,6 +1715,19 @@
         m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1Payload, arg1Tag);
         return appendCallSetResult(operation, result);
     }
+
+    JITCompiler::Call callOperation(C_JITOperation_EJJ operation, GPRReg result, GPRReg arg1Tag, GPRReg arg1Payload, GPRReg arg2Tag, GPRReg arg2Payload)
+    {
+        m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1Payload, arg1Tag, arg2Payload, arg2Tag);
+        return appendCallSetResult(operation, result);
+    }
+
+    JITCompiler::Call callOperation(C_JITOperation_EJJJ operation, GPRReg result, GPRReg arg1Tag, GPRReg arg1Payload, GPRReg arg2Tag, GPRReg arg2Payload, GPRReg arg3Tag, GPRReg arg3Payload)
+    {
+        m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1Payload, arg1Tag, arg2Payload, arg2Tag, arg3Payload, arg3Tag);
+        return appendCallSetResult(operation, result);
+    }
+
     JITCompiler::Call callOperation(S_JITOperation_EJ operation, GPRReg result, GPRReg arg1Tag, GPRReg arg1Payload)
     {
         m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1Payload, arg1Tag);
@@ -2493,6 +2516,8 @@
 #endif
     {
         ASSERT(m_jit);
+        if (!edge)
+            return;
         ASSERT_UNUSED(mode, mode == ManualOperandSpeculation || edge.useKind() == UntypedUse);
 #if USE(JSVALUE64)
         if (jit->isFilled(node()))
@@ -2507,6 +2532,8 @@
 
     ~JSValueOperand()
     {
+        if (!m_edge)
+            return;
 #if USE(JSVALUE64)
         ASSERT(m_gprOrInvalid != InvalidGPRReg);
         m_jit->unlock(m_gprOrInvalid);

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (188824 => 188825)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp	2015-08-22 18:05:50 UTC (rev 188824)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp	2015-08-22 18:35:47 UTC (rev 188825)
@@ -2057,6 +2057,38 @@
         break;
     }
 
+    case StrCat: {
+        JSValueOperand op1(this, node->child1());
+        JSValueOperand op2(this, node->child2());
+        JSValueOperand op3(this, node->child3());
+        
+        GPRReg op1TagGPR = op1.tagGPR();
+        GPRReg op1PayloadGPR = op1.payloadGPR();
+        GPRReg op2TagGPR = op2.tagGPR();
+        GPRReg op2PayloadGPR = op2.payloadGPR();
+        GPRReg op3TagGPR;
+        GPRReg op3PayloadGPR;
+        if (node->child3()) {
+            op3TagGPR = op3.tagGPR();
+            op3PayloadGPR = op3.payloadGPR();
+        } else {
+            op3TagGPR = InvalidGPRReg;
+            op3PayloadGPR = InvalidGPRReg;
+        }
+        
+        flushRegisters();
+
+        GPRFlushedCallResult result(this);
+        if (node->child3())
+            callOperation(operationStrCat3, result.gpr(), op1TagGPR, op1PayloadGPR, op2TagGPR, op2PayloadGPR, op3TagGPR, op3PayloadGPR);
+        else
+            callOperation(operationStrCat2, result.gpr(), op1TagGPR, op1PayloadGPR, op2TagGPR, op2PayloadGPR);
+        m_jit.exceptionCheck();
+        
+        cellResult(result.gpr(), node);
+        break;
+    }
+
     case ArithAdd:
         compileAdd(node);
         break;

Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (188824 => 188825)


--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp	2015-08-22 18:05:50 UTC (rev 188824)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp	2015-08-22 18:35:47 UTC (rev 188825)
@@ -2189,6 +2189,32 @@
         break;
     }
         
+    case StrCat: {
+        JSValueOperand op1(this, node->child1());
+        JSValueOperand op2(this, node->child2());
+        JSValueOperand op3(this, node->child3());
+        
+        GPRReg op1GPR = op1.gpr();
+        GPRReg op2GPR = op2.gpr();
+        GPRReg op3GPR;
+        if (node->child3())
+            op3GPR = op3.gpr();
+        else
+            op3GPR = InvalidGPRReg;
+        
+        flushRegisters();
+
+        GPRFlushedCallResult result(this);
+        if (node->child3())
+            callOperation(operationStrCat3, result.gpr(), op1GPR, op2GPR, op3GPR);
+        else
+            callOperation(operationStrCat2, result.gpr(), op1GPR, op2GPR);
+        m_jit.exceptionCheck();
+        
+        cellResult(result.gpr(), node);
+        break;
+    }
+
     case ArithAdd:
         compileAdd(node);
         break;

Modified: trunk/Source/_javascript_Core/dfg/DFGValidate.cpp (188824 => 188825)


--- trunk/Source/_javascript_Core/dfg/DFGValidate.cpp	2015-08-22 18:05:50 UTC (rev 188824)
+++ trunk/Source/_javascript_Core/dfg/DFGValidate.cpp	2015-08-22 18:35:47 UTC (rev 188825)
@@ -237,6 +237,7 @@
                 case CompareGreaterEq:
                 case CompareEq:
                 case CompareStrictEq:
+                case StrCat:
                     VALIDATE((node), !!node->child1());
                     VALIDATE((node), !!node->child2());
                     break;

Modified: trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp (188824 => 188825)


--- trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp	2015-08-22 18:05:50 UTC (rev 188824)
+++ trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp	2015-08-22 18:35:47 UTC (rev 188825)
@@ -78,6 +78,7 @@
     case GetGlobalVar:
     case PutGlobalVar:
     case ValueAdd:
+    case StrCat:
     case ArithAdd:
     case ArithClz32:
     case ArithSub:

Modified: trunk/Source/_javascript_Core/ftl/FTLIntrinsicRepository.h (188824 => 188825)


--- trunk/Source/_javascript_Core/ftl/FTLIntrinsicRepository.h	2015-08-22 18:05:50 UTC (rev 188824)
+++ trunk/Source/_javascript_Core/ftl/FTLIntrinsicRepository.h	2015-08-22 18:35:47 UTC (rev 188825)
@@ -63,6 +63,8 @@
     macro(C_JITOperation_ECZC, functionType(intPtr, intPtr, intPtr, int32, intPtr)) \
     macro(C_JITOperation_EGC, functionType(intPtr, intPtr, intPtr, intPtr)) \
     macro(C_JITOperation_EJ, functionType(intPtr, intPtr, int64)) \
+    macro(C_JITOperation_EJJ, functionType(intPtr, intPtr, int64, int64)) \
+    macro(C_JITOperation_EJJJ, functionType(intPtr, intPtr, int64, int64, int64)) \
     macro(C_JITOperation_EJssJss, functionType(intPtr, intPtr, intPtr, intPtr)) \
     macro(C_JITOperation_EJssJssJss, functionType(intPtr, intPtr, intPtr, intPtr, intPtr)) \
     macro(C_JITOperation_ESt, functionType(intPtr, intPtr, intPtr)) \

Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToLLVM.cpp (188824 => 188825)


--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToLLVM.cpp	2015-08-22 18:05:50 UTC (rev 188824)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToLLVM.cpp	2015-08-22 18:35:47 UTC (rev 188825)
@@ -454,6 +454,9 @@
         case ValueAdd:
             compileValueAdd();
             break;
+        case StrCat:
+            compileStrCat();
+            break;
         case ArithAdd:
         case ArithSub:
             compileArithAddOrSub();
@@ -1315,6 +1318,22 @@
             lowJSValue(m_node->child1()), lowJSValue(m_node->child2())));
     }
     
+    void compileStrCat()
+    {
+        LValue result;
+        if (m_node->child3()) {
+            result = vmCall(
+                m_out.operation(operationStrCat3), m_callFrame,
+                lowJSValue(m_node->child1()), lowJSValue(m_node->child2()),
+                lowJSValue(m_node->child3()));
+        } else {
+            result = vmCall(
+                m_out.operation(operationStrCat2), m_callFrame,
+                lowJSValue(m_node->child1()), lowJSValue(m_node->child2()));
+        }
+        setJSValue(result);
+    }
+    
     void compileArithAddOrSub()
     {
         bool isSub =  m_node->op() == ArithSub;

Modified: trunk/Source/_javascript_Core/jit/JITOperations.h (188824 => 188825)


--- trunk/Source/_javascript_Core/jit/JITOperations.h	2015-08-22 18:05:50 UTC (rev 188824)
+++ trunk/Source/_javascript_Core/jit/JITOperations.h	2015-08-22 18:35:47 UTC (rev 188825)
@@ -145,7 +145,9 @@
 typedef JSCell* JIT_OPERATION (*C_JITOperation_EJscC)(ExecState*, JSScope*, JSCell*);
 typedef JSCell* JIT_OPERATION (*C_JITOperation_EJZ)(ExecState*, EncodedJSValue, int32_t);
 typedef JSCell* JIT_OPERATION (*C_JITOperation_EJZC)(ExecState*, EncodedJSValue, int32_t, JSCell*);
+typedef JSCell* JIT_OPERATION (*C_JITOperation_EJJ)(ExecState*, EncodedJSValue, EncodedJSValue);
 typedef JSCell* JIT_OPERATION (*C_JITOperation_EJJC)(ExecState*, EncodedJSValue, EncodedJSValue, JSCell*);
+typedef JSCell* JIT_OPERATION (*C_JITOperation_EJJJ)(ExecState*, EncodedJSValue, EncodedJSValue, 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*);

Added: trunk/Source/_javascript_Core/tests/stress/exception-effect-strcat.js (0 => 188825)


--- trunk/Source/_javascript_Core/tests/stress/exception-effect-strcat.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/exception-effect-strcat.js	2015-08-22 18:35:47 UTC (rev 188825)
@@ -0,0 +1,42 @@
+function foo(a, b) {
+    return a + b;
+}
+
+noInline(foo);
+
+var bStr = "b";
+for (var i = 0; i < 30; ++i)
+    bStr = bStr + bStr;
+
+var effects = 0;
+var b = {toString: function() { effects++; return bStr; }};
+
+for (var i = 0; i < 10000; ++i) {
+    effects = 0;
+    var result = foo("a", b);
+    if (result.length != "a".length + bStr.length)
+        throw "Error: bad result in loop: " + result;
+    if (effects != 1)
+        throw "Error: bad number of effects: " + effects;
+}
+
+// Create a large string.
+var a = "a";
+for (var i = 0; i < 30; ++i)
+    a = a + a;
+
+effects = 0;
+var result = null;
+var didThrow = false;
+try {
+    result = foo(a, b);
+} catch (e) {
+    didThrow = true;
+}
+
+if (!didThrow)
+    throw "Error: did not throw.";
+if (result !== null)
+    throw "Error: did set result to something: " + result;
+if (effects != 1)
+    throw "Error: bad number of effects at end: " + effects;

Added: trunk/Source/_javascript_Core/tests/stress/exception-in-strcat-string-overflow.js (0 => 188825)


--- trunk/Source/_javascript_Core/tests/stress/exception-in-strcat-string-overflow.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/exception-in-strcat-string-overflow.js	2015-08-22 18:35:47 UTC (rev 188825)
@@ -0,0 +1,37 @@
+function foo(a, b) {
+    return a + "x" + b;
+}
+
+noInline(foo);
+
+for (var i = 0; i < 10000; ++i) {
+    var b;
+    var expected;
+    if (i & 1) {
+        b = 42;
+        expected = "ax42";
+    } else {
+        b = "b";
+        expected = "axb";
+    }
+    var result = foo("a", b);
+    if (result != expected)
+        throw "Error: bad result: " + result;
+}
+
+var longStr = "l";
+for (var i = 0; i < 30; ++i)
+    longStr = longStr + longStr;
+
+var result = null;
+var didThrow = false;
+try {
+    result = foo(longStr, longStr);
+} catch (e) {
+    didThrow = true;
+}
+
+if (!didThrow)
+    throw "Error: did not throw";
+if (result !== null)
+    throw "Error: did set result: " + result;

Added: trunk/Source/_javascript_Core/tests/stress/exception-in-strcat.js (0 => 188825)


--- trunk/Source/_javascript_Core/tests/stress/exception-in-strcat.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/exception-in-strcat.js	2015-08-22 18:35:47 UTC (rev 188825)
@@ -0,0 +1,24 @@
+function foo(a, b) {
+    return a + "x" + b;
+}
+
+noInline(foo);
+
+for (var i = 0; i < 10000; ++i) {
+    var result = foo({toString: function() { return "a"; }}, 42);
+    if (result != "ax42")
+        throw "Error: bad result: " + result;
+}
+
+var result = null;
+var didThrow = false;
+try {
+    result = foo({toString: function() { throw "error"; }}, 42);
+} catch (e) {
+    didThrow = true;
+}
+
+if (!didThrow)
+    throw "Error: did not throw";
+if (result !== null)
+    throw "Error: did set result: " + result;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to