Changeset 243530 in webkit


Ignore:
Timestamp:
Mar 26, 2019 5:08:46 PM (5 years ago)
Author:
sbarati@apple.com
Message:

FTL: Emit code to validate AI's state when running the compiled code
https://bugs.webkit.org/show_bug.cgi?id=195924
<rdar://problem/49003422>

Reviewed by Filip Pizlo.

This patch adds code that between the execution of each node that validates
the types that AI proves. This option is too expensive to turn on for our
regression testing, but we think it will be valuable in other types of running
modes, such as when running with a fuzzer.

This patch also adds options to only probabilistically run this validation
after the execution of each node. As the probability is lowered, there is
less of a perf hit.

This patch just adds this validation in the FTL. A follow-up patch will land
it in the DFG too: https://bugs.webkit.org/show_bug.cgi?id=196219

  • ftl/FTLLowerDFGToB3.cpp:

(JSC::FTL::DFG::LowerDFGToB3::LowerDFGToB3):
(JSC::FTL::DFG::LowerDFGToB3::compileBlock):
(JSC::FTL::DFG::LowerDFGToB3::validateAIState):
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::lowJSValue):

  • runtime/Options.h:
Location:
trunk/Source/JavaScriptCore
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r243514 r243530  
     12019-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
    1292019-03-26  Tadeu Zagallo  <tzagallo@apple.com>
    230
  • trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp

    r243364 r243530  
    4747#include "DFGDominators.h"
    4848#include "DFGInPlaceAbstractState.h"
     49#include "DFGLivenessAnalysisPhase.h"
    4950#include "DFGMayExit.h"
    5051#include "DFGOSRAvailabilityAnalysisPhase.h"
     
    8384#include "JSMap.h"
    8485#include "OperandsInlines.h"
     86#include "ProbeContext.h"
    8587#include "RegExpObject.h"
    8688#include "ScopedArguments.h"
     
    156158        , m_indexMaskingMode(Options::enableSpectreMitigations() ?  IndexMaskingEnabled : IndexMaskingDisabled)
    157159    {
     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        }
    158183    }
    159184   
     
    474499            return;
    475500        }
     501
     502        m_aiCheckedNodes.clear();
    476503       
    477504        m_availabilityCalculator.beginBlock(m_highBlock);
     
    509536    }
    510537
     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
    511661    bool compileNode(unsigned nodeIndex)
    512662    {
     
    527677        m_interpreter.startExecuting();
    528678        m_interpreter.executeKnownEdgeTypes(m_node);
     679
     680        if (Options::validateAbstractInterpreterState())
     681            validateAIState(m_node);
    529682
    530683        if (validateDFGDoesGC) {
     
    1518315336        }
    1518415337       
    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());
    1518615339        return 0;
    1518715340    }
     
    1720917362    unsigned m_nodeIndex;
    1721017363    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;
    1721117369};
    1721217370
  • trunk/Source/JavaScriptCore/runtime/Options.h

    r243086 r243530  
    511511    v(optionString, diskCachePath, nullptr, Restricted, nullptr) \
    512512    v(bool, forceDiskCache, false, Restricted, nullptr) \
     513    v(bool, validateAbstractInterpreterState, false, Restricted, nullptr) \
     514    v(double, validateAbstractInterpreterStateProbability, 0.5, Normal, nullptr) \
    513515
    514516
Note: See TracChangeset for help on using the changeset viewer.