Ignore:
Timestamp:
Jul 24, 2013, 8:58:47 PM (12 years ago)
Author:
[email protected]
Message:

fourthTier: value profiles and array profiles should be thread-safe enough to be accessible in a concurrent compilation thread
https://bugs.webkit.org/show_bug.cgi?id=114906

Source/JavaScriptCore:

Reviewed by Oliver Hunt.

This introduces thread safety to value profiles, array profiles, and
array allocation profiles.

We already have three separate operations that happen on profiles:
(1) writing, which the JIT, LLInt, and OSR exit do; (2) updating,
which happens during GC, from OSR entry slow-paths, and in the DFG;
and (3) reading, which happens in the DFG. For example, the JIT/LLInt
and OSR exit write to ValueProfile::m_buckets, which gets synthesized
into ValueProfile::m_prediction (and other fields) during update, and
the latter gets read by the DFG. Note that (2) must also happen in
the DFG since only the DFG knows which code blocks it will inline,
and those blocks' profiles may not have otherwise been updated via
any other mechanism.

I refer to these three operations as writing, updating, and reading.

Consequently, both profile updating and profile reading may happen
asynchronously, if the JIT is asynchronous.

The locking protocol for profiles works as follows:

  • Writing does not require locking, but is only allowed on the main thread. We require that these fields can be stored atomically by the profiling code, even without locks. For value profiles, this only works on 64-bit platforms, currently. For array profiles, which consist of multiple separate fields, this means that an asynchronous update of the profile may see slight inconsistencies (like a structure that doesn't quite match the array modes bits), but these should be harmless: at worst, the DFG will specialize too much and we'll have OSR exits.
  • Updating a value profile requires holding a lock, but must assume that the fields written by the profiling code in JIT/LLInt may be written to without locking.
  • Reading a value profile requires holding a lock.

The one major exception to these rules is the ArrayAllocationProfile,
which requires no locking. We do this because it's used so often and
in places where we don't necessarily have access to the owning
CodeBlock, so if we did want it to be locked it would have to have
its own lock. Also, I believe that it is sound to just make this
profile racy and not worry about locking at all. All that was needed
were some changes to ensure that we explicitly read some raced-over
fields only once.

Two additional interesting things in this change:

  • To make it easy to see which profile methods require locking, they take a const CodeBlockLocker& as an argument. I saw this idiom for identifying which methods require which locks to be held being used in LLVM, and I quite like it.
  • Lazy operand value profiles, which are created lazily and at any time, require the CodeBlockLock to be held when they are being created. Writes to them are lockless and main-thread-only, but as with other profiles, updates and reads require locking.
  • JavaScriptCore.xcodeproj/project.pbxproj:
  • bytecode/ArrayAllocationProfile.cpp:

(JSC::ArrayAllocationProfile::updateIndexingType):

  • bytecode/ArrayAllocationProfile.h:

(JSC::ArrayAllocationProfile::selectIndexingType):

  • bytecode/ArrayProfile.cpp:

(JSC::ArrayProfile::computeUpdatedPrediction):
(JSC::ArrayProfile::briefDescription):

  • bytecode/ArrayProfile.h:

(ArrayProfile):
(JSC::ArrayProfile::expectedStructure):
(JSC::ArrayProfile::structureIsPolymorphic):
(JSC::ArrayProfile::hasDefiniteStructure):
(JSC::ArrayProfile::observedArrayModes):
(JSC::ArrayProfile::mayInterceptIndexedAccesses):
(JSC::ArrayProfile::mayStoreToHole):
(JSC::ArrayProfile::outOfBounds):
(JSC::ArrayProfile::usesOriginalArrayStructures):

  • bytecode/CallLinkStatus.cpp:

(JSC::CallLinkStatus::computeFor):

  • bytecode/CodeBlock.cpp:

(JSC::CodeBlock::dumpValueProfiling):
(JSC::CodeBlock::dumpArrayProfiling):
(JSC::CodeBlock::updateAllPredictionsAndCountLiveness):
(JSC::CodeBlock::updateAllArrayPredictions):

  • bytecode/CodeBlock.h:

(JSC::CodeBlock::valueProfilePredictionForBytecodeOffset):
(JSC::CodeBlock::updateAllPredictionsAndCheckIfShouldOptimizeNow):
(CodeBlock):

  • bytecode/CodeBlockLock.h: Added.

(JSC):

  • bytecode/GetByIdStatus.cpp:

(JSC::GetByIdStatus::computeFor):

  • bytecode/LazyOperandValueProfile.cpp:

(JSC::CompressedLazyOperandValueProfileHolder::computeUpdatedPredictions):
(JSC::CompressedLazyOperandValueProfileHolder::add):
(JSC::LazyOperandValueProfileParser::LazyOperandValueProfileParser):
(JSC::LazyOperandValueProfileParser::~LazyOperandValueProfileParser):
(JSC):
(JSC::LazyOperandValueProfileParser::initialize):
(JSC::LazyOperandValueProfileParser::prediction):

  • bytecode/LazyOperandValueProfile.h:

(CompressedLazyOperandValueProfileHolder):
(LazyOperandValueProfileParser):

  • bytecode/MethodOfGettingAValueProfile.cpp:

(JSC::MethodOfGettingAValueProfile::getSpecFailBucket):

  • bytecode/PutByIdStatus.cpp:

(JSC::PutByIdStatus::computeFor):

  • bytecode/ResolveGlobalStatus.cpp:

(JSC::ResolveGlobalStatus::computeFor):

  • bytecode/ValueProfile.h:

(JSC::ValueProfileBase::briefDescription):
(ValueProfileBase):
(JSC::ValueProfileBase::computeUpdatedPrediction):

  • dfg/DFGArrayMode.cpp:

(JSC::DFG::ArrayMode::fromObserved):

  • dfg/DFGArrayMode.h:

(ArrayMode):
(JSC::DFG::ArrayMode::withProfile):

  • dfg/DFGByteCodeParser.cpp:

(JSC::DFG::ByteCodeParser::injectLazyOperandSpeculation):
(JSC::DFG::ByteCodeParser::getPredictionWithoutOSRExit):
(JSC::DFG::ByteCodeParser::getArrayMode):
(JSC::DFG::ByteCodeParser::getArrayModeAndEmitChecks):
(JSC::DFG::ByteCodeParser::parseResolveOperations):
(JSC::DFG::ByteCodeParser::parseBlock):
(JSC::DFG::ByteCodeParser::InlineStackEntry::InlineStackEntry):

  • dfg/DFGFixupPhase.cpp:

(JSC::DFG::FixupPhase::fixupNode):

  • dfg/DFGOSRExitPreparation.cpp:

(JSC::DFG::prepareCodeOriginForOSRExit):

  • dfg/DFGPredictionInjectionPhase.cpp:

(JSC::DFG::PredictionInjectionPhase::run):

  • jit/JITInlines.h:

(JSC::JIT::chooseArrayMode):

  • jit/JITStubs.cpp:

(JSC::tryCachePutByID):
(JSC::tryCacheGetByID):
(JSC::DEFINE_STUB_FUNCTION):
(JSC::lazyLinkFor):

  • llint/LLIntSlowPaths.cpp:

(JSC::LLInt::LLINT_SLOW_PATH_DECL):
(JSC::LLInt::setUpCall):

  • profiler/ProfilerBytecodeSequence.cpp:

(JSC::Profiler::BytecodeSequence::BytecodeSequence):

  • runtime/JSScope.cpp:

(JSC::JSScope::resolveContainingScopeInternal):
(JSC::JSScope::resolvePut):

Source/WTF:

Reviewed by Oliver Hunt.

Add ability to abstract whether or not the CodeBlock requires locking at all,
since some platforms may not support the byte spin-locking and/or may not want
to, if they turn off concurrent JIT.

  • WTF.xcodeproj/project.pbxproj:
  • wtf/ByteSpinLock.h:
  • wtf/NoLock.h: Added.

(WTF):
(NoLock):
(WTF::NoLock::lock):
(WTF::NoLock::unlock):
(WTF::NoLock::isHeld):

  • wtf/Platform.h:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/bytecode/ArrayProfile.h

    r149834 r153123  
    11/*
    2  * Copyright (C) 2012 Apple Inc. All rights reserved.
     2 * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2727#define ArrayProfile_h
    2828
     29#include "CodeBlockLock.h"
    2930#include "JSArray.h"
    3031#include "Structure.h"
     
    164165    }
    165166   
    166     void computeUpdatedPrediction(CodeBlock*, OperationInProgress = NoOperation);
    167    
    168     Structure* expectedStructure() const
    169     {
    170         if (structureIsPolymorphic())
     167    void computeUpdatedPrediction(const CodeBlockLocker&, CodeBlock*, OperationInProgress = NoOperation);
     168   
     169    Structure* expectedStructure(const CodeBlockLocker& locker) const
     170    {
     171        if (structureIsPolymorphic(locker))
    171172            return 0;
    172173        return m_expectedStructure;
    173174    }
    174     bool structureIsPolymorphic() const
     175    bool structureIsPolymorphic(const CodeBlockLocker&) const
    175176    {
    176177        return m_expectedStructure == polymorphicStructure();
    177178    }
    178     bool hasDefiniteStructure() const
    179     {
    180         return !structureIsPolymorphic() && m_expectedStructure;
    181     }
    182     ArrayModes observedArrayModes() const { return m_observedArrayModes; }
    183     ArrayModes updatedObservedArrayModes() const; // Computes the observed array modes without updating the profile.
    184     bool mayInterceptIndexedAccesses() const { return m_mayInterceptIndexedAccesses; }
    185    
    186     bool mayStoreToHole() const { return m_mayStoreToHole; }
    187     bool outOfBounds() const { return m_outOfBounds; }
    188    
    189     bool usesOriginalArrayStructures() const { return m_usesOriginalArrayStructures; }
    190    
    191     CString briefDescription(CodeBlock*);
     179    bool hasDefiniteStructure(const CodeBlockLocker& locker) const
     180    {
     181        return !structureIsPolymorphic(locker) && m_expectedStructure;
     182    }
     183    ArrayModes observedArrayModes(const CodeBlockLocker&) const { return m_observedArrayModes; }
     184    bool mayInterceptIndexedAccesses(const CodeBlockLocker&) const { return m_mayInterceptIndexedAccesses; }
     185   
     186    bool mayStoreToHole(const CodeBlockLocker&) const { return m_mayStoreToHole; }
     187    bool outOfBounds(const CodeBlockLocker&) const { return m_outOfBounds; }
     188   
     189    bool usesOriginalArrayStructures(const CodeBlockLocker&) const { return m_usesOriginalArrayStructures; }
     190   
     191    CString briefDescription(const CodeBlockLocker&, CodeBlock*);
    192192   
    193193private:
Note: See TracChangeset for help on using the changeset viewer.