Changeset 243530 in webkit
- Timestamp:
- Mar 26, 2019 5:08:46 PM (5 years ago)
- Location:
- trunk/Source/JavaScriptCore
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/ChangeLog
r243514 r243530 1 2019-03-26 Saam Barati <sbarati@apple.com> 2 3 FTL: Emit code to validate AI's state when running the compiled code 4 https://bugs.webkit.org/show_bug.cgi?id=195924 5 <rdar://problem/49003422> 6 7 Reviewed by Filip Pizlo. 8 9 This patch adds code that between the execution of each node that validates 10 the types that AI proves. This option is too expensive to turn on for our 11 regression testing, but we think it will be valuable in other types of running 12 modes, such as when running with a fuzzer. 13 14 This patch also adds options to only probabilistically run this validation 15 after the execution of each node. As the probability is lowered, there is 16 less of a perf hit. 17 18 This patch just adds this validation in the FTL. A follow-up patch will land 19 it in the DFG too: https://bugs.webkit.org/show_bug.cgi?id=196219 20 21 * ftl/FTLLowerDFGToB3.cpp: 22 (JSC::FTL::DFG::LowerDFGToB3::LowerDFGToB3): 23 (JSC::FTL::DFG::LowerDFGToB3::compileBlock): 24 (JSC::FTL::DFG::LowerDFGToB3::validateAIState): 25 (JSC::FTL::DFG::LowerDFGToB3::compileNode): 26 (JSC::FTL::DFG::LowerDFGToB3::lowJSValue): 27 * runtime/Options.h: 28 1 29 2019-03-26 Tadeu Zagallo <tzagallo@apple.com> 2 30 -
trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
r243364 r243530 47 47 #include "DFGDominators.h" 48 48 #include "DFGInPlaceAbstractState.h" 49 #include "DFGLivenessAnalysisPhase.h" 49 50 #include "DFGMayExit.h" 50 51 #include "DFGOSRAvailabilityAnalysisPhase.h" … … 83 84 #include "JSMap.h" 84 85 #include "OperandsInlines.h" 86 #include "ProbeContext.h" 85 87 #include "RegExpObject.h" 86 88 #include "ScopedArguments.h" … … 156 158 , m_indexMaskingMode(Options::enableSpectreMitigations() ? IndexMaskingEnabled : IndexMaskingDisabled) 157 159 { 160 if (Options::validateAbstractInterpreterState()) { 161 performLivenessAnalysis(m_graph); 162 163 // We only use node liveness here, not combined liveness, as we only track 164 // AI state for live nodes. 165 for (DFG::BasicBlock* block : m_graph.blocksInNaturalOrder()) { 166 NodeSet live; 167 168 for (NodeFlowProjection node : block->ssa->liveAtTail) { 169 if (node.kind() == NodeFlowProjection::Primary) 170 live.addVoid(node.node()); 171 } 172 173 for (unsigned i = block->size(); i--; ) { 174 Node* node = block->at(i); 175 live.remove(node); 176 m_graph.doToChildren(node, [&] (Edge child) { 177 live.addVoid(child.node()); 178 }); 179 m_liveInToNode.add(node, live); 180 } 181 } 182 } 158 183 } 159 184 … … 474 499 return; 475 500 } 501 502 m_aiCheckedNodes.clear(); 476 503 477 504 m_availabilityCalculator.beginBlock(m_highBlock); … … 509 536 } 510 537 538 void validateAIState(Node* node) 539 { 540 if (!m_graphDump) { 541 StringPrintStream out; 542 m_graph.dump(out); 543 m_graphDump = out.toString(); 544 } 545 546 switch (node->op()) { 547 case MovHint: 548 case ZombieHint: 549 case JSConstant: 550 case LazyJSConstant: 551 case DoubleConstant: 552 case Int52Constant: 553 case GetStack: 554 case PutStack: 555 case KillStack: 556 case ExitOK: 557 return; 558 default: 559 break; 560 } 561 562 // Before we execute node. 563 NodeSet& live = m_liveInToNode.find(node)->value; 564 unsigned highParentIndex = node->index(); 565 { 566 uint64_t hash = WTF::intHash(highParentIndex); 567 if (hash >= static_cast<uint64_t>((static_cast<double>(std::numeric_limits<unsigned>::max()) + 1) * Options::validateAbstractInterpreterStateProbability())) 568 return; 569 } 570 571 for (Node* node : live) { 572 if (node->isPhantomAllocation()) 573 continue; 574 575 if (node->op() == CheckInBounds) 576 continue; 577 578 AbstractValue value = m_interpreter.forNode(node); 579 { 580 auto iter = m_aiCheckedNodes.find(node); 581 if (iter != m_aiCheckedNodes.end()) { 582 AbstractValue checkedValue = iter->value; 583 if (checkedValue == value) { 584 if (!(value.m_type & SpecCell)) 585 continue; 586 } 587 } 588 m_aiCheckedNodes.set(node, value); 589 } 590 591 FlushFormat flushFormat; 592 LValue input; 593 if (node->hasJSResult()) { 594 input = lowJSValue(Edge(node, UntypedUse)); 595 flushFormat = FlushedJSValue; 596 } else if (node->hasDoubleResult()) { 597 input = lowDouble(Edge(node, DoubleRepUse)); 598 flushFormat = FlushedDouble; 599 } else if (node->hasInt52Result()) { 600 input = strictInt52ToJSValue(lowStrictInt52(Edge(node, Int52RepUse))); 601 flushFormat = FlushedInt52; 602 } else 603 continue; 604 605 unsigned highChildIndex = node->index(); 606 607 String graphDump = m_graphDump; 608 609 PatchpointValue* patchpoint = m_out.patchpoint(Void); 610 patchpoint->effects = Effects::none(); 611 patchpoint->effects.writesLocalState = true; 612 patchpoint->appendSomeRegister(input); 613 patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) { 614 GPRReg reg = InvalidGPRReg; 615 FPRReg fpReg = InvalidFPRReg; 616 if (flushFormat == FlushedDouble) 617 fpReg = params[0].fpr(); 618 else 619 reg = params[0].gpr(); 620 jit.probe([=] (Probe::Context& context) { 621 JSValue input; 622 double doubleInput; 623 624 auto dumpAndCrash = [&] { 625 dataLogLn("Validation failed at node: @", highParentIndex); 626 dataLogLn("Failed validating live value: @", highChildIndex); 627 dataLogLn(); 628 dataLogLn("Expected AI value = ", value); 629 if (flushFormat != FlushedDouble) 630 dataLogLn("Unexpected value = ", input); 631 else 632 dataLogLn("Unexpected double value = ", doubleInput); 633 dataLogLn(); 634 dataLogLn(graphDump); 635 CRASH(); 636 }; 637 638 if (flushFormat == FlushedDouble) { 639 doubleInput = context.fpr(fpReg); 640 SpeculatedType type; 641 if (!std::isnan(doubleInput)) 642 type = speculationFromValue(jsDoubleNumber(doubleInput)); 643 else if (isImpureNaN(doubleInput)) 644 type = SpecDoubleImpureNaN; 645 else 646 type = SpecDoublePureNaN; 647 648 if (!value.couldBeType(type)) 649 dumpAndCrash(); 650 } else { 651 input = JSValue::decode(context.gpr(reg)); 652 if (!value.validateOSREntryValue(input, flushFormat)) 653 dumpAndCrash(); 654 } 655 656 }); 657 }); 658 } 659 } 660 511 661 bool compileNode(unsigned nodeIndex) 512 662 { … … 527 677 m_interpreter.startExecuting(); 528 678 m_interpreter.executeKnownEdgeTypes(m_node); 679 680 if (Options::validateAbstractInterpreterState()) 681 validateAIState(m_node); 529 682 530 683 if (validateDFGDoesGC) { … … 15183 15336 } 15184 15337 15185 DFG_CRASH(m_graph, m_node, "Value not defined");15338 DFG_CRASH(m_graph, m_node, makeString("Value not defined: ", String::number(edge.node()->index())).ascii().data()); 15186 15339 return 0; 15187 15340 } … … 17209 17362 unsigned m_nodeIndex; 17210 17363 Node* m_node; 17364 17365 // These are used for validating AI state. 17366 HashMap<Node*, NodeSet> m_liveInToNode; 17367 HashMap<Node*, AbstractValue> m_aiCheckedNodes; 17368 String m_graphDump; 17211 17369 }; 17212 17370 -
trunk/Source/JavaScriptCore/runtime/Options.h
r243086 r243530 511 511 v(optionString, diskCachePath, nullptr, Restricted, nullptr) \ 512 512 v(bool, forceDiskCache, false, Restricted, nullptr) \ 513 v(bool, validateAbstractInterpreterState, false, Restricted, nullptr) \ 514 v(double, validateAbstractInterpreterStateProbability, 0.5, Normal, nullptr) \ 513 515 514 516
Note: See TracChangeset
for help on using the changeset viewer.