Skip to content

Commit

Permalink
Prevent HiddenClass from being garbage collected
Browse files Browse the repository at this point in the history
Summary: To support the inline cache profiler, we need to keep track of the HiddenClass. These are weakly pointed to by the cache, so hidden classes could be garbage collected when the object is as well. This prevents that from happening.

Reviewed By: davedets

Differential Revision: D17059879

fbshipit-source-id: 4dced994b717f2c00efd807d05bf24596125bbdf
  • Loading branch information
JacksonGL authored and facebook-github-bot committed Sep 6, 2019
1 parent d023390 commit f04ba7b
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 0 deletions.
36 changes: 36 additions & 0 deletions include/hermes/VM/Runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "hermes/VM/StackFrame.h"
#include "hermes/VM/SymbolRegistry.h"
#include "hermes/VM/TwineChar16.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"

#include <atomic>
Expand Down Expand Up @@ -72,6 +73,10 @@ class ScopedNativeDepthTracker;
class ScopedNativeCallFrame;
class SamplingProfiler;

#ifdef HERMESVM_PROFILER_BB
class JSArray;
#endif

/// Number of stack words after the top of frame that we always ensure are
/// available. This is necessary so we can perform native calls with small
/// number of arguments without checking.
Expand Down Expand Up @@ -723,6 +728,25 @@ class Runtime : public HandleRootOwner,
}
#endif

#ifdef HERMESVM_PROFILER_BB
/// Inserts the Hidden class as a root to prevent it from being garbage
/// collected.
void preventHCGC(HiddenClass *hc);

/// Inserts Hidden Classes into InlineCacheProfiler
void recordHiddenClass(
CodeBlock *codeBlock,
const Inst *cacheMissInst,
SymbolID symbolID,
HiddenClass *objectHiddenClass,
HiddenClass *cachedHiddenClass);

using DebugId = uint64_t;

/// Resolves HiddenClass pointers from its DebugId.
HiddenClass *resolveHCDebugId(DebugId debugId);
#endif

protected:
/// Construct a Runtime on the stack.
/// NOTE: This should only be used by StackRuntime. All other uses should use
Expand Down Expand Up @@ -995,6 +1019,18 @@ class Runtime : public HandleRootOwner,

#ifdef HERMESVM_PROFILER_BB
BasicBlockExecutionInfo basicBlockExecInfo_;

/// Keep track of the current index of the hidden class array.
/// used by the inline caching profiler
uint32_t hcIdx_{0};

/// Store an array of hidden classes that will be used
/// by inline caching profiler.
JSArray *cachedHiddenClassesRawPtr_;

/// Store a map from object Id to array index in the array
/// referenced by cachedHiddenClassesRawPtr_.
llvm::DenseMap<DebugId, int32_t> debugIdToIdx_;
#endif

/// Store a key for the function that is executed if a crash occurs.
Expand Down
50 changes: 50 additions & 0 deletions lib/VM/Runtime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,19 @@
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"

#ifdef HERMESVM_PROFILER_BB
#include "hermes/VM/IterationKind.h"
#include "hermes/VM/JSArray.h"
#include "llvm/ADT/DenseMap.h"
#endif

namespace hermes {
namespace vm {

#ifdef HERMESVM_PROFILER_BB
using DebugId = Runtime::DebugId;
#endif

namespace {

/// The maximum number of registers that can be requested in a RuntimeConfig.
Expand Down Expand Up @@ -335,6 +345,11 @@ Runtime::Runtime(StorageProvider *provider, const RuntimeConfig &runtimeConfig)

samplingProfiler_ = SamplingProfiler::getInstance();
samplingProfiler_->registerRuntime(this);

#ifdef HERMESVM_PROFILER_BB
cachedHiddenClassesRawPtr_ =
ignoreAllocationFailure(JSArray::create(this, 4, 4)).get();
#endif
}

Runtime::~Runtime() {
Expand Down Expand Up @@ -495,6 +510,9 @@ void Runtime::markRoots(RootAcceptor &acceptor, bool markLongLived) {
if (samplingProfiler_) {
samplingProfiler_->markRoots(acceptor);
}
#ifdef HERMESVM_PROFILER_BB
acceptor.acceptPtr(cachedHiddenClassesRawPtr_, "");
#endif
acceptor.endRootSection();
}

Expand Down Expand Up @@ -1561,6 +1579,38 @@ std::string Runtime::getCallStackNoAlloc(const Inst *ip) {
return res;
}

#ifdef HERMESVM_PROFILER_BB
void Runtime::preventHCGC(HiddenClass *hc) {
auto ret = debugIdToIdx_.insert(
std::pair<DebugId, uint32_t>(heap_.getObjectID(hc), hcIdx_));
if (ret.second) {
JSArray::setElementAt(
makeHandle(cachedHiddenClassesRawPtr_), this, hcIdx_++, makeHandle(hc));
}
}

void Runtime::recordHiddenClass(
CodeBlock *codeBlock,
const Inst *cacheMissInst,
SymbolID symbolID,
HiddenClass *objectHiddenClass,
HiddenClass *cachedHiddenClass) {
if (objectHiddenClass != nullptr) {
preventHCGC(objectHiddenClass);
}
if (cachedHiddenClass != nullptr) {
preventHCGC(cachedHiddenClass);
}
}

HiddenClass *Runtime::resolveHCDebugId(DebugId debugId) {
auto hcHermesVal =
cachedHiddenClassesRawPtr_->at(this, debugIdToIdx_[debugId]);
return vmcast<HiddenClass>(hcHermesVal);
}

#endif

#ifdef HERMESVM_SERIALIZE
void Runtime::serialize(Serializer &s) {
// Full GC here.
Expand Down

0 comments on commit f04ba7b

Please sign in to comment.