⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 242990 in webkit


Ignore:
Timestamp:
Mar 14, 2019, 10:45:07 PM (7 years ago)
Author:
ysuzuki@apple.com
Message:

REGRESSION(r242841): Fix conservative DFG OSR entry validation to accept values which will be stored in AnyInt / Double flush formats
https://bugs.webkit.org/show_bug.cgi?id=195752

Reviewed by Saam Barati.

We fixed the bug skipping AbstractValue validations when the flush format is Double or AnyInt. But it
was too conservative. While validating inputs with AbstractValue is mandatory (without it, whole CFA
falls into wrong condition), our validation does not care AnyInt and Double representations in lower
tiers. For example, if a value is stored in Double flush format in DFG, its AbstractValue becomes
SpecFullDouble. However, it does not include Int32 and OSR entry is rejected if Int32 comes for DoubleRep
OSR entry value. This is wrong since we later convert these numbers into DoubleRep representation
before entering DFG code.

This patch performs AbstractValue validation onto the correctly converted value with flush format hint.

And it still does not fix OSR entry failures in navier-stokes. This is because AbstractValue representation
in navier-stokes's lin_solve was too strict. Then, this patch reverts r242627. Instead of removing must handle
value handling in CFA, DFG OSR entry now correctly validates inputs with AbstractValues even if the flush format
is Double or AnyInt. As long as DFG OSR entry validates inputs, merging must handle values as proven constants is OK.

We can see that # of OSR entry failures in navier-stokes.js becomes the same to the previous count. And we can see
AnyInt OSR entry actually works in microbenchmarks/large-int.js. However, AnyInt effect is hard to observe because this
is super rare. Since we inject type prediction based on must handle value, the flush format tends to be SpecAnyIntAsDouble
and it accepts JSValues simply.

  • bytecode/SpeculatedType.cpp:

(JSC::dumpSpeculation):

  • dfg/DFGAbstractValue.cpp:

(JSC::DFG::AbstractValue::filterValueByType):

  • dfg/DFGAbstractValue.h:

(JSC::DFG::AbstractValue::validateOSREntryValue const):
(JSC::DFG::AbstractValue::validateTypeAcceptingBoxedInt52 const):
(JSC::DFG::AbstractValue::validate const): Deleted.
(JSC::DFG::AbstractValue::validateType const): Deleted.

  • dfg/DFGCFAPhase.cpp:

(JSC::DFG::CFAPhase::run):
(JSC::DFG::CFAPhase::injectOSR):
(JSC::DFG::CFAPhase::performBlockCFA):

  • dfg/DFGOSREntry.cpp:

(JSC::DFG::prepareOSREntry):

Location:
trunk/Source/JavaScriptCore
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r242989 r242990  
     12019-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
    1442019-03-14  Saam barati  <sbarati@apple.com>
    245
  • trunk/Source/JavaScriptCore/bytecode/SpeculatedType.cpp

    r238923 r242990  
    262262       
    263263        if (value & SpecNonIntAsDouble)
    264             strOut.print("NonIntAsdouble");
     264            strOut.print("NonIntAsDouble");
    265265        else
    266266            isTop = false;
  • trunk/Source/JavaScriptCore/dfg/DFGAbstractValue.cpp

    r240023 r242990  
    345345        // The type is still non-empty. It may be that the new type renders
    346346        // 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))
    348348            clear();
    349349        return;
     
    352352    // The type has been rendered empty. That means that the value must now be invalid,
    353353    // as well.
    354     ASSERT(!m_value || !validateType(m_value));
     354    ASSERT(!m_value || !validateTypeAcceptingBoxedInt52(m_value));
    355355    m_value = JSValue();
    356356}
  • trunk/Source/JavaScriptCore/dfg/DFGAbstractValue.h

    r240023 r242990  
    3131#include "DFGAbstractValueClobberEpoch.h"
    3232#include "DFGFiltrationResult.h"
     33#include "DFGFlushFormat.h"
    3334#include "DFGFrozenValue.h"
    3435#include "DFGNodeFlags.h"
     
    370371    bool contains(RegisteredStructure) const;
    371372
    372     bool validate(JSValue value) const
     373    bool validateOSREntryValue(JSValue value, FlushFormat format) const
    373374    {
    374375        if (isHeapTop())
     
    378379            return false;
    379380       
    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            }
    386392        }
    387393       
     
    491497    }
    492498   
    493     bool validateType(JSValue value) const
     499    bool validateTypeAcceptingBoxedInt52(JSValue value) const
    494500    {
    495501        if (isHeapTop())
  • trunk/Source/JavaScriptCore/dfg/DFGCFAPhase.cpp

    r242627 r242990  
    7777        m_state.initialize();
    7878       
     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
    79116        do {
    80117            m_changed = false;
     
    83120       
    84121        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       
    85136            // Make sure we record the intersection of all proofs that we ever allowed the
    86137            // compiler to rely upon.
     
    107158   
    108159private:
     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   
    109203    void performBlockCFA(BasicBlock* block)
    110204    {
     
    116210            dataLog("   Block ", *block, ":\n");
    117211       
     212        if (m_blocksWithOSR.remove(block))
     213            injectOSR(block);
     214       
    118215        m_state.beginBasicBlock(block);
    119216        if (m_verbose) {
     
    171268    InPlaceAbstractState m_state;
    172269    AbstractInterpreter<InPlaceAbstractState> m_interpreter;
     270    BlockSet m_blocksWithOSR;
    173271   
    174272    bool m_verbose;
  • trunk/Source/JavaScriptCore/dfg/DFGOSREntry.cpp

    r242841 r242990  
    101101
    102102    if (!Options::useOSREntryToDFG())
    103         return 0;
     103        return nullptr;
    104104
    105105    if (Options::verboseOSR()) {
     
    138138        if (Options::verboseOSR())
    139139            dataLog("    OSR failed because the target code block is not DFG.\n");
    140         return 0;
     140        return nullptr;
    141141    }
    142142   
     
    147147        if (Options::verboseOSR())
    148148            dataLogF("    OSR failed because the entrypoint was optimized out.\n");
    149         return 0;
     149        return nullptr;
    150150    }
    151151   
     
    183183                dataLogF(".\n");
    184184            }
    185             return 0;
     185            return nullptr;
    186186        }
    187187       
     
    192192            value = exec->argument(argument - 1);
    193193       
    194         if (!entry->m_expectedValues.argument(argument).validate(value)) {
     194        if (!entry->m_expectedValues.argument(argument).validateOSREntryValue(value, FlushedJSValue)) {
    195195            if (Options::verboseOSR()) {
    196196                dataLog(
     
    198198                    ", expected ", entry->m_expectedValues.argument(argument), ".\n");
    199199            }
    200             return 0;
     200            return nullptr;
    201201        }
    202202    }
     
    205205        int localOffset = virtualRegisterForLocal(local).offset();
    206206        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 ",
    211213                    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
    216221        if (entry->m_localsForcedDouble.get(local)) {
    217222            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;
    238238        }
    239239    }
     
    250250        if (Options::verboseOSR())
    251251            dataLogF("    OSR failed because stack growth failed.\n");
    252         return 0;
     252        return nullptr;
    253253    }
    254254   
Note: See TracChangeset for help on using the changeset viewer.