Skip to content

Commit

Permalink
Record inline caching misses during execution
Browse files Browse the repository at this point in the history
Summary:
This diff adds the implementation of data structure that keeps track of inline caching misses during the execution.

Whenever an inline caching miss is detected. The inline caching profiler adds a record in the data structure, which is a two-layer hash map. The first layer is indexed by source location (i.e., code block pointer and instruction offset), the second layer is indexed by the tuple of <property id, object hidden class, cached hidden class>.

Reviewed By: dulinriley

Differential Revision: D17165022

fbshipit-source-id: 23880f597ff5a2f46618292e5d1c0f6a56eefade
  • Loading branch information
JacksonGL authored and facebook-github-bot committed Sep 11, 2019
1 parent 1d4413a commit 8504f1f
Show file tree
Hide file tree
Showing 7 changed files with 229 additions and 31 deletions.
2 changes: 1 addition & 1 deletion doc/VM.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ Objects have a special `ObjectVTable`.

### Objects

Each JS object is represented by `Object` (or a class derived from `Object).
Each JS object is represented by `Object` (or a class derived from `Object`).
JS objects have a set of name/value pairs, and some optional "indexed storage".
Read more about how `Object` works in `ObjectModel.h`.
The Runtime contains a global object which is used to store in global scope.
Expand Down
103 changes: 103 additions & 0 deletions include/hermes/VM/Profiler/InlineCacheProfiler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the LICENSE
* file in the root directory of this source tree.
*/
#ifndef INLINECACHE_PROFILER_H
#define INLINECACHE_PROFILER_H

#include "hermes/VM/SymbolID.h"
#include "llvm/ADT/DenseMap.h"

namespace hermes {
namespace vm {

class CodeBlock;
class HiddenClass;
class JSArray;

class InlineCacheProfiler {
public:
using ClassId = uint64_t;
using PropertyId = uint32_t;
using ICSrcKey = std::pair<uint32_t, CodeBlock *>;
using HiddenClassPair = std::pair<ClassId, ClassId>;
using ICMissKey = std::pair<PropertyId, HiddenClassPair>;

/// Stores all inline caching misses at one specific source location, which
/// is uniquely identified by code block and instruction offset.
struct ICMiss {
/// Increment the inline caching miss count for a pair of hidden classes.
void insertMiss(ICMissKey icRecord) {
auto ret =
hiddenClasses.insert(std::pair<ICMissKey, uint32_t>(icRecord, 1));
if (!ret.second) {
++(ret.first->second);
}
++missCount;
}

/// Total number of inline caching misses at the source location.
uint32_t missCount{0};

/// Internal map that keeps track of the mapping between
/// <property, object hidden class, cached hidden class> and its frequency.
llvm::DenseMap<ICMissKey, uint32_t> hiddenClasses;
};

/// Add an inline caching miss record.
bool insertICMiss(
CodeBlock *codeblock,
uint32_t instOffset,
SymbolID &propertyID,
ClassId objectHiddenClassId,
ClassId cachedHiddenClassId);

/// Get the total number of inline caching misses.
uint32_t getTotalMisses() {
return totalMisses_;
}

/// Get a JS array containing all hidden classes that shouldn't be
/// garbage collected.
JSArray *getHiddenClassArray();

/// Set a JS array containing all hidden classes that shouldn't be
/// garbage collected.
void setHiddenClassArray(JSArray *hiddenClassArray);

/// Get the current index of element in the hidden class array
/// used by the inline caching profiler.
uint32_t &getHiddenClassArrayIndex();

/// Get a map from object Id to array index in the array
/// referenced by cachedHiddenClassesRawPtr_.
llvm::DenseMap<InlineCacheProfiler::ClassId, int32_t> &getClassIdtoIndexMap();

private:
/// 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<InlineCacheProfiler::ClassId, int32_t> classIdToIdx_;

/// Total number of inline caching misses during the program execution.
uint32_t totalMisses_{0};

/// Store the data structure of all inline caching misses information.
/// The map is keyed by pairs <instruction offset, CodeBlock> and maps
/// to ICMiss objects, which keeps track of hidden classes and frequency.
llvm::DenseMap<ICSrcKey, ICMiss> cacheMisses_;
};

} // namespace vm
} // namespace hermes

#endif
26 changes: 11 additions & 15 deletions include/hermes/VM/Runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@
#include "hermes/VM/StackFrame.h"
#include "hermes/VM/SymbolRegistry.h"
#include "hermes/VM/TwineChar16.h"

#ifdef HERMESVM_PROFILER_BB
#include "hermes/VM/Profiler/InlineCacheProfiler.h"
#endif

#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"

Expand Down Expand Up @@ -729,6 +734,8 @@ class Runtime : public HandleRootOwner,
#endif

#ifdef HERMESVM_PROFILER_BB
using ClassId = InlineCacheProfiler::ClassId;

/// Inserts the Hidden class as a root to prevent it from being garbage
/// collected.
void preventHCGC(HiddenClass *hc);
Expand All @@ -741,10 +748,8 @@ class Runtime : public HandleRootOwner,
HiddenClass *objectHiddenClass,
HiddenClass *cachedHiddenClass);

using DebugId = uint64_t;

/// Resolves HiddenClass pointers from its DebugId.
HiddenClass *resolveHCDebugId(DebugId debugId);
/// Resolve HiddenClass pointers from its hidden class Id.
HiddenClass *resolveHiddenClassId(ClassId classId);
#endif

protected:
Expand Down Expand Up @@ -1020,17 +1025,8 @@ 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_;
/// Store all inline caching miss information.
InlineCacheProfiler inlineCacheProfiler_;
#endif

/// Store a key for the function that is executed if a crash occurs.
Expand Down
1 change: 1 addition & 0 deletions lib/VM/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ set(source_files
RuntimeModule.cpp
RuntimeStats.cpp
Profiler/ChromeTraceSerializerPosix.cpp
Profiler/InlineCacheProfiler.cpp
Profiler/SamplingProfilerWindows.cpp
Profiler/SamplingProfilerPosix.cpp
Serializer.cpp
Expand Down
15 changes: 15 additions & 0 deletions lib/VM/Interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2171,6 +2171,13 @@ CallResult<HermesValue> Interpreter::interpretFunction(
DISPATCH;
}
auto id = ID(idVal);
#ifdef HERMESVM_PROFILER_BB
if (LLVM_LIKELY(cacheEntry->clazz && cacheEntry->clazz != clazz)) {
runtime->recordHiddenClass(
curCodeBlock, ip, id, clazz, cacheEntry->clazz);
gcScope.flushToSmallCount(KEEP_HANDLES);
}
#endif
NamedPropertyDescriptor desc;
OptValue<bool> fastPathResult =
JSObject::tryGetOwnNamedDescriptorFast(obj, runtime, id, desc);
Expand Down Expand Up @@ -2304,6 +2311,14 @@ CallResult<HermesValue> Interpreter::interpretFunction(
DISPATCH;
}
auto id = ID(idVal);

#ifdef HERMESVM_PROFILER_BB
if (LLVM_LIKELY(cacheEntry->clazz && cacheEntry->clazz != clazz)) {
runtime->recordHiddenClass(
curCodeBlock, ip, id, clazz, cacheEntry->clazz);
gcScope.flushToSmallCount(KEEP_HANDLES);
}
#endif
NamedPropertyDescriptor desc;
OptValue<bool> hasOwnProp =
JSObject::tryGetOwnNamedDescriptorFast(obj, runtime, id, desc);
Expand Down
68 changes: 68 additions & 0 deletions lib/VM/Profiler/InlineCacheProfiler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the LICENSE
* file in the root directory of this source tree.
*/
#ifdef HERMESVM_PROFILER_BB
#ifndef INLINECACHE_PROFILER_H
#include "hermes/VM/Profiler/InlineCacheProfiler.h"
#include "hermes/VM/CodeBlock.h"
#include "hermes/VM/Handle.h"
#include "hermes/VM/HiddenClass.h"
#include "hermes/VM/JSArray.h"
#include "hermes/VM/Runtime.h"
#include "hermes/VM/StringView.h"
#include "hermes/VM/SymbolID.h"

#include <vector>

namespace hermes {
namespace vm {

bool InlineCacheProfiler::insertICMiss(
CodeBlock *codeblock,
uint32_t instOffset,
SymbolID &propertyID,
ClassId objectHiddenClassId,
ClassId cachedHiddenClassId) {
// if not exist, create inline caching entry for the source location
ICSrcKey icKey(instOffset, codeblock);
auto it = cacheMisses_.find(icKey);
if (it == cacheMisses_.end()) {
ICMiss icMiss;
auto p = cacheMisses_.try_emplace(icKey, icMiss);
assert(p.second && "Guaranteed to insert");
it = p.first;
}
// record the hidden class pair for the source location
auto hcPair =
std::pair<ClassId, ClassId>(objectHiddenClassId, cachedHiddenClassId);
auto icRecord =
std::pair<PropertyId, HiddenClassPair>(propertyID.unsafeGetRaw(), hcPair);
it->second.insertMiss(icRecord);

++totalMisses_;
return true;
}

JSArray *InlineCacheProfiler::getHiddenClassArray() {
return cachedHiddenClassesRawPtr_;
}

void InlineCacheProfiler::setHiddenClassArray(JSArray *hiddenClassArray) {
cachedHiddenClassesRawPtr_ = hiddenClassArray;
}

uint32_t &InlineCacheProfiler::getHiddenClassArrayIndex() {
return hcIdx_;
}

llvm::DenseMap<InlineCacheProfiler::ClassId, int32_t>
&InlineCacheProfiler::getClassIdtoIndexMap() {
return classIdToIdx_;
}
} // namespace vm
} // namespace hermes
#endif
#endif
45 changes: 30 additions & 15 deletions lib/VM/Runtime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,13 @@
#ifdef HERMESVM_PROFILER_BB
#include "hermes/VM/IterationKind.h"
#include "hermes/VM/JSArray.h"
#include "hermes/VM/Profiler/InlineCacheProfiler.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 @@ -352,8 +349,8 @@ Runtime::Runtime(StorageProvider *provider, const RuntimeConfig &runtimeConfig)
samplingProfiler_->registerRuntime(this);

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

Expand Down Expand Up @@ -516,7 +513,9 @@ void Runtime::markRoots(RootAcceptor &acceptor, bool markLongLived) {
samplingProfiler_->markRoots(acceptor);
}
#ifdef HERMESVM_PROFILER_BB
acceptor.acceptPtr(cachedHiddenClassesRawPtr_, "");
auto *hiddenCalssArray = inlineCacheProfiler_.getHiddenClassArray();
acceptor.acceptPtr(hiddenCalssArray, "");
inlineCacheProfiler_.setHiddenClassArray(hiddenCalssArray);
#endif
acceptor.endRootSection();
}
Expand Down Expand Up @@ -1586,11 +1585,14 @@ std::string Runtime::getCallStackNoAlloc(const Inst *ip) {

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

Expand All @@ -1600,17 +1602,30 @@ void Runtime::recordHiddenClass(
SymbolID symbolID,
HiddenClass *objectHiddenClass,
HiddenClass *cachedHiddenClass) {
if (objectHiddenClass != nullptr) {
auto offset = codeBlock->getOffsetOf(cacheMissInst);
ClassId noId =
static_cast<ClassId>(GCBase::IDTracker::ReservedObjectID::NoID);
ClassId objectHiddenClassId = noId;
ClassId cachedHiddenClassId = noId;
if (objectHiddenClass) {
preventHCGC(objectHiddenClass);
objectHiddenClassId = heap_.getObjectID(objectHiddenClass);
}
if (cachedHiddenClass != nullptr) {
if (cachedHiddenClass) {
preventHCGC(cachedHiddenClass);
cachedHiddenClassId = heap_.getObjectID(cachedHiddenClass);
}
inlineCacheProfiler_.insertICMiss(
codeBlock, offset, symbolID, objectHiddenClassId, cachedHiddenClassId);
}

HiddenClass *Runtime::resolveHCDebugId(DebugId debugId) {
auto hcHermesVal =
cachedHiddenClassesRawPtr_->at(this, debugIdToIdx_[debugId]);
HiddenClass *Runtime::resolveHiddenClassId(ClassId classId) {
if (classId == 0) {
return nullptr;
}
auto &classIdToIdxMap = inlineCacheProfiler_.getClassIdtoIndexMap();
auto *hiddenClassArray = inlineCacheProfiler_.getHiddenClassArray();
auto hcHermesVal = hiddenClassArray->at(this, classIdToIdxMap[classId]);
return vmcast<HiddenClass>(hcHermesVal);
}

Expand Down

0 comments on commit 8504f1f

Please sign in to comment.