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:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@153123 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/bytecode/LazyOperandValueProfile.cpp b/Source/JavaScriptCore/bytecode/LazyOperandValueProfile.cpp
index 97b8f3b..ea9534b 100644
--- a/Source/JavaScriptCore/bytecode/LazyOperandValueProfile.cpp
+++ b/Source/JavaScriptCore/bytecode/LazyOperandValueProfile.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -35,17 +35,17 @@
 CompressedLazyOperandValueProfileHolder::CompressedLazyOperandValueProfileHolder() { }
 CompressedLazyOperandValueProfileHolder::~CompressedLazyOperandValueProfileHolder() { }
 
-void CompressedLazyOperandValueProfileHolder::computeUpdatedPredictions(OperationInProgress operation)
+void CompressedLazyOperandValueProfileHolder::computeUpdatedPredictions(const CodeBlockLocker& locker, OperationInProgress operation)
 {
     if (!m_data)
         return;
     
     for (unsigned i = 0; i < m_data->size(); ++i)
-        m_data->at(i).computeUpdatedPrediction(operation);
+        m_data->at(i).computeUpdatedPrediction(locker, operation);
 }
 
 LazyOperandValueProfile* CompressedLazyOperandValueProfileHolder::add(
-    const LazyOperandValueProfileKey& key)
+    const CodeBlockLocker&, const LazyOperandValueProfileKey& key)
 {
     if (!m_data)
         m_data = adoptPtr(new LazyOperandValueProfile::List());
@@ -60,20 +60,22 @@
     return &m_data->last();
 }
 
-LazyOperandValueProfileParser::LazyOperandValueProfileParser(
-    CompressedLazyOperandValueProfileHolder& holder)
-    : m_holder(holder)
+LazyOperandValueProfileParser::LazyOperandValueProfileParser() { }
+LazyOperandValueProfileParser::~LazyOperandValueProfileParser() { }
+
+void LazyOperandValueProfileParser::initialize(
+    const CodeBlockLocker&, CompressedLazyOperandValueProfileHolder& holder)
 {
-    if (!m_holder.m_data)
+    ASSERT(m_map.isEmpty());
+    
+    if (!holder.m_data)
         return;
     
-    LazyOperandValueProfile::List& data = *m_holder.m_data;
+    LazyOperandValueProfile::List& data = *holder.m_data;
     for (unsigned i = 0; i < data.size(); ++i)
         m_map.add(data[i].key(), &data[i]);
 }
 
-LazyOperandValueProfileParser::~LazyOperandValueProfileParser() { }
-
 LazyOperandValueProfile* LazyOperandValueProfileParser::getIfPresent(
     const LazyOperandValueProfileKey& key) const
 {
@@ -87,13 +89,13 @@
 }
 
 SpeculatedType LazyOperandValueProfileParser::prediction(
-    const LazyOperandValueProfileKey& key) const
+    const CodeBlockLocker& locker, const LazyOperandValueProfileKey& key) const
 {
     LazyOperandValueProfile* profile = getIfPresent(key);
     if (!profile)
         return SpecNone;
     
-    return profile->computeUpdatedPrediction();
+    return profile->computeUpdatedPrediction(locker);
 }
 
 } // namespace JSC