Changeset 199069 in webkit
- Timestamp:
- Apr 5, 2016, 12:58:04 PM (11 years ago)
- Location:
- trunk
- Files:
-
- 5 added
- 15 edited
-
LayoutTests/ChangeLog (modified) (1 diff)
-
LayoutTests/js/regress/megamorphic-load-expected.txt (added)
-
LayoutTests/js/regress/megamorphic-load.html (added)
-
LayoutTests/js/regress/script-tests/megamorphic-load.js (added)
-
LayoutTests/js/regress/string-repeat-not-resolving-no-inline-expected.txt (added)
-
LayoutTests/js/regress/string-repeat-not-resolving-no-inline.html (added)
-
Source/JavaScriptCore/ChangeLog (modified) (1 diff)
-
Source/JavaScriptCore/bytecode/PolymorphicAccess.cpp (modified) (17 diffs)
-
Source/JavaScriptCore/bytecode/PolymorphicAccess.h (modified) (11 diffs)
-
Source/JavaScriptCore/bytecode/StructureStubInfo.cpp (modified) (4 diffs)
-
Source/JavaScriptCore/bytecode/StructureStubInfo.h (modified) (4 diffs)
-
Source/JavaScriptCore/jit/AssemblyHelpers.cpp (modified) (1 diff)
-
Source/JavaScriptCore/jit/AssemblyHelpers.h (modified) (2 diffs)
-
Source/JavaScriptCore/jit/GPRInfo.cpp (modified) (1 diff)
-
Source/JavaScriptCore/jit/GPRInfo.h (modified) (2 diffs)
-
Source/JavaScriptCore/jit/Repatch.cpp (modified) (5 diffs)
-
Source/JavaScriptCore/jit/ThunkGenerators.cpp (modified) (1 diff)
-
Source/JavaScriptCore/runtime/Options.h (modified) (1 diff)
-
Source/JavaScriptCore/runtime/PropertyMapHashTable.h (modified) (7 diffs)
-
Source/JavaScriptCore/runtime/Structure.h (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r199064 r199069 1 2016-04-04 Filip Pizlo <fpizlo@apple.com> 2 3 PolymorphicAccess should have a MegamorphicLoad case 4 https://bugs.webkit.org/show_bug.cgi?id=156182 5 6 Reviewed by Geoffrey Garen and Keith Miller. 7 8 * js/regress/megamorphic-load-expected.txt: Added. 9 * js/regress/megamorphic-load.html: Added. 10 * js/regress/script-tests/megamorphic-load.js: Added. 11 * js/regress/string-repeat-not-resolving-no-inline-expected.txt: Added. 12 * js/regress/string-repeat-not-resolving-no-inline.html: Added. 13 1 14 2016-04-05 Antti Koivisto <antti@apple.com> 2 15 -
trunk/Source/JavaScriptCore/ChangeLog
r199062 r199069 1 2016-04-04 Filip Pizlo <fpizlo@apple.com> 2 3 PolymorphicAccess should have a MegamorphicLoad case 4 https://bugs.webkit.org/show_bug.cgi?id=156182 5 6 Reviewed by Geoffrey Garen and Keith Miller. 7 8 This introduces a new case to PolymorphicAccess called MegamorphicLoad. This inlines the lookup in 9 the PropertyTable. It's cheaper than switching on a huge number of cases and it's cheaper than 10 calling into C++ to do the same job - particularly since inlining the lookup into an access means 11 that we can precompute the hash code. 12 13 When writing the inline code for the hashtable lookup, I found that our hashing algorithm was not 14 optimal. It used a double-hashing method for reducing collision pathologies. This is great for 15 improving the performance of some worst-case scenarios. But this misses the point of a hashtable: we 16 want to optimize the average-case performance. When optimizing for average-case, we can choose to 17 either focus on maximizing the likelihood of the fast case happening, or to minimize the cost of the 18 worst-case, or to minimize the cost of the fast case. Even a very basic hashtable will achieve a high 19 probability of hitting the fast case. So, doing work to reduce the likelihood of a worst-case 20 pathology only makes sense if it also preserves the good performance of the fast case, or reduces the 21 likelihood of the worst-case by so much that it's a win for the average case even with a slow-down in 22 the fast case. 23 24 I don't believe, based on looking at how the double-hashing is implemented, that it's possible that 25 this preserves the good performance of the fast case. It requires at least one more value to be live 26 around the loop, and dramatically increases the register pressure at key points inside the loop. The 27 biggest offender is the doubleHash() method itself. There is no getting around how bad this is: if 28 the compiler live-range-splits that method to death to avoid degrading register pressure elsewhere 29 then we will pay a steep price anytime we take the second iteration around the loop; but if the 30 compiler doesn't split around the call then the hashtable lookup fast path will be full of spills on 31 some architectures (I performed biological register allocation and found that I needed 9 registers 32 for complete lookup, while x86-64 has only 6 callee-saves; OTOH ARM64 has 10 callee-saves so it might 33 be better off). 34 35 Hence, this patch changes the hashtable lookup to use simple linear probing. This was not a slow-down 36 on anything, and it made MegamorphicLoad much more sensible since it is less likely to have to spill. 37 38 There are some other small changes in this patch, like rationalizing the IC's choice between giving 39 up after a repatch (i.e. never trying again) and just pretending that nothing happened (so we can 40 try to repatch again in the future). It looked like the code in Repatch.cpp was set up to be able to 41 choose between those options, but we weren't fully taking advantage of it because the 42 regenerateWithCase() method just returned null for any failure, and didn't say whether it was the 43 sort of failure that renders the inline cache unrepatchable (like memory allocation failure). Now 44 this is all made explicit. I wanted to make sure this change happened in this patch since the 45 MegamorphicLoad code automagically generates a MegamorphicLoad case by coalescing other cases. Since 46 this is intended to avoid blowing out the cache and making it unrepatchable, I wanted to make sure 47 that the rules for giving up were something that made sense to me. 48 49 This is a big win on microbenchmarks. It's neutral on traditional JS benchmarks. It's a slight 50 speed-up for page loading, because many real websites like to have megamorphic property accesses. 51 52 * bytecode/PolymorphicAccess.cpp: 53 (JSC::AccessGenerationResult::dump): 54 (JSC::AccessGenerationState::addWatchpoint): 55 (JSC::AccessCase::get): 56 (JSC::AccessCase::megamorphicLoad): 57 (JSC::AccessCase::replace): 58 (JSC::AccessCase::guardedByStructureCheck): 59 (JSC::AccessCase::couldStillSucceed): 60 (JSC::AccessCase::canBeReplacedByMegamorphicLoad): 61 (JSC::AccessCase::canReplace): 62 (JSC::AccessCase::generateWithGuard): 63 (JSC::AccessCase::generate): 64 (JSC::PolymorphicAccess::PolymorphicAccess): 65 (JSC::PolymorphicAccess::~PolymorphicAccess): 66 (JSC::PolymorphicAccess::regenerateWithCases): 67 (JSC::PolymorphicAccess::regenerateWithCase): 68 (WTF::printInternal): 69 * bytecode/PolymorphicAccess.h: 70 (JSC::AccessCase::isGet): 71 (JSC::AccessCase::isPut): 72 (JSC::AccessCase::isIn): 73 (JSC::AccessGenerationResult::AccessGenerationResult): 74 (JSC::AccessGenerationResult::operator==): 75 (JSC::AccessGenerationResult::operator!=): 76 (JSC::AccessGenerationResult::operator bool): 77 (JSC::AccessGenerationResult::kind): 78 (JSC::AccessGenerationResult::code): 79 (JSC::AccessGenerationResult::madeNoChanges): 80 (JSC::AccessGenerationResult::gaveUp): 81 (JSC::AccessGenerationResult::generatedNewCode): 82 (JSC::PolymorphicAccess::isEmpty): 83 (JSC::AccessGenerationState::AccessGenerationState): 84 * bytecode/StructureStubInfo.cpp: 85 (JSC::StructureStubInfo::aboutToDie): 86 (JSC::StructureStubInfo::addAccessCase): 87 * bytecode/StructureStubInfo.h: 88 * jit/AssemblyHelpers.cpp: 89 (JSC::AssemblyHelpers::emitStoreStructureWithTypeInfo): 90 (JSC::AssemblyHelpers::loadProperty): 91 (JSC::emitRandomThunkImpl): 92 (JSC::AssemblyHelpers::emitRandomThunk): 93 (JSC::AssemblyHelpers::emitLoadStructure): 94 * jit/AssemblyHelpers.h: 95 (JSC::AssemblyHelpers::loadValue): 96 (JSC::AssemblyHelpers::moveValueRegs): 97 (JSC::AssemblyHelpers::argumentsStart): 98 (JSC::AssemblyHelpers::emitStoreStructureWithTypeInfo): 99 (JSC::AssemblyHelpers::emitLoadStructure): Deleted. 100 * jit/GPRInfo.cpp: 101 (JSC::JSValueRegs::dump): 102 * jit/GPRInfo.h: 103 (JSC::JSValueRegs::uses): 104 * jit/Repatch.cpp: 105 (JSC::replaceWithJump): 106 (JSC::tryCacheGetByID): 107 (JSC::tryCachePutByID): 108 (JSC::tryRepatchIn): 109 * jit/ThunkGenerators.cpp: 110 (JSC::virtualThunkFor): 111 * runtime/Options.h: 112 * runtime/PropertyMapHashTable.h: 113 (JSC::PropertyTable::begin): 114 (JSC::PropertyTable::find): 115 (JSC::PropertyTable::get): 116 * runtime/Structure.h: 117 1 118 2016-04-05 Antoine Quint <graouts@apple.com> 2 119 -
trunk/Source/JavaScriptCore/bytecode/PolymorphicAccess.cpp
r198212 r199069 1 1 /* 2 * Copyright (C) 2014 , 2015Apple Inc. All rights reserved.2 * Copyright (C) 2014-2016 Apple Inc. All rights reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 47 47 static const bool verbose = false; 48 48 49 void AccessGenerationResult::dump(PrintStream& out) const 50 { 51 out.print(m_kind); 52 if (m_code) 53 out.print(":", m_code); 54 } 55 49 56 Watchpoint* AccessGenerationState::addWatchpoint(const ObjectPropertyCondition& condition) 50 57 { … … 176 183 } 177 184 185 std::unique_ptr<AccessCase> AccessCase::megamorphicLoad(VM& vm, JSCell* owner) 186 { 187 UNUSED_PARAM(vm); 188 UNUSED_PARAM(owner); 189 190 if (GPRInfo::numberOfRegisters < 9) 191 return nullptr; 192 193 std::unique_ptr<AccessCase> result(new AccessCase()); 194 195 result->m_type = MegamorphicLoad; 196 197 return result; 198 } 199 178 200 std::unique_ptr<AccessCase> AccessCase::replace( 179 201 VM& vm, JSCell* owner, Structure* structure, PropertyOffset offset) … … 323 345 324 346 switch (m_type) { 347 case MegamorphicLoad: 325 348 case ArrayLength: 326 349 case StringLength: … … 343 366 } 344 367 345 bool AccessCase::canReplace(const AccessCase& other) 368 bool AccessCase::canBeReplacedByMegamorphicLoad() const 369 { 370 return type() == Load 371 && !viaProxy() 372 && conditionSet().isEmpty() 373 && !additionalSet() 374 && !customSlotBase(); 375 } 376 377 bool AccessCase::canReplace(const AccessCase& other) const 346 378 { 347 379 // We could do a lot better here, but for now we just do something obvious. 380 381 if (type() == MegamorphicLoad && other.canBeReplacedByMegamorphicLoad()) 382 return true; 348 383 349 384 if (!guardedByStructureCheck() || !other.guardedByStructureCheck()) { … … 408 443 { 409 444 CCallHelpers& jit = *state.jit; 445 VM& vm = *jit.vm(); 446 const Identifier& ident = *state.ident; 447 StructureStubInfo& stubInfo = *state.stubInfo; 448 JSValueRegs valueRegs = state.valueRegs; 449 GPRReg baseGPR = state.baseGPR; 450 GPRReg scratchGPR = state.scratchGPR; 451 452 UNUSED_PARAM(vm); 410 453 411 454 switch (m_type) { 412 455 case ArrayLength: { 413 456 ASSERT(!viaProxy()); 414 jit.load8(CCallHelpers::Address( state.baseGPR, JSCell::indexingTypeOffset()), state.scratchGPR);457 jit.load8(CCallHelpers::Address(baseGPR, JSCell::indexingTypeOffset()), scratchGPR); 415 458 fallThrough.append( 416 459 jit.branchTest32( 417 CCallHelpers::Zero, s tate.scratchGPR, CCallHelpers::TrustedImm32(IsArray)));460 CCallHelpers::Zero, scratchGPR, CCallHelpers::TrustedImm32(IsArray))); 418 461 fallThrough.append( 419 462 jit.branchTest32( 420 CCallHelpers::Zero, s tate.scratchGPR, CCallHelpers::TrustedImm32(IndexingShapeMask)));463 CCallHelpers::Zero, scratchGPR, CCallHelpers::TrustedImm32(IndexingShapeMask))); 421 464 break; 422 465 } … … 427 470 jit.branch8( 428 471 CCallHelpers::NotEqual, 429 CCallHelpers::Address( state.baseGPR, JSCell::typeInfoTypeOffset()),472 CCallHelpers::Address(baseGPR, JSCell::typeInfoTypeOffset()), 430 473 CCallHelpers::TrustedImm32(StringType))); 431 474 break; 475 } 476 477 case MegamorphicLoad: { 478 UniquedStringImpl* key = ident.impl(); 479 unsigned hash = IdentifierRepHash::hash(key); 480 481 ScratchRegisterAllocator allocator(stubInfo.patch.usedRegisters); 482 allocator.lock(baseGPR); 483 #if USE(JSVALUE32_64) 484 allocator.lock(static_cast<GPRReg>(stubInfo.patch.baseTagGPR)); 485 #endif 486 allocator.lock(valueRegs); 487 allocator.lock(scratchGPR); 488 489 GPRReg intermediateGPR = scratchGPR; 490 GPRReg maskGPR = allocator.allocateScratchGPR(); 491 GPRReg maskedHashGPR = allocator.allocateScratchGPR(); 492 GPRReg indexGPR = allocator.allocateScratchGPR(); 493 GPRReg offsetGPR = allocator.allocateScratchGPR(); 494 495 if (verbose) { 496 dataLog("baseGPR = ", baseGPR, "\n"); 497 dataLog("valueRegs = ", valueRegs, "\n"); 498 dataLog("scratchGPR = ", scratchGPR, "\n"); 499 dataLog("intermediateGPR = ", intermediateGPR, "\n"); 500 dataLog("maskGPR = ", maskGPR, "\n"); 501 dataLog("maskedHashGPR = ", maskedHashGPR, "\n"); 502 dataLog("indexGPR = ", indexGPR, "\n"); 503 dataLog("offsetGPR = ", offsetGPR, "\n"); 504 } 505 506 ScratchRegisterAllocator::PreservedState preservedState = 507 allocator.preserveReusedRegistersByPushing(jit, ScratchRegisterAllocator::ExtraStackSpace::SpaceForCCall); 508 509 CCallHelpers::JumpList myFailAndIgnore; 510 CCallHelpers::JumpList myFallThrough; 511 512 jit.emitLoadStructure(baseGPR, intermediateGPR, maskGPR); 513 jit.loadPtr( 514 CCallHelpers::Address(intermediateGPR, Structure::propertyTableUnsafeOffset()), 515 intermediateGPR); 516 517 myFailAndIgnore.append(jit.branchTestPtr(CCallHelpers::Zero, intermediateGPR)); 518 519 jit.load32(CCallHelpers::Address(intermediateGPR, PropertyTable::offsetOfIndexMask()), maskGPR); 520 jit.loadPtr(CCallHelpers::Address(intermediateGPR, PropertyTable::offsetOfIndex()), indexGPR); 521 jit.load32( 522 CCallHelpers::Address(intermediateGPR, PropertyTable::offsetOfIndexSize()), 523 intermediateGPR); 524 525 jit.move(maskGPR, maskedHashGPR); 526 jit.and32(CCallHelpers::TrustedImm32(hash), maskedHashGPR); 527 jit.lshift32(CCallHelpers::TrustedImm32(2), intermediateGPR); 528 jit.addPtr(indexGPR, intermediateGPR); 529 530 CCallHelpers::Label loop = jit.label(); 531 532 jit.load32(CCallHelpers::BaseIndex(indexGPR, maskedHashGPR, CCallHelpers::TimesFour), offsetGPR); 533 534 myFallThrough.append( 535 jit.branch32( 536 CCallHelpers::Equal, 537 offsetGPR, 538 CCallHelpers::TrustedImm32(PropertyTable::EmptyEntryIndex))); 539 540 jit.sub32(CCallHelpers::TrustedImm32(1), offsetGPR); 541 jit.mul32(CCallHelpers::TrustedImm32(sizeof(PropertyMapEntry)), offsetGPR, offsetGPR); 542 jit.addPtr(intermediateGPR, offsetGPR); 543 544 CCallHelpers::Jump collision = jit.branchPtr( 545 CCallHelpers::NotEqual, 546 CCallHelpers::Address(offsetGPR, OBJECT_OFFSETOF(PropertyMapEntry, key)), 547 CCallHelpers::TrustedImmPtr(key)); 548 549 // offsetGPR currently holds a pointer to the PropertyMapEntry, which has the offset and attributes. 550 // Check them and then attempt the load. 551 552 myFallThrough.append( 553 jit.branchTest32( 554 CCallHelpers::NonZero, 555 CCallHelpers::Address(offsetGPR, OBJECT_OFFSETOF(PropertyMapEntry, attributes)), 556 CCallHelpers::TrustedImm32(Accessor | CustomAccessor))); 557 558 jit.load32(CCallHelpers::Address(offsetGPR, OBJECT_OFFSETOF(PropertyMapEntry, offset)), offsetGPR); 559 560 jit.loadProperty(baseGPR, offsetGPR, valueRegs); 561 562 allocator.restoreReusedRegistersByPopping(jit, preservedState); 563 state.succeed(); 564 565 collision.link(&jit); 566 567 jit.add32(CCallHelpers::TrustedImm32(1), maskedHashGPR); 568 569 // FIXME: We could be smarter about this. Currently we're burning a GPR for the mask. But looping 570 // around isn't super common so we could, for example, recompute the mask from the difference between 571 // the table and index. But before we do that we should probably make it easier to multiply and 572 // divide by the size of PropertyMapEntry. That probably involves making PropertyMapEntry be arranged 573 // to have a power-of-2 size. 574 jit.and32(maskGPR, maskedHashGPR); 575 jit.jump().linkTo(loop, &jit); 576 577 if (allocator.didReuseRegisters()) { 578 myFailAndIgnore.link(&jit); 579 allocator.restoreReusedRegistersByPopping(jit, preservedState); 580 state.failAndIgnore.append(jit.jump()); 581 582 myFallThrough.link(&jit); 583 allocator.restoreReusedRegistersByPopping(jit, preservedState); 584 fallThrough.append(jit.jump()); 585 } else { 586 state.failAndIgnore.append(myFailAndIgnore); 587 fallThrough.append(myFallThrough); 588 } 589 return; 432 590 } 433 591 … … 437 595 jit.branch8( 438 596 CCallHelpers::NotEqual, 439 CCallHelpers::Address( state.baseGPR, JSCell::typeInfoTypeOffset()),597 CCallHelpers::Address(baseGPR, JSCell::typeInfoTypeOffset()), 440 598 CCallHelpers::TrustedImm32(PureForwardingProxyType))); 441 599 442 jit.loadPtr( 443 CCallHelpers::Address(state.baseGPR, JSProxy::targetOffset()), 444 state.scratchGPR); 600 jit.loadPtr(CCallHelpers::Address(baseGPR, JSProxy::targetOffset()), scratchGPR); 445 601 446 602 fallThrough.append( 447 603 jit.branchStructure( 448 604 CCallHelpers::NotEqual, 449 CCallHelpers::Address(s tate.scratchGPR, JSCell::structureIDOffset()),605 CCallHelpers::Address(scratchGPR, JSCell::structureIDOffset()), 450 606 structure())); 451 607 } else { … … 453 609 jit.branchStructure( 454 610 CCallHelpers::NotEqual, 455 CCallHelpers::Address( state.baseGPR, JSCell::structureIDOffset()),611 CCallHelpers::Address(baseGPR, JSCell::structureIDOffset()), 456 612 structure())); 457 613 } … … 1092 1248 emitIntrinsicGetter(state); 1093 1249 return; 1094 } } 1250 } 1251 1252 case MegamorphicLoad: 1253 // These need to be handled by generateWithGuard(), since the guard is part of the megamorphic load 1254 // algorithm. We can be sure that nobody will call generate() directly for MegamorphicLoad since 1255 // MegamorphicLoad is not guarded by a structure check. 1256 RELEASE_ASSERT_NOT_REACHED(); 1257 } 1095 1258 1096 1259 RELEASE_ASSERT_NOT_REACHED(); … … 1100 1263 PolymorphicAccess::~PolymorphicAccess() { } 1101 1264 1102 MacroAssemblerCodePtrPolymorphicAccess::regenerateWithCases(1265 AccessGenerationResult PolymorphicAccess::regenerateWithCases( 1103 1266 VM& vm, CodeBlock* codeBlock, StructureStubInfo& stubInfo, const Identifier& ident, 1104 1267 Vector<std::unique_ptr<AccessCase>> originalCasesToAdd) … … 1115 1278 // add more things after failure. 1116 1279 1117 // First, verify that we can generate code for all of the new cases while eliminating any of the 1118 // new cases that replace each other. 1280 // First ensure that the originalCasesToAdd doesn't contain duplicates. 1119 1281 Vector<std::unique_ptr<AccessCase>> casesToAdd; 1120 1282 for (unsigned i = 0; i < originalCasesToAdd.size(); ++i) { … … 1143 1305 // keep doing what they were doing before. 1144 1306 if (casesToAdd.isEmpty()) 1145 return MacroAssemblerCodePtr();1307 return AccessGenerationResult::MadeNoChanges; 1146 1308 1147 1309 // Now construct the list of cases as they should appear if we are successful. This means putting … … 1172 1334 if (verbose) 1173 1335 dataLog("newCases: ", listDump(newCases), "\n"); 1336 1337 // See if we are close to having too many cases and if some of those cases can be subsumed by a 1338 // megamorphic load. 1339 if (newCases.size() >= Options::maxAccessVariantListSize()) { 1340 unsigned numSelfLoads = 0; 1341 for (auto& newCase : newCases) { 1342 if (newCase->canBeReplacedByMegamorphicLoad()) 1343 numSelfLoads++; 1344 } 1345 1346 if (numSelfLoads >= Options::megamorphicLoadCost()) { 1347 if (auto mega = AccessCase::megamorphicLoad(vm, codeBlock)) { 1348 newCases.removeAllMatching( 1349 [&] (std::unique_ptr<AccessCase>& newCase) -> bool { 1350 return newCase->canBeReplacedByMegamorphicLoad(); 1351 }); 1352 1353 newCases.append(WTFMove(mega)); 1354 } 1355 } 1356 } 1174 1357 1175 1358 if (newCases.size() > Options::maxAccessVariantListSize()) { 1176 1359 if (verbose) 1177 1360 dataLog("Too many cases.\n"); 1178 return MacroAssemblerCodePtr();1361 return AccessGenerationResult::GaveUp; 1179 1362 } 1180 1363 1181 1364 MacroAssemblerCodePtr result = regenerate(vm, codeBlock, stubInfo, ident, newCases); 1182 1365 if (!result) 1183 return MacroAssemblerCodePtr();1366 return AccessGenerationResult::GaveUp; 1184 1367 1185 1368 m_list = WTFMove(newCases); … … 1187 1370 } 1188 1371 1189 MacroAssemblerCodePtrPolymorphicAccess::regenerateWithCase(1372 AccessGenerationResult PolymorphicAccess::regenerateWithCase( 1190 1373 VM& vm, CodeBlock* codeBlock, StructureStubInfo& stubInfo, const Identifier& ident, 1191 1374 std::unique_ptr<AccessCase> newAccess) … … 1404 1587 using namespace JSC; 1405 1588 1589 void printInternal(PrintStream& out, AccessGenerationResult::Kind kind) 1590 { 1591 switch (kind) { 1592 case AccessGenerationResult::MadeNoChanges: 1593 out.print("MadeNoChanges"); 1594 return; 1595 case AccessGenerationResult::GaveUp: 1596 out.print("GaveUp"); 1597 return; 1598 case AccessGenerationResult::GeneratedNewCode: 1599 out.print("GeneratedNewCode"); 1600 return; 1601 } 1602 1603 RELEASE_ASSERT_NOT_REACHED(); 1604 } 1605 1406 1606 void printInternal(PrintStream& out, AccessCase::AccessType type) 1407 1607 { … … 1410 1610 out.print("Load"); 1411 1611 return; 1612 case AccessCase::MegamorphicLoad: 1613 out.print("MegamorphicLoad"); 1614 return; 1412 1615 case AccessCase::Transition: 1413 1616 out.print("Transition"); -
trunk/Source/JavaScriptCore/bytecode/PolymorphicAccess.h
r196331 r199069 1 1 /* 2 * Copyright (C) 2014 , 2015Apple Inc. All rights reserved.2 * Copyright (C) 2014-2016 Apple Inc. All rights reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 54 54 enum AccessType { 55 55 Load, 56 MegamorphicLoad, 56 57 Transition, 57 58 Replace, … … 82 83 return false; 83 84 case Load: 85 case MegamorphicLoad: 84 86 case Miss: 85 87 case Getter: … … 97 99 switch (type) { 98 100 case Load: 101 case MegamorphicLoad: 99 102 case Miss: 100 103 case Getter: … … 120 123 switch (type) { 121 124 case Load: 125 case MegamorphicLoad: 122 126 case Miss: 123 127 case Getter: … … 146 150 PropertySlot::GetValueFunc = nullptr, 147 151 JSObject* customSlotBase = nullptr); 148 152 153 static std::unique_ptr<AccessCase> megamorphicLoad(VM&, JSCell* owner); 154 149 155 static std::unique_ptr<AccessCase> replace(VM&, JSCell* owner, Structure*, PropertyOffset); 150 156 … … 248 254 // Is it still possible for this case to ever be taken? 249 255 bool couldStillSucceed() const; 250 256 251 257 static bool canEmitIntrinsicGetter(JSFunction*, Structure*); 258 259 bool canBeReplacedByMegamorphicLoad() const; 252 260 253 261 // If this method returns true, then it's a good idea to remove 'other' from the access once 'this' 254 262 // is added. This method assumes that in case of contradictions, 'this' represents a newer, and so 255 263 // more useful, truth. This method can be conservative; it will return false when it doubt. 256 bool canReplace(const AccessCase& other) ;264 bool canReplace(const AccessCase& other) const; 257 265 258 266 void dump(PrintStream& out) const; … … 309 317 }; 310 318 319 class AccessGenerationResult { 320 public: 321 enum Kind { 322 MadeNoChanges, 323 GaveUp, 324 GeneratedNewCode 325 }; 326 327 AccessGenerationResult() 328 { 329 } 330 331 AccessGenerationResult(Kind kind) 332 : m_kind(kind) 333 { 334 ASSERT(kind != GeneratedNewCode); 335 } 336 337 AccessGenerationResult(MacroAssemblerCodePtr code) 338 : m_kind(GeneratedNewCode) 339 , m_code(code) 340 { 341 RELEASE_ASSERT(code); 342 } 343 344 bool operator==(const AccessGenerationResult& other) const 345 { 346 return m_kind == other.m_kind && m_code == other.m_code; 347 } 348 349 bool operator!=(const AccessGenerationResult& other) const 350 { 351 return !(*this == other); 352 } 353 354 explicit operator bool() const 355 { 356 return *this != AccessGenerationResult(); 357 } 358 359 Kind kind() const { return m_kind; } 360 361 const MacroAssemblerCodePtr& code() const { return m_code; } 362 363 bool madeNoChanges() const { return m_kind == MadeNoChanges; } 364 bool gaveUp() const { return m_kind == GaveUp; } 365 bool generatedNewCode() const { return m_kind == GeneratedNewCode; } 366 367 void dump(PrintStream&) const; 368 369 private: 370 Kind m_kind; 371 MacroAssemblerCodePtr m_code; 372 }; 373 311 374 class PolymorphicAccess { 312 375 WTF_MAKE_NONCOPYABLE(PolymorphicAccess); … … 319 382 // pass a vector of non-null access cases. This will prune the access cases by rejecting any case 320 383 // in the list that is subsumed by a later case in the list. 321 MacroAssemblerCodePtrregenerateWithCases(384 AccessGenerationResult regenerateWithCases( 322 385 VM&, CodeBlock*, StructureStubInfo&, const Identifier&, Vector<std::unique_ptr<AccessCase>>); 323 386 324 MacroAssemblerCodePtrregenerateWithCase(387 AccessGenerationResult regenerateWithCase( 325 388 VM&, CodeBlock*, StructureStubInfo&, const Identifier&, std::unique_ptr<AccessCase>); 326 389 … … 363 426 struct AccessGenerationState { 364 427 AccessGenerationState() 365 : m_calculatedRegistersForCallAndExceptionHandling(false)366 , m_needsToRestoreRegistersIfException(false)367 , m_calculatedCallSiteIndex(false)428 : m_calculatedRegistersForCallAndExceptionHandling(false) 429 , m_needsToRestoreRegistersIfException(false) 430 , m_calculatedCallSiteIndex(false) 368 431 { 369 432 } … … 442 505 namespace WTF { 443 506 507 void printInternal(PrintStream&, JSC::AccessGenerationResult::Kind); 444 508 void printInternal(PrintStream&, JSC::AccessCase::AccessType); 445 509 -
trunk/Source/JavaScriptCore/bytecode/StructureStubInfo.cpp
r195865 r199069 1 1 /* 2 * Copyright (C) 2008, 2014 , 2015Apple Inc. All rights reserved.2 * Copyright (C) 2008, 2014-2016 Apple Inc. All rights reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 105 105 } 106 106 107 MacroAssemblerCodePtrStructureStubInfo::addAccessCase(107 AccessGenerationResult StructureStubInfo::addAccessCase( 108 108 CodeBlock* codeBlock, const Identifier& ident, std::unique_ptr<AccessCase> accessCase) 109 109 { … … 111 111 112 112 if (!accessCase) 113 return MacroAssemblerCodePtr();113 return AccessGenerationResult::MadeNoChanges; 114 114 115 115 if (cacheType == CacheType::Stub) … … 127 127 accessCases.append(WTFMove(accessCase)); 128 128 129 MacroAssemblerCodePtrresult =129 AccessGenerationResult result = 130 130 access->regenerateWithCases(vm, codeBlock, *this, ident, WTFMove(accessCases)); 131 131 132 if (!result )133 return MacroAssemblerCodePtr();132 if (!result.generatedNewCode()) 133 return result; 134 134 135 135 initStub(codeBlock, WTFMove(access)); -
trunk/Source/JavaScriptCore/bytecode/StructureStubInfo.h
r195865 r199069 1 1 /* 2 * Copyright (C) 2008, 2012-201 5Apple Inc. All rights reserved.2 * Copyright (C) 2008, 2012-2016 Apple Inc. All rights reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 34 34 #include "Opcode.h" 35 35 #include "Options.h" 36 #include "PolymorphicAccess.h"37 36 #include "RegisterSet.h" 38 37 #include "Structure.h" … … 43 42 #if ENABLE(JIT) 44 43 44 class AccessCase; 45 class AccessGenerationResult; 45 46 class PolymorphicAccess; 46 47 … … 69 70 void initStub(CodeBlock*, std::unique_ptr<PolymorphicAccess>); 70 71 71 MacroAssemblerCodePtr addAccessCase( 72 CodeBlock*, const Identifier&, std::unique_ptr<AccessCase>); 72 AccessGenerationResult addAccessCase(CodeBlock*, const Identifier&, std::unique_ptr<AccessCase>); 73 73 74 74 void reset(CodeBlock*); -
trunk/Source/JavaScriptCore/jit/AssemblyHelpers.cpp
r198364 r199069 420 420 } 421 421 422 void AssemblyHelpers::loadProperty(GPRReg object, GPRReg offset, JSValueRegs result) 423 { 424 Jump isInline = branch32(LessThan, offset, TrustedImm32(firstOutOfLineOffset)); 425 426 loadPtr(Address(object, JSObject::butterflyOffset()), result.payloadGPR()); 427 neg32(offset); 428 signExtend32ToPtr(offset, offset); 429 Jump ready = jump(); 430 431 isInline.link(this); 432 addPtr( 433 TrustedImm32( 434 static_cast<int32_t>(sizeof(JSObject)) - 435 (static_cast<int32_t>(firstOutOfLineOffset) - 2) * static_cast<int32_t>(sizeof(EncodedJSValue))), 436 object, result.payloadGPR()); 437 438 ready.link(this); 439 440 loadValue( 441 BaseIndex( 442 result.payloadGPR(), offset, TimesEight, (firstOutOfLineOffset - 2) * sizeof(EncodedJSValue)), 443 result); 444 } 445 446 void AssemblyHelpers::emitLoadStructure(RegisterID source, RegisterID dest, RegisterID scratch) 447 { 448 #if USE(JSVALUE64) 449 load32(MacroAssembler::Address(source, JSCell::structureIDOffset()), dest); 450 loadPtr(vm()->heap.structureIDTable().base(), scratch); 451 loadPtr(MacroAssembler::BaseIndex(scratch, dest, MacroAssembler::TimesEight), dest); 452 #else 453 UNUSED_PARAM(scratch); 454 loadPtr(MacroAssembler::Address(source, JSCell::structureIDOffset()), dest); 455 #endif 456 } 457 422 458 #if USE(JSVALUE64) 423 459 template<typename LoadFromHigh, typename StoreToHigh, typename LoadFromLow, typename StoreToLow> -
trunk/Source/JavaScriptCore/jit/AssemblyHelpers.h
r198364 r199069 150 150 #endif 151 151 } 152 153 // Note that this clobbers offset. 154 void loadProperty(GPRReg object, GPRReg offset, JSValueRegs result); 152 155 153 156 void moveValueRegs(JSValueRegs srcRegs, JSValueRegs destRegs) … … 1210 1213 } 1211 1214 1212 void emitLoadStructure(RegisterID source, RegisterID dest, RegisterID scratch) 1213 { 1214 #if USE(JSVALUE64) 1215 load32(MacroAssembler::Address(source, JSCell::structureIDOffset()), dest); 1216 loadPtr(vm()->heap.structureIDTable().base(), scratch); 1217 loadPtr(MacroAssembler::BaseIndex(scratch, dest, MacroAssembler::TimesEight), dest); 1218 #else 1219 UNUSED_PARAM(scratch); 1220 loadPtr(MacroAssembler::Address(source, JSCell::structureIDOffset()), dest); 1221 #endif 1222 } 1223 1224 static void emitLoadStructure(AssemblyHelpers& jit, RegisterID base, RegisterID dest, RegisterID scratch) 1225 { 1226 #if USE(JSVALUE64) 1227 jit.load32(MacroAssembler::Address(base, JSCell::structureIDOffset()), dest); 1228 jit.loadPtr(jit.vm()->heap.structureIDTable().base(), scratch); 1229 jit.loadPtr(MacroAssembler::BaseIndex(scratch, dest, MacroAssembler::TimesEight), dest); 1230 #else 1231 UNUSED_PARAM(scratch); 1232 jit.loadPtr(MacroAssembler::Address(base, JSCell::structureIDOffset()), dest); 1233 #endif 1234 } 1215 void emitLoadStructure(RegisterID source, RegisterID dest, RegisterID scratch); 1235 1216 1236 1217 void emitStoreStructureWithTypeInfo(TrustedImmPtr structure, RegisterID dest, RegisterID) -
trunk/Source/JavaScriptCore/jit/GPRInfo.cpp
r194894 r199069 31 31 namespace JSC { 32 32 33 void JSValueRegs::dump(PrintStream& out) const 34 { 35 #if USE(JSVALUE64) 36 out.print(m_gpr); 37 #else 38 out.print("(tag:", tagGPR(), ", payload:", payloadGPR(), ")"); 39 #endif 40 } 41 33 42 // This is in the .cpp file to work around clang issues. 34 43 #if CPU(X86_64) -
trunk/Source/JavaScriptCore/jit/GPRInfo.h
r195182 r199069 78 78 bool uses(GPRReg gpr) const { return m_gpr == gpr; } 79 79 80 void dump(PrintStream&) const; 81 80 82 private: 81 83 GPRReg m_gpr; … … 202 204 203 205 bool uses(GPRReg gpr) const { return m_tagGPR == gpr || m_payloadGPR == gpr; } 206 207 void dump(PrintStream&) const; 204 208 205 209 private: -
trunk/Source/JavaScriptCore/jit/Repatch.cpp
r196331 r199069 1 1 /* 2 * Copyright (C) 2011-201 5Apple Inc. All rights reserved.2 * Copyright (C) 2011-2016 Apple Inc. All rights reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 162 162 static void replaceWithJump(StructureStubInfo& stubInfo, const MacroAssemblerCodePtr target) 163 163 { 164 RELEASE_ASSERT(target); 165 164 166 if (MacroAssembler::canJumpReplacePatchableBranch32WithPatch()) { 165 167 MacroAssembler::replaceWithJump( … … 316 318 } 317 319 318 MacroAssemblerCodePtr codePtr = 319 stubInfo.addAccessCase(codeBlock, propertyName, WTFMove(newCase)); 320 321 if (!codePtr) 322 return GiveUpOnCache; 323 324 replaceWithJump(stubInfo, codePtr); 320 AccessGenerationResult result = stubInfo.addAccessCase(codeBlock, propertyName, WTFMove(newCase)); 321 322 if (result.gaveUp()) 323 return GiveUpOnCache; 324 if (result.madeNoChanges()) 325 return RetryCacheLater; 326 327 RELEASE_ASSERT(result.code()); 328 replaceWithJump(stubInfo, result.code()); 325 329 326 330 return RetryCacheLater; … … 458 462 } 459 463 460 MacroAssemblerCodePtr codePtr = stubInfo.addAccessCase(codeBlock, ident, WTFMove(newCase)); 461 462 if (!codePtr) 463 return GiveUpOnCache; 464 464 AccessGenerationResult result = stubInfo.addAccessCase(codeBlock, ident, WTFMove(newCase)); 465 466 if (result.gaveUp()) 467 return GiveUpOnCache; 468 if (result.madeNoChanges()) 469 return RetryCacheLater; 470 471 RELEASE_ASSERT(result.code()); 465 472 resetPutByIDCheckAndLoad(stubInfo); 466 473 MacroAssembler::repatchJump( 467 474 stubInfo.callReturnLocation.jumpAtOffset( 468 475 stubInfo.patch.deltaCallToJump), 469 CodeLocationLabel( codePtr));476 CodeLocationLabel(result.code())); 470 477 471 478 return RetryCacheLater; … … 515 522 vm, codeBlock, wasFound ? AccessCase::InHit : AccessCase::InMiss, structure, conditionSet); 516 523 517 MacroAssemblerCodePtr codePtr = stubInfo.addAccessCase(codeBlock, ident, WTFMove(newCase)); 518 if (!codePtr) 519 return GiveUpOnCache; 520 524 AccessGenerationResult result = stubInfo.addAccessCase(codeBlock, ident, WTFMove(newCase)); 525 if (result.gaveUp()) 526 return GiveUpOnCache; 527 if (result.madeNoChanges()) 528 return RetryCacheLater; 529 530 RELEASE_ASSERT(result.code()); 521 531 MacroAssembler::repatchJump( 522 532 stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.deltaCallToJump), 523 CodeLocationLabel( codePtr));533 CodeLocationLabel(result.code())); 524 534 525 535 return RetryCacheLater; -
trunk/Source/JavaScriptCore/jit/ThunkGenerators.cpp
r198981 r199069 192 192 CCallHelpers::TrustedImm32(JSValue::CellTag))); 193 193 #endif 194 AssemblyHelpers::emitLoadStructure(jit,GPRInfo::regT0, GPRInfo::regT4, GPRInfo::regT1);194 jit.emitLoadStructure(GPRInfo::regT0, GPRInfo::regT4, GPRInfo::regT1); 195 195 slowCase.append( 196 196 jit.branchPtr( -
trunk/Source/JavaScriptCore/runtime/Options.h
r198975 r199069 188 188 v(bool, clobberAllRegsInFTLICSlowPath, !ASSERT_DISABLED, nullptr) \ 189 189 v(bool, useAccessInlining, true, nullptr) \ 190 v(unsigned, maxAccessVariantListSize, 8, nullptr) \ 190 v(unsigned, maxAccessVariantListSize, 13, nullptr) \ 191 v(unsigned, megamorphicLoadCost, 10, nullptr) \ 191 192 v(bool, usePolyvariantDevirtualization, true, nullptr) \ 192 193 v(bool, usePolymorphicAccessInlining, true, nullptr) \ -
trunk/Source/JavaScriptCore/runtime/PropertyMapHashTable.h
r184828 r199069 192 192 void checkConsistency(); 193 193 #endif 194 195 static ptrdiff_t offsetOfIndexSize() { return OBJECT_OFFSETOF(PropertyTable, m_indexSize); } 196 static ptrdiff_t offsetOfIndexMask() { return OBJECT_OFFSETOF(PropertyTable, m_indexMask); } 197 static ptrdiff_t offsetOfIndex() { return OBJECT_OFFSETOF(PropertyTable, m_index); } 198 199 static const unsigned EmptyEntryIndex = 0; 194 200 195 201 private: … … 245 251 246 252 static const unsigned MinimumTableSize = 16; 247 static const unsigned EmptyEntryIndex = 0;248 253 }; 249 254 … … 273 278 ASSERT(key->isAtomic() || key->isSymbol()); 274 279 unsigned hash = IdentifierRepHash::hash(key); 275 unsigned step = 0;276 280 277 281 #if DUMP_PROPERTYMAP_STATS … … 286 290 return std::make_pair(&table()[entryIndex - 1], hash & m_indexMask); 287 291 288 if (!step)289 step = WTF::doubleHash(IdentifierRepHash::hash(key)) | 1;290 291 292 #if DUMP_PROPERTYMAP_STATS 292 293 ++propertyMapHashTableStats->numCollisions; … … 294 295 295 296 #if DUMP_PROPERTYMAP_COLLISIONS 296 dataLog("PropertyTable collision for ", key, " (", hash, ") with step ", step, "\n");297 dataLog("PropertyTable collision for ", key, " (", hash, ")\n"); 297 298 dataLog("Collided with ", table()[entryIndex - 1].key, "(", IdentifierRepHash::hash(table()[entryIndex - 1].key), ")\n"); 298 299 #endif 299 300 300 hash += step;301 hash++; 301 302 } 302 303 } … … 311 312 312 313 unsigned hash = IdentifierRepHash::hash(key); 313 unsigned step = 0;314 314 315 315 #if DUMP_PROPERTYMAP_STATS … … 328 328 #endif 329 329 330 if (!step) 331 step = WTF::doubleHash(IdentifierRepHash::hash(key)) | 1; 332 hash += step; 330 hash++; 333 331 } 334 332 } -
trunk/Source/JavaScriptCore/runtime/Structure.h
r197712 r199069 421 421 return OBJECT_OFFSETOF(Structure, m_blob) + StructureIDBlob::indexingTypeOffset(); 422 422 } 423 424 static ptrdiff_t propertyTableUnsafeOffset() 425 { 426 return OBJECT_OFFSETOF(Structure, m_propertyTableUnsafe); 427 } 423 428 424 429 static Structure* createStructure(VM&);
Note:
See TracChangeset
for help on using the changeset viewer.