Diff
Modified: branches/safari-603-branch/JSTests/ChangeLog (217461 => 217462)
--- branches/safari-603-branch/JSTests/ChangeLog 2017-05-26 00:42:58 UTC (rev 217461)
+++ branches/safari-603-branch/JSTests/ChangeLog 2017-05-26 00:43:19 UTC (rev 217462)
@@ -1,3 +1,12 @@
+2017-05-25 Saam Barati <sbar...@apple.com>
+
+ Cherry-pick r217438. rdar://problem/32385704
+
+ * stress/for-in-invalidation-for-any-write.js: Added.
+ (assert):
+ (test):
+ (test.i):
+
2017-05-24 Jason Marcell <jmarc...@apple.com>
Cherry-pick r217018. rdar://problem/32380054
Added: branches/safari-603-branch/JSTests/stress/for-in-invalidation-for-any-write.js (0 => 217462)
--- branches/safari-603-branch/JSTests/stress/for-in-invalidation-for-any-write.js (rev 0)
+++ branches/safari-603-branch/JSTests/stress/for-in-invalidation-for-any-write.js 2017-05-26 00:43:19 UTC (rev 217462)
@@ -0,0 +1,146 @@
+function assert(b) {
+ if (!b)
+ throw new Error("Bad assertion");
+}
+noInline(assert);
+
+function test(f) {
+ noInline(f);
+ for (let i = 0; i < 1000; ++i)
+ f();
+}
+
+test(function() {
+ let o = {xx: 42};
+ for (let i in o) {
+ for (let j = 0; j < 2; j++) {
+ let r = o[i];
+ if (i === "xx")
+ assert(r === 42);
+ i = function() { }
+ }
+ }
+});
+
+test(function() {
+ let o = {xx: 42};
+ for (let i in {xx: 0}) {
+ for (let j = 0; j < 2; j++) {
+ let r = o[i];
+ if (i === "xx")
+ assert(r === 42);
+ i = new Uint32Array([0, 1, 0x777777, 0, 0]);
+ }
+ }
+});
+
+test(function() {
+ let o = {xx: 42};
+ for (let i in {xx: 0}) {
+ for (let j = 0; j < 2; j++) {
+ let r = o[i];
+ if (i === "xx")
+ assert(r === 42);
+ ([i] = [new Uint32Array([0, 1, 0x777777, 0, 0])]);
+ }
+ }
+});
+
+test(function() {
+ let o = {xx: 42};
+ for (let i in {xx: 0}) {
+ for (let j = 0; j < 2; j++) {
+ let r = o[i];
+ if (i === "xx")
+ assert(r === 42);
+ ({xyz: i} = {xyz: new Uint32Array([0, 1, 0x777777, 0, 0])});
+ }
+ }
+});
+
+test(function() {
+ let o = [1,2,3];
+ let toStringCalls = 0;
+ let first;
+ let num = 0;
+ let total = 0;
+ for (let i in o) {
+ first = true;
+ for (let j = 0; j < 3; j++) {
+ let r = o[i];
+ if (first)
+ assert(r === o[num]);
+ else
+ assert(r === undefined);
+ first = false;
+ i = {
+ toString() {
+ ++toStringCalls;
+ return "hello!";
+ }
+ }
+ }
+ ++num;
+ }
+
+ // Should be called twice per outer for-in loop.
+ assert(toStringCalls === o.length * 2);
+});
+
+test(function() {
+ let o = [1,2,3];
+ let toStringCalls = 0;
+ let first;
+ let num = 0;
+ let total = 0;
+ for (let i in o) {
+ first = true;
+ for (let j = 0; j < 3; j++) {
+ let r = o[i];
+ if (first)
+ assert(r === o[num]);
+ else
+ assert(r === undefined);
+ first = false;
+ ([i] = [{
+ toString() {
+ ++toStringCalls;
+ return "hello!";
+ }
+ }]);
+ }
+ ++num;
+ }
+
+ // Should be called twice per outer for-in loop.
+ assert(toStringCalls === o.length * 2);
+});
+
+test(function() {
+ let o = [1,2,3];
+ let toStringCalls = 0;
+ let first;
+ let num = 0;
+ let total = 0;
+ for (let i in o) {
+ first = true;
+ for (let j = 0; j < 3; j++) {
+ let r = o[i];
+ if (first)
+ assert(r === o[num]);
+ else
+ assert(r === undefined);
+ first = false;
+ ({xyz: i} = {xyz: {
+ toString() {
+ ++toStringCalls;
+ return "hello!";
+ }
+ }});
+ }
+ ++num;
+ }
+
+ // Should be called twice per outer for-in loop.
+ assert(toStringCalls === o.length * 2);
+});
Modified: branches/safari-603-branch/Source/_javascript_Core/ChangeLog (217461 => 217462)
--- branches/safari-603-branch/Source/_javascript_Core/ChangeLog 2017-05-26 00:42:58 UTC (rev 217461)
+++ branches/safari-603-branch/Source/_javascript_Core/ChangeLog 2017-05-26 00:43:19 UTC (rev 217462)
@@ -1,3 +1,34 @@
+2017-05-25 Saam Barati <sbar...@apple.com>
+
+ Cherry-pick r217438. rdar://problem/32385704
+
+ * bytecode/BytecodeList.json:
+ * bytecode/BytecodeUseDef.h:
+ (JSC::computeUsesForBytecodeOffset):
+ (JSC::computeDefsForBytecodeOffset):
+ * bytecode/CodeBlock.cpp:
+ (JSC::CodeBlock::dumpBytecode):
+ * bytecompiler/BytecodeGenerator.cpp:
+ (JSC::BytecodeGenerator::emitGetByVal):
+ (JSC::BytecodeGenerator::popIndexedForInScope):
+ (JSC::BytecodeGenerator::popStructureForInScope):
+ (JSC::BytecodeGenerator::invalidateForInContextForLocal):
+ (JSC::StructureForInContext::finalize):
+ (JSC::IndexedForInContext::finalize):
+ * bytecompiler/BytecodeGenerator.h:
+ (JSC::StructureForInContext::addGetInst):
+ (JSC::IndexedForInContext::addGetInst):
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::parseBlock):
+ * dfg/DFGCapabilities.cpp:
+ (JSC::DFG::capabilityLevel):
+ * jit/JIT.cpp:
+ (JSC::JIT::privateCompileMainPass):
+ * jit/JIT.h:
+ * jit/JITOpcodes.cpp:
+ (JSC::JIT::emit_op_nop):
+ * llint/LowLevelInterpreter.asm:
+
2017-05-24 Jason Marcell <jmarc...@apple.com>
Cherry-pick r217016. rdar://problem/32380054
Modified: branches/safari-603-branch/Source/_javascript_Core/bytecode/BytecodeList.json (217461 => 217462)
--- branches/safari-603-branch/Source/_javascript_Core/bytecode/BytecodeList.json 2017-05-26 00:42:58 UTC (rev 217461)
+++ branches/safari-603-branch/Source/_javascript_Core/bytecode/BytecodeList.json 2017-05-26 00:43:19 UTC (rev 217462)
@@ -152,7 +152,8 @@
{ "name" : "op_yield", "length" : 4 },
{ "name" : "op_watchdog", "length" : 1 },
{ "name" : "op_log_shadow_chicken_prologue", "length" : 2},
- { "name" : "op_log_shadow_chicken_tail", "length" : 3}
+ { "name" : "op_log_shadow_chicken_tail", "length" : 3},
+ { "name" : "op_nop", "length" : 1}
]
},
{
Modified: branches/safari-603-branch/Source/_javascript_Core/bytecode/BytecodeUseDef.h (217461 => 217462)
--- branches/safari-603-branch/Source/_javascript_Core/bytecode/BytecodeUseDef.h 2017-05-26 00:42:58 UTC (rev 217461)
+++ branches/safari-603-branch/Source/_javascript_Core/bytecode/BytecodeUseDef.h 2017-05-26 00:43:19 UTC (rev 217462)
@@ -55,6 +55,7 @@
case op_get_rest_length:
case op_watchdog:
case op_get_argument:
+ case op_nop:
return;
case op_assert:
case op_get_scope:
@@ -363,6 +364,7 @@
case op_log_shadow_chicken_prologue:
case op_log_shadow_chicken_tail:
case op_yield:
+ case op_nop:
#define LLINT_HELPER_OPCODES(opcode, length) case opcode:
FOR_EACH_LLINT_OPCODE_EXTENSION(LLINT_HELPER_OPCODES);
#undef LLINT_HELPER_OPCODES
Modified: branches/safari-603-branch/Source/_javascript_Core/bytecode/CodeBlock.cpp (217461 => 217462)
--- branches/safari-603-branch/Source/_javascript_Core/bytecode/CodeBlock.cpp 2017-05-26 00:42:58 UTC (rev 217461)
+++ branches/safari-603-branch/Source/_javascript_Core/bytecode/CodeBlock.cpp 2017-05-26 00:43:19 UTC (rev 217462)
@@ -1388,6 +1388,10 @@
printLocationAndOp(out, exec, location, it, "watchdog");
break;
}
+ case op_nop: {
+ printLocationAndOp(out, exec, location, it, "nop");
+ break;
+ }
case op_log_shadow_chicken_prologue: {
int r0 = (++it)->u.operand;
printLocationAndOp(out, exec, location, it, "log_shadow_chicken_prologue");
Modified: branches/safari-603-branch/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp (217461 => 217462)
--- branches/safari-603-branch/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp 2017-05-26 00:42:58 UTC (rev 217461)
+++ branches/safari-603-branch/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp 2017-05-26 00:43:19 UTC (rev 217462)
@@ -2775,15 +2775,15 @@
RegisterID* BytecodeGenerator::emitGetByVal(RegisterID* dst, RegisterID* base, RegisterID* property)
{
- for (size_t i = m_forInContextStack.size(); i > 0; i--) {
- ForInContext* context = m_forInContextStack[i - 1].get();
+ for (size_t i = m_forInContextStack.size(); i--; ) {
+ ForInContext* context = m_forInContextStack[i].get();
if (context->local() != property)
continue;
- if (!context->isValid())
- break;
+ unsigned instIndex = instructions().size();
if (context->type() == ForInContext::IndexedForInContextType) {
+ static_cast<IndexedForInContext*>(context)->addGetInst(instIndex, property->index());
property = static_cast<IndexedForInContext*>(context)->index();
break;
}
@@ -2797,6 +2797,8 @@
instructions().append(structureContext->index()->index());
instructions().append(structureContext->enumerator()->index());
instructions().append(profile);
+
+ structureContext->addGetInst(instIndex, property->index(), profile);
return dst;
}
@@ -4594,6 +4596,9 @@
{
if (!localRegister)
return;
+
+ ASSERT(m_forInContextStack.last()->type() == ForInContext::IndexedForInContextType);
+ static_cast<IndexedForInContext*>(m_forInContextStack.last().get())->finalize(*this);
m_forInContextStack.removeLast();
}
@@ -4702,6 +4707,8 @@
{
if (!localRegister)
return;
+ ASSERT(m_forInContextStack.last()->type() == ForInContext::StructureForInContextType);
+ static_cast<StructureForInContext*>(m_forInContextStack.last().get())->finalize(*this);
m_forInContextStack.removeLast();
}
@@ -4718,8 +4725,8 @@
// to perform some flow-sensitive analysis to see if/when the loop iteration variable was
// reassigned, or we'd have to resort to runtime checks to see if the variable had been
// reassigned from its original value.
- for (size_t i = m_forInContextStack.size(); i > 0; i--) {
- ForInContext* context = m_forInContextStack[i - 1].get();
+ for (size_t i = m_forInContextStack.size(); i--; ) {
+ ForInContext* context = m_forInContextStack[i].get();
if (context->local() != localRegister)
continue;
context->invalidate();
@@ -4941,6 +4948,51 @@
emitPutById(generatorRegister(), propertyNames().builtinNames().generatorStatePrivateName(), completedState);
}
+void StructureForInContext::finalize(BytecodeGenerator& generator)
+{
+ if (isValid())
+ return;
+
+ for (const auto& instTuple : m_getInsts) {
+ unsigned instIndex = std::get<0>(instTuple);
+ int propertyRegIndex = std::get<1>(instTuple);
+ UnlinkedValueProfile valueProfile = std::get<2>(instTuple);
+ OpcodeID op = generator.instructions()[instIndex].u.opcode;
+ RELEASE_ASSERT(op == op_get_direct_pname);
+ ASSERT(opcodeLength(op_get_direct_pname) == 7);
+ ASSERT(opcodeLength(op_get_by_val) == 6);
+
+ // 0. Change the opcode to get_by_val.
+ generator.instructions()[instIndex].u.opcode = op_get_by_val;
+ // 1. dst stays the same.
+ // 2. base stays the same.
+ // 3. property gets switched to the original property.
+ generator.instructions()[instIndex + 3].u.operand = propertyRegIndex;
+ // 4. add an array profile.
+ generator.instructions()[instIndex + 4].u.operand = generator.newArrayProfile();
+ // 5. set the result value profile.
+ generator.instructions()[instIndex + 5].u.operand = valueProfile;
+ // 6. nop out the last instruction word.
+ generator.instructions()[instIndex + 6].u.opcode = op_nop;
+ }
+}
+
+void IndexedForInContext::finalize(BytecodeGenerator& generator)
+{
+ if (isValid())
+ return;
+
+ for (const auto& instPair : m_getInsts) {
+ unsigned instIndex = instPair.first;
+ int propertyRegIndex = instPair.second;
+ OpcodeID op = generator.instructions()[instIndex].u.opcode;
+ RELEASE_ASSERT(op == op_get_by_val);
+ // We just need to perform the get_by_val with the original property here,
+ // not the indexed one.
+ generator.instructions()[instIndex + 3].u.operand = propertyRegIndex;
+ }
+}
+
} // namespace JSC
namespace WTF {
Modified: branches/safari-603-branch/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h (217461 => 217462)
--- branches/safari-603-branch/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h 2017-05-26 00:42:58 UTC (rev 217461)
+++ branches/safari-603-branch/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h 2017-05-26 00:43:19 UTC (rev 217462)
@@ -148,10 +148,18 @@
RegisterID* property() const { return m_propertyRegister.get(); }
RegisterID* enumerator() const { return m_enumeratorRegister.get(); }
+ void addGetInst(unsigned instIndex, int propertyRegIndex, UnlinkedValueProfile valueProfile)
+ {
+ m_getInsts.append({ instIndex, propertyRegIndex, valueProfile });
+ }
+
+ void finalize(BytecodeGenerator&);
+
private:
RefPtr<RegisterID> m_indexRegister;
RefPtr<RegisterID> m_propertyRegister;
RefPtr<RegisterID> m_enumeratorRegister;
+ Vector<std::tuple<unsigned, int, UnlinkedValueProfile>> m_getInsts;
};
class IndexedForInContext : public ForInContext {
@@ -169,8 +177,12 @@
RegisterID* index() const { return m_indexRegister.get(); }
+ void finalize(BytecodeGenerator&);
+ void addGetInst(unsigned instIndex, int propertyIndex) { m_getInsts.append({ instIndex, propertyIndex }); }
+
private:
RefPtr<RegisterID> m_indexRegister;
+ Vector<std::pair<unsigned, int>> m_getInsts;
};
struct TryData {
@@ -772,6 +784,7 @@
void popLexicalScope(VariableEnvironmentNode*);
void prepareLexicalScopeForNextForLoopIteration(VariableEnvironmentNode*, RegisterID* loopSymbolTable);
int labelScopeDepth() const;
+ UnlinkedArrayProfile newArrayProfile();
private:
ParserError generate();
@@ -781,7 +794,6 @@
void emitOpcode(OpcodeID);
UnlinkedArrayAllocationProfile newArrayAllocationProfile();
UnlinkedObjectAllocationProfile newObjectAllocationProfile();
- UnlinkedArrayProfile newArrayProfile();
UnlinkedValueProfile emitProfiledOpcode(OpcodeID);
int kill(RegisterID* dst)
{
Modified: branches/safari-603-branch/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (217461 => 217462)
--- branches/safari-603-branch/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2017-05-26 00:42:58 UTC (rev 217461)
+++ branches/safari-603-branch/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2017-05-26 00:43:19 UTC (rev 217462)
@@ -5328,6 +5328,11 @@
addToGraph(CheckWatchdogTimer);
NEXT_OPCODE(op_watchdog);
}
+
+ case op_nop: {
+ addToGraph(Check); // We add a nop here so that basic block linking doesn't break.
+ NEXT_OPCODE(op_nop);
+ }
case op_create_lexical_environment: {
VirtualRegister symbolTableRegister(currentInstruction[3].u.operand);
Modified: branches/safari-603-branch/Source/_javascript_Core/dfg/DFGCapabilities.cpp (217461 => 217462)
--- branches/safari-603-branch/Source/_javascript_Core/dfg/DFGCapabilities.cpp 2017-05-26 00:42:58 UTC (rev 217461)
+++ branches/safari-603-branch/Source/_javascript_Core/dfg/DFGCapabilities.cpp 2017-05-26 00:43:19 UTC (rev 217462)
@@ -188,6 +188,7 @@
case op_jngreatereq:
case op_loop_hint:
case op_watchdog:
+ case op_nop:
case op_ret:
case op_end:
case op_new_object:
Modified: branches/safari-603-branch/Source/_javascript_Core/jit/JIT.cpp (217461 => 217462)
--- branches/safari-603-branch/Source/_javascript_Core/jit/JIT.cpp 2017-05-26 00:42:58 UTC (rev 217461)
+++ branches/safari-603-branch/Source/_javascript_Core/jit/JIT.cpp 2017-05-26 00:43:19 UTC (rev 217462)
@@ -329,6 +329,7 @@
DEFINE_OP(op_jtrue)
DEFINE_OP(op_loop_hint)
DEFINE_OP(op_watchdog)
+ DEFINE_OP(op_nop)
DEFINE_OP(op_lshift)
DEFINE_OP(op_mod)
DEFINE_OP(op_mov)
Modified: branches/safari-603-branch/Source/_javascript_Core/jit/JIT.h (217461 => 217462)
--- branches/safari-603-branch/Source/_javascript_Core/jit/JIT.h 2017-05-26 00:42:58 UTC (rev 217461)
+++ branches/safari-603-branch/Source/_javascript_Core/jit/JIT.h 2017-05-26 00:43:19 UTC (rev 217462)
@@ -531,6 +531,7 @@
void emit_op_jtrue(Instruction*);
void emit_op_loop_hint(Instruction*);
void emit_op_watchdog(Instruction*);
+ void emit_op_nop(Instruction*);
void emit_op_lshift(Instruction*);
void emit_op_mod(Instruction*);
void emit_op_mov(Instruction*);
Modified: branches/safari-603-branch/Source/_javascript_Core/jit/JITOpcodes.cpp (217461 => 217462)
--- branches/safari-603-branch/Source/_javascript_Core/jit/JITOpcodes.cpp 2017-05-26 00:42:58 UTC (rev 217461)
+++ branches/safari-603-branch/Source/_javascript_Core/jit/JITOpcodes.cpp 2017-05-26 00:43:19 UTC (rev 217462)
@@ -956,6 +956,10 @@
callOperation(operationHandleWatchdogTimer);
}
+void JIT::emit_op_nop(Instruction*)
+{
+}
+
void JIT::emit_op_new_regexp(Instruction* currentInstruction)
{
callOperation(operationNewRegexp, currentInstruction[1].u.operand, m_codeBlock->regexp(currentInstruction[2].u.operand));
Modified: branches/safari-603-branch/Source/_javascript_Core/llint/LowLevelInterpreter.asm (217461 => 217462)
--- branches/safari-603-branch/Source/_javascript_Core/llint/LowLevelInterpreter.asm 2017-05-26 00:42:58 UTC (rev 217461)
+++ branches/safari-603-branch/Source/_javascript_Core/llint/LowLevelInterpreter.asm 2017-05-26 00:43:19 UTC (rev 217462)
@@ -1601,6 +1601,10 @@
end
+_llint_op_nop:
+ dispatch(1)
+
+
_llint_op_switch_string:
traceExecution()
callOpcodeSlowPath(_llint_slow_path_switch_string)