Changeset 262354 in webkit
- Timestamp:
- May 30, 2020 8:20:40 PM (4 years ago)
- Location:
- trunk
- Files:
-
- 5 added
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/JSTests/ChangeLog
r262342 r262354 1 2020-05-30 Yusuke Suzuki <ysuzuki@apple.com> 2 3 [JSC] for-in should allocate new temporary register for base 4 https://bugs.webkit.org/show_bug.cgi?id=212519 5 <rdar://problem/63722044> 6 7 Reviewed by Saam Barati. 8 9 * microbenchmarks/has-own-property-for-in-loop-with-heap-variable.js: Added. 10 (assert): 11 (test1.count): 12 (test1): 13 * microbenchmarks/has-own-property-for-in-loop-with-this.js: Added. 14 (assert): 15 (test1.count): 16 (test1): 17 * stress/for-in-body-replace-enumerable.js: Added. 18 (foo): 19 * stress/for-in-enumerable-shadow.js: Added. 20 (assert): 21 (test1.count): 22 (test1): 23 * stress/for-in-enumerable-this-arrow.js: Added. 24 (assert): 25 (test1): 26 1 27 2020-05-29 Yusuke Suzuki <ysuzuki@apple.com> 2 28 -
trunk/Source/JavaScriptCore/ChangeLog
r262353 r262354 1 2020-05-30 Yusuke Suzuki <ysuzuki@apple.com> 2 3 [JSC] for-in should allocate new temporary register for base 4 https://bugs.webkit.org/show_bug.cgi?id=212519 5 <rdar://problem/63722044> 6 7 Reviewed by Saam Barati. 8 9 While r262233 keeps for-in's enumerated object in variable register if possible to use this register for heuristics driving an optimization, 10 for-in body can replace the content of this register during enumeration and confuse enumerator. 11 12 Instead, we record Variable information in StructureForInContext. This allows us to detect patterns using heap-variables too. 13 Further, this patch extends pattern-matching code to support ThisNode too. 14 15 * bytecompiler/BytecodeGenerator.cpp: 16 (JSC::BytecodeGenerator::pushStructureForInScope): 17 * bytecompiler/BytecodeGenerator.h: 18 (JSC::Variable::Variable): 19 (JSC::Variable::isResolved const): 20 (JSC::Variable::symbolTableConstantIndex const): 21 (JSC::Variable::ident const): 22 (JSC::Variable::offset const): 23 (JSC::Variable::isLocal const): 24 (JSC::Variable::local const): 25 (JSC::Variable::isReadOnly const): 26 (JSC::Variable::isSpecial const): 27 (JSC::Variable::isConst const): 28 (JSC::Variable::setIsReadOnly): 29 (JSC::Variable::operator== const): 30 (JSC::StructureForInContext::StructureForInContext): 31 (JSC::StructureForInContext::baseVariable const): 32 (JSC::StructureForInContext::base const): Deleted. 33 * bytecompiler/NodesCodegen.cpp: 34 (JSC::HasOwnPropertyFunctionCallDotNode::emitBytecode): 35 (JSC::ForInNode::emitBytecode): 36 * parser/ASTBuilder.h: 37 (JSC::ASTBuilder::makeFunctionCallNode): 38 * parser/Nodes.h: 39 (JSC::ExpressionNode::isThisNode const): 40 1 41 2020-05-30 Yusuke Suzuki <ysuzuki@apple.com> 2 42 -
trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
r262233 r262354 4534 4534 } 4535 4535 4536 void BytecodeGenerator::pushStructureForInScope(RegisterID* localRegister, RegisterID* indexRegister, RegisterID* propertyRegister, RegisterID* enumeratorRegister, RegisterID* baseRegister)4536 void BytecodeGenerator::pushStructureForInScope(RegisterID* localRegister, RegisterID* indexRegister, RegisterID* propertyRegister, RegisterID* enumeratorRegister, Optional<Variable> baseVariable) 4537 4537 { 4538 4538 if (!localRegister) 4539 4539 return; 4540 4540 unsigned bodyBytecodeStartOffset = instructions().size(); 4541 m_forInContextStack.append(adoptRef(*new StructureForInContext(localRegister, indexRegister, propertyRegister, enumeratorRegister, base Register, bodyBytecodeStartOffset)));4541 m_forInContextStack.append(adoptRef(*new StructureForInContext(localRegister, indexRegister, propertyRegister, enumeratorRegister, baseVariable, bodyBytecodeStartOffset))); 4542 4542 } 4543 4543 -
trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
r262233 r262354 93 93 }; 94 94 95 class Variable { 96 public: 97 enum VariableKind { NormalVariable, SpecialVariable }; 98 99 Variable() = default; 100 101 Variable(const Identifier& ident) 102 : m_ident(ident) 103 , m_local(nullptr) 104 , m_attributes(0) 105 , m_kind(NormalVariable) // This is somewhat meaningless here for this kind of Variable. 106 , m_symbolTableConstantIndex(0) // This is meaningless here for this kind of Variable. 107 , m_isLexicallyScoped(false) 108 { 109 } 110 111 Variable(const Identifier& ident, VarOffset offset, RegisterID* local, unsigned attributes, VariableKind kind, int symbolTableConstantIndex, bool isLexicallyScoped) 112 : m_ident(ident) 113 , m_offset(offset) 114 , m_local(local) 115 , m_attributes(attributes) 116 , m_kind(kind) 117 , m_symbolTableConstantIndex(symbolTableConstantIndex) 118 , m_isLexicallyScoped(isLexicallyScoped) 119 { 120 } 121 122 // If it's unset, then it is a non-locally-scoped variable. If it is set, then it could be 123 // a stack variable, a scoped variable in a local scope, or a variable captured in the 124 // direct arguments object. 125 bool isResolved() const { return !!m_offset; } 126 int symbolTableConstantIndex() const { ASSERT(isResolved() && !isSpecial()); return m_symbolTableConstantIndex; } 127 128 const Identifier& ident() const { return m_ident; } 129 130 VarOffset offset() const { return m_offset; } 131 bool isLocal() const { return m_offset.isStack(); } 132 RegisterID* local() const { return m_local; } 133 134 bool isReadOnly() const { return m_attributes & PropertyAttribute::ReadOnly; } 135 bool isSpecial() const { return m_kind != NormalVariable; } 136 bool isConst() const { return isReadOnly() && m_isLexicallyScoped; } 137 void setIsReadOnly() { m_attributes |= PropertyAttribute::ReadOnly; } 138 139 void dump(PrintStream&) const; 140 141 bool operator==(const Variable& other) const 142 { 143 return m_ident == other.m_ident 144 && m_offset == other.m_offset 145 && m_local == other.m_local 146 && m_attributes == other.m_attributes 147 && m_kind == other.m_kind 148 && m_symbolTableConstantIndex == other.m_symbolTableConstantIndex 149 && m_isLexicallyScoped == other.m_isLexicallyScoped; 150 } 151 152 private: 153 Identifier m_ident; 154 VarOffset m_offset { }; 155 RegisterID* m_local { nullptr }; 156 unsigned m_attributes { 0 }; 157 VariableKind m_kind { NormalVariable }; 158 int m_symbolTableConstantIndex { 0 }; // This is meaningless here for this default NormalVariable kind of Variable. 159 bool m_isLexicallyScoped { false }; 160 }; 161 95 162 // https://tc39.github.io/ecma262/#sec-completion-record-specification-type 96 163 // … … 242 309 using HasOwnPropertyJumpInst = std::tuple<unsigned, unsigned>; 243 310 244 StructureForInContext(RegisterID* localRegister, RegisterID* indexRegister, RegisterID* propertyRegister, RegisterID* enumeratorRegister, RegisterID* base, unsigned bodyBytecodeStartOffset)311 StructureForInContext(RegisterID* localRegister, RegisterID* indexRegister, RegisterID* propertyRegister, RegisterID* enumeratorRegister, Optional<Variable> baseVariable, unsigned bodyBytecodeStartOffset) 245 312 : ForInContext(localRegister, Type::StructureForIn, bodyBytecodeStartOffset) 246 313 , m_indexRegister(indexRegister) 247 314 , m_propertyRegister(propertyRegister) 248 315 , m_enumeratorRegister(enumeratorRegister) 249 , m_base (base)316 , m_baseVariable(baseVariable) 250 317 { 251 318 } … … 254 321 RegisterID* property() const { return m_propertyRegister.get(); } 255 322 RegisterID* enumerator() const { return m_enumeratorRegister.get(); } 256 RegisterID* base() const { return m_base.get(); }323 const Optional<Variable>& baseVariable() const { return m_baseVariable; } 257 324 258 325 void addGetInst(unsigned instIndex, int propertyRegIndex) … … 277 344 RefPtr<RegisterID> m_propertyRegister; 278 345 RefPtr<RegisterID> m_enumeratorRegister; 279 RefPtr<RegisterID> m_base;346 Optional<Variable> m_baseVariable; 280 347 Vector<GetInst> m_getInsts; 281 348 Vector<InInst> m_inInsts; … … 310 377 Ref<Label> start; 311 378 TryData* tryData; 312 };313 314 class Variable {315 public:316 enum VariableKind { NormalVariable, SpecialVariable };317 318 Variable()319 : m_offset()320 , m_local(nullptr)321 , m_attributes(0)322 , m_kind(NormalVariable)323 , m_symbolTableConstantIndex(0) // This is meaningless here for this kind of Variable.324 , m_isLexicallyScoped(false)325 {326 }327 328 Variable(const Identifier& ident)329 : m_ident(ident)330 , m_local(nullptr)331 , m_attributes(0)332 , m_kind(NormalVariable) // This is somewhat meaningless here for this kind of Variable.333 , m_symbolTableConstantIndex(0) // This is meaningless here for this kind of Variable.334 , m_isLexicallyScoped(false)335 {336 }337 338 Variable(const Identifier& ident, VarOffset offset, RegisterID* local, unsigned attributes, VariableKind kind, int symbolTableConstantIndex, bool isLexicallyScoped)339 : m_ident(ident)340 , m_offset(offset)341 , m_local(local)342 , m_attributes(attributes)343 , m_kind(kind)344 , m_symbolTableConstantIndex(symbolTableConstantIndex)345 , m_isLexicallyScoped(isLexicallyScoped)346 {347 }348 349 // If it's unset, then it is a non-locally-scoped variable. If it is set, then it could be350 // a stack variable, a scoped variable in a local scope, or a variable captured in the351 // direct arguments object.352 bool isResolved() const { return !!m_offset; }353 int symbolTableConstantIndex() const { ASSERT(isResolved() && !isSpecial()); return m_symbolTableConstantIndex; }354 355 const Identifier& ident() const { return m_ident; }356 357 VarOffset offset() const { return m_offset; }358 bool isLocal() const { return m_offset.isStack(); }359 RegisterID* local() const { return m_local; }360 361 bool isReadOnly() const { return m_attributes & PropertyAttribute::ReadOnly; }362 bool isSpecial() const { return m_kind != NormalVariable; }363 bool isConst() const { return isReadOnly() && m_isLexicallyScoped; }364 void setIsReadOnly() { m_attributes |= PropertyAttribute::ReadOnly; }365 366 void dump(PrintStream&) const;367 368 private:369 Identifier m_ident;370 VarOffset m_offset;371 RegisterID* m_local;372 unsigned m_attributes;373 VariableKind m_kind;374 int m_symbolTableConstantIndex;375 bool m_isLexicallyScoped;376 379 }; 377 380 … … 1026 1029 void pushIndexedForInScope(RegisterID* local, RegisterID* index); 1027 1030 void popIndexedForInScope(RegisterID* local); 1028 void pushStructureForInScope(RegisterID* local, RegisterID* index, RegisterID* property, RegisterID* enumerator, RegisterID*base);1031 void pushStructureForInScope(RegisterID* local, RegisterID* index, RegisterID* property, RegisterID* enumerator, Optional<Variable> base); 1029 1032 void popStructureForInScope(RegisterID* local); 1030 1033 -
trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
r262233 r262354 1853 1853 } 1854 1854 1855 if (structureContext && structureContext->base() == base.get()) { 1855 auto canUseFastHasOwnProperty = [&] { 1856 if (!structureContext) 1857 return false; 1858 if (!structureContext->baseVariable()) 1859 return false; 1860 if (m_base->isResolveNode()) 1861 return generator.variable(static_cast<ResolveNode*>(m_base)->identifier()) == structureContext->baseVariable().value(); 1862 if (m_base->isThisNode()) { 1863 // After generator.ensureThis (which must be invoked in |base|'s materialization), we can ensure that |this| is in local this-register. 1864 ASSERT(base); 1865 return generator.variable(generator.propertyNames().thisIdentifier, ThisResolutionType::Local) == structureContext->baseVariable().value(); 1866 } 1867 return false; 1868 }; 1869 1870 if (canUseFastHasOwnProperty()) { 1871 // It is possible that base register is variable and each for-in body replaces JS object in the base register with a different one. 1872 // Even though, this is OK since HasOwnStructureProperty will reject the replaced JS object. 1856 1873 Ref<Label> realCall = generator.newLabel(); 1857 1874 Ref<Label> end = generator.newLabel(); … … 3748 3765 generator.emitNode(generator.ignoredResult(), m_lexpr); 3749 3766 3767 RefPtr<RegisterID> base = generator.newTemporary(); 3750 3768 RefPtr<RegisterID> length; 3751 3769 RefPtr<RegisterID> enumerator; 3752 3770 3753 RefPtr<RegisterID> base = generator.emitNode(m_expr);3771 generator.emitNode(base.get(), m_expr); 3754 3772 RefPtr<RegisterID> local = this->tryGetBoundLocal(generator); 3755 3773 RefPtr<RegisterID> enumeratorIndex; 3774 3775 Optional<Variable> baseVariable; 3776 if (m_expr->isResolveNode()) 3777 baseVariable = generator.variable(static_cast<ResolveNode*>(m_expr)->identifier()); 3778 else if (m_expr->isThisNode()) { 3779 // After generator.ensureThis (which must be invoked in |base|'s materialization), we can ensure that |this| is in local this-register. 3780 ASSERT(base); 3781 baseVariable = generator.variable(generator.propertyNames().thisIdentifier, ThisResolutionType::Local); 3782 } 3756 3783 3757 3784 // Pause at the assignment expression for each for..in iteration. … … 3829 3856 generator.emitProfileControlFlow(profilerStartOffset); 3830 3857 3831 generator.pushStructureForInScope(local.get(), enumeratorIndex.get(), propertyName.get(), enumerator.get(), base .get());3858 generator.pushStructureForInScope(local.get(), enumeratorIndex.get(), propertyName.get(), enumerator.get(), baseVariable); 3832 3859 generator.emitNode(dst, m_statement); 3833 3860 generator.popStructureForInScope(local.get()); -
trunk/Source/JavaScriptCore/parser/ASTBuilder.h
r262233 r262354 1443 1443 // FIXME: This check is only needed because we haven't taught the bytecode generator to inline 1444 1444 // Reflect.apply yet. See https://bugs.webkit.org/show_bug.cgi?id=190668. 1445 if (!dot->base()->isResolveNode() || static_cast<ResolveNode*>(dot->base())->identifier() != "Reflect")1445 if (!dot->base()->isResolveNode() || static_cast<ResolveNode*>(dot->base())->identifier() != m_vm.propertyNames->Reflect) 1446 1446 node = new (m_parserArena) ApplyFunctionCallDotNode(location, dot->base(), dot->identifier(), args, divot, divotStart, divotEnd, callOrApplyChildDepth); 1447 1447 } else if (!previousBaseWasSuper … … 1451 1451 && args->m_listNode->m_expr->isResolveNode() 1452 1452 && !args->m_listNode->m_next 1453 && dot->base()->isResolveNode() 1454 && static_cast<ResolveNode*>(dot->base())->identifier() != "Reflect") { 1453 && ((dot->base()->isResolveNode() && static_cast<ResolveNode*>(dot->base())->identifier() != m_vm.propertyNames->Reflect) || dot->base()->isThisNode())) { 1455 1454 // We match the AST pattern: 1456 // <resolveNode >.hasOwnProperty(<resolveNode>)1455 // <resolveNode|thisNode>.hasOwnProperty(<resolveNode>) 1457 1456 // i.e: 1458 1457 // o.hasOwnProperty(p) -
trunk/Source/JavaScriptCore/parser/Nodes.h
r262233 r262354 201 201 virtual bool isSubtract() const { return false; } 202 202 virtual bool isBoolean() const { return false; } 203 virtual bool isThisNode() const { return false; } 203 204 virtual bool isSpreadExpression() const { return false; } 204 205 virtual bool isSuperNode() const { return false; } … … 613 614 614 615 private: 616 bool isThisNode() const final { return true; } 615 617 RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = nullptr) final; 616 618 };
Note: See TracChangeset
for help on using the changeset viewer.