Changeset 242990 in webkit
- Timestamp:
- Mar 14, 2019, 10:45:07 PM (7 years ago)
- Location:
- trunk/Source/JavaScriptCore
- Files:
-
- 6 edited
-
ChangeLog (modified) (1 diff)
-
bytecode/SpeculatedType.cpp (modified) (1 diff)
-
dfg/DFGAbstractValue.cpp (modified) (2 diffs)
-
dfg/DFGAbstractValue.h (modified) (4 diffs)
-
dfg/DFGCFAPhase.cpp (modified) (5 diffs)
-
dfg/DFGOSREntry.cpp (modified) (8 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/ChangeLog
r242989 r242990 1 2019-03-14 Yusuke Suzuki <ysuzuki@apple.com> 2 3 REGRESSION(r242841): Fix conservative DFG OSR entry validation to accept values which will be stored in AnyInt / Double flush formats 4 https://bugs.webkit.org/show_bug.cgi?id=195752 5 6 Reviewed by Saam Barati. 7 8 We fixed the bug skipping AbstractValue validations when the flush format is Double or AnyInt. But it 9 was too conservative. While validating inputs with AbstractValue is mandatory (without it, whole CFA 10 falls into wrong condition), our validation does not care AnyInt and Double representations in lower 11 tiers. For example, if a value is stored in Double flush format in DFG, its AbstractValue becomes 12 SpecFullDouble. However, it does not include Int32 and OSR entry is rejected if Int32 comes for DoubleRep 13 OSR entry value. This is wrong since we later convert these numbers into DoubleRep representation 14 before entering DFG code. 15 16 This patch performs AbstractValue validation onto the correctly converted value with flush format hint. 17 18 And it still does not fix OSR entry failures in navier-stokes. This is because AbstractValue representation 19 in navier-stokes's lin_solve was too strict. Then, this patch reverts r242627. Instead of removing must handle 20 value handling in CFA, DFG OSR entry now correctly validates inputs with AbstractValues even if the flush format 21 is Double or AnyInt. As long as DFG OSR entry validates inputs, merging must handle values as proven constants is OK. 22 23 We can see that # of OSR entry failures in navier-stokes.js becomes the same to the previous count. And we can see 24 AnyInt OSR entry actually works in microbenchmarks/large-int.js. However, AnyInt effect is hard to observe because this 25 is super rare. Since we inject type prediction based on must handle value, the flush format tends to be SpecAnyIntAsDouble 26 and it accepts JSValues simply. 27 28 * bytecode/SpeculatedType.cpp: 29 (JSC::dumpSpeculation): 30 * dfg/DFGAbstractValue.cpp: 31 (JSC::DFG::AbstractValue::filterValueByType): 32 * dfg/DFGAbstractValue.h: 33 (JSC::DFG::AbstractValue::validateOSREntryValue const): 34 (JSC::DFG::AbstractValue::validateTypeAcceptingBoxedInt52 const): 35 (JSC::DFG::AbstractValue::validate const): Deleted. 36 (JSC::DFG::AbstractValue::validateType const): Deleted. 37 * dfg/DFGCFAPhase.cpp: 38 (JSC::DFG::CFAPhase::run): 39 (JSC::DFG::CFAPhase::injectOSR): 40 (JSC::DFG::CFAPhase::performBlockCFA): 41 * dfg/DFGOSREntry.cpp: 42 (JSC::DFG::prepareOSREntry): 43 1 44 2019-03-14 Saam barati <sbarati@apple.com> 2 45 -
trunk/Source/JavaScriptCore/bytecode/SpeculatedType.cpp
r238923 r242990 262 262 263 263 if (value & SpecNonIntAsDouble) 264 strOut.print("NonIntAs double");264 strOut.print("NonIntAsDouble"); 265 265 else 266 266 isTop = false; -
trunk/Source/JavaScriptCore/dfg/DFGAbstractValue.cpp
r240023 r242990 345 345 // The type is still non-empty. It may be that the new type renders 346 346 // the value empty because it contravenes the constant value we had. 347 if (m_value && !validateType (m_value))347 if (m_value && !validateTypeAcceptingBoxedInt52(m_value)) 348 348 clear(); 349 349 return; … … 352 352 // The type has been rendered empty. That means that the value must now be invalid, 353 353 // as well. 354 ASSERT(!m_value || !validateType (m_value));354 ASSERT(!m_value || !validateTypeAcceptingBoxedInt52(m_value)); 355 355 m_value = JSValue(); 356 356 } -
trunk/Source/JavaScriptCore/dfg/DFGAbstractValue.h
r240023 r242990 31 31 #include "DFGAbstractValueClobberEpoch.h" 32 32 #include "DFGFiltrationResult.h" 33 #include "DFGFlushFormat.h" 33 34 #include "DFGFrozenValue.h" 34 35 #include "DFGNodeFlags.h" … … 370 371 bool contains(RegisteredStructure) const; 371 372 372 bool validate (JSValue value) const373 bool validateOSREntryValue(JSValue value, FlushFormat format) const 373 374 { 374 375 if (isHeapTop()) … … 378 379 return false; 379 380 380 if (mergeSpeculations(m_type, speculationFromValue(value)) != m_type) 381 return false; 382 383 if (value.isEmpty()) { 384 ASSERT(m_type & SpecEmpty); 385 return true; 381 if (format == FlushedInt52) { 382 if (!validateTypeAcceptingBoxedInt52(value)) 383 return false; 384 } else { 385 if (mergeSpeculations(m_type, speculationFromValue(value)) != m_type) 386 return false; 387 388 if (value.isEmpty()) { 389 ASSERT(m_type & SpecEmpty); 390 return true; 391 } 386 392 } 387 393 … … 491 497 } 492 498 493 bool validateType (JSValue value) const499 bool validateTypeAcceptingBoxedInt52(JSValue value) const 494 500 { 495 501 if (isHeapTop()) -
trunk/Source/JavaScriptCore/dfg/DFGCFAPhase.cpp
r242627 r242990 77 77 m_state.initialize(); 78 78 79 if (m_graph.m_form != SSA) { 80 if (m_verbose) 81 dataLog(" Widening state at OSR entry block.\n"); 82 83 // Widen the abstract values at the block that serves as the must-handle OSR entry. 84 for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) { 85 BasicBlock* block = m_graph.block(blockIndex); 86 if (!block) 87 continue; 88 89 if (!block->isOSRTarget) 90 continue; 91 if (block->bytecodeBegin != m_graph.m_plan.osrEntryBytecodeIndex()) 92 continue; 93 94 // We record that the block needs some OSR stuff, but we don't do that yet. We want to 95 // handle OSR entry data at the right time in order to get the best compile times. If we 96 // simply injected OSR data right now, then we'd potentially cause a loop body to be 97 // interpreted with just the constants we feed it, which is more expensive than if we 98 // interpreted it with non-constant values. If we always injected this data after the 99 // main pass of CFA ran, then we would potentially spend a bunch of time rerunning CFA 100 // after convergence. So, we try very hard to inject OSR data for a block when we first 101 // naturally come to see it - see the m_blocksWithOSR check in performBlockCFA(). This 102 // way, we: 103 // 104 // - Reduce the likelihood of interpreting the block with constants, since we will inject 105 // the OSR entry constants on top of whatever abstract values we got for that block on 106 // the first pass. The mix of those two things is likely to not be constant. 107 // 108 // - Reduce the total number of CFA reexecutions since we inject the OSR data as part of 109 // the normal flow of CFA instead of having to do a second fixpoint. We may still have 110 // to do a second fixpoint if we don't even reach the OSR entry block during the main 111 // run of CFA, but in that case at least we're not being redundant. 112 m_blocksWithOSR.add(block); 113 } 114 } 115 79 116 do { 80 117 m_changed = false; … … 83 120 84 121 if (m_graph.m_form != SSA) { 122 for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) { 123 BasicBlock* block = m_graph.block(blockIndex); 124 if (!block) 125 continue; 126 127 if (m_blocksWithOSR.remove(block)) 128 m_changed |= injectOSR(block); 129 } 130 131 while (m_changed) { 132 m_changed = false; 133 performForwardCFA(); 134 } 135 85 136 // Make sure we record the intersection of all proofs that we ever allowed the 86 137 // compiler to rely upon. … … 107 158 108 159 private: 160 bool injectOSR(BasicBlock* block) 161 { 162 if (m_verbose) 163 dataLog(" Found must-handle block: ", *block, "\n"); 164 165 // This merges snapshot of stack values while CFA phase want to have proven types and values. This is somewhat tricky. 166 // But this is OK as long as DFG OSR entry validates the inputs with *proven* AbstracValue values. And it turns out that this 167 // type widening is critical to navier-stokes. Without it, navier-stokes has more strict constraint on OSR entry and 168 // fails OSR entry repeatedly. 169 bool changed = false; 170 const Operands<Optional<JSValue>>& mustHandleValues = m_graph.m_plan.mustHandleValues(); 171 for (size_t i = mustHandleValues.size(); i--;) { 172 int operand = mustHandleValues.operandForIndex(i); 173 Optional<JSValue> value = mustHandleValues[i]; 174 if (!value) { 175 if (m_verbose) 176 dataLog(" Not live in bytecode: ", VirtualRegister(operand), "\n"); 177 continue; 178 } 179 Node* node = block->variablesAtHead.operand(operand); 180 if (!node) { 181 if (m_verbose) 182 dataLog(" Not live: ", VirtualRegister(operand), "\n"); 183 continue; 184 } 185 186 if (m_verbose) 187 dataLog(" Widening ", VirtualRegister(operand), " with ", value.value(), "\n"); 188 189 AbstractValue& target = block->valuesAtHead.operand(operand); 190 changed |= target.mergeOSREntryValue(m_graph, value.value()); 191 target.fixTypeForRepresentation( 192 m_graph, resultFor(node->variableAccessData()->flushFormat()), node); 193 } 194 195 if (changed || !block->cfaHasVisited) { 196 block->cfaShouldRevisit = true; 197 return true; 198 } 199 200 return false; 201 } 202 109 203 void performBlockCFA(BasicBlock* block) 110 204 { … … 116 210 dataLog(" Block ", *block, ":\n"); 117 211 212 if (m_blocksWithOSR.remove(block)) 213 injectOSR(block); 214 118 215 m_state.beginBasicBlock(block); 119 216 if (m_verbose) { … … 171 268 InPlaceAbstractState m_state; 172 269 AbstractInterpreter<InPlaceAbstractState> m_interpreter; 270 BlockSet m_blocksWithOSR; 173 271 174 272 bool m_verbose; -
trunk/Source/JavaScriptCore/dfg/DFGOSREntry.cpp
r242841 r242990 101 101 102 102 if (!Options::useOSREntryToDFG()) 103 return 0;103 return nullptr; 104 104 105 105 if (Options::verboseOSR()) { … … 138 138 if (Options::verboseOSR()) 139 139 dataLog(" OSR failed because the target code block is not DFG.\n"); 140 return 0;140 return nullptr; 141 141 } 142 142 … … 147 147 if (Options::verboseOSR()) 148 148 dataLogF(" OSR failed because the entrypoint was optimized out.\n"); 149 return 0;149 return nullptr; 150 150 } 151 151 … … 183 183 dataLogF(".\n"); 184 184 } 185 return 0;185 return nullptr; 186 186 } 187 187 … … 192 192 value = exec->argument(argument - 1); 193 193 194 if (!entry->m_expectedValues.argument(argument).validate (value)) {194 if (!entry->m_expectedValues.argument(argument).validateOSREntryValue(value, FlushedJSValue)) { 195 195 if (Options::verboseOSR()) { 196 196 dataLog( … … 198 198 ", expected ", entry->m_expectedValues.argument(argument), ".\n"); 199 199 } 200 return 0;200 return nullptr; 201 201 } 202 202 } … … 205 205 int localOffset = virtualRegisterForLocal(local).offset(); 206 206 JSValue value = exec->registers()[localOffset].asanUnsafeJSValue(); 207 if (!entry->m_expectedValues.local(local).validate(value)) { 208 if (Options::verboseOSR()) { 209 dataLog( 210 " OSR failed because variable ", VirtualRegister(localOffset), " is ", 207 FlushFormat format = FlushedJSValue; 208 209 if (entry->m_localsForcedAnyInt.get(local)) { 210 if (!value.isAnyInt()) { 211 dataLogLnIf(Options::verboseOSR(), 212 " OSR failed because variable ", localOffset, " is ", 211 213 value, ", expected ", 212 entry->m_expectedValues.local(local), ".\n"); 213 } 214 return 0; 215 } 214 "machine int."); 215 return nullptr; 216 } 217 // Constant AnyInt value is stored as usual boxed value in AbstractValue. 218 format = FlushedInt52; 219 } 220 216 221 if (entry->m_localsForcedDouble.get(local)) { 217 222 if (!value.isNumber()) { 218 if (Options::verboseOSR()) { 219 dataLog( 220 " OSR failed because variable ", localOffset, " is ", 221 value, ", expected number.\n"); 222 } 223 return 0; 224 } 225 continue; 226 } 227 if (entry->m_localsForcedAnyInt.get(local)) { 228 if (!value) { 229 if (Options::verboseOSR()) { 230 dataLog( 231 " OSR failed because variable ", localOffset, " is ", 232 value, ", expected ", 233 "machine int.\n"); 234 } 235 return 0; 236 } 237 continue; 223 dataLogLnIf(Options::verboseOSR(), 224 " OSR failed because variable ", localOffset, " is ", 225 value, ", expected number."); 226 return nullptr; 227 } 228 value = jsDoubleNumber(value.asNumber()); 229 format = FlushedDouble; 230 } 231 232 if (!entry->m_expectedValues.local(local).validateOSREntryValue(value, format)) { 233 dataLogLnIf(Options::verboseOSR(), 234 " OSR failed because variable ", VirtualRegister(localOffset), " is ", 235 value, ", expected ", 236 entry->m_expectedValues.local(local), "."); 237 return nullptr; 238 238 } 239 239 } … … 250 250 if (Options::verboseOSR()) 251 251 dataLogF(" OSR failed because stack growth failed.\n"); 252 return 0;252 return nullptr; 253 253 } 254 254
Note:
See TracChangeset
for help on using the changeset viewer.