Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (185239 => 185240)
--- trunk/Source/_javascript_Core/ChangeLog 2015-06-05 04:59:28 UTC (rev 185239)
+++ trunk/Source/_javascript_Core/ChangeLog 2015-06-05 05:20:45 UTC (rev 185240)
@@ -1,3 +1,104 @@
+2015-06-04 Benjamin Poulain <bpoul...@apple.com>
+
+ [JSC] Always track out-of-bounds array access explicitly instead of relying on the slow case
+ https://bugs.webkit.org/show_bug.cgi?id=145673
+
+ Reviewed by Geoffrey Garen.
+
+ Previously, we were deciding to use out-of-bounds speculation based on two informations:
+ -Explicitly detected out-of-bounds accesses tracked on ArrayProfile.
+ -The number of time we took the slow cases in the baseline JIT.
+
+ The heuristic based on slow cases was a little too fragile.
+
+ In some cases, we were running into that limit just because the indexing type changes between
+ two values (typically Int32Array and DoubleArray). Sometimes we were just unlucky on what
+ we used for the inline cache.
+
+ In Kraken, this was hurting us on "audio-beat-detection" and "audio-fft". The array types we see
+ change between Int32 and Double. We run into the slow path a bit but never hit
+ out-of-bounds.
+
+ By the time we compile in DFG, we have stable Double Arrays but we speculate out-of-bounds based
+ on the number of slow cases we took. Because of that, we start boxing the double on GetByVal,
+ using DoubleRep, etc. adding a ton of overhead over otherwise very simple operations.
+
+ WebXPRT was also suffering from this problem but the other way arround: we were missing
+ the out-of-bounds accesses due to changes in indexing types, we were below the threshold
+ of slow-path access, thus we predicted in-bounds accesses for code that was doing plenty
+ of out-of-bands.
+
+
+ This patch fixes the problem by tracking the out-of-bounds access explicitly any time we go
+ into the slow path in baseline JIT. Since we no longer miss any out-of-bounds, we can remove
+ the slow-path heuristic.
+
+ There is new additional special case in the C code regarding out-of-bounds: Arguments access.
+ Mispredicting out-of-bounds accesses on arguments is a disaster for performance, so those are
+ tracked in the way DFG expect it.
+
+
+ There are a few important cases that are still not covered optimally:
+ -PutByVal on Arguments.
+ -Get/Put ByVal on TypedArray.
+ Those are simply not used by DFG in any way. TypedArrays should probably be looked at in the future.
+
+ * bytecode/ArrayProfile.cpp:
+ (JSC::ArrayProfile::computeUpdatedPrediction):
+ The inline-cache repatch cases now update the ArrayProfile information. This has no value in baseline
+ JIT but it helps avoiding one recompile in DFG for the missing ArrayProfile information.
+
+ * bytecode/ArrayProfile.h:
+ (JSC::ArrayProfile::setOutOfBounds):
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::getArrayMode):
+ (JSC::DFG::ByteCodeParser::parseBlock):
+ (JSC::DFG::ByteCodeParser::getArrayModeConsideringSlowPath): Deleted.
+ * jit/CCallHelpers.h:
+ (JSC::CCallHelpers::setupArgumentsWithExecState):
+ * jit/JIT.h:
+ * jit/JITInlines.h:
+ (JSC::JIT::callOperation):
+ * jit/JITOpcodes.cpp:
+ (JSC::JIT::emitSlow_op_has_indexed_property):
+ * jit/JITOpcodes32_64.cpp:
+ (JSC::JIT::emitSlow_op_has_indexed_property):
+ * jit/JITOperations.cpp:
+ (JSC::canUseFastArgumentAccess):
+ This is not my favorite part of this patch.
+
+ I tried having JSObject::canGetIndexQuickly() handle arguments which would put everything
+ on the generic path. Unfortunately, that code is very performance sensitive and some benchmarks were
+ impacted by over 10%
+
+ I left JSObject::canGetIndexQuickly() alone, and I added the canUseFastArgumentAccess() mirroring
+ how DFG uses out-of-bounds for Arguments.
+
+ (JSC::getByVal):
+ * jit/JITOperations.h:
+ * jit/JITPropertyAccess.cpp:
+ (JSC::JIT::emitSlow_op_get_by_val):
+ (JSC::JIT::emitSlow_op_put_by_val):
+ * jit/JITPropertyAccess32_64.cpp:
+ (JSC::JIT::emitSlow_op_get_by_val):
+ (JSC::JIT::emitSlow_op_put_by_val):
+ * runtime/JSPromiseFunctions.cpp:
+ * tests/stress/get-by-val-out-of-bounds-basics.js: Added.
+ (opaqueGetByValOnInt32ArrayEarlyOutOfBounds):
+ (testInt32ArrayEarlyOutOfBounds):
+ (testIndexingTypeChangesOnInt32Array):
+ (opaqueGetByValOnStringArrayHotOutOfBounds):
+ (testStringArrayHotOutOfBounds):
+ (testIndexingTypeChangesOnStringArray):
+ (opaqueGetByValOnStringAndInt32ArrayHotOutOfBounds):
+ (testStringAndInt32ArrayHotOutOfBounds):
+ (opaqueGetByValOnDoubleArrayHotOutOfBounds):
+ * tests/stress/put-by-val-out-of-bounds-basics.js: Added.
+ (opaquePutByValOnInt32ArrayEarlyOutOfBounds):
+ (testInt32ArrayEarlyOutOfBounds):
+ (opaquePutByValOnStringArrayHotOutOfBounds):
+ (testStringArrayHotOutOfBounds):
+
2015-06-03 Filip Pizlo <fpi...@apple.com>
Simplify unboxing of double JSValues known to be not NaN and not Int32
Modified: trunk/Source/_javascript_Core/bytecode/ArrayProfile.cpp (185239 => 185240)
--- trunk/Source/_javascript_Core/bytecode/ArrayProfile.cpp 2015-06-05 04:59:28 UTC (rev 185239)
+++ trunk/Source/_javascript_Core/bytecode/ArrayProfile.cpp 2015-06-05 05:20:45 UTC (rev 185240)
@@ -94,12 +94,18 @@
out.print(comma, "Float64ArrayMode");
}
-void ArrayProfile::computeUpdatedPrediction(const ConcurrentJITLocker&, CodeBlock* codeBlock)
+void ArrayProfile::computeUpdatedPrediction(const ConcurrentJITLocker& locker, CodeBlock* codeBlock)
{
if (!m_lastSeenStructureID)
return;
Structure* lastSeenStructure = codeBlock->heap()->structureIDTable().get(m_lastSeenStructureID);
+ computeUpdatedPrediction(locker, codeBlock, lastSeenStructure);
+ m_lastSeenStructureID = 0;
+}
+
+void ArrayProfile::computeUpdatedPrediction(const ConcurrentJITLocker&, CodeBlock* codeBlock, Structure* lastSeenStructure)
+{
m_observedArrayModes |= arrayModeFromStructure(lastSeenStructure);
if (!m_didPerformFirstRunPruning
@@ -114,7 +120,6 @@
if (!globalObject->isOriginalArrayStructure(lastSeenStructure)
&& !globalObject->isOriginalTypedArrayStructure(lastSeenStructure))
m_usesOriginalArrayStructures = false;
- m_lastSeenStructureID = 0;
}
CString ArrayProfile::briefDescription(const ConcurrentJITLocker& locker, CodeBlock* codeBlock)
Modified: trunk/Source/_javascript_Core/bytecode/ArrayProfile.h (185239 => 185240)
--- trunk/Source/_javascript_Core/bytecode/ArrayProfile.h 2015-06-05 04:59:28 UTC (rev 185239)
+++ trunk/Source/_javascript_Core/bytecode/ArrayProfile.h 2015-06-05 05:20:45 UTC (rev 185240)
@@ -209,6 +209,8 @@
StructureID* addressOfLastSeenStructureID() { return &m_lastSeenStructureID; }
ArrayModes* addressOfArrayModes() { return &m_observedArrayModes; }
bool* addressOfMayStoreToHole() { return &m_mayStoreToHole; }
+
+ void setOutOfBounds() { m_outOfBounds = true; }
bool* addressOfOutOfBounds() { return &m_outOfBounds; }
void observeStructure(Structure* structure)
@@ -217,6 +219,7 @@
}
void computeUpdatedPrediction(const ConcurrentJITLocker&, CodeBlock*);
+ void computeUpdatedPrediction(const ConcurrentJITLocker&, CodeBlock*, Structure* lastSeenStructure);
ArrayModes observedArrayModes(const ConcurrentJITLocker&) const { return m_observedArrayModes; }
bool mayInterceptIndexedAccesses(const ConcurrentJITLocker&) const { return m_mayInterceptIndexedAccesses; }
Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (185239 => 185240)
--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2015-06-05 04:59:28 UTC (rev 185239)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2015-06-05 05:20:45 UTC (rev 185240)
@@ -745,7 +745,8 @@
{
ConcurrentJITLocker locker(m_inlineStackTop->m_profiledBlock->m_lock);
profile->computeUpdatedPrediction(locker, m_inlineStackTop->m_profiledBlock);
- return ArrayMode::fromObserved(locker, profile, action, false);
+ bool makeSafe = profile->outOfBounds(locker);
+ return ArrayMode::fromObserved(locker, profile, action, makeSafe);
}
ArrayMode getArrayMode(ArrayProfile* profile)
@@ -753,21 +754,6 @@
return getArrayMode(profile, Array::Read);
}
- ArrayMode getArrayModeConsideringSlowPath(ArrayProfile* profile, Array::Action action)
- {
- ConcurrentJITLocker locker(m_inlineStackTop->m_profiledBlock->m_lock);
-
- profile->computeUpdatedPrediction(locker, m_inlineStackTop->m_profiledBlock);
-
- bool makeSafe =
- m_inlineStackTop->m_profiledBlock->likelyToTakeSlowCase(m_currentIndex)
- || profile->outOfBounds(locker);
-
- ArrayMode result = ArrayMode::fromObserved(locker, profile, action, makeSafe);
-
- return result;
- }
-
Node* makeSafe(Node* node)
{
if (m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, Overflow))
@@ -3099,7 +3085,7 @@
SpeculatedType prediction = getPredictionWithoutOSRExit();
Node* base = get(VirtualRegister(currentInstruction[2].u.operand));
- ArrayMode arrayMode = getArrayModeConsideringSlowPath(currentInstruction[4].u.arrayProfile, Array::Read);
+ ArrayMode arrayMode = getArrayMode(currentInstruction[4].u.arrayProfile, Array::Read);
Node* property = get(VirtualRegister(currentInstruction[3].u.operand));
Node* getByVal = addToGraph(GetByVal, OpInfo(arrayMode.asWord()), OpInfo(prediction), base, property);
set(VirtualRegister(currentInstruction[1].u.operand), getByVal);
@@ -3111,7 +3097,7 @@
case op_put_by_val: {
Node* base = get(VirtualRegister(currentInstruction[1].u.operand));
- ArrayMode arrayMode = getArrayModeConsideringSlowPath(currentInstruction[4].u.arrayProfile, Array::Write);
+ ArrayMode arrayMode = getArrayMode(currentInstruction[4].u.arrayProfile, Array::Write);
Node* property = get(VirtualRegister(currentInstruction[2].u.operand));
Node* value = get(VirtualRegister(currentInstruction[3].u.operand));
@@ -3864,7 +3850,7 @@
case op_has_indexed_property: {
Node* base = get(VirtualRegister(currentInstruction[2].u.operand));
- ArrayMode arrayMode = getArrayModeConsideringSlowPath(currentInstruction[4].u.arrayProfile, Array::Read);
+ ArrayMode arrayMode = getArrayMode(currentInstruction[4].u.arrayProfile, Array::Read);
Node* property = get(VirtualRegister(currentInstruction[3].u.operand));
Node* hasIterableProperty = addToGraph(HasIndexedProperty, OpInfo(arrayMode.asWord()), base, property);
set(VirtualRegister(currentInstruction[1].u.operand), hasIterableProperty);
Modified: trunk/Source/_javascript_Core/jit/CCallHelpers.h (185239 => 185240)
--- trunk/Source/_javascript_Core/jit/CCallHelpers.h 2015-06-05 04:59:28 UTC (rev 185239)
+++ trunk/Source/_javascript_Core/jit/CCallHelpers.h 2015-06-05 05:20:45 UTC (rev 185240)
@@ -734,6 +734,19 @@
addCallArgument(arg6);
}
+ ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, GPRReg arg3, GPRReg arg4, GPRReg arg5, GPRReg arg6, TrustedImmPtr arg7)
+ {
+ resetCallArguments();
+ addCallArgument(GPRInfo::callFrameRegister);
+ addCallArgument(arg1);
+ addCallArgument(arg2);
+ addCallArgument(arg3);
+ addCallArgument(arg4);
+ addCallArgument(arg5);
+ addCallArgument(arg6);
+ addCallArgument(arg7);
+ }
+
ALWAYS_INLINE void setupArgumentsWithExecState(FPRReg arg1, GPRReg arg2)
{
resetCallArguments();
@@ -1466,6 +1479,13 @@
setupArgumentsWithExecState(arg1, arg2, arg3);
}
+ ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, GPRReg arg3, GPRReg arg4, TrustedImmPtr arg5)
+ {
+ poke(arg5, POKE_ARGUMENT_OFFSET + 1);
+ poke(arg4, POKE_ARGUMENT_OFFSET);
+ setupArgumentsWithExecState(arg1, arg2, arg3);
+ }
+
ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, TrustedImmPtr arg2, GPRReg arg3, GPRReg arg4)
{
poke(arg4, POKE_ARGUMENT_OFFSET);
@@ -1726,6 +1746,15 @@
setupArgumentsWithExecState(arg1, arg2, arg3);
}
+ ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, GPRReg arg3, GPRReg arg4, GPRReg arg5, GPRReg arg6, TrustedImmPtr arg7)
+ {
+ poke(arg7, POKE_ARGUMENT_OFFSET + 3);
+ poke(arg6, POKE_ARGUMENT_OFFSET + 2);
+ poke(arg5, POKE_ARGUMENT_OFFSET + 1);
+ poke(arg4, POKE_ARGUMENT_OFFSET);
+ setupArgumentsWithExecState(arg1, arg2, arg3);
+ }
+
ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, TrustedImmPtr arg3, GPRReg arg4, GPRReg arg5)
{
poke(arg5, POKE_ARGUMENT_OFFSET + 1);
@@ -1830,6 +1859,13 @@
setupThreeStubArgsGPR<GPRInfo::argumentGPR1, GPRInfo::argumentGPR3, GPRInfo::argumentGPR4>(arg1, arg3, arg4);
}
+ ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, GPRReg arg3, TrustedImmPtr arg4)
+ {
+ setupThreeStubArgsGPR<GPRInfo::argumentGPR1, GPRInfo::argumentGPR2, GPRInfo::argumentGPR3>(arg1, arg2, arg3);
+ move(arg4, GPRInfo::argumentGPR4);
+ move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+ }
+
ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, TrustedImmPtr arg2, TrustedImm32 arg3, GPRReg arg4)
{
setupTwoStubArgsGPR<GPRInfo::argumentGPR1, GPRInfo::argumentGPR4>(arg1, arg4);
Modified: trunk/Source/_javascript_Core/jit/JIT.h (185239 => 185240)
--- trunk/Source/_javascript_Core/jit/JIT.h 2015-06-05 04:59:28 UTC (rev 185239)
+++ trunk/Source/_javascript_Core/jit/JIT.h 2015-06-05 05:20:45 UTC (rev 185240)
@@ -702,6 +702,7 @@
#endif
MacroAssembler::Call callOperation(J_JITOperation_EJIdc, int, GPRReg, const Identifier*);
MacroAssembler::Call callOperation(J_JITOperation_EJJ, int, GPRReg, GPRReg);
+ MacroAssembler::Call callOperation(J_JITOperation_EJJAp, int, GPRReg, GPRReg, ArrayProfile*);
MacroAssembler::Call callOperation(C_JITOperation_EJsc, GPRReg);
MacroAssembler::Call callOperation(J_JITOperation_EJscC, int, GPRReg, JSCell*);
MacroAssembler::Call callOperation(C_JITOperation_EJscZ, GPRReg, int32_t);
@@ -743,6 +744,7 @@
MacroAssembler::Call callOperation(V_JITOperation_ESsiJJI, StructureStubInfo*, RegisterID, RegisterID, RegisterID, RegisterID, UniquedStringImpl*);
#endif
MacroAssembler::Call callOperation(V_JITOperation_EJJJ, RegisterID, RegisterID, RegisterID);
+ MacroAssembler::Call callOperation(V_JITOperation_EJJJAp, RegisterID, RegisterID, RegisterID, ArrayProfile*);
MacroAssembler::Call callOperation(V_JITOperation_EJZJ, RegisterID, int32_t, RegisterID);
MacroAssembler::Call callOperation(V_JITOperation_EJZ, RegisterID, int32_t);
MacroAssembler::Call callOperation(V_JITOperation_EPc, Instruction*);
@@ -758,12 +760,14 @@
MacroAssembler::Call callOperation(J_JITOperation_EJ, int, GPRReg, GPRReg);
MacroAssembler::Call callOperation(J_JITOperation_EJIdc, int, GPRReg, GPRReg, const Identifier*);
MacroAssembler::Call callOperation(J_JITOperation_EJJ, int, GPRReg, GPRReg, GPRReg, GPRReg);
+ MacroAssembler::Call callOperation(J_JITOperation_EJJAp, int, GPRReg, GPRReg, GPRReg, GPRReg, ArrayProfile*);
MacroAssembler::Call callOperation(P_JITOperation_EJS, GPRReg, GPRReg, size_t);
MacroAssembler::Call callOperation(S_JITOperation_EJ, RegisterID, RegisterID);
MacroAssembler::Call callOperation(S_JITOperation_EJJ, RegisterID, RegisterID, RegisterID, RegisterID);
MacroAssembler::Call callOperation(V_JITOperation_EZSymtabJ, int, SymbolTable*, RegisterID, RegisterID);
MacroAssembler::Call callOperation(V_JITOperation_EJ, RegisterID, RegisterID);
MacroAssembler::Call callOperation(V_JITOperation_EJJJ, RegisterID, RegisterID, RegisterID, RegisterID, RegisterID, RegisterID);
+ MacroAssembler::Call callOperation(V_JITOperation_EJJJAp, RegisterID, RegisterID, RegisterID, RegisterID, RegisterID, RegisterID, ArrayProfile*);
MacroAssembler::Call callOperation(V_JITOperation_EJZ, RegisterID, RegisterID, int32_t);
MacroAssembler::Call callOperation(V_JITOperation_EJZJ, RegisterID, RegisterID, int32_t, RegisterID, RegisterID);
MacroAssembler::Call callOperation(V_JITOperation_EZJ, int32_t, RegisterID, RegisterID);
Modified: trunk/Source/_javascript_Core/jit/JITInlines.h (185239 => 185240)
--- trunk/Source/_javascript_Core/jit/JITInlines.h 2015-06-05 04:59:28 UTC (rev 185239)
+++ trunk/Source/_javascript_Core/jit/JITInlines.h 2015-06-05 05:20:45 UTC (rev 185240)
@@ -399,6 +399,12 @@
return appendCallWithExceptionCheck(operation);
}
+ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(V_JITOperation_EJJJAp operation, RegisterID regOp1, RegisterID regOp2, RegisterID regOp3, ArrayProfile* arrayProfile)
+{
+ setupArgumentsWithExecState(regOp1, regOp2, regOp3, TrustedImmPtr(arrayProfile));
+ return appendCallWithExceptionCheck(operation);
+}
+
ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(V_JITOperation_EZJ operation, int dst, GPRReg arg)
{
setupArgumentsWithExecState(TrustedImm32(dst), arg);
@@ -441,6 +447,12 @@
return appendCallWithExceptionCheckSetJSValueResult(operation, dst);
}
+ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(J_JITOperation_EJJAp operation, int dst, GPRReg arg1, GPRReg arg2, ArrayProfile* arrayProfile)
+{
+ setupArgumentsWithExecState(arg1, arg2, TrustedImmPtr(arrayProfile));
+ return appendCallWithExceptionCheckSetJSValueResult(operation, dst);
+}
+
ALWAYS_INLINE MacroAssembler::Call JIT::callOperationNoExceptionCheck(V_JITOperation_EJ operation, GPRReg arg1)
{
setupArgumentsWithExecState(arg1);
@@ -570,6 +582,12 @@
return appendCallWithExceptionCheckSetJSValueResult(operation, dst);
}
+ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(J_JITOperation_EJJAp operation, int dst, GPRReg arg1Tag, GPRReg arg1Payload, GPRReg arg2Tag, GPRReg arg2Payload, ArrayProfile* arrayProfile)
+{
+ setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1Payload, arg1Tag, SH4_32BIT_DUMMY_ARG arg2Payload, arg2Tag, TrustedImmPtr(arrayProfile));
+ return appendCallWithExceptionCheckSetJSValueResult(operation, dst);
+}
+
ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(JIT::WithProfileTag, J_JITOperation_EJJ operation, int dst, GPRReg arg1Tag, GPRReg arg1Payload, GPRReg arg2Tag, GPRReg arg2Payload)
{
setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1Payload, arg1Tag, SH4_32BIT_DUMMY_ARG arg2Payload, arg2Tag);
@@ -630,6 +648,12 @@
return appendCallWithExceptionCheck(operation);
}
+ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(V_JITOperation_EJJJAp operation, RegisterID regOp1Tag, RegisterID regOp1Payload, RegisterID regOp2Tag, RegisterID regOp2Payload, RegisterID regOp3Tag, RegisterID regOp3Payload, ArrayProfile* arrayProfile)
+{
+ setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG regOp1Payload, regOp1Tag, SH4_32BIT_DUMMY_ARG regOp2Payload, regOp2Tag, regOp3Payload, regOp3Tag, TrustedImmPtr(arrayProfile));
+ return appendCallWithExceptionCheck(operation);
+}
+
ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(V_JITOperation_EZJ operation, int dst, RegisterID regOp1Tag, RegisterID regOp1Payload)
{
setupArgumentsWithExecState(TrustedImm32(dst), regOp1Payload, regOp1Tag);
Modified: trunk/Source/_javascript_Core/jit/JITOpcodes.cpp (185239 => 185240)
--- trunk/Source/_javascript_Core/jit/JITOpcodes.cpp 2015-06-05 04:59:28 UTC (rev 185239)
+++ trunk/Source/_javascript_Core/jit/JITOpcodes.cpp 2015-06-05 05:20:45 UTC (rev 185240)
@@ -1125,21 +1125,14 @@
linkSlowCaseIfNotJSCell(iter, base); // base cell check
linkSlowCase(iter); // base array check
-
- Jump skipProfiling = jump();
-
linkSlowCase(iter); // vector length check
linkSlowCase(iter); // empty value
- emitArrayProfileOutOfBoundsSpecialCase(profile);
-
- skipProfiling.link(this);
-
Label slowPath = label();
emitGetVirtualRegister(base, regT0);
emitGetVirtualRegister(property, regT1);
- Call call = callOperation(operationHasIndexedPropertyDefault, dst, regT0, regT1);
+ Call call = callOperation(operationHasIndexedPropertyDefault, dst, regT0, regT1, profile);
m_byValCompilationInfo[m_byValInstructionIndex].slowPathTarget = slowPath;
m_byValCompilationInfo[m_byValInstructionIndex].returnAddress = call;
Modified: trunk/Source/_javascript_Core/jit/JITOpcodes32_64.cpp (185239 => 185240)
--- trunk/Source/_javascript_Core/jit/JITOpcodes32_64.cpp 2015-06-05 04:59:28 UTC (rev 185239)
+++ trunk/Source/_javascript_Core/jit/JITOpcodes32_64.cpp 2015-06-05 05:20:45 UTC (rev 185240)
@@ -1122,21 +1122,14 @@
linkSlowCaseIfNotJSCell(iter, base); // base cell check
linkSlowCase(iter); // base array check
-
- Jump skipProfiling = jump();
-
linkSlowCase(iter); // vector length check
linkSlowCase(iter); // empty value
-
- emitArrayProfileOutOfBoundsSpecialCase(profile);
-
- skipProfiling.link(this);
-
+
Label slowPath = label();
emitLoad(base, regT1, regT0);
emitLoad(property, regT3, regT2);
- Call call = callOperation(operationHasIndexedPropertyDefault, dst, regT1, regT0, regT3, regT2);
+ Call call = callOperation(operationHasIndexedPropertyDefault, dst, regT1, regT0, regT3, regT2, profile);
m_byValCompilationInfo[m_byValInstructionIndex].slowPathTarget = slowPath;
m_byValCompilationInfo[m_byValInstructionIndex].returnAddress = call;
Modified: trunk/Source/_javascript_Core/jit/JITOperations.cpp (185239 => 185240)
--- trunk/Source/_javascript_Core/jit/JITOperations.cpp 2015-06-05 04:59:28 UTC (rev 185239)
+++ trunk/Source/_javascript_Core/jit/JITOperations.cpp 2015-06-05 05:20:45 UTC (rev 185240)
@@ -35,6 +35,7 @@
#include "DFGThunks.h"
#include "DFGWorklist.h"
#include "Debugger.h"
+#include "DirectArguments.h"
#include "Error.h"
#include "ErrorHandlingScope.h"
#include "ExceptionFuzz.h"
@@ -56,6 +57,7 @@
#include "PropertyName.h"
#include "Repatch.h"
#include "RepatchBuffer.h"
+#include "ScopedArguments.h"
#include "TestRunnerUtils.h"
#include "TypeProfilerLog.h"
#include <wtf/InlineASM.h>
@@ -497,7 +499,7 @@
base->putDirect(vm, offset, JSValue::decode(value));
}
-static void putByVal(CallFrame* callFrame, JSValue baseValue, JSValue subscript, JSValue value)
+static void putByVal(CallFrame* callFrame, JSValue baseValue, JSValue subscript, JSValue value, ArrayProfile* arrayProfile)
{
VM& vm = callFrame->vm();
if (LIKELY(subscript.isUInt32())) {
@@ -506,8 +508,10 @@
JSObject* object = asObject(baseValue);
if (object->canSetIndexQuickly(i))
object->setIndexQuickly(callFrame->vm(), i, value);
- else
+ else {
+ arrayProfile->setOutOfBounds();
object->methodTable(vm)->putByIndex(object, callFrame, i, value, callFrame->codeBlock()->isStrictMode());
+ }
} else
baseValue.putByIndex(callFrame, i, value, callFrame->codeBlock()->isStrictMode());
} else {
@@ -519,13 +523,20 @@
}
}
-static void directPutByVal(CallFrame* callFrame, JSObject* baseObject, JSValue subscript, JSValue value)
+static void directPutByVal(CallFrame* callFrame, JSObject* baseObject, JSValue subscript, JSValue value, ArrayProfile* arrayProfile)
{
bool isStrictMode = callFrame->codeBlock()->isStrictMode();
if (LIKELY(subscript.isUInt32())) {
// Despite its name, JSValue::isUInt32 will return true only for positive boxed int32_t; all those values are valid array indices.
- ASSERT(isIndex(subscript.asUInt32()));
- baseObject->putDirectIndex(callFrame, subscript.asUInt32(), value, 0, isStrictMode ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow);
+ uint32_t index = subscript.asUInt32();
+ ASSERT(isIndex(index));
+ if (baseObject->canSetIndexQuicklyForPutDirect(index)) {
+ baseObject->setIndexQuickly(callFrame->vm(), index, value);
+ return;
+ }
+
+ arrayProfile->setOutOfBounds();
+ baseObject->putDirectIndex(callFrame, index, value, 0, isStrictMode ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow);
return;
}
@@ -550,7 +561,7 @@
baseObject->putDirect(callFrame->vm(), property, value, slot);
}
}
-void JIT_OPERATION operationPutByVal(ExecState* exec, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue)
+void JIT_OPERATION operationPutByVal(ExecState* exec, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, ArrayProfile* arrayProfile)
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
@@ -569,10 +580,15 @@
ByValInfo& byValInfo = exec->codeBlock()->getByValInfo(bytecodeOffset - 1);
ASSERT(!byValInfo.stubRoutine);
- if (hasOptimizableIndexing(object->structure(vm))) {
+ Structure* structure = object->structure(vm);
+ if (hasOptimizableIndexing(structure)) {
// Attempt to optimize.
- JITArrayMode arrayMode = jitArrayModeForStructure(object->structure(vm));
+ JITArrayMode arrayMode = jitArrayModeForStructure(structure);
if (jitArrayModePermitsPut(arrayMode) && arrayMode != byValInfo.arrayMode) {
+ CodeBlock* codeBlock = exec->codeBlock();
+ ConcurrentJITLocker locker(codeBlock->m_lock);
+ arrayProfile->computeUpdatedPrediction(locker, codeBlock, structure);
+
JIT::compilePutByVal(&vm, exec->codeBlock(), &byValInfo, ReturnAddressPtr(OUR_RETURN_ADDRESS), arrayMode);
didOptimize = true;
}
@@ -592,10 +608,10 @@
}
}
- putByVal(exec, baseValue, subscript, value);
+ putByVal(exec, baseValue, subscript, value, arrayProfile);
}
-void JIT_OPERATION operationDirectPutByVal(ExecState* callFrame, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue)
+void JIT_OPERATION operationDirectPutByVal(ExecState* callFrame, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, ArrayProfile* arrayProfile)
{
VM& vm = callFrame->vm();
NativeCallFrameTracer tracer(&vm, callFrame);
@@ -613,11 +629,16 @@
ASSERT(bytecodeOffset);
ByValInfo& byValInfo = callFrame->codeBlock()->getByValInfo(bytecodeOffset - 1);
ASSERT(!byValInfo.stubRoutine);
-
- if (hasOptimizableIndexing(object->structure(vm))) {
+
+ Structure* structure = object->structure(vm);
+ if (hasOptimizableIndexing(structure)) {
// Attempt to optimize.
- JITArrayMode arrayMode = jitArrayModeForStructure(object->structure(vm));
+ JITArrayMode arrayMode = jitArrayModeForStructure(structure);
if (jitArrayModePermitsPut(arrayMode) && arrayMode != byValInfo.arrayMode) {
+ CodeBlock* codeBlock = callFrame->codeBlock();
+ ConcurrentJITLocker locker(codeBlock->m_lock);
+ arrayProfile->computeUpdatedPrediction(locker, codeBlock, structure);
+
JIT::compileDirectPutByVal(&vm, callFrame->codeBlock(), &byValInfo, ReturnAddressPtr(OUR_RETURN_ADDRESS), arrayMode);
didOptimize = true;
}
@@ -636,10 +657,10 @@
}
}
}
- directPutByVal(callFrame, object, subscript, value);
+ directPutByVal(callFrame, object, subscript, value, arrayProfile);
}
-void JIT_OPERATION operationPutByValGeneric(ExecState* exec, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue)
+void JIT_OPERATION operationPutByValGeneric(ExecState* exec, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, ArrayProfile* arrayProfile)
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
@@ -648,11 +669,11 @@
JSValue subscript = JSValue::decode(encodedSubscript);
JSValue value = JSValue::decode(encodedValue);
- putByVal(exec, baseValue, subscript, value);
+ putByVal(exec, baseValue, subscript, value, arrayProfile);
}
-void JIT_OPERATION operationDirectPutByValGeneric(ExecState* exec, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue)
+void JIT_OPERATION operationDirectPutByValGeneric(ExecState* exec, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, ArrayProfile* arrayProfile)
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
@@ -661,7 +682,7 @@
JSValue subscript = JSValue::decode(encodedSubscript);
JSValue value = JSValue::decode(encodedValue);
RELEASE_ASSERT(baseValue.isObject());
- directPutByVal(exec, asObject(baseValue), subscript, value);
+ directPutByVal(exec, asObject(baseValue), subscript, value, arrayProfile);
}
EncodedJSValue JIT_OPERATION operationCallEval(ExecState* exec, ExecState* execCallee)
@@ -1480,8 +1501,29 @@
}
-static JSValue getByVal(ExecState* exec, JSValue baseValue, JSValue subscript, ReturnAddressPtr returnAddress)
+static bool canAccessArgumentIndexQuickly(JSObject& object, uint32_t index)
{
+ switch (object.structure()->typeInfo().type()) {
+ case DirectArgumentsType: {
+ DirectArguments* directArguments = jsCast<DirectArguments*>(&object);
+ if (directArguments->canAccessArgumentIndexQuicklyInDFG(index))
+ return true;
+ break;
+ }
+ case ScopedArgumentsType: {
+ ScopedArguments* scopedArguments = jsCast<ScopedArguments*>(&object);
+ if (scopedArguments->canAccessArgumentIndexQuicklyInDFG(index))
+ return true;
+ break;
+ }
+ default:
+ break;
+ }
+ return false;
+}
+
+static JSValue getByVal(ExecState* exec, JSValue baseValue, JSValue subscript, ArrayProfile* arrayProfile, ReturnAddressPtr returnAddress)
+{
if (LIKELY(baseValue.isCell() && subscript.isString())) {
VM& vm = exec->vm();
Structure& structure = *baseValue.asCell()->structure(vm);
@@ -1495,10 +1537,21 @@
if (subscript.isUInt32()) {
uint32_t i = subscript.asUInt32();
- if (isJSString(baseValue) && asString(baseValue)->canGetIndex(i)) {
- ctiPatchCallByReturnAddress(exec->codeBlock(), returnAddress, FunctionPtr(operationGetByValString));
- return asString(baseValue)->getIndex(exec, i);
+ if (isJSString(baseValue)) {
+ if (asString(baseValue)->canGetIndex(i)) {
+ ctiPatchCallByReturnAddress(exec->codeBlock(), returnAddress, FunctionPtr(operationGetByValString));
+ return asString(baseValue)->getIndex(exec, i);
+ }
+ arrayProfile->setOutOfBounds();
+ } else if (baseValue.isObject()) {
+ JSObject* object = asObject(baseValue);
+ if (object->canGetIndexQuickly(i))
+ return object->getIndexQuickly(i);
+
+ if (!canAccessArgumentIndexQuickly(*object, i))
+ arrayProfile->setOutOfBounds();
}
+
return baseValue.get(exec, i);
}
@@ -1513,18 +1566,18 @@
extern "C" {
-EncodedJSValue JIT_OPERATION operationGetByValGeneric(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript)
+EncodedJSValue JIT_OPERATION operationGetByValGeneric(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ArrayProfile* arrayProfile)
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
JSValue baseValue = JSValue::decode(encodedBase);
JSValue subscript = JSValue::decode(encodedSubscript);
- JSValue result = getByVal(exec, baseValue, subscript, ReturnAddressPtr(OUR_RETURN_ADDRESS));
+ JSValue result = getByVal(exec, baseValue, subscript, arrayProfile, ReturnAddressPtr(OUR_RETURN_ADDRESS));
return JSValue::encode(result);
}
-EncodedJSValue JIT_OPERATION operationGetByValDefault(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript)
+EncodedJSValue JIT_OPERATION operationGetByValDefault(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ArrayProfile* arrayProfile)
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
@@ -1543,8 +1596,15 @@
if (hasOptimizableIndexing(object->structure(vm))) {
// Attempt to optimize.
- JITArrayMode arrayMode = jitArrayModeForStructure(object->structure(vm));
+ Structure* structure = object->structure(vm);
+ JITArrayMode arrayMode = jitArrayModeForStructure(structure);
if (arrayMode != byValInfo.arrayMode) {
+ // If we reached this case, we got an interesting array mode we did not expect when we compiled.
+ // Let's update the profile to do better next time.
+ CodeBlock* codeBlock = exec->codeBlock();
+ ConcurrentJITLocker locker(codeBlock->m_lock);
+ arrayProfile->computeUpdatedPrediction(locker, codeBlock, structure);
+
JIT::compileGetByVal(&vm, exec->codeBlock(), &byValInfo, ReturnAddressPtr(OUR_RETURN_ADDRESS), arrayMode);
didOptimize = true;
}
@@ -1564,11 +1624,11 @@
}
}
- JSValue result = getByVal(exec, baseValue, subscript, ReturnAddressPtr(OUR_RETURN_ADDRESS));
+ JSValue result = getByVal(exec, baseValue, subscript, arrayProfile, ReturnAddressPtr(OUR_RETURN_ADDRESS));
return JSValue::encode(result);
}
-EncodedJSValue JIT_OPERATION operationHasIndexedPropertyDefault(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript)
+EncodedJSValue JIT_OPERATION operationHasIndexedPropertyDefault(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ArrayProfile* arrayProfile)
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
@@ -1607,11 +1667,17 @@
ctiPatchCallByReturnAddress(exec->codeBlock(), ReturnAddressPtr(OUR_RETURN_ADDRESS), FunctionPtr(operationHasIndexedPropertyGeneric));
}
}
-
- return JSValue::encode(jsBoolean(object->hasProperty(exec, subscript.asUInt32())));
+
+ uint32_t index = subscript.asUInt32();
+ if (object->canGetIndexQuickly(index))
+ return JSValue::encode(JSValue(JSValue::JSTrue));
+
+ if (!canAccessArgumentIndexQuickly(*object, index))
+ arrayProfile->setOutOfBounds();
+ return JSValue::encode(jsBoolean(object->hasProperty(exec, index)));
}
-EncodedJSValue JIT_OPERATION operationHasIndexedPropertyGeneric(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript)
+EncodedJSValue JIT_OPERATION operationHasIndexedPropertyGeneric(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ArrayProfile* arrayProfile)
{
VM& vm = exec->vm();
NativeCallFrameTracer tracer(&vm, exec);
@@ -1622,6 +1688,12 @@
ASSERT(subscript.isUInt32());
JSObject* object = asObject(baseValue);
+ uint32_t index = subscript.asUInt32();
+ if (object->canGetIndexQuickly(index))
+ return JSValue::encode(JSValue(JSValue::JSTrue));
+
+ if (!canAccessArgumentIndexQuickly(*object, index))
+ arrayProfile->setOutOfBounds();
return JSValue::encode(jsBoolean(object->hasProperty(exec, subscript.asUInt32())));
}
Modified: trunk/Source/_javascript_Core/jit/JITOperations.h (185239 => 185240)
--- trunk/Source/_javascript_Core/jit/JITOperations.h 2015-06-05 04:59:28 UTC (rev 185239)
+++ trunk/Source/_javascript_Core/jit/JITOperations.h 2015-06-05 05:20:45 UTC (rev 185240)
@@ -57,6 +57,7 @@
Key:
A: JSArray*
Aap: ArrayAllocationProfile*
+ Ap: ArrayProfile*
C: JSCell*
Cb: CodeBlock*
Cli: CallLinkInfo*
@@ -111,6 +112,7 @@
typedef EncodedJSValue JIT_OPERATION (*J_JITOperation_EJI)(ExecState*, EncodedJSValue, UniquedStringImpl*);
typedef EncodedJSValue JIT_OPERATION (*J_JITOperation_EJIdc)(ExecState*, EncodedJSValue, const Identifier*);
typedef EncodedJSValue JIT_OPERATION (*J_JITOperation_EJJ)(ExecState*, EncodedJSValue, EncodedJSValue);
+typedef EncodedJSValue JIT_OPERATION (*J_JITOperation_EJJAp)(ExecState*, EncodedJSValue, EncodedJSValue, ArrayProfile*);
typedef EncodedJSValue JIT_OPERATION (*J_JITOperation_EJssZ)(ExecState*, JSString*, int32_t);
typedef EncodedJSValue JIT_OPERATION (*J_JITOperation_EJP)(ExecState*, EncodedJSValue, void*);
typedef EncodedJSValue JIT_OPERATION (*J_JITOperation_EP)(ExecState*, void*);
@@ -191,6 +193,7 @@
typedef void JIT_OPERATION (*V_JITOperation_EJIdJ)(ExecState*, EncodedJSValue, Identifier*, EncodedJSValue);
typedef void JIT_OPERATION (*V_JITOperation_EJIdJJ)(ExecState*, EncodedJSValue, Identifier*, EncodedJSValue, EncodedJSValue);
typedef void JIT_OPERATION (*V_JITOperation_EJJJ)(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue);
+typedef void JIT_OPERATION (*V_JITOperation_EJJJAp)(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue, ArrayProfile*);
typedef void JIT_OPERATION (*V_JITOperation_EJPP)(ExecState*, EncodedJSValue, void*, void*);
typedef void JIT_OPERATION (*V_JITOperation_EJZJ)(ExecState*, EncodedJSValue, int32_t, EncodedJSValue);
typedef void JIT_OPERATION (*V_JITOperation_EJZ)(ExecState*, EncodedJSValue, int32_t);
@@ -255,10 +258,10 @@
void JIT_OPERATION operationPutByIdDirectStrictBuildList(ExecState*, StructureStubInfo*, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl*) WTF_INTERNAL;
void JIT_OPERATION operationPutByIdDirectNonStrictBuildList(ExecState*, StructureStubInfo*, EncodedJSValue encodedValue, EncodedJSValue encodedBase, UniquedStringImpl*) WTF_INTERNAL;
void JIT_OPERATION operationReallocateStorageAndFinishPut(ExecState*, JSObject*, Structure*, PropertyOffset, EncodedJSValue) WTF_INTERNAL;
-void JIT_OPERATION operationPutByVal(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue) WTF_INTERNAL;
-void JIT_OPERATION operationDirectPutByVal(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue) WTF_INTERNAL;
-void JIT_OPERATION operationPutByValGeneric(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue) WTF_INTERNAL;
-void JIT_OPERATION operationDirectPutByValGeneric(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue) WTF_INTERNAL;
+void JIT_OPERATION operationPutByVal(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue, ArrayProfile*) WTF_INTERNAL;
+void JIT_OPERATION operationDirectPutByVal(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue, ArrayProfile*) WTF_INTERNAL;
+void JIT_OPERATION operationPutByValGeneric(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue, ArrayProfile*) WTF_INTERNAL;
+void JIT_OPERATION operationDirectPutByValGeneric(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue, ArrayProfile*) WTF_INTERNAL;
EncodedJSValue JIT_OPERATION operationCallEval(ExecState*, ExecState*) WTF_INTERNAL;
char* JIT_OPERATION operationLinkCall(ExecState*, CallLinkInfo*) WTF_INTERNAL;
char* JIT_OPERATION operationLinkPolymorphicCall(ExecState*, CallLinkInfo*) WTF_INTERNAL;
@@ -315,11 +318,11 @@
void JIT_OPERATION operationProfileWillCall(ExecState*, EncodedJSValue) WTF_INTERNAL;
EncodedJSValue JIT_OPERATION operationCheckHasInstance(ExecState*, EncodedJSValue, EncodedJSValue baseVal) WTF_INTERNAL;
JSCell* JIT_OPERATION operationCreateActivation(ExecState*, JSScope* currentScope) WTF_INTERNAL;
-EncodedJSValue JIT_OPERATION operationGetByValDefault(ExecState*, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript) WTF_INTERNAL;
-EncodedJSValue JIT_OPERATION operationGetByValGeneric(ExecState*, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationGetByValDefault(ExecState*, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ArrayProfile*) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationGetByValGeneric(ExecState*, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ArrayProfile*) WTF_INTERNAL;
EncodedJSValue JIT_OPERATION operationGetByValString(ExecState*, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript) WTF_INTERNAL;
-EncodedJSValue JIT_OPERATION operationHasIndexedPropertyDefault(ExecState*, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript) WTF_INTERNAL;
-EncodedJSValue JIT_OPERATION operationHasIndexedPropertyGeneric(ExecState*, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationHasIndexedPropertyDefault(ExecState*, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ArrayProfile*) WTF_INTERNAL;
+EncodedJSValue JIT_OPERATION operationHasIndexedPropertyGeneric(ExecState*, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ArrayProfile*) WTF_INTERNAL;
EncodedJSValue JIT_OPERATION operationDeleteById(ExecState*, EncodedJSValue base, const Identifier*) WTF_INTERNAL;
JSCell* JIT_OPERATION operationGetPNames(ExecState*, JSObject*) WTF_INTERNAL;
EncodedJSValue JIT_OPERATION operationInstanceOf(ExecState*, EncodedJSValue, EncodedJSValue proto) WTF_INTERNAL;
Modified: trunk/Source/_javascript_Core/jit/JITPropertyAccess.cpp (185239 => 185240)
--- trunk/Source/_javascript_Core/jit/JITPropertyAccess.cpp 2015-06-05 04:59:28 UTC (rev 185239)
+++ trunk/Source/_javascript_Core/jit/JITPropertyAccess.cpp 2015-06-05 05:20:45 UTC (rev 185240)
@@ -217,20 +217,14 @@
notString.link(this);
nonCell.link(this);
- Jump skipProfiling = jump();
-
linkSlowCase(iter); // vector length check
linkSlowCase(iter); // empty value
- emitArrayProfileOutOfBoundsSpecialCase(profile);
-
- skipProfiling.link(this);
-
Label slowPath = label();
emitGetVirtualRegister(base, regT0);
emitGetVirtualRegister(property, regT1);
- Call call = callOperation(operationGetByValDefault, dst, regT0, regT1);
+ Call call = callOperation(operationGetByValDefault, dst, regT0, regT1, profile);
m_byValCompilationInfo[m_byValInstructionIndex].slowPathTarget = slowPath;
m_byValCompilationInfo[m_byValInstructionIndex].returnAddress = call;
@@ -430,7 +424,7 @@
emitGetVirtualRegister(property, regT1);
emitGetVirtualRegister(value, regT2);
bool isDirect = m_interpreter->getOpcodeID(currentInstruction->u.opcode) == op_put_by_val_direct;
- Call call = callOperation(isDirect ? operationDirectPutByVal : operationPutByVal, regT0, regT1, regT2);
+ Call call = callOperation(isDirect ? operationDirectPutByVal : operationPutByVal, regT0, regT1, regT2, profile);
m_byValCompilationInfo[m_byValInstructionIndex].slowPathTarget = slowPath;
m_byValCompilationInfo[m_byValInstructionIndex].returnAddress = call;
Modified: trunk/Source/_javascript_Core/jit/JITPropertyAccess32_64.cpp (185239 => 185240)
--- trunk/Source/_javascript_Core/jit/JITPropertyAccess32_64.cpp 2015-06-05 04:59:28 UTC (rev 185239)
+++ trunk/Source/_javascript_Core/jit/JITPropertyAccess32_64.cpp 2015-06-05 05:20:45 UTC (rev 185240)
@@ -257,21 +257,15 @@
failed.link(this);
notString.link(this);
nonCell.link(this);
-
- Jump skipProfiling = jump();
linkSlowCase(iter); // vector length check
linkSlowCase(iter); // empty value
- emitArrayProfileOutOfBoundsSpecialCase(profile);
-
- skipProfiling.link(this);
-
Label slowPath = label();
emitLoad(base, regT1, regT0);
emitLoad(property, regT3, regT2);
- Call call = callOperation(operationGetByValDefault, dst, regT1, regT0, regT3, regT2);
+ Call call = callOperation(operationGetByValDefault, dst, regT1, regT0, regT3, regT2, profile);
m_byValCompilationInfo[m_byValInstructionIndex].slowPathTarget = slowPath;
m_byValCompilationInfo[m_byValInstructionIndex].returnAddress = call;
@@ -464,6 +458,7 @@
emitLoad(value, regT0, regT1);
addCallArgument(regT1);
addCallArgument(regT0);
+ addCallArgument(TrustedImmPtr(profile));
Call call = appendCallWithExceptionCheck(isDirect ? operationDirectPutByVal : operationPutByVal);
#else
// The register selection below is chosen to reduce register swapping on ARM.
@@ -471,7 +466,7 @@
emitLoad(base, regT2, regT1);
emitLoad(property, regT3, regT0);
emitLoad(value, regT5, regT4);
- Call call = callOperation(isDirect ? operationDirectPutByVal : operationPutByVal, regT2, regT1, regT3, regT0, regT5, regT4);
+ Call call = callOperation(isDirect ? operationDirectPutByVal : operationPutByVal, regT2, regT1, regT3, regT0, regT5, regT4, profile);
#endif
m_byValCompilationInfo[m_byValInstructionIndex].slowPathTarget = slowPath;
Modified: trunk/Source/_javascript_Core/runtime/DirectArguments.h (185239 => 185240)
--- trunk/Source/_javascript_Core/runtime/DirectArguments.h 2015-06-05 04:59:28 UTC (rev 185239)
+++ trunk/Source/_javascript_Core/runtime/DirectArguments.h 2015-06-05 05:20:45 UTC (rev 185240)
@@ -75,7 +75,12 @@
{
return i < m_length && (!m_overrides || !m_overrides.get()[i]);
}
-
+
+ bool canAccessArgumentIndexQuicklyInDFG(uint32_t i) const
+ {
+ return i < m_length && !overrodeThings();
+ }
+
JSValue getIndexQuickly(uint32_t i) const
{
ASSERT_WITH_SECURITY_IMPLICATION(canAccessIndexQuickly(i));
Modified: trunk/Source/_javascript_Core/runtime/JSPromiseFunctions.cpp (185239 => 185240)
--- trunk/Source/_javascript_Core/runtime/JSPromiseFunctions.cpp 2015-06-05 04:59:28 UTC (rev 185239)
+++ trunk/Source/_javascript_Core/runtime/JSPromiseFunctions.cpp 2015-06-05 05:20:45 UTC (rev 185240)
@@ -29,8 +29,7 @@
#if ENABLE(PROMISES)
#include "Error.h"
-#include "JSCJSValueInlines.h"
-#include "JSCellInlines.h"
+#include "JSCInlines.h"
#include "JSPromise.h"
#include "JSPromiseConstructor.h"
#include "JSPromiseDeferred.h"
Modified: trunk/Source/_javascript_Core/runtime/ScopedArguments.h (185239 => 185240)
--- trunk/Source/_javascript_Core/runtime/ScopedArguments.h 2015-06-05 04:59:28 UTC (rev 185239)
+++ trunk/Source/_javascript_Core/runtime/ScopedArguments.h 2015-06-05 05:20:45 UTC (rev 185240)
@@ -80,6 +80,11 @@
return !!m_table->get(i);
return !!overflowStorage()[i - namedLength].get();
}
+
+ bool canAccessArgumentIndexQuicklyInDFG(uint32_t i) const
+ {
+ return canAccessIndexQuickly(i);
+ }
JSValue getIndexQuickly(uint32_t i) const
{
Added: trunk/Source/_javascript_Core/tests/stress/get-by-val-out-of-bounds-basics.js (0 => 185240)
--- trunk/Source/_javascript_Core/tests/stress/get-by-val-out-of-bounds-basics.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/get-by-val-out-of-bounds-basics.js 2015-06-05 05:20:45 UTC (rev 185240)
@@ -0,0 +1,221 @@
+// Get early out-of-bound data.
+function opaqueGetByValOnInt32ArrayEarlyOutOfBounds(array, index)
+{
+ return array[index];
+}
+noInline(opaqueGetByValOnInt32ArrayEarlyOutOfBounds);
+
+function testInt32ArrayEarlyOutOfBounds()
+{
+ // Warm up with an immediate out of bounds.
+ var int32Array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
+ for (var i = 0; i <= 10; ++i) {
+ var value = opaqueGetByValOnInt32ArrayEarlyOutOfBounds(int32Array, i);
+ if ((i < 10 && value !== i) || (i >= 10 && value !== undefined))
+ throw "Failed opaqueGetByValOnInt32ArrayEarlyOutOfBounds(int32Array, i) warmup with i = " + i + " value = " + value;
+ }
+
+ // We then do plenty of in-bounds accesses.
+ for (var i = 0; i < 1e4; ++i) {
+ for (var j = 0; j < 10; ++j) {
+ var value = opaqueGetByValOnInt32ArrayEarlyOutOfBounds(int32Array, j);
+ if (j < 10 && value !== j)
+ throw "Failed opaqueGetByValOnInt32ArrayEarlyOutOfBounds(int32Array, j) in-bounds with j = " + j + " value = " + value;
+ }
+ }
+
+ // Followed by plenty of out-of-bounds accesses.
+ for (var i = 0; i < 1e4; ++i) {
+ for (var j = 0; j <= 10; ++j) {
+ var value = opaqueGetByValOnInt32ArrayEarlyOutOfBounds(int32Array, j);
+ if ((j < 10 && value !== j) || (j >= 10 && value !== undefined))
+ throw "Failed opaqueGetByValOnInt32ArrayEarlyOutOfBounds(int32Array, j) out-of-bounds with j = " + j + " value = " + value;
+ }
+ }
+}
+testInt32ArrayEarlyOutOfBounds();
+
+// One more access, with a completely different array type.
+function testIndexingTypeChangesOnInt32Array()
+{
+ var doubleArray = [-0, 5.5, -42.1];
+ var value = opaqueGetByValOnInt32ArrayEarlyOutOfBounds(doubleArray, 0);
+ if (value || 1 / value !== -Infinity)
+ throw "Failed opaqueGetByValOnInt32ArrayEarlyOutOfBounds(doubleArray, 0)";
+ var value = opaqueGetByValOnInt32ArrayEarlyOutOfBounds(doubleArray, 1);
+ if (value !== 5.5)
+ throw "Failed opaqueGetByValOnInt32ArrayEarlyOutOfBounds(doubleArray, 1)";
+ var value = opaqueGetByValOnInt32ArrayEarlyOutOfBounds(doubleArray, 2);
+ if (value !== -42.1)
+ throw "Failed opaqueGetByValOnInt32ArrayEarlyOutOfBounds(doubleArray, 2)";
+}
+testIndexingTypeChangesOnInt32Array();
+
+
+
+// Get out-of-bound data after a thousand run.
+function opaqueGetByValOnStringArrayHotOutOfBounds(array, index)
+{
+ return array[index];
+}
+noInline(opaqueGetByValOnStringArrayHotOutOfBounds);
+
+function testStringArrayHotOutOfBounds()
+{
+ // Warm up with in bounds access.
+ var stringArray = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
+ for (var i = 0; i < 1e2; ++i) {
+ for (var j = 0; j < 10; ++j) {
+ var value = opaqueGetByValOnStringArrayHotOutOfBounds(stringArray, j);
+ if (value !== "" + j)
+ throw "Failed opaqueGetByValOnStringArrayHotOutOfBounds(stringArray, j) in-bounds with j = " + j + " value = " + value;
+ }
+ }
+
+ // Do a single out of bounds after warmup.
+ var value = opaqueGetByValOnStringArrayHotOutOfBounds(stringArray, 10);
+ if (value !== undefined)
+ throw "Failed opaqueGetByValOnStringArrayHotOutOfBounds(stringArray, 10) with i = " + i + " value = " + value;
+
+ // We then do plenty of in-bounds accesses.
+ for (var i = 0; i < 1e3; ++i) {
+ for (var j = 0; j < 10; ++j) {
+ var value = opaqueGetByValOnStringArrayHotOutOfBounds(stringArray, j);
+ if (value !== "" + j)
+ throw "Failed opaqueGetByValOnStringArrayHotOutOfBounds(stringArray, j) in-bounds with j = " + j + " value = " + value;
+ }
+ }
+
+ // Followed by plenty of out-of-bounds accesses.
+ for (var i = 0; i < 1e3; ++i) {
+ for (var j = 0; j <= 10; ++j) {
+ var value = opaqueGetByValOnStringArrayHotOutOfBounds(stringArray, j);
+ if ((j < 10 && value !== "" + j) || (j >= 10 && value !== undefined))
+ throw "Failed opaqueGetByValOnStringArrayHotOutOfBounds(stringArray, j) out-of-bounds with j = " + j + " value = " + value;
+ }
+ }
+}
+testStringArrayHotOutOfBounds();
+
+function testIndexingTypeChangesOnStringArray()
+{
+ var doubleArray = [-0, 5.5, -42.1];
+ var value = opaqueGetByValOnStringArrayHotOutOfBounds(doubleArray, 0);
+ if (value || 1 / value !== -Infinity)
+ throw "Failed opaqueGetByValOnStringArrayHotOutOfBounds(doubleArray, 0)";
+ var value = opaqueGetByValOnStringArrayHotOutOfBounds(doubleArray, 1);
+ if (value !== 5.5)
+ throw "Failed opaqueGetByValOnStringArrayHotOutOfBounds(doubleArray, 1)";
+ var value = opaqueGetByValOnStringArrayHotOutOfBounds(doubleArray, 2);
+ if (value !== -42.1)
+ throw "Failed opaqueGetByValOnStringArrayHotOutOfBounds(doubleArray, 2)";
+}
+testIndexingTypeChangesOnStringArray();
+
+
+
+// Get out-of-bound data after a thousand run, but from a different array type.
+function opaqueGetByValOnStringAndInt32ArrayHotOutOfBounds(array, index)
+{
+ return array[index];
+}
+noInline(opaqueGetByValOnStringAndInt32ArrayHotOutOfBounds);
+
+function testStringAndInt32ArrayHotOutOfBounds()
+{
+ // Warm up with in bounds access.
+ var stringArray = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
+ for (var i = 0; i < 1e2; ++i) {
+ for (var j = 0; j < 10; ++j) {
+ var value = opaqueGetByValOnStringAndInt32ArrayHotOutOfBounds(stringArray, j);
+ if (value !== "" + j)
+ throw "Failed opaqueGetByValOnStringAndInt32ArrayHotOutOfBounds(stringArray, j) in-bounds with j = " + j + " value = " + value;
+ }
+ }
+
+ // Do a single out of bounds after warmup.
+ var int32Array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
+ var value = opaqueGetByValOnStringAndInt32ArrayHotOutOfBounds(int32Array, 10);
+ if (value !== undefined)
+ throw "Failed opaqueGetByValOnStringAndInt32ArrayHotOutOfBounds(stringArray, 10) with i = " + i + " value = " + value;
+
+ // We then do plenty of in-bounds accesses.
+ for (var i = 0; i < 1e3; ++i) {
+ for (var j = 0; j < 10; ++j) {
+ var value = opaqueGetByValOnStringAndInt32ArrayHotOutOfBounds(stringArray, j);
+ if (value !== "" + j)
+ throw "Failed opaqueGetByValOnStringAndInt32ArrayHotOutOfBounds(stringArray, j) in-bounds with j = " + j + " value = " + value;
+
+ var value = opaqueGetByValOnStringAndInt32ArrayHotOutOfBounds(int32Array, j);
+ if (value !== j)
+ throw "Failed opaqueGetByValOnStringAndInt32ArrayHotOutOfBounds(int32Array, j) in-bounds with j = " + j + " value = " + value;
+ }
+ }
+
+ // Followed by plenty of out-of-bounds accesses.
+ for (var i = 0; i < 1e3; ++i) {
+ for (var j = 0; j <= 10; ++j) {
+ var value = opaqueGetByValOnStringAndInt32ArrayHotOutOfBounds(int32Array, j);
+ if ((j < 10 && value !== j) || (j >= 10 && value !== undefined))
+ throw "Failed opaqueGetByValOnStringAndInt32ArrayHotOutOfBounds(int32Array, j) out-of-bounds with j = " + j + " value = " + value;
+
+ var value = opaqueGetByValOnStringAndInt32ArrayHotOutOfBounds(stringArray, j);
+ if ((j < 10 && value !== "" + j) || (j >= 10 && value !== undefined))
+ throw "Failed opaqueGetByValOnStringAndInt32ArrayHotOutOfBounds(stringArray, j) out-of-bounds with j = " + j + " value = " + value;
+ }
+ }
+}
+testStringAndInt32ArrayHotOutOfBounds();
+
+
+// Get out-of-bound data from a hole after a thousand run.
+function opaqueGetByValOnDoubleArrayHotOutOfBounds(array, index)
+{
+ return array[index];
+}
+noInline(opaqueGetByValOnDoubleArrayHotOutOfBounds);
+
+function testStringArrayHotOutOfBounds()
+{
+ // Warm up with in bounds access.
+ var doubleArray = new Array(10);
+ for (var i = 0; i < 10; ++i) {
+ if (i !== 5)
+ doubleArray[i] = i + 0.5;
+ }
+ for (var i = 0; i < 1e2; ++i) {
+ for (var j = 0; j < 10; ++j) {
+ if (j !== 5) {
+ var value = opaqueGetByValOnDoubleArrayHotOutOfBounds(doubleArray, j);
+ if (value !== j + 0.5)
+ throw "Failed opaqueGetByValOnDoubleArrayHotOutOfBounds(doubleArray, j) in-bounds with j = " + j + " value = " + value;
+ }
+ }
+ }
+
+ // Do a single out of bounds after warmup.
+ var value = opaqueGetByValOnDoubleArrayHotOutOfBounds(doubleArray, 5);
+ if (value !== undefined)
+ throw "Failed opaqueGetByValOnDoubleArrayHotOutOfBounds(doubleArray, 5) with i = " + i + " value = " + value;
+
+ // We then do plenty of in-bounds accesses.
+ for (var i = 0; i < 1e3; ++i) {
+ for (var j = 0; j < 10; ++j) {
+ if (j !== 5) {
+ var value = opaqueGetByValOnDoubleArrayHotOutOfBounds(doubleArray, j);
+ if (value !== j + 0.5)
+ throw "Failed opaqueGetByValOnDoubleArrayHotOutOfBounds(doubleArray, j) in-bounds with j = " + j + " value = " + value;
+ }
+ }
+ }
+
+ // Followed by plenty of out-of-bounds accesses.
+ for (var i = 0; i < 1e3; ++i) {
+ for (var j = 0; j < 10; ++j) {
+ var value = opaqueGetByValOnDoubleArrayHotOutOfBounds(doubleArray, j);
+ if ((j !== 5 && value !== j + 0.5) || (j === 10 && value !== undefined))
+ throw "Failed opaqueGetByValOnDoubleArrayHotOutOfBounds(doubleArray, j) out-of-bounds with j = " + j + " value = " + value;
+ }
+ }
+}
+testStringArrayHotOutOfBounds();
\ No newline at end of file
Added: trunk/Source/_javascript_Core/tests/stress/put-by-val-out-of-bounds-basics.js (0 => 185240)
--- trunk/Source/_javascript_Core/tests/stress/put-by-val-out-of-bounds-basics.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/put-by-val-out-of-bounds-basics.js 2015-06-05 05:20:45 UTC (rev 185240)
@@ -0,0 +1,87 @@
+// Put early out-of-bound data.
+function opaquePutByValOnInt32ArrayEarlyOutOfBounds(array, index, value)
+{
+ array[index] = value;
+}
+noInline(opaquePutByValOnInt32ArrayEarlyOutOfBounds);
+
+function testInt32ArrayEarlyOutOfBounds()
+{
+ // Warm up with an immediate out of bounds.
+ var int32Array = new Array(10);
+ for (var i = 0; i < 10; ++i) {
+ opaquePutByValOnInt32ArrayEarlyOutOfBounds(int32Array, i, i);
+ var value = int32Array[i];
+ if (value !== i)
+ throw "Failed opaquePutByValOnInt32ArrayEarlyOutOfBounds(int32Array, i, i) warmup with i = " + i + " value = " + value;
+ }
+ opaquePutByValOnInt32ArrayEarlyOutOfBounds(int32Array, 1042, 1);
+ var value = int32Array[1042];
+ if (value !== 1)
+ throw "Failed opaquePutByValOnInt32ArrayEarlyOutOfBounds(int32Array, 1042, 1) value = " + value;
+
+ var length = int32Array.length;
+ if (int32Array.length !== 1043)
+ throw "Incorrect int32Array.length, length = " + length;
+
+
+ // We then do plenty of in-bounds accesses.
+ for (var i = 0; i < 1e4; ++i) {
+ for (var j = 0; j < 10; ++j) {
+ opaquePutByValOnInt32ArrayEarlyOutOfBounds(int32Array, j, i);
+ var value = int32Array[j];
+ if (value !== i)
+ throw "Failed opaquePutByValOnInt32ArrayEarlyOutOfBounds(int32Array, j, i) in-bounds with i = " + i + " j = " + j + " value = " + value;
+ }
+ }
+}
+testInt32ArrayEarlyOutOfBounds();
+
+
+// Get out-of-bound data after a thousand run.
+function opaquePutByValOnStringArrayHotOutOfBounds(array, index, value)
+{
+ array[index] = value;
+}
+noInline(opaquePutByValOnStringArrayHotOutOfBounds);
+
+function testStringArrayHotOutOfBounds()
+{
+ // Warm up with in bounds access.
+ var stringArray = new Array(10);
+ for (var i = 0; i < 1e2; ++i) {
+ for (var j = 0; j < 10; ++j) {
+ opaquePutByValOnStringArrayHotOutOfBounds(stringArray, j, "" + i);
+ var value = stringArray[j];
+ if (value !== "" + i)
+ throw "Failed opaquePutByValOnStringArrayHotOutOfBounds(stringArray, j, i) in-bounds with i = " + i + " j = " + j + " value = " + value;
+ }
+ }
+
+ // Do a single out of bounds after warmup.
+ opaquePutByValOnStringArrayHotOutOfBounds(stringArray, 513, 42);
+ var value = stringArray[513];
+ if (value !== 42)
+ throw "Failed opaquePutByValOnStringArrayHotOutOfBounds(stringArray, 513, 42), value = " + value;
+
+ // We then do plenty of in-bounds accesses.
+ for (var i = 0; i < 1e3; ++i) {
+ for (var j = 0; j < 10; ++j) {
+ opaquePutByValOnStringArrayHotOutOfBounds(stringArray, j, "" + i);
+ var value = stringArray[j];
+ if (value !== "" + i)
+ throw "Failed opaquePutByValOnStringArrayHotOutOfBounds(stringArray, j, i) in-bounds with i = " + i + " j = " + j + " value = " + value;
+ }
+ }
+
+ // Followed by plenty of out-of-bounds accesses.
+ for (var j = 514; j <= 1025; ++j)
+ opaquePutByValOnStringArrayHotOutOfBounds(stringArray, j, "" + j);
+
+ for (var j = 514; j <= 1025; ++j) {
+ var value = stringArray[j];
+ if (value !== "" + j)
+ throw "Failed opaquePutByValOnStringArrayHotOutOfBounds(stringArray, j, j) in-bounds with j = " + j + " value = " + value;
+ }
+}
+testStringArrayHotOutOfBounds();