Diff
Modified: trunk/JSTests/ChangeLog (225201 => 225202)
--- trunk/JSTests/ChangeLog 2017-11-27 23:51:21 UTC (rev 225201)
+++ trunk/JSTests/ChangeLog 2017-11-28 00:14:07 UTC (rev 225202)
@@ -1,3 +1,17 @@
+2017-11-27 Saam Barati <sbar...@apple.com>
+
+ Spread can escape when CreateRest does not
+ https://bugs.webkit.org/show_bug.cgi?id=180057
+ <rdar://problem/35676119>
+
+ Reviewed by JF Bastien.
+
+ * stress/spread-escapes-but-create-rest-does-not.js: Added.
+ (assert):
+ (getProperties):
+ (theFunc):
+ (let.obj.valueOf):
+
2017-11-21 Yusuke Suzuki <utatane....@gmail.com>
[DFG] Add NormalizeMapKey DFG IR
Added: trunk/JSTests/stress/spread-escapes-but-create-rest-does-not.js (0 => 225202)
--- trunk/JSTests/stress/spread-escapes-but-create-rest-does-not.js (rev 0)
+++ trunk/JSTests/stress/spread-escapes-but-create-rest-does-not.js 2017-11-28 00:14:07 UTC (rev 225202)
@@ -0,0 +1,35 @@
+function assert(b) {
+ if (!b)
+ throw new Error;
+}
+noInline(assert);
+
+function getProperties(obj) {
+ let properties = [];
+ for (let name of Object.getOwnPropertyNames(obj)) {
+ properties.push(name);
+ }
+ return properties;
+}
+
+function theFunc(obj, index, ...args) {
+ let functions = getProperties(obj);
+ let func = functions[index % functions.length];
+ obj[func](...args);
+}
+
+let o = {};
+let obj = {
+ valueOf: function (x, y) {
+ assert(x === 42);
+ assert(y === o);
+ try {
+ } catch (e) {}
+ }
+};
+
+for (let i = 0; i < 1e5; ++i) {
+ for (let _i = 0; _i < 100; _i++) {
+ }
+ theFunc(obj, 897989, 42, o);
+}
Modified: trunk/Source/_javascript_Core/ChangeLog (225201 => 225202)
--- trunk/Source/_javascript_Core/ChangeLog 2017-11-27 23:51:21 UTC (rev 225201)
+++ trunk/Source/_javascript_Core/ChangeLog 2017-11-28 00:14:07 UTC (rev 225202)
@@ -1,3 +1,28 @@
+2017-11-27 Saam Barati <sbar...@apple.com>
+
+ Spread can escape when CreateRest does not
+ https://bugs.webkit.org/show_bug.cgi?id=180057
+ <rdar://problem/35676119>
+
+ Reviewed by JF Bastien.
+
+ We previously did not handle Spread(PhantomCreateRest) only because I did not
+ think it was possible to generate this IR. I was wrong. We can generate
+ such IR when we have a PutStack(Spread) but nothing escapes the CreateRest.
+ This IR is rare to generate since we normally don't PutStack(Spread) because
+ the SetLocal almost always gets eliminated because of how our bytecode generates
+ op_spread. However, there exists a test case showing it is possible. Supporting
+ this IR pattern in FTLLower is trivial. This patch implements it and rewrites
+ the Validation rule for Spread.
+
+ * dfg/DFGOperations.cpp:
+ * dfg/DFGOperations.h:
+ * dfg/DFGValidate.cpp:
+ * ftl/FTLLowerDFGToB3.cpp:
+ (JSC::FTL::DFG::LowerDFGToB3::compileSpread):
+ * runtime/JSFixedArray.h:
+ (JSC::JSFixedArray::tryCreate):
+
2017-11-27 Don Olmstead <don.olmst...@sony.com>
[CMake][Win] Conditionally select DLL CRT or static CRT
Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.cpp (225201 => 225202)
--- trunk/Source/_javascript_Core/dfg/DFGOperations.cpp 2017-11-27 23:51:21 UTC (rev 225201)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.cpp 2017-11-28 00:14:07 UTC (rev 225202)
@@ -2384,6 +2384,19 @@
return result;
}
+JSCell* operationCreateFixedArray(ExecState* exec, unsigned length)
+{
+ VM& vm = exec->vm();
+ NativeCallFrameTracer tracer(&vm, exec);
+ auto scope = DECLARE_THROW_SCOPE(vm);
+
+ if (JSFixedArray* result = JSFixedArray::tryCreate(vm, vm.fixedArrayStructure.get(), length))
+ return result;
+
+ throwOutOfMemoryError(exec, scope);
+ return nullptr;
+}
+
JSCell* JIT_OPERATION operationSpreadGeneric(ExecState* exec, JSCell* iterable)
{
VM& vm = exec->vm();
Modified: trunk/Source/_javascript_Core/dfg/DFGOperations.h (225201 => 225202)
--- trunk/Source/_javascript_Core/dfg/DFGOperations.h 2017-11-27 23:51:21 UTC (rev 225201)
+++ trunk/Source/_javascript_Core/dfg/DFGOperations.h 2017-11-28 00:14:07 UTC (rev 225202)
@@ -235,6 +235,7 @@
JSCell* JIT_OPERATION operationSpreadFastArray(ExecState*, JSCell*);
JSCell* JIT_OPERATION operationSpreadGeneric(ExecState*, JSCell*);
JSCell* JIT_OPERATION operationNewArrayWithSpreadSlow(ExecState*, void*, uint32_t);
+JSCell* JIT_OPERATION operationCreateFixedArray(ExecState*, unsigned length);
JSCell* JIT_OPERATION operationResolveScope(ExecState*, JSScope*, UniquedStringImpl*);
EncodedJSValue JIT_OPERATION operationResolveScopeForHoistingFuncDeclInEval(ExecState*, JSScope*, UniquedStringImpl*);
Modified: trunk/Source/_javascript_Core/dfg/DFGValidate.cpp (225201 => 225202)
--- trunk/Source/_javascript_Core/dfg/DFGValidate.cpp 2017-11-27 23:51:21 UTC (rev 225201)
+++ trunk/Source/_javascript_Core/dfg/DFGValidate.cpp 2017-11-28 00:14:07 UTC (rev 225202)
@@ -790,6 +790,10 @@
break;
}
+ case Spread:
+ VALIDATE((node), !node->child1()->isPhantomAllocation() || node->child1()->op() == PhantomCreateRest);
+ break;
+
case EntrySwitch:
VALIDATE((node), node->entrySwitchData()->cases.size() == m_graph.m_numberOfEntrypoints);
break;
Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp (225201 => 225202)
--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2017-11-27 23:51:21 UTC (rev 225201)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToB3.cpp 2017-11-28 00:14:07 UTC (rev 225202)
@@ -5273,13 +5273,56 @@
void compileSpread()
{
- // It would be trivial to support this, but for now, we never create
- // IR that would necessitate this. The reason is that Spread is only
- // consumed by NewArrayWithSpread and Varargs operations. And it is
- // never anything else. Also, any Spread(PhantomCreateRest) will turn
- // into PhantomSpread(PhantomCreateRest).
- RELEASE_ASSERT(m_node->child1()->op() != PhantomCreateRest);
+ if (m_node->child1()->op() == PhantomCreateRest) {
+ // This IR is rare to generate since it requires escaping the Spread
+ // but not the CreateRest. In bytecode, we have only few operations that
+ // accept Spread's result as input. This usually leads to the Spread node not
+ // escaping. However, this can happen if for example we generate a PutStack on
+ // the Spread but nothing escapes the CreateRest.
+ LBasicBlock loopHeader = m_out.newBlock();
+ LBasicBlock loopBody = m_out.newBlock();
+ LBasicBlock slowAllocation = m_out.newBlock();
+ LBasicBlock continuation = m_out.newBlock();
+ LBasicBlock lastNext = m_out.insertNewBlocksBefore(loopHeader);
+ InlineCallFrame* inlineCallFrame = m_node->child1()->origin.semantic.inlineCallFrame;
+ unsigned numberOfArgumentsToSkip = m_node->child1()->numberOfArgumentsToSkip();
+ LValue sourceStart = getArgumentsStart(inlineCallFrame, numberOfArgumentsToSkip);
+ LValue length = getSpreadLengthFromInlineCallFrame(inlineCallFrame, numberOfArgumentsToSkip);
+ static_assert(sizeof(JSValue) == 8 && 1 << 3 == 8, "Assumed in the code below.");
+ LValue size = m_out.add(
+ m_out.shl(m_out.zeroExtPtr(length), m_out.constInt32(3)),
+ m_out.constIntPtr(JSFixedArray::offsetOfData()));
+
+ LValue fastArrayValue = allocateVariableSizedCell<JSFixedArray>(size, m_graph.m_vm.fixedArrayStructure.get(), slowAllocation);
+ m_out.store32(length, fastArrayValue, m_heaps.JSFixedArray_size);
+ ValueFromBlock fastArray = m_out.anchor(fastArrayValue);
+ m_out.jump(loopHeader);
+
+ m_out.appendTo(slowAllocation, loopHeader);
+ ValueFromBlock slowArray = m_out.anchor(vmCall(Int64, m_out.operation(operationCreateFixedArray), m_callFrame, length));
+ m_out.jump(loopHeader);
+
+ m_out.appendTo(loopHeader, loopBody);
+ LValue fixedArray = m_out.phi(Int64, fastArray, slowArray);
+ ValueFromBlock startIndex = m_out.anchor(m_out.constIntPtr(0));
+ m_out.branch(m_out.isZero32(length), unsure(continuation), unsure(loopBody));
+
+ m_out.appendTo(loopBody, continuation);
+ LValue index = m_out.phi(pointerType(), startIndex);
+ LValue value = m_out.load64(
+ m_out.baseIndex(m_heaps.variables, sourceStart, index));
+ m_out.store64(value, m_out.baseIndex(m_heaps.JSFixedArray_buffer, fixedArray, index));
+ LValue nextIndex = m_out.add(m_out.constIntPtr(1), index);
+ m_out.addIncomingToPhi(index, m_out.anchor(nextIndex));
+ m_out.branch(m_out.below(nextIndex, m_out.zeroExtPtr(length)), unsure(loopBody), unsure(continuation));
+
+ m_out.appendTo(continuation, lastNext);
+ mutatorFence();
+ setJSValue(fixedArray);
+ return;
+ }
+
LValue argument = lowCell(m_node->child1());
LValue result;
Modified: trunk/Source/_javascript_Core/runtime/JSFixedArray.h (225201 => 225202)
--- trunk/Source/_javascript_Core/runtime/JSFixedArray.h 2017-11-27 23:51:21 UTC (rev 225201)
+++ trunk/Source/_javascript_Core/runtime/JSFixedArray.h 2017-11-28 00:14:07 UTC (rev 225202)
@@ -43,6 +43,20 @@
return Structure::create(vm, globalObject, prototype, TypeInfo(JSFixedArrayType, StructureFlags), info());
}
+ ALWAYS_INLINE static JSFixedArray* tryCreate(VM& vm, Structure* structure, unsigned size)
+ {
+ Checked<size_t, RecordOverflow> checkedAllocationSize = allocationSize(size);
+ if (UNLIKELY(checkedAllocationSize.hasOverflowed()))
+ return nullptr;
+
+ void* buffer = tryAllocateCell<JSFixedArray>(vm.heap, checkedAllocationSize.unsafeGet());
+ if (UNLIKELY(!buffer))
+ return nullptr;
+ JSFixedArray* result = new (NotNull, buffer) JSFixedArray(vm, structure, size);
+ result->finishCreation(vm);
+ return result;
+ }
+
ALWAYS_INLINE static JSFixedArray* createFromArray(ExecState* exec, VM& vm, JSArray* array)
{
auto throwScope = DECLARE_THROW_SCOPE(vm);
@@ -118,23 +132,6 @@
void copyToArguments(ExecState*, VirtualRegister firstElementDest, unsigned offset, unsigned length);
private:
- unsigned m_size;
-
- ALWAYS_INLINE static JSFixedArray* tryCreate(VM& vm, Structure* structure, unsigned size)
- {
- Checked<size_t, RecordOverflow> checkedAllocationSize = allocationSize(size);
- if (UNLIKELY(checkedAllocationSize.hasOverflowed()))
- return nullptr;
-
- void* buffer = tryAllocateCell<JSFixedArray>(vm.heap, checkedAllocationSize.unsafeGet());
- if (UNLIKELY(!buffer))
- return nullptr;
- JSFixedArray* result = new (NotNull, buffer) JSFixedArray(vm, structure, size);
- result->finishCreation(vm);
- return result;
- }
-
-
JSFixedArray(VM& vm, Structure* structure, unsigned size)
: Base(vm, structure)
, m_size(size)
@@ -143,11 +140,12 @@
buffer()[i].setStartingValue(JSValue());
}
-
static Checked<size_t, RecordOverflow> allocationSize(Checked<size_t, RecordOverflow> numItems)
{
return offsetOfData() + numItems * sizeof(WriteBarrier<Unknown>);
}
+
+ unsigned m_size;
};
} // namespace JSC