Diff
Modified: trunk/Source/_javascript_Core/CMakeLists.txt (184031 => 184032)
--- trunk/Source/_javascript_Core/CMakeLists.txt 2015-05-09 00:13:52 UTC (rev 184031)
+++ trunk/Source/_javascript_Core/CMakeLists.txt 2015-05-09 00:18:43 UTC (rev 184032)
@@ -605,7 +605,6 @@
set(_javascript_Core_LUT_FILES
runtime/ArrayConstructor.cpp
runtime/ArrayIteratorPrototype.cpp
- runtime/ArrayPrototype.cpp
runtime/BooleanPrototype.cpp
runtime/DateConstructor.cpp
runtime/DatePrototype.cpp
Modified: trunk/Source/_javascript_Core/ChangeLog (184031 => 184032)
--- trunk/Source/_javascript_Core/ChangeLog 2015-05-09 00:13:52 UTC (rev 184031)
+++ trunk/Source/_javascript_Core/ChangeLog 2015-05-09 00:18:43 UTC (rev 184032)
@@ -1,3 +1,43 @@
+2015-05-08 Filip Pizlo <[email protected]>
+
+ Extend the SaneChain optimization to Contiguous arrays
+ https://bugs.webkit.org/show_bug.cgi?id=144664
+
+ Reviewed by Mark Lam.
+
+ Previously if you loaded from a hole, you'd either have to take slow path for the array
+ load (which means C++ calls and prototype chain walks) or you'd exit (if you hadn't
+ gathered the necessary profiling yet). But that's unnecessary if we know that the
+ prototype chain is sane - i.e. has no indexed properties. Then we can just return
+ Undefined for the hole.
+
+ Making this change requires setting more watchpoints on the array prototype chain. But
+ that hit a horrible bug: ArrayPrototype still uses the static lookup tables and builds
+ itself up lazily. This means that this increased the number of recompilations we'd get
+ due to the array prototype chain being built up.
+
+ So, this change also removes the laziness and static tables from ArrayPrototype.
+
+ But to make that change, I also had to add a helper for eagerly building up a prototype
+ that has builtin functions.
+
+ * CMakeLists.txt:
+ * DerivedSources.make:
+ * dfg/DFGArrayMode.h:
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode):
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * ftl/FTLLowerDFGToLLVM.cpp:
+ (JSC::FTL::LowerDFGToLLVM::compileGetByVal):
+ * runtime/ArrayPrototype.cpp:
+ (JSC::ArrayPrototype::finishCreation):
+ (JSC::ArrayPrototype::getOwnPropertySlot): Deleted.
+ * runtime/ArrayPrototype.h:
+ * runtime/JSObject.h:
+
2015-05-08 Michael Saboff <[email protected]>
Creating a large MarkedBlock sometimes results in more than one cell in the block
Modified: trunk/Source/_javascript_Core/DerivedSources.make (184031 => 184032)
--- trunk/Source/_javascript_Core/DerivedSources.make 2015-05-09 00:13:52 UTC (rev 184031)
+++ trunk/Source/_javascript_Core/DerivedSources.make 2015-05-09 00:18:43 UTC (rev 184032)
@@ -1,4 +1,4 @@
-# Copyright (C) 2006, 2007, 2008, 2009, 2011, 2013 Apple Inc. All rights reserved.
+# Copyright (C) 2006, 2007, 2008, 2009, 2011, 2013, 2015 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
@@ -37,7 +37,6 @@
all : \
ArrayConstructor.lut.h \
ArrayIteratorPrototype.lut.h \
- ArrayPrototype.lut.h \
BooleanPrototype.lut.h \
DateConstructor.lut.h \
DatePrototype.lut.h \
Modified: trunk/Source/_javascript_Core/dfg/DFGArrayMode.h (184031 => 184032)
--- trunk/Source/_javascript_Core/dfg/DFGArrayMode.h 2015-05-09 00:13:52 UTC (rev 184031)
+++ trunk/Source/_javascript_Core/dfg/DFGArrayMode.h 2015-05-09 00:18:43 UTC (rev 184032)
@@ -89,6 +89,7 @@
enum Speculation {
SaneChain, // In bounds and the array prototype chain is still intact, i.e. loading a hole doesn't require special treatment.
+
InBounds, // In bounds and not loading a hole.
ToHole, // Potentially storing to a hole.
OutOfBounds // Out-of-bounds access and anything can happen.
Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (184031 => 184032)
--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2015-05-09 00:13:52 UTC (rev 184031)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2015-05-09 00:18:43 UTC (rev 184032)
@@ -538,17 +538,61 @@
ArrayMode arrayMode = node->arrayMode();
switch (arrayMode.type()) {
+ case Array::Contiguous:
case Array::Double:
if (arrayMode.arrayClass() == Array::OriginalArray
&& arrayMode.speculation() == Array::InBounds) {
JSGlobalObject* globalObject = m_graph.globalObjectFor(node->origin.semantic);
- if (globalObject->arrayPrototypeChainIsSane()
- && !(node->flags() & NodeBytecodeUsesAsOther)) {
- m_graph.watchpoints().addLazily(
- globalObject->arrayPrototype()->structure()->transitionWatchpointSet());
- m_graph.watchpoints().addLazily(
- globalObject->objectPrototype()->structure()->transitionWatchpointSet());
- node->setArrayMode(arrayMode.withSpeculation(Array::SaneChain));
+ if (globalObject->arrayPrototypeChainIsSane()) {
+ // Check if SaneChain will work on a per-type basis. Note that:
+ //
+ // 1) We don't want double arrays to sometimes return undefined, since
+ // that would require a change to the return type and it would pessimise
+ // things a lot. So, we'd only want to do that if we actually had
+ // evidence that we could read from a hole. That's pretty annoying.
+ // Likely the best way to handle that case is with an equivalent of
+ // SaneChain for OutOfBounds. For now we just detect when Undefined and
+ // NaN are indistinguishable according to backwards propagation, and just
+ // use SaneChain in that case. This happens to catch a lot of cases.
+ //
+ // 2) We don't want int32 array loads to have to do a hole check just to
+ // coerce to Undefined, since that would mean twice the checks.
+ //
+ // This has two implications. First, we have to do more checks than we'd
+ // like. It's unfortunate that we have to do the hole check. Second,
+ // some accesses that hit a hole will now need to take the full-blown
+ // out-of-bounds slow path. We can fix that with:
+ // https://bugs.webkit.org/show_bug.cgi?id=144668
+
+ bool canDoSaneChain = false;
+ switch (arrayMode.type()) {
+ case Array::Contiguous:
+ // This is happens to be entirely natural. We already would have
+ // returned any JSValue, and now we'll return Undefined. We still do
+ // the check but it doesn't require taking any kind of slow path.
+ canDoSaneChain = true;
+ break;
+
+ case Array::Double:
+ if (!(node->flags() & NodeBytecodeUsesAsOther)) {
+ // Holes look like NaN already, so if the user doesn't care
+ // about the difference between Undefined and NaN then we can
+ // do this.
+ canDoSaneChain = true;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (canDoSaneChain) {
+ m_graph.watchpoints().addLazily(
+ globalObject->arrayPrototype()->structure()->transitionWatchpointSet());
+ m_graph.watchpoints().addLazily(
+ globalObject->objectPrototype()->structure()->transitionWatchpointSet());
+ node->setArrayMode(arrayMode.withSpeculation(Array::SaneChain));
+ }
}
}
break;
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (184031 => 184032)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2015-05-09 00:13:52 UTC (rev 184031)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2015-05-09 00:18:43 UTC (rev 184032)
@@ -2309,21 +2309,46 @@
GPRTemporary resultPayload(this);
if (node->arrayMode().type() == Array::Int32) {
+ ASSERT(!node->arrayMode().isSaneChain());
+
speculationCheck(
OutOfBounds, JSValueRegs(), 0,
m_jit.branch32(
MacroAssembler::Equal,
- MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag)),
+ MacroAssembler::BaseIndex(
+ storageReg, propertyReg, MacroAssembler::TimesEight, TagOffset),
TrustedImm32(JSValue::EmptyValueTag)));
- m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload)), resultPayload.gpr());
+ m_jit.load32(
+ MacroAssembler::BaseIndex(
+ storageReg, propertyReg, MacroAssembler::TimesEight, PayloadOffset),
+ resultPayload.gpr());
int32Result(resultPayload.gpr(), node);
break;
}
GPRTemporary resultTag(this);
- m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag)), resultTag.gpr());
- speculationCheck(LoadFromHole, JSValueRegs(), 0, m_jit.branch32(MacroAssembler::Equal, resultTag.gpr(), TrustedImm32(JSValue::EmptyValueTag)));
- m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload)), resultPayload.gpr());
+ m_jit.load32(
+ MacroAssembler::BaseIndex(
+ storageReg, propertyReg, MacroAssembler::TimesEight, TagOffset),
+ resultTag.gpr());
+ m_jit.load32(
+ MacroAssembler::BaseIndex(
+ storageReg, propertyReg, MacroAssembler::TimesEight, PayloadOffset),
+ resultPayload.gpr());
+ if (node->arrayMode().isSaneChain()) {
+ JITCompiler::Jump notHole = m_jit.branch32(
+ MacroAssembler::NotEqual, resultTag.gpr(),
+ TrustedImm32(JSValue::EmptyValueTag));
+ m_jit.move(TrustedImm32(JSValue::UndefinedTag), resultTag.gpr());
+ m_jit.move(TrustedImm32(0), resultPayload.gpr());
+ notHole.link(&m_jit);
+ } else {
+ speculationCheck(
+ LoadFromHole, JSValueRegs(), 0,
+ m_jit.branch32(
+ MacroAssembler::Equal, resultTag.gpr(),
+ TrustedImm32(JSValue::EmptyValueTag)));
+ }
jsValueResult(resultTag.gpr(), resultPayload.gpr(), node);
break;
}
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (184031 => 184032)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2015-05-09 00:13:52 UTC (rev 184031)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2015-05-09 00:18:43 UTC (rev 184032)
@@ -2447,7 +2447,17 @@
GPRTemporary result(this);
m_jit.load64(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight), result.gpr());
- speculationCheck(LoadFromHole, JSValueRegs(), 0, m_jit.branchTest64(MacroAssembler::Zero, result.gpr()));
+ if (node->arrayMode().isSaneChain()) {
+ ASSERT(node->arrayMode().type() == Array::Contiguous);
+ JITCompiler::Jump notHole = m_jit.branchTest64(
+ MacroAssembler::NonZero, result.gpr());
+ m_jit.move(TrustedImm64(JSValue::encode(jsUndefined())), result.gpr());
+ notHole.link(&m_jit);
+ } else {
+ speculationCheck(
+ LoadFromHole, JSValueRegs(), 0,
+ m_jit.branchTest64(MacroAssembler::Zero, result.gpr()));
+ }
jsValueResult(result.gpr(), node, node->arrayMode().type() == Array::Int32 ? DataFormatJSInt32 : DataFormatJS);
break;
}
Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToLLVM.cpp (184031 => 184032)
--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToLLVM.cpp 2015-05-09 00:13:52 UTC (rev 184031)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToLLVM.cpp 2015-05-09 00:18:43 UTC (rev 184032)
@@ -2263,7 +2263,14 @@
if (m_node->arrayMode().isInBounds()) {
LValue result = m_out.load64(baseIndex(heap, storage, index, m_node->child2()));
- speculate(LoadFromHole, noValue(), 0, m_out.isZero64(result));
+ LValue isHole = m_out.isZero64(result);
+ if (m_node->arrayMode().isSaneChain()) {
+ DFG_ASSERT(
+ m_graph, m_node, m_node->arrayMode().type() == Array::Contiguous);
+ result = m_out.select(
+ isHole, m_out.constInt64(JSValue::encode(jsUndefined())), result);
+ } else
+ speculate(LoadFromHole, noValue(), 0, isHole);
setJSValue(result);
return;
}
Modified: trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp (184031 => 184032)
--- trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp 2015-05-09 00:13:52 UTC (rev 184031)
+++ trunk/Source/_javascript_Core/runtime/ArrayPrototype.cpp 2015-05-09 00:18:43 UTC (rev 184032)
@@ -32,12 +32,13 @@
#include "Interpreter.h"
#include "JIT.h"
#include "JSArrayIterator.h"
+#include "JSCBuiltins.h"
+#include "JSCInlines.h"
#include "JSStringBuilder.h"
#include "JSStringJoiner.h"
#include "Lookup.h"
#include "ObjectConstructor.h"
#include "ObjectPrototype.h"
-#include "JSCInlines.h"
#include "StringRecursionChecker.h"
#include <algorithm>
#include <wtf/Assertions.h>
@@ -63,48 +64,10 @@
EncodedJSValue JSC_HOST_CALL arrayProtoFuncKeys(ExecState*);
EncodedJSValue JSC_HOST_CALL arrayProtoFuncEntries(ExecState*);
-}
-
-#include "ArrayPrototype.lut.h"
-
-namespace JSC {
-
// ------------------------------ ArrayPrototype ----------------------------
-const ClassInfo ArrayPrototype::s_info = {"Array", &JSArray::s_info, &arrayPrototypeTable, CREATE_METHOD_TABLE(ArrayPrototype)};
+const ClassInfo ArrayPrototype::s_info = {"Array", &JSArray::s_info, nullptr, CREATE_METHOD_TABLE(ArrayPrototype)};
-/* Source for ArrayPrototype.lut.h
-@begin arrayPrototypeTable 16
- toString arrayProtoFuncToString DontEnum|Function 0
- toLocaleString arrayProtoFuncToLocaleString DontEnum|Function 0
- concat arrayProtoFuncConcat DontEnum|Function 1
- fill arrayProtoFuncFill DontEnum|Function 1
- join arrayProtoFuncJoin DontEnum|Function 1
- pop arrayProtoFuncPop DontEnum|Function 0
- push arrayProtoFuncPush DontEnum|Function 1
- reverse arrayProtoFuncReverse DontEnum|Function 0
- shift arrayProtoFuncShift DontEnum|Function 0
- slice arrayProtoFuncSlice DontEnum|Function 2
- sort arrayProtoFuncSort DontEnum|Function 1
- splice arrayProtoFuncSplice DontEnum|Function 2
- unshift arrayProtoFuncUnShift DontEnum|Function 1
- every arrayProtoFuncEvery DontEnum|Function 1
- forEach arrayProtoFuncForEach DontEnum|Function 1
- some arrayProtoFuncSome DontEnum|Function 1
- indexOf arrayProtoFuncIndexOf DontEnum|Function 1
- lastIndexOf arrayProtoFuncLastIndexOf DontEnum|Function 1
- filter arrayProtoFuncFilter DontEnum|Function 1
- reduce arrayProtoFuncReduce DontEnum|Function 1
- reduceRight arrayProtoFuncReduceRight DontEnum|Function 1
- map arrayProtoFuncMap DontEnum|Function 1
- entries arrayProtoFuncEntries DontEnum|Function 0
- keys arrayProtoFuncKeys DontEnum|Function 0
- find arrayProtoFuncFind DontEnum|Function 1
- findIndex arrayProtoFuncFindIndex DontEnum|Function 1
- includes arrayProtoFuncIncludes DontEnum|Function 1
-@end
-*/
-
ArrayPrototype* ArrayPrototype::create(VM& vm, JSGlobalObject* globalObject, Structure* structure)
{
ArrayPrototype* prototype = new (NotNull, allocateCell<ArrayPrototype>(vm.heap)) ArrayPrototype(vm, structure);
@@ -126,7 +89,35 @@
putDirectWithoutTransition(vm, vm.propertyNames->values, globalObject->arrayProtoValuesFunction(), DontEnum);
putDirectWithoutTransition(vm, vm.propertyNames->iteratorSymbol, globalObject->arrayProtoValuesFunction(), DontEnum);
-
+
+ JSC_NATIVE_FUNCTION(vm.propertyNames->toString, arrayProtoFuncToString, DontEnum, 0);
+ JSC_NATIVE_FUNCTION(vm.propertyNames->toLocaleString, arrayProtoFuncToLocaleString, DontEnum, 0);
+ JSC_NATIVE_FUNCTION("concat", arrayProtoFuncConcat, DontEnum, 1);
+ JSC_BUILTIN_FUNCTION("fill", arrayPrototypeFillCodeGenerator, DontEnum);
+ JSC_NATIVE_FUNCTION(vm.propertyNames->join, arrayProtoFuncJoin, DontEnum, 1);
+ JSC_NATIVE_INTRINSIC_FUNCTION("pop", arrayProtoFuncPop, DontEnum, 0, ArrayPopIntrinsic);
+ JSC_NATIVE_INTRINSIC_FUNCTION("push", arrayProtoFuncPush, DontEnum, 1, ArrayPushIntrinsic);
+ JSC_NATIVE_FUNCTION("reverse", arrayProtoFuncReverse, DontEnum, 0);
+ JSC_NATIVE_FUNCTION("shift", arrayProtoFuncShift, DontEnum, 0);
+ JSC_NATIVE_FUNCTION(vm.propertyNames->slice, arrayProtoFuncSlice, DontEnum, 2);
+ JSC_BUILTIN_FUNCTION("sort", arrayPrototypeSortCodeGenerator, DontEnum);
+ JSC_NATIVE_FUNCTION("splice", arrayProtoFuncSplice, DontEnum, 2);
+ JSC_NATIVE_FUNCTION("unshift", arrayProtoFuncUnShift, DontEnum, 1);
+ JSC_BUILTIN_FUNCTION("every", arrayPrototypeEveryCodeGenerator, DontEnum);
+ JSC_BUILTIN_FUNCTION("forEach", arrayPrototypeForEachCodeGenerator, DontEnum);
+ JSC_BUILTIN_FUNCTION("some", arrayPrototypeSomeCodeGenerator, DontEnum);
+ JSC_NATIVE_FUNCTION("indexOf", arrayProtoFuncIndexOf, DontEnum, 1);
+ JSC_NATIVE_FUNCTION("lastIndexOf", arrayProtoFuncLastIndexOf, DontEnum, 1);
+ JSC_BUILTIN_FUNCTION("filter", arrayPrototypeFilterCodeGenerator, DontEnum);
+ JSC_NATIVE_FUNCTION("reduce", arrayProtoFuncReduce, DontEnum, 1);
+ JSC_NATIVE_FUNCTION("reduceRight", arrayProtoFuncReduceRight, DontEnum, 1);
+ JSC_BUILTIN_FUNCTION("map", arrayPrototypeMapCodeGenerator, DontEnum);
+ JSC_NATIVE_FUNCTION(vm.propertyNames->entries, arrayProtoFuncEntries, DontEnum, 0);
+ JSC_NATIVE_FUNCTION(vm.propertyNames->keys, arrayProtoFuncKeys, DontEnum, 0);
+ JSC_BUILTIN_FUNCTION("find", arrayPrototypeFindCodeGenerator, DontEnum);
+ JSC_BUILTIN_FUNCTION("findIndex", arrayPrototypeFindIndexCodeGenerator, DontEnum);
+ JSC_BUILTIN_FUNCTION("includes", arrayPrototypeIncludesCodeGenerator, DontEnum);
+
if (!globalObject->runtimeFlags().isSymbolDisabled()) {
JSObject* unscopables = constructEmptyObject(globalObject->globalExec(), globalObject->nullPrototypeObjectStructure());
const char* unscopableNames[] = {
@@ -144,11 +135,6 @@
}
}
-bool ArrayPrototype::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
-{
- return getStaticFunctionSlot<JSArray>(exec, arrayPrototypeTable, jsCast<ArrayPrototype*>(object), propertyName, slot);
-}
-
// ------------------------------ Array Functions ----------------------------
// Helper function
Modified: trunk/Source/_javascript_Core/runtime/ArrayPrototype.h (184031 => 184032)
--- trunk/Source/_javascript_Core/runtime/ArrayPrototype.h 2015-05-09 00:13:52 UTC (rev 184031)
+++ trunk/Source/_javascript_Core/runtime/ArrayPrototype.h 2015-05-09 00:18:43 UTC (rev 184032)
@@ -1,6 +1,6 @@
/*
* Copyright (C) 1999-2000 Harri Porten ([email protected])
- * Copyright (C) 2007, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2007, 2011, 2015 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -35,8 +35,6 @@
static ArrayPrototype* create(VM&, JSGlobalObject*, Structure*);
- static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&);
-
DECLARE_INFO;
static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
Modified: trunk/Source/_javascript_Core/runtime/JSObject.h (184031 => 184032)
--- trunk/Source/_javascript_Core/runtime/JSObject.h 2015-05-09 00:13:52 UTC (rev 184031)
+++ trunk/Source/_javascript_Core/runtime/JSObject.h 2015-05-09 00:18:43 UTC (rev 184032)
@@ -1,7 +1,7 @@
/*
* Copyright (C) 1999-2001 Harri Porten ([email protected])
* Copyright (C) 2001 Peter Kelly ([email protected])
- * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2012, 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2003-2009, 2012-2015 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -1557,6 +1557,12 @@
#define JSC_NATIVE_FUNCTION(jsName, cppName, attributes, length) \
JSC_NATIVE_INTRINSIC_FUNCTION(jsName, cppName, (attributes), (length), NoIntrinsic)
+// Identical helpers but for builtins. Note that currently, we don't support builtins that are
+// also intrinsics, but we probably will do that eventually.
+#define JSC_BUILTIN_FUNCTION(jsName, generatorName, attributes) \
+ putDirectBuiltinFunction(\
+ vm, globalObject, makeIdentifier(vm, (jsName)), (generatorName)(vm), (attributes))
+
} // namespace JSC
#endif // JSObject_h