Bug 1127156 - Rework optimization tracking JSAPI to be more usable from the profiler. (r=djvj)

This commit is contained in:
Shu-yu Guo 2015-02-04 13:40:02 -08:00
parent b397eba5c7
commit 0b016e2921
17 changed files with 880 additions and 462 deletions

View File

@ -103,6 +103,8 @@ class JS_PUBLIC_API(ProfilingFrameIterator)
void *returnAddress;
void *activation;
const char *label;
bool hasTrackedOptimizations;
uint8_t trackedOptimizationIndex;
};
uint32_t extractStack(Frame *frames, uint32_t offset, uint32_t end) const;

View File

@ -0,0 +1,316 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef js_TrackedOptimizationInfo_h
#define js_TrackedOptimizationInfo_h
namespace JS {
#define TRACKED_STRATEGY_LIST(_) \
_(GetProp_ArgumentsLength, \
"getprop arguments.length") \
_(GetProp_ArgumentsCallee, \
"getprop arguments.callee") \
_(GetProp_InferredConstant, \
"getprop inferred constant") \
_(GetProp_Constant, \
"getprop constant") \
_(GetProp_TypedObject, \
"getprop TypedObject") \
_(GetProp_DefiniteSlot, \
"getprop definite slot") \
_(GetProp_Unboxed, \
"getprop unboxed object") \
_(GetProp_CommonGetter, \
"getprop common getter") \
_(GetProp_InlineAccess, \
"getprop inline access") \
_(GetProp_Innerize, \
"getprop innerize (access on global window)") \
_(GetProp_InlineCache, \
"getprop IC") \
\
_(SetProp_CommonSetter, \
"setprop common setter") \
_(SetProp_TypedObject, \
"setprop TypedObject") \
_(SetProp_DefiniteSlot, \
"setprop definite slot") \
_(SetProp_Unboxed, \
"setprop unboxed object") \
_(SetProp_InlineAccess, \
"setprop inline access") \
\
_(GetElem_TypedObject, \
"getprop TypedObject") \
_(GetElem_Dense, \
"getelem dense") \
_(GetElem_TypedStatic, \
"getelem TypedArray static") \
_(GetElem_TypedArray, \
"getelem TypedArray") \
_(GetElem_String, \
"getelem string") \
_(GetElem_Arguments, \
"getelem arguments") \
_(GetElem_ArgumentsInlined, \
"getelem arguments inlined") \
_(GetElem_InlineCache, \
"getelem IC") \
\
_(SetElem_TypedObject, \
"setelem TypedObject") \
_(SetElem_TypedStatic, \
"setelem TypedArray static") \
_(SetElem_TypedArray, \
"setelem TypedArray") \
_(SetElem_Dense, \
"setelem dense") \
_(SetElem_Arguments, \
"setelem arguments") \
_(SetElem_InlineCache, \
"setelem IC") \
\
_(Call_Inline, \
"call inline")
// Ordering is important below. All outcomes before GenericSuccess will be
// considered failures, and all outcomes after GenericSuccess will be
// considered successes.
#define TRACKED_OUTCOME_LIST(_) \
_(GenericFailure, \
"failure") \
_(Disabled, \
"disabled") \
_(NoTypeInfo, \
"no type info") \
_(NoAnalysisInfo, \
"no newscript analysis") \
_(NoShapeInfo, \
"cannot determine shape") \
_(UnknownObject, \
"unknown object") \
_(UnknownProperties, \
"unknown properties") \
_(Singleton, \
"is singleton") \
_(NotSingleton, \
"is not singleton") \
_(NotFixedSlot, \
"property not in fixed slot") \
_(InconsistentFixedSlot, \
"property not in a consistent fixed slot") \
_(NotObject, \
"not definitely an object") \
_(NotStruct, \
"not definitely a TypedObject struct") \
_(NotUnboxed, \
"not definitely an unboxed object") \
_(StructNoField, \
"struct doesn't definitely have field") \
_(InconsistentFieldType, \
"unboxed property does not have consistent type") \
_(InconsistentFieldOffset, \
"unboxed property does not have consistent offset") \
_(NeedsTypeBarrier, \
"needs type barrier") \
_(InDictionaryMode, \
"object in dictionary mode") \
_(NoProtoFound, \
"no proto found") \
_(MultiProtoPaths, \
"not all paths to property go through same proto") \
_(NonWritableProperty, \
"non-writable property") \
_(ProtoIndexedProps, \
"prototype has indexed properties") \
_(ArrayBadFlags, \
"array observed to be sparse, overflowed .length, or has been iterated") \
_(ArrayDoubleConversion, \
"array has ambiguous double conversion") \
_(ArrayRange, \
"array range issue (.length problems)") \
_(ArraySeenNegativeIndex, \
"has seen array access with negative index") \
_(TypedObjectNeutered, \
"TypedObject might have been neutered") \
_(TypedObjectArrayRange, \
"TypedObject array of unknown length") \
_(AccessNotDense, \
"access not on dense native (check receiver, index, and result types)") \
_(AccessNotTypedObject, \
"access not on typed array (check receiver and index types)") \
_(AccessNotTypedArray, \
"access not on typed array (check receiver, index, and result types)") \
_(AccessNotString, \
"getelem not on string (check receiver and index types)") \
_(StaticTypedArrayUint32, \
"static uint32 arrays currently cannot be optimized") \
_(StaticTypedArrayCantComputeMask, \
"can't compute mask for static typed array access (index isn't constant or not int32)") \
_(OutOfBounds, \
"observed out of bounds access") \
_(GetElemStringNotCached, \
"getelem on strings is not inline cached") \
_(NonNativeReceiver, \
"observed non-native receiver") \
_(IndexType, \
"index type must be int32, string, or symbol") \
_(SetElemNonDenseNonTANotCached, \
"setelem on non-dense non-TAs are not inline cached") \
\
_(CantInlineGeneric, \
"can't inline") \
_(CantInlineNoTarget, \
"can't inline: no target") \
_(CantInlineNotInterpreted, \
"can't inline: not interpreted") \
_(CantInlineNoBaseline, \
"can't inline: no baseline code") \
_(CantInlineLazy, \
"can't inline: lazy script") \
_(CantInlineNotConstructor, \
"can't inline: calling non-constructor with 'new'") \
_(CantInlineDisabledIon, \
"can't inline: ion disabled for callee") \
_(CantInlineTooManyArgs, \
"can't inline: too many arguments") \
_(CantInlineRecursive, \
"can't inline: recursive") \
_(CantInlineHeavyweight, \
"can't inline: heavyweight") \
_(CantInlineNeedsArgsObj, \
"can't inline: needs arguments object") \
_(CantInlineDebuggee, \
"can't inline: debuggee") \
_(CantInlineUnknownProps, \
"can't inline: type has unknown properties") \
_(CantInlineExceededDepth, \
"can't inline: exceeded inlining depth") \
_(CantInlineBigLoop, \
"can't inline: big function with a loop") \
_(CantInlineBigCaller, \
"can't inline: big caller") \
_(CantInlineBigCallee, \
"can't inline: big callee") \
_(CantInlineNotHot, \
"can't inline: not hot enough") \
_(CantInlineNotInDispatch, \
"can't inline: not in dispatch table") \
_(CantInlineNativeBadForm, \
"can't inline native: bad form (arity mismatch/constructing)") \
_(CantInlineNativeBadType, \
"can't inline native: bad argument or return type observed") \
_(CantInlineNativeNoTemplateObj, \
"can't inline native: no template object") \
_(CantInlineBound, \
"can't inline bound function invocation") \
\
_(GenericSuccess, \
"success") \
_(Inlined, \
"inlined") \
_(DOM, \
"DOM") \
_(Monomorphic, \
"monomorphic") \
_(Polymorphic, \
"polymorphic")
#define TRACKED_TYPESITE_LIST(_) \
_(Receiver, \
"receiver object") \
_(Index, \
"index") \
_(Value, \
"value") \
_(Call_Target, \
"call target") \
_(Call_This, \
"call 'this'") \
_(Call_Arg, \
"call argument") \
_(Call_Return, \
"call return")
enum class TrackedStrategy : uint32_t {
#define STRATEGY_OP(name, msg) name,
TRACKED_STRATEGY_LIST(STRATEGY_OP)
#undef STRATEGY_OPT
Count
};
enum class TrackedOutcome : uint32_t {
#define OUTCOME_OP(name, msg) name,
TRACKED_OUTCOME_LIST(OUTCOME_OP)
#undef OUTCOME_OP
Count
};
enum class TrackedTypeSite : uint32_t {
#define TYPESITE_OP(name, msg) name,
TRACKED_TYPESITE_LIST(TYPESITE_OP)
#undef TYPESITE_OP
Count
};
extern JS_PUBLIC_API(const char *)
TrackedStrategyString(TrackedStrategy strategy);
extern JS_PUBLIC_API(const char *)
TrackedOutcomeString(TrackedOutcome outcome);
extern JS_PUBLIC_API(const char *)
TrackedTypeSiteString(TrackedTypeSite site);
struct ForEachTrackedOptimizationAttemptOp
{
virtual void operator()(TrackedStrategy strategy, TrackedOutcome outcome) = 0;
};
JS_PUBLIC_API(void)
ForEachTrackedOptimizationAttempt(JSRuntime *rt, void *addr, uint8_t index,
ForEachTrackedOptimizationAttemptOp &op);
struct ForEachTrackedOptimizationTypeInfoOp
{
// Called 0+ times per entry, once for each type in the type set that Ion
// saw during MIR construction. readType is always called _before_
// operator() on the same entry.
//
// The keyedBy parameter describes how the type is keyed:
// - "primitive" for primitive types
// - "constructor" for object types tied to a scripted constructor
// function.
// - "alloc site" for object types tied to an allocation site.
// - "prototype" for object types tied neither to a constructor nor
// to an allocation site.
//
// The name parameter is the string representation of the type. If the
// type is keyed by "constructor", or if the type itself refers to a
// scripted function, the name is the function's displayAtom.
//
// If the type is keyed by "constructor", "alloc site", or if the type
// itself refers to a scripted function, the location and lineno
// parameters will be respectively non-nullptr and non-0.
virtual void readType(const char *keyedBy, const char *name,
const char *location, unsigned lineno) = 0;
// Called once per entry.
virtual void operator()(TrackedTypeSite site, const char *mirType) = 0;
};
extern JS_PUBLIC_API(void)
ForEachTrackedOptimizationTypeInfo(JSRuntime *rt, void *addr, uint8_t index,
ForEachTrackedOptimizationTypeInfoOp &op);
} // namespace JS
#endif // js_TrackedOptimizationInfo_h

View File

@ -7328,7 +7328,7 @@ CodeGenerator::link(JSContext *cx, types::CompilerConstraintList *constraints)
// Generate the tracked optimizations map.
if (isOptimizationTrackingEnabled()) {
// Treat OOMs and failures as if optimization tracking were turned off.
types::TypeSet::TypeList *allTypes = cx->new_<types::TypeSet::TypeList>();
IonTrackedTypeVector *allTypes = cx->new_<IonTrackedTypeVector>();
if (allTypes && generateCompactTrackedOptimizationsMap(cx, code, allTypes)) {
const uint8_t *optsRegionTableAddr = trackedOptimizationsMap_ +
trackedOptimizationsRegionTableOffset_;

View File

@ -38,6 +38,10 @@ using mozilla::AssertedCast;
using mozilla::DebugOnly;
using mozilla::Maybe;
using JS::TrackedStrategy;
using JS::TrackedOutcome;
using JS::TrackedTypeSite;
class jit::BaselineFrameInspector
{
public:

View File

@ -1116,13 +1116,13 @@ class IonBuilder
// The track* methods below are called often. Do not combine them with the
// unchecked variants, despite the unchecked variants having no other
// callers.
void trackTypeInfo(TrackedTypeSite site, MIRType mirType,
void trackTypeInfo(JS::TrackedTypeSite site, MIRType mirType,
types::TemporaryTypeSet *typeSet)
{
if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
trackTypeInfoUnchecked(site, mirType, typeSet);
}
void trackTypeInfo(TrackedTypeSite site, JSObject *obj) {
void trackTypeInfo(JS::TrackedTypeSite site, JSObject *obj) {
if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
trackTypeInfoUnchecked(site, obj);
}
@ -1130,7 +1130,7 @@ class IonBuilder
if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
trackTypeInfoUnchecked(callInfo);
}
void trackOptimizationAttempt(TrackedStrategy strategy) {
void trackOptimizationAttempt(JS::TrackedStrategy strategy) {
if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
trackOptimizationAttemptUnchecked(strategy);
}
@ -1138,7 +1138,7 @@ class IonBuilder
if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
amendOptimizationAttemptUnchecked(index);
}
void trackOptimizationOutcome(TrackedOutcome outcome) {
void trackOptimizationOutcome(JS::TrackedOutcome outcome) {
if (MOZ_UNLIKELY(current->trackedSite()->hasOptimizations()))
trackOptimizationOutcomeUnchecked(outcome);
}
@ -1153,13 +1153,13 @@ class IonBuilder
// Out-of-line variants that don't check if optimization tracking is
// enabled.
void trackTypeInfoUnchecked(TrackedTypeSite site, MIRType mirType,
void trackTypeInfoUnchecked(JS::TrackedTypeSite site, MIRType mirType,
types::TemporaryTypeSet *typeSet);
void trackTypeInfoUnchecked(TrackedTypeSite site, JSObject *obj);
void trackTypeInfoUnchecked(JS::TrackedTypeSite site, JSObject *obj);
void trackTypeInfoUnchecked(CallInfo &callInfo);
void trackOptimizationAttemptUnchecked(TrackedStrategy strategy);
void trackOptimizationAttemptUnchecked(JS::TrackedStrategy strategy);
void amendOptimizationAttemptUnchecked(uint32_t index);
void trackOptimizationOutcomeUnchecked(TrackedOutcome outcome);
void trackOptimizationOutcomeUnchecked(JS::TrackedOutcome outcome);
void trackOptimizationSuccessUnchecked();
void trackInlineSuccessUnchecked(InliningStatus status);
};

View File

@ -115,14 +115,14 @@ class JitcodeGlobalEntry
// attempts vectors.
//
// All pointers point into the same block of memory; the beginning of
// the block is optimizationRegionTable_->payloadStart().
// the block is optRegionTable_->payloadStart().
const IonTrackedOptimizationsRegionTable *optsRegionTable_;
const IonTrackedOptimizationsTypesTable *optsTypesTable_;
const IonTrackedOptimizationsAttemptsTable *optsAttemptsTable_;
// The types table above records type sets, which have been gathered
// into one vector here.
types::TypeSet::TypeList *optsAllTypes_;
IonTrackedTypeVector *optsAllTypes_;
struct ScriptNamePair {
JSScript *script;
@ -163,7 +163,7 @@ class JitcodeGlobalEntry
void initTrackedOptimizations(const IonTrackedOptimizationsRegionTable *regionTable,
const IonTrackedOptimizationsTypesTable *typesTable,
const IonTrackedOptimizationsAttemptsTable *attemptsTable,
types::TypeSet::TypeList *allTypes)
IonTrackedTypeVector *allTypes)
{
optsRegionTable_ = regionTable;
optsTypesTable_ = typesTable;
@ -214,7 +214,22 @@ class JitcodeGlobalEntry
return !!optsRegionTable_;
}
bool optimizationAttemptsAtAddr(void *ptr, mozilla::Maybe<AttemptsVector> &attempts);
IonTrackedOptimizationsAttempts trackedOptimizationAttempts(uint8_t index) {
MOZ_ASSERT(hasTrackedOptimizations());
return optsAttemptsTable_->entry(index);
}
IonTrackedOptimizationsTypeInfo trackedOptimizationTypeInfo(uint8_t index) {
MOZ_ASSERT(hasTrackedOptimizations());
return optsTypesTable_->entry(index);
}
const IonTrackedTypeVector *allTrackedTypes() {
MOZ_ASSERT(hasTrackedOptimizations());
return optsAllTypes_;
}
mozilla::Maybe<uint8_t> trackedOptimizationIndexAtAddr(void *ptr);
};
struct BaselineEntry : public BaseEntry
@ -545,6 +560,46 @@ class JitcodeGlobalEntry
// Compute a profiling string for a given script.
static char *createScriptString(JSContext *cx, JSScript *script, size_t *length=nullptr);
bool hasTrackedOptimizations() const {
switch (kind()) {
case Ion:
return ionEntry().hasTrackedOptimizations();
case Baseline:
case IonCache:
case Dummy:
break;
default:
MOZ_CRASH("Invalid JitcodeGlobalEntry kind.");
}
return false;
}
mozilla::Maybe<uint8_t> trackedOptimizationIndexAtAddr(void *addr) {
switch (kind()) {
case Ion:
return ionEntry().trackedOptimizationIndexAtAddr(addr);
case Baseline:
case IonCache:
case Dummy:
break;
default:
MOZ_CRASH("Invalid JitcodeGlobalEntry kind.");
}
return mozilla::Nothing();
}
IonTrackedOptimizationsAttempts trackedOptimizationAttempts(uint8_t index) {
return ionEntry().trackedOptimizationAttempts(index);
}
IonTrackedOptimizationsTypeInfo trackedOptimizationTypeInfo(uint8_t index) {
return ionEntry().trackedOptimizationTypeInfo(index);
}
const IonTrackedTypeVector *allTrackedTypes() {
return ionEntry().allTrackedTypes();
}
};
/*

View File

@ -24,6 +24,10 @@
using mozilla::ArrayLength;
using JS::TrackedStrategy;
using JS::TrackedOutcome;
using JS::TrackedTypeSite;
namespace js {
namespace jit {

View File

@ -10,6 +10,7 @@
#include "jit/IonBuilder.h"
#include "jit/JitcodeMap.h"
#include "jit/JitSpewer.h"
#include "js/TrackedOptimizationInfo.h"
using namespace js;
using namespace js::jit;
@ -18,10 +19,14 @@ using mozilla::Maybe;
using mozilla::Some;
using mozilla::Nothing;
typedef CodeGeneratorShared::NativeToTrackedOptimizations NativeToTrackedOptimizations;
using JS::TrackedStrategy;
using JS::TrackedOutcome;
using JS::TrackedTypeSite;
using JS::ForEachTrackedOptimizationAttemptOp;
using JS::ForEachTrackedOptimizationTypeInfoOp;
bool
TrackedOptimizations::trackTypeInfo(TrackedTypeInfo &&ty)
TrackedOptimizations::trackTypeInfo(OptimizationTypeInfo &&ty)
{
return types_.append(mozilla::Move(ty));
}
@ -67,20 +72,19 @@ VectorContentsMatch(const Vec *xs, const Vec *ys)
}
bool
TrackedOptimizations::matchTypes(const TempTrackedTypeInfoVector &other) const
TrackedOptimizations::matchTypes(const TempOptimizationTypeInfoVector &other) const
{
return VectorContentsMatch(&types_, &other);
}
bool
TrackedOptimizations::matchAttempts(const TempAttemptsVector &other) const
TrackedOptimizations::matchAttempts(const TempOptimizationAttemptsVector &other) const
{
return VectorContentsMatch(&attempts_, &other);
}
#ifdef DEBUG
static const char *
StrategyString(TrackedStrategy strategy)
JS_PUBLIC_API(const char *)
JS::TrackedStrategyString(TrackedStrategy strategy)
{
switch (strategy) {
#define STRATEGY_CASE(name, msg) \
@ -94,8 +98,8 @@ StrategyString(TrackedStrategy strategy)
}
}
static const char *
OutcomeString(TrackedOutcome outcome)
JS_PUBLIC_API(const char *)
JS::TrackedOutcomeString(TrackedOutcome outcome)
{
switch (outcome) {
#define OUTCOME_CASE(name, msg) \
@ -109,10 +113,10 @@ OutcomeString(TrackedOutcome outcome)
}
}
static const char *
TypeSiteString(TrackedTypeSite kind)
JS_PUBLIC_API(const char *)
JS::TrackedTypeSiteString(TrackedTypeSite site)
{
switch (kind) {
switch (site) {
#define TYPESITE_CASE(name, msg) \
case TrackedTypeSite::name: \
return msg;
@ -123,15 +127,16 @@ TypeSiteString(TrackedTypeSite kind)
MOZ_CRASH("bad type site");
}
}
#endif // DEBUG
void
SpewTempTrackedTypeInfoVector(const TempTrackedTypeInfoVector *types, const char *indent = nullptr)
SpewTempOptimizationTypeInfoVector(const TempOptimizationTypeInfoVector *types,
const char *indent = nullptr)
{
#ifdef DEBUG
for (const TrackedTypeInfo *t = types->begin(); t != types->end(); t++) {
JitSpewStart(JitSpew_OptimizationTracking, " %s%s of type %s, type set", indent ? indent : "",
TypeSiteString(t->site()), StringFromMIRType(t->mirType()));
for (const OptimizationTypeInfo *t = types->begin(); t != types->end(); t++) {
JitSpewStart(JitSpew_OptimizationTracking, " %s%s of type %s, type set",
indent ? indent : "",
TrackedTypeSiteString(t->site()), StringFromMIRType(t->mirType()));
for (uint32_t i = 0; i < t->types().length(); i++)
JitSpewCont(JitSpew_OptimizationTracking, " %s", types::TypeString(t->types()[i]));
JitSpewFin(JitSpew_OptimizationTracking);
@ -140,12 +145,13 @@ SpewTempTrackedTypeInfoVector(const TempTrackedTypeInfoVector *types, const char
}
void
SpewTempAttemptsVector(const TempAttemptsVector *attempts, const char *indent = nullptr)
SpewTempOptimizationAttemptsVector(const TempOptimizationAttemptsVector *attempts,
const char *indent = nullptr)
{
#ifdef DEBUG
for (const OptimizationAttempt *a = attempts->begin(); a != attempts->end(); a++) {
JitSpew(JitSpew_OptimizationTracking, " %s%s: %s", indent ? indent : "",
StrategyString(a->strategy()), OutcomeString(a->outcome()));
TrackedStrategyString(a->strategy()), TrackedOutcomeString(a->outcome()));
}
#endif
}
@ -154,13 +160,13 @@ void
TrackedOptimizations::spew() const
{
#ifdef DEBUG
SpewTempTrackedTypeInfoVector(&types_);
SpewTempAttemptsVector(&attempts_);
SpewTempOptimizationTypeInfoVector(&types_);
SpewTempOptimizationAttemptsVector(&attempts_);
#endif
}
bool
TrackedTypeInfo::trackTypeSet(types::TemporaryTypeSet *typeSet)
OptimizationTypeInfo::trackTypeSet(types::TemporaryTypeSet *typeSet)
{
if (!typeSet)
return true;
@ -168,20 +174,20 @@ TrackedTypeInfo::trackTypeSet(types::TemporaryTypeSet *typeSet)
}
bool
TrackedTypeInfo::trackType(types::Type type)
OptimizationTypeInfo::trackType(types::Type type)
{
return types_.append(type);
}
bool
TrackedTypeInfo::operator ==(const TrackedTypeInfo &other) const
OptimizationTypeInfo::operator ==(const OptimizationTypeInfo &other) const
{
return site_ == other.site_ && mirType_ == other.mirType_ &&
VectorContentsMatch(&types_, &other.types_);
}
bool
TrackedTypeInfo::operator !=(const TrackedTypeInfo &other) const
OptimizationTypeInfo::operator !=(const OptimizationTypeInfo &other) const
{
return !(*this == other);
}
@ -213,7 +219,7 @@ HashTypeList(const types::TypeSet::TypeList &types)
}
HashNumber
TrackedTypeInfo::hash() const
OptimizationTypeInfo::hash() const
{
return ((HashNumber(site_) << 24) + (HashNumber(mirType_) << 16)) ^ HashTypeList(types_);
}
@ -434,28 +440,54 @@ IonTrackedOptimizationsRegion::RangeIterator::readNext(uint32_t *startOffset, ui
MOZ_ASSERT(cur_ <= end_);
}
bool
JitcodeGlobalEntry::IonEntry::optimizationAttemptsAtAddr(void *ptr,
Maybe<AttemptsVector> &attempts)
Maybe<uint8_t>
JitcodeGlobalEntry::IonEntry::trackedOptimizationIndexAtAddr(void *ptr)
{
MOZ_ASSERT(!attempts);
MOZ_ASSERT(hasTrackedOptimizations());
MOZ_ASSERT(containsPointer(ptr));
uint32_t ptrOffset = ((uint8_t *) ptr) - ((uint8_t *) nativeStartAddr());
Maybe<IonTrackedOptimizationsRegion> region = optsRegionTable_->findRegion(ptrOffset);
if (region.isNothing())
return true;
Maybe<uint8_t> attemptsIdx = region->findAttemptsIndex(ptrOffset);
if (attemptsIdx.isNothing())
return true;
IonTrackedOptimizationsAttempts attemptsEntry = optsAttemptsTable_->entry(*attemptsIdx);
attempts.emplace();
if (!attemptsEntry.readVector(attempts.ptr()))
return false;
return true;
return Nothing();
return region->findIndex(ptrOffset);
}
void
IonTrackedOptimizationsAttempts::forEach(ForEachTrackedOptimizationAttemptOp &op)
{
CompactBufferReader reader(start_, end_);
const uint8_t *cur = start_;
while (cur != end_) {
TrackedStrategy strategy = TrackedStrategy(reader.readUnsigned());
TrackedOutcome outcome = TrackedOutcome(reader.readUnsigned());
MOZ_ASSERT(strategy < TrackedStrategy::Count);
MOZ_ASSERT(outcome < TrackedOutcome::Count);
op(strategy, outcome);
cur = reader.currentPosition();
MOZ_ASSERT(cur <= end_);
}
}
void
IonTrackedOptimizationsTypeInfo::forEach(ForEachOp &op, const IonTrackedTypeVector *allTypes)
{
CompactBufferReader reader(start_, end_);
const uint8_t *cur = start_;
while (cur != end_) {
TrackedTypeSite site = JS::TrackedTypeSite(reader.readUnsigned());
MOZ_ASSERT(site < JS::TrackedTypeSite::Count);
MIRType mirType = MIRType(reader.readUnsigned());
uint32_t length = reader.readUnsigned();
for (uint32_t i = 0; i < length; i++)
op.readType((*allTypes)[reader.readByte()]);
op(site, mirType);
cur = reader.currentPosition();
MOZ_ASSERT(cur <= end_);
}
}
Maybe<uint8_t>
IonTrackedOptimizationsRegion::findAttemptsIndex(uint32_t offset) const
IonTrackedOptimizationsRegion::findIndex(uint32_t offset) const
{
if (offset < startOffset_ || offset >= endOffset_)
return Nothing();
@ -555,7 +587,7 @@ OptimizationAttempt::writeCompact(CompactBufferWriter &writer) const
}
bool
TrackedTypeInfo::writeCompact(CompactBufferWriter &writer,
OptimizationTypeInfo::writeCompact(CompactBufferWriter &writer,
UniqueTrackedTypes &uniqueTypes) const
{
writer.writeUnsigned((uint32_t) site_);
@ -747,32 +779,6 @@ IonTrackedOptimizationsRegion::WriteRun(CompactBufferWriter &writer,
return true;
}
TrackedTypeInfo::TrackedTypeInfo(CompactBufferReader &reader)
: site_(TrackedTypeSite(reader.readUnsigned())),
mirType_(MIRType(reader.readUnsigned()))
{
MOZ_ASSERT(site_ < TrackedTypeSite::Count);
}
bool
TrackedTypeInfo::readTypes(CompactBufferReader &reader, const types::TypeSet::TypeList *allTypes)
{
uint32_t length = reader.readUnsigned();
for (uint32_t i = 0; i < length; i++) {
if (!types_.append((*allTypes)[reader.readByte()]))
return false;
}
return true;
}
OptimizationAttempt::OptimizationAttempt(CompactBufferReader &reader)
: strategy_(TrackedStrategy(reader.readUnsigned())),
outcome_(TrackedOutcome(reader.readUnsigned()))
{
MOZ_ASSERT(strategy_ < TrackedStrategy::Count);
MOZ_ASSERT(outcome_ < TrackedOutcome::Count);
}
static bool
WriteOffsetsTable(CompactBufferWriter &writer, const Vector<uint32_t, 16> &offsets,
uint32_t *tableOffsetp)
@ -807,6 +813,50 @@ WriteOffsetsTable(CompactBufferWriter &writer, const Vector<uint32_t, 16> &offse
return true;
}
static JSFunction *
MaybeConstructorFromType(types::Type ty)
{
if (ty.isUnknown() || ty.isAnyObject() || !ty.isGroup())
return nullptr;
types::ObjectGroup *obj = ty.group();
types::TypeNewScript *newScript = obj->newScript();
if (!newScript && obj->maybeUnboxedLayout())
newScript = obj->unboxedLayout().newScript();
return newScript ? newScript->function() : nullptr;
}
static void
SpewConstructor(types::Type ty, JSFunction *constructor)
{
#ifdef DEBUG
char buf[512];
PutEscapedString(buf, 512, constructor->displayAtom(), 0);
const char *filename;
uint32_t lineno;
if (constructor->hasScript()) {
filename = constructor->nonLazyScript()->filename();
lineno = constructor->nonLazyScript()->lineno();
} else {
filename = constructor->lazyScript()->filename();
lineno = constructor->lazyScript()->lineno();
}
JitSpew(JitSpew_OptimizationTracking, " Unique type %s has constructor %s (%s:%u)",
types::TypeString(ty), buf, filename, lineno);
#endif
}
static void
SpewAllocationSite(types::Type ty, JSScript *script, uint32_t offset)
{
#ifdef DEBUG
JitSpew(JitSpew_OptimizationTracking, " Unique type %s has alloc site %s:%u",
types::TypeString(ty), script->filename(),
PCToLineNumber(script, script->offsetToPC(offset)));
#endif
}
bool
jit::WriteIonTrackedOptimizationsTable(JSContext *cx, CompactBufferWriter &writer,
const NativeToTrackedOptimizations *start,
@ -816,7 +866,7 @@ jit::WriteIonTrackedOptimizationsTable(JSContext *cx, CompactBufferWriter &write
uint32_t *regionTableOffsetp,
uint32_t *typesTableOffsetp,
uint32_t *optimizationTableOffsetp,
types::TypeSet::TypeList *allTypes)
IonTrackedTypeVector *allTypes)
{
MOZ_ASSERT(unique.sorted());
@ -873,23 +923,47 @@ jit::WriteIonTrackedOptimizationsTable(JSContext *cx, CompactBufferWriter &write
return false;
for (const UniqueTrackedOptimizations::SortEntry *p = vec.begin(); p != vec.end(); p++) {
const TempTrackedTypeInfoVector *v = p->types;
const TempOptimizationTypeInfoVector *v = p->types;
JitSpew(JitSpew_OptimizationTracking, " Type info entry %u of length %u, offset %u",
p - vec.begin(), v->length(), writer.length());
SpewTempTrackedTypeInfoVector(v, " ");
SpewTempOptimizationTypeInfoVector(v, " ");
if (!offsets.append(writer.length()))
return false;
for (const TrackedTypeInfo *t = v->begin(); t != v->end(); t++) {
for (const OptimizationTypeInfo *t = v->begin(); t != v->end(); t++) {
if (!t->writeCompact(writer, uniqueTypes))
return false;
}
}
// Copy the unique type list into the outparam TypeList.
if (!uniqueTypes.enumerate(allTypes))
// Enumerate the unique types, and pull out any 'new' script constructor
// functions and allocation site information. We do this during linking
// instead of during profiling to avoid touching compartment tables during
// profiling. Additionally, TypeNewScript is subject to GC in the
// meantime.
types::TypeSet::TypeList uniqueTypeList;
if (!uniqueTypes.enumerate(&uniqueTypeList))
return false;
for (uint32_t i = 0; i < uniqueTypeList.length(); i++) {
types::Type ty = uniqueTypeList[i];
if (JSFunction *constructor = MaybeConstructorFromType(ty)) {
if (!allTypes->append(IonTrackedTypeWithAddendum(ty, constructor)))
return false;
SpewConstructor(ty, constructor);
} else {
JSScript *script;
uint32_t offset;
if (cx->findAllocationSiteForType(ty, &script, &offset)) {
if (!allTypes->append(IonTrackedTypeWithAddendum(ty, script, offset)))
return false;
SpewAllocationSite(ty, script, offset);
} else {
if (!allTypes->append(IonTrackedTypeWithAddendum(ty)))
return false;
}
}
}
if (!WriteOffsetsTable(writer, offsets, typesTableOffsetp))
return false;
@ -897,10 +971,10 @@ jit::WriteIonTrackedOptimizationsTable(JSContext *cx, CompactBufferWriter &write
// Write out attempts payloads.
for (const UniqueTrackedOptimizations::SortEntry *p = vec.begin(); p != vec.end(); p++) {
const TempAttemptsVector *v = p->attempts;
const TempOptimizationAttemptsVector *v = p->attempts;
JitSpew(JitSpew_OptimizationTracking, " Attempts entry %u of length %u, offset %u",
p - vec.begin(), v->length(), writer.length());
SpewTempAttemptsVector(v, " ");
SpewTempOptimizationAttemptsVector(v, " ");
if (!offsets.append(writer.length()))
return false;
@ -962,7 +1036,7 @@ IonBuilder::trackTypeInfoUnchecked(TrackedTypeSite kind, MIRType mirType,
{
BytecodeSite *site = current->trackedSite();
// OOMs are handled as if optimization tracking were turned off.
TrackedTypeInfo typeInfo(kind, mirType);
OptimizationTypeInfo typeInfo(kind, mirType);
if (!typeInfo.trackTypeSet(typeSet)) {
site->setOptimizations(nullptr);
return;
@ -976,7 +1050,7 @@ IonBuilder::trackTypeInfoUnchecked(TrackedTypeSite kind, JSObject *obj)
{
BytecodeSite *site = current->trackedSite();
// OOMs are handled as if optimization tracking were turned off.
TrackedTypeInfo typeInfo(kind, MIRType_Object);
OptimizationTypeInfo typeInfo(kind, MIRType_Object);
if (!typeInfo.trackType(types::Type::ObjectType(obj)))
return;
if (!site->optimizations()->trackTypeInfo(mozilla::Move(typeInfo)))
@ -1035,3 +1109,104 @@ IonBuilder::trackInlineSuccessUnchecked(InliningStatus status)
if (status == InliningStatus_Inlined)
trackOptimizationOutcome(TrackedOutcome::Inlined);
}
JS_PUBLIC_API(void)
JS::ForEachTrackedOptimizationAttempt(JSRuntime *rt, void *addr, uint8_t index,
ForEachTrackedOptimizationAttemptOp &op)
{
JitcodeGlobalTable *table = rt->jitRuntime()->getJitcodeGlobalTable();
JitcodeGlobalEntry entry;
table->lookupInfallible(addr, &entry, rt);
entry.trackedOptimizationAttempts(index).forEach(op);
}
static void
InterpretedFunctionFilenameAndLineNumber(JSFunction *fun, const char **filename, unsigned *lineno)
{
ScriptSource *source;
if (fun->hasScript()) {
source = fun->nonLazyScript()->maybeForwardedScriptSource();
*lineno = fun->nonLazyScript()->lineno();
} else {
source = fun->lazyScript()->maybeForwardedScriptSource();
*lineno = fun->lazyScript()->lineno();
}
*filename = source->introducerFilename();
}
static JSFunction *
InterpretedFunctionFromTrackedType(const IonTrackedTypeWithAddendum &tracked)
{
if (tracked.hasConstructor())
return tracked.constructor;
types::Type ty = tracked.type;
if (ty.isSingleton()) {
JSObject *obj = ty.singleton();
return obj->is<JSFunction>() ? &obj->as<JSFunction>() : nullptr;
}
return ty.group()->maybeInterpretedFunction();
}
// This adapter is needed as the internal API can deal with engine-internal
// data structures directly, while the public API cannot.
class ForEachTypeInfoAdapter : public IonTrackedOptimizationsTypeInfo::ForEachOp
{
ForEachTrackedOptimizationTypeInfoOp &op_;
public:
explicit ForEachTypeInfoAdapter(ForEachTrackedOptimizationTypeInfoOp &op)
: op_(op)
{ }
void readType(const IonTrackedTypeWithAddendum &tracked) MOZ_OVERRIDE {
types::Type ty = tracked.type;
if (ty.isPrimitive() || ty.isUnknown() || ty.isAnyObject()) {
op_.readType("primitive", types::NonObjectTypeString(ty), nullptr, 0);
return;
}
char buf[512];
const uint32_t bufsize = mozilla::ArrayLength(buf);
if (JSFunction *fun = InterpretedFunctionFromTrackedType(tracked)) {
PutEscapedString(buf, bufsize, fun->displayAtom(), 0);
const char *filename;
unsigned lineno;
InterpretedFunctionFilenameAndLineNumber(fun, &filename, &lineno);
op_.readType(tracked.constructor ? "constructor" : "function", buf, filename, lineno);
return;
}
const char *className = ty.objectKey()->clasp()->name;
JS_snprintf(buf, bufsize, "[object %s]", className);
if (tracked.hasAllocationSite()) {
JSScript *script = tracked.script;
op_.readType("alloc site", buf,
script->maybeForwardedScriptSource()->introducerFilename(),
PCToLineNumber(script, script->offsetToPC(tracked.offset)));
return;
}
op_.readType("prototype", buf, nullptr, 0);
}
void operator()(JS::TrackedTypeSite site, MIRType mirType) MOZ_OVERRIDE {
op_(site, StringFromMIRType(mirType));
}
};
JS_PUBLIC_API(void)
JS::ForEachTrackedOptimizationTypeInfo(JSRuntime *rt, void *addr, uint8_t index,
ForEachTrackedOptimizationTypeInfoOp &op)
{
JitcodeGlobalTable *table = rt->jitRuntime()->getJitcodeGlobalTable();
JitcodeGlobalEntry entry;
table->lookupInfallible(addr, &entry, rt);
ForEachTypeInfoAdapter adapter(op);
entry.trackedOptimizationTypeInfo(index).forEach(adapter, entry.allTrackedTypes());
}

View File

@ -13,280 +13,30 @@
#include "jit/CompactBuffer.h"
#include "jit/CompileInfo.h"
#include "jit/JitAllocPolicy.h"
#include "jit/shared/CodeGenerator-shared.h"
#include "js/TrackedOptimizationInfo.h"
namespace js {
namespace jit {
#define TRACKED_STRATEGY_LIST(_) \
_(GetProp_ArgumentsLength, \
"getprop arguments.length") \
_(GetProp_ArgumentsCallee, \
"getprop arguments.callee") \
_(GetProp_InferredConstant, \
"getprop inferred constant") \
_(GetProp_Constant, \
"getprop constant") \
_(GetProp_TypedObject, \
"getprop TypedObject") \
_(GetProp_DefiniteSlot, \
"getprop definite slot") \
_(GetProp_Unboxed, \
"getprop unboxed object") \
_(GetProp_CommonGetter, \
"getprop common getter") \
_(GetProp_InlineAccess, \
"getprop inline access") \
_(GetProp_Innerize, \
"getprop innerize (access on global window)") \
_(GetProp_InlineCache, \
"getprop IC") \
\
_(SetProp_CommonSetter, \
"setprop common setter") \
_(SetProp_TypedObject, \
"setprop TypedObject") \
_(SetProp_DefiniteSlot, \
"setprop definite slot") \
_(SetProp_Unboxed, \
"setprop unboxed object") \
_(SetProp_InlineAccess, \
"setprop inline access") \
\
_(GetElem_TypedObject, \
"getprop TypedObject") \
_(GetElem_Dense, \
"getelem dense") \
_(GetElem_TypedStatic, \
"getelem TypedArray static") \
_(GetElem_TypedArray, \
"getelem TypedArray") \
_(GetElem_String, \
"getelem string") \
_(GetElem_Arguments, \
"getelem arguments") \
_(GetElem_ArgumentsInlined, \
"getelem arguments inlined") \
_(GetElem_InlineCache, \
"getelem IC") \
\
_(SetElem_TypedObject, \
"setelem TypedObject") \
_(SetElem_TypedStatic, \
"setelem TypedArray static") \
_(SetElem_TypedArray, \
"setelem TypedArray") \
_(SetElem_Dense, \
"setelem dense") \
_(SetElem_Arguments, \
"setelem arguments") \
_(SetElem_InlineCache, \
"setelem IC") \
\
_(Call_Inline, \
"call inline")
// Ordering is important below. All outcomes before GenericSuccess will be
// considered failures, and all outcomes after GenericSuccess will be
// considered successes.
#define TRACKED_OUTCOME_LIST(_) \
_(GenericFailure, \
"failure") \
_(Disabled, \
"disabled") \
_(NoTypeInfo, \
"no type info") \
_(NoAnalysisInfo, \
"no newscript analysis") \
_(NoShapeInfo, \
"cannot determine shape") \
_(UnknownObject, \
"unknown object") \
_(UnknownProperties, \
"unknown properties") \
_(Singleton, \
"is singleton") \
_(NotSingleton, \
"is not singleton") \
_(NotFixedSlot, \
"property not in fixed slot") \
_(InconsistentFixedSlot, \
"property not in a consistent fixed slot") \
_(NotObject, \
"not definitely an object") \
_(NotStruct, \
"not definitely a TypedObject struct") \
_(NotUnboxed, \
"not definitely an unboxed object") \
_(StructNoField, \
"struct doesn't definitely have field") \
_(InconsistentFieldType, \
"unboxed property does not consistent type") \
_(InconsistentFieldOffset, \
"unboxed property does not consistent offset") \
_(NeedsTypeBarrier, \
"needs type barrier") \
_(InDictionaryMode, \
"object in dictionary mode") \
_(NoProtoFound, \
"no proto found") \
_(MultiProtoPaths, \
"not all paths to property go through same proto") \
_(NonWritableProperty, \
"non-writable property") \
_(ProtoIndexedProps, \
"prototype has indexed properties") \
_(ArrayBadFlags, \
"array observed to be sparse, overflowed .length, or has been iterated") \
_(ArrayDoubleConversion, \
"array has ambiguous double conversion") \
_(ArrayRange, \
"array range issue (.length problems)") \
_(ArraySeenNegativeIndex, \
"has seen array access with negative index") \
_(TypedObjectNeutered, \
"TypedObject might have been neutered") \
_(TypedObjectArrayRange, \
"TypedObject array of unknown length") \
_(AccessNotDense, \
"access not on dense native (check receiver, index, and result types)") \
_(AccessNotTypedObject, \
"access not on typed array (check receiver and index types)") \
_(AccessNotTypedArray, \
"access not on typed array (check receiver, index, and result types)") \
_(AccessNotString, \
"getelem not on string (check receiver and index types)") \
_(StaticTypedArrayUint32, \
"static uint32 arrays currently cannot be optimized") \
_(StaticTypedArrayCantComputeMask, \
"can't compute mask for static typed array access (index isn't constant or not int32)") \
_(OutOfBounds, \
"observed out of bounds access") \
_(GetElemStringNotCached, \
"getelem on strings is not inline cached") \
_(NonNativeReceiver, \
"observed non-native receiver") \
_(IndexType, \
"index type must be int32, string, or symbol") \
_(SetElemNonDenseNonTANotCached, \
"setelem on non-dense non-TAs are not inline cached") \
\
_(CantInlineGeneric, \
"can't inline") \
_(CantInlineNoTarget, \
"can't inline: no target") \
_(CantInlineNotInterpreted, \
"can't inline: not interpreted") \
_(CantInlineNoBaseline, \
"can't inline: no baseline code") \
_(CantInlineLazy, \
"can't inline: lazy script") \
_(CantInlineNotConstructor, \
"can't inline: calling non-constructor with 'new'") \
_(CantInlineDisabledIon, \
"can't inline: ion disabled for callee") \
_(CantInlineTooManyArgs, \
"can't inline: too many arguments") \
_(CantInlineRecursive, \
"can't inline: recursive") \
_(CantInlineHeavyweight, \
"can't inline: heavyweight") \
_(CantInlineNeedsArgsObj, \
"can't inline: needs arguments object") \
_(CantInlineDebuggee, \
"can't inline: debuggee") \
_(CantInlineUnknownProps, \
"can't inline: type has unknown properties") \
_(CantInlineExceededDepth, \
"can't inline: exceeded inlining depth") \
_(CantInlineBigLoop, \
"can't inline: big function with a loop") \
_(CantInlineBigCaller, \
"can't inline: big caller") \
_(CantInlineBigCallee, \
"can't inline: big callee") \
_(CantInlineNotHot, \
"can't inline: not hot enough") \
_(CantInlineNotInDispatch, \
"can't inline: not in dispatch table") \
_(CantInlineNativeBadForm, \
"can't inline native: bad form (arity mismatch/constructing)") \
_(CantInlineNativeBadType, \
"can't inline native: bad argument or return type observed") \
_(CantInlineNativeNoTemplateObj, \
"can't inline native: no template object") \
_(CantInlineBound, \
"can't inline bound function invocation") \
\
_(GenericSuccess, \
"success") \
_(Inlined, \
"inlined") \
_(DOM, \
"DOM") \
_(Monomorphic, \
"monomorphic") \
_(Polymorphic, \
"polymorphic")
#define TRACKED_TYPESITE_LIST(_) \
_(Receiver, \
"receiver object") \
_(Index, \
"index") \
_(Value, \
"value") \
_(Call_Target, \
"call target") \
_(Call_This, \
"call 'this'") \
_(Call_Arg, \
"call argument") \
_(Call_Return, \
"call return")
enum class TrackedStrategy : uint32_t {
#define STRATEGY_OP(name, msg) name,
TRACKED_STRATEGY_LIST(STRATEGY_OP)
#undef STRATEGY_OPT
Count
};
enum class TrackedOutcome : uint32_t {
#define OUTCOME_OP(name, msg) name,
TRACKED_OUTCOME_LIST(OUTCOME_OP)
#undef OUTCOME_OP
Count
};
enum class TrackedTypeSite : uint32_t {
#define TYPESITE_OP(name, msg) name,
TRACKED_TYPESITE_LIST(TYPESITE_OP)
#undef TYPESITE_OP
Count
};
struct NativeToTrackedOptimizations;
class OptimizationAttempt
{
TrackedStrategy strategy_;
TrackedOutcome outcome_;
JS::TrackedStrategy strategy_;
JS::TrackedOutcome outcome_;
public:
OptimizationAttempt(TrackedStrategy strategy, TrackedOutcome outcome)
OptimizationAttempt(JS::TrackedStrategy strategy, JS::TrackedOutcome outcome)
: strategy_(strategy),
outcome_(outcome)
{ }
void setOutcome(TrackedOutcome outcome) { outcome_ = outcome; }
bool succeeded() const { return outcome_ >= TrackedOutcome::GenericSuccess; }
bool failed() const { return outcome_ < TrackedOutcome::GenericSuccess; }
TrackedStrategy strategy() const { return strategy_; }
TrackedOutcome outcome() const { return outcome_; }
void setOutcome(JS::TrackedOutcome outcome) { outcome_ = outcome; }
bool succeeded() const { return outcome_ >= JS::TrackedOutcome::GenericSuccess; }
bool failed() const { return outcome_ < JS::TrackedOutcome::GenericSuccess; }
JS::TrackedStrategy strategy() const { return strategy_; }
JS::TrackedOutcome outcome() const { return outcome_; }
bool operator ==(const OptimizationAttempt &other) const {
return strategy_ == other.strategy_ && outcome_ == other.outcome_;
@ -298,29 +48,27 @@ class OptimizationAttempt
return (HashNumber(strategy_) << 8) + HashNumber(outcome_);
}
explicit OptimizationAttempt(CompactBufferReader &reader);
void writeCompact(CompactBufferWriter &writer) const;
};
typedef Vector<OptimizationAttempt, 4, JitAllocPolicy> TempAttemptsVector;
typedef Vector<OptimizationAttempt, 4, SystemAllocPolicy> AttemptsVector;
typedef Vector<OptimizationAttempt, 4, JitAllocPolicy> TempOptimizationAttemptsVector;
class UniqueTrackedTypes;
class TrackedTypeInfo
class OptimizationTypeInfo
{
TrackedTypeSite site_;
JS::TrackedTypeSite site_;
MIRType mirType_;
types::TypeSet::TypeList types_;
public:
TrackedTypeInfo(TrackedTypeInfo &&other)
OptimizationTypeInfo(OptimizationTypeInfo &&other)
: site_(other.site_),
mirType_(other.mirType_),
types_(mozilla::Move(other.types_))
{ }
TrackedTypeInfo(TrackedTypeSite site, MIRType mirType)
OptimizationTypeInfo(JS::TrackedTypeSite site, MIRType mirType)
: site_(site),
mirType_(mirType)
{ }
@ -328,32 +76,26 @@ class TrackedTypeInfo
bool trackTypeSet(types::TemporaryTypeSet *typeSet);
bool trackType(types::Type type);
TrackedTypeSite site() const { return site_; }
JS::TrackedTypeSite site() const { return site_; }
MIRType mirType() const { return mirType_; }
const types::TypeSet::TypeList &types() const { return types_; }
bool operator ==(const TrackedTypeInfo &other) const;
bool operator !=(const TrackedTypeInfo &other) const;
bool operator ==(const OptimizationTypeInfo &other) const;
bool operator !=(const OptimizationTypeInfo &other) const;
HashNumber hash() const;
// This constructor is designed to be used in conjunction with readTypes
// below it. The same reader must be passed to readTypes after
// instantiating the TrackedTypeInfo.
explicit TrackedTypeInfo(CompactBufferReader &reader);
bool readTypes(CompactBufferReader &reader, const types::TypeSet::TypeList *allTypes);
bool writeCompact(CompactBufferWriter &writer, UniqueTrackedTypes &uniqueTypes) const;
};
typedef Vector<TrackedTypeInfo, 1, JitAllocPolicy> TempTrackedTypeInfoVector;
typedef Vector<TrackedTypeInfo, 1, SystemAllocPolicy> TrackedTypeInfoVector;
typedef Vector<OptimizationTypeInfo, 1, JitAllocPolicy> TempOptimizationTypeInfoVector;
// Tracks the optimization attempts made at a bytecode location.
class TrackedOptimizations : public TempObject
{
friend class UniqueTrackedOptimizations;
TempTrackedTypeInfoVector types_;
TempAttemptsVector attempts_;
TempOptimizationTypeInfoVector types_;
TempOptimizationAttemptsVector attempts_;
uint32_t currentAttempt_;
public:
@ -363,15 +105,15 @@ class TrackedOptimizations : public TempObject
currentAttempt_(UINT32_MAX)
{ }
bool trackTypeInfo(TrackedTypeInfo &&ty);
bool trackTypeInfo(OptimizationTypeInfo &&ty);
bool trackAttempt(TrackedStrategy strategy);
bool trackAttempt(JS::TrackedStrategy strategy);
void amendAttempt(uint32_t index);
void trackOutcome(TrackedOutcome outcome);
void trackOutcome(JS::TrackedOutcome outcome);
void trackSuccess();
bool matchTypes(const TempTrackedTypeInfoVector &other) const;
bool matchAttempts(const TempAttemptsVector &other) const;
bool matchTypes(const TempOptimizationTypeInfoVector &other) const;
bool matchAttempts(const TempOptimizationAttemptsVector &other) const;
void spew() const;
};
@ -383,8 +125,8 @@ class UniqueTrackedOptimizations
public:
struct SortEntry
{
const TempTrackedTypeInfoVector *types;
const TempAttemptsVector *attempts;
const TempOptimizationTypeInfoVector *types;
const TempOptimizationAttemptsVector *attempts;
uint32_t frequency;
};
typedef Vector<SortEntry, 4> SortedVector;
@ -392,8 +134,8 @@ class UniqueTrackedOptimizations
private:
struct Key
{
const TempTrackedTypeInfoVector *types;
const TempAttemptsVector *attempts;
const TempOptimizationTypeInfoVector *types;
const TempOptimizationAttemptsVector *attempts;
typedef Key Lookup;
static HashNumber hash(const Lookup &lookup);
@ -409,12 +151,12 @@ class UniqueTrackedOptimizations
uint32_t frequency;
};
// Map of unique (TempTrackedTypeInfoVector, TempAttemptsVector) pairs to
// indices.
// Map of unique (TempOptimizationTypeInfoVector,
// TempOptimizationAttemptsVector) pairs to indices.
typedef HashMap<Key, Entry, Key> AttemptsMap;
AttemptsMap map_;
// TempAttemptsVectors sorted by frequency.
// TempOptimizationAttemptsVectors sorted by frequency.
SortedVector sorted_;
public:
@ -457,7 +199,7 @@ class UniqueTrackedOptimizations
// | Optimization type info 1 | |
// |------------------------------------------------| |
// | Optimization type info 2 | |-- PayloadT of list of
// |------------------------------------------------| | IonTrackedOptimizationTypeInfo in
// |------------------------------------------------| | OptimizationTypeInfo in
// | ... | | order of decreasing frequency
// |------------------------------------------------| |
// | Optimization type info N | |
@ -475,7 +217,7 @@ class UniqueTrackedOptimizations
// | Optimization attempts 1 | |
// |------------------------------------------------| |
// | Optimization attempts 2 | |-- PayloadA of list of
// |------------------------------------------------| | IonTrackedOptimizationAttempts in
// |------------------------------------------------| | OptimizationAttempts in
// | ... | | order of decreasing frequency
// |------------------------------------------------| |
// | Optimization attempts N | |
@ -569,7 +311,9 @@ class IonTrackedOptimizationsRegion
RangeIterator ranges() const { return RangeIterator(rangesStart_, end_, startOffset_); }
mozilla::Maybe<uint8_t> findAttemptsIndex(uint32_t offset) const;
// Find the index of tracked optimization info (e.g., type info and
// attempts) at a native code offset.
mozilla::Maybe<uint8_t> findIndex(uint32_t offset) const;
// For the variants below, S stands for startDelta, L for length, and I
// for index. These were automatically generated from training on the
@ -646,7 +390,6 @@ class IonTrackedOptimizationsRegion
static const uint32_t MAX_RUN_LENGTH = 100;
typedef CodeGeneratorShared::NativeToTrackedOptimizations NativeToTrackedOptimizations;
static uint32_t ExpectedRunLength(const NativeToTrackedOptimizations *start,
const NativeToTrackedOptimizations *end);
@ -673,20 +416,55 @@ class IonTrackedOptimizationsAttempts
MOZ_ASSERT(start < end);
}
template <class T>
bool readVector(T *attempts) {
CompactBufferReader reader(start_, end_);
const uint8_t *cur = start_;
while (cur != end_) {
if (!attempts->append(OptimizationAttempt(reader)))
return false;
cur = reader.currentPosition();
MOZ_ASSERT(cur <= end_);
}
return true;
}
void forEach(JS::ForEachTrackedOptimizationAttemptOp &op);
};
struct IonTrackedTypeWithAddendum
{
types::Type type;
enum HasAddendum {
HasNothing,
HasAllocationSite,
HasConstructor
};
HasAddendum hasAddendum;
// If type is a type object and is tied to a site, the script and pc are
// resolved early and stored below. This is done to avoid accessing the
// compartment during profiling time.
union {
struct {
JSScript *script;
uint32_t offset;
};
JSFunction *constructor;
};
explicit IonTrackedTypeWithAddendum(types::Type type)
: type(type),
hasAddendum(HasNothing)
{ }
IonTrackedTypeWithAddendum(types::Type type, JSScript *script, uint32_t offset)
: type(type),
hasAddendum(HasAllocationSite),
script(script),
offset(offset)
{ }
IonTrackedTypeWithAddendum(types::Type type, JSFunction *constructor)
: type(type),
hasAddendum(HasConstructor),
constructor(constructor)
{ }
bool hasAllocationSite() const { return hasAddendum == HasAllocationSite; }
bool hasConstructor() const { return hasAddendum == HasConstructor; }
};
typedef Vector<IonTrackedTypeWithAddendum, 1, SystemAllocPolicy> IonTrackedTypeVector;
class IonTrackedOptimizationsTypeInfo
{
const uint8_t *start_;
@ -701,21 +479,17 @@ class IonTrackedOptimizationsTypeInfo
bool empty() const { return start_ == end_; }
template <class T>
bool readVector(T *types, const types::TypeSet::TypeList *allTypes) {
CompactBufferReader reader(start_, end_);
const uint8_t *cur = start_;
while (cur != end_) {
TrackedTypeInfo ty(reader);
if (!ty.readTypes(reader, allTypes))
return false;
if (!types->append(mozilla::Move(ty)))
return false;
cur = reader.currentPosition();
MOZ_ASSERT(cur <= end_);
}
return true;
}
// Unlike IonTrackedOptimizationAttempts,
// JS::ForEachTrackedOptimizaitonTypeInfoOp cannot be used directly. The
// internal API needs to deal with engine-internal data structures (e.g.,
// types::Type) directly.
struct ForEachOp
{
virtual void readType(const IonTrackedTypeWithAddendum &tracked) = 0;
virtual void operator()(JS::TrackedTypeSite site, MIRType mirType) = 0;
};
void forEach(ForEachOp &op, const IonTrackedTypeVector *allTypes);
};
template <class Entry>
@ -763,12 +537,12 @@ typedef IonTrackedOptimizationsOffsetsTable<IonTrackedOptimizationsTypeInfo>
bool
WriteIonTrackedOptimizationsTable(JSContext *cx, CompactBufferWriter &writer,
const CodeGeneratorShared::NativeToTrackedOptimizations *start,
const CodeGeneratorShared::NativeToTrackedOptimizations *end,
const NativeToTrackedOptimizations *start,
const NativeToTrackedOptimizations *end,
const UniqueTrackedOptimizations &unique,
uint32_t *numRegions, uint32_t *regionTableOffsetp,
uint32_t *typesTableOffsetp, uint32_t *attemptsTableOffsetp,
types::TypeSet::TypeList *allTypes);
IonTrackedTypeVector *allTypes);
} // namespace jit
} // namespace js

View File

@ -777,7 +777,7 @@ CodeGeneratorShared::verifyCompactNativeToBytecodeMap(JitCode *code)
bool
CodeGeneratorShared::generateCompactTrackedOptimizationsMap(JSContext *cx, JitCode *code,
types::TypeSet::TypeList *allTypes)
IonTrackedTypeVector *allTypes)
{
MOZ_ASSERT(trackedOptimizationsMap_ == nullptr);
MOZ_ASSERT(trackedOptimizationsMapSize_ == 0);
@ -848,15 +848,57 @@ CodeGeneratorShared::generateCompactTrackedOptimizationsMap(JSContext *cx, JitCo
data, data + trackedOptimizationsMapSize_, trackedOptimizationsMapSize_);
JitSpew(JitSpew_OptimizationTracking,
" with type list of length %u, size %u",
allTypes->length(), allTypes->length() * sizeof(types::Type));
allTypes->length(), allTypes->length() * sizeof(IonTrackedTypeWithAddendum));
return true;
}
#ifdef DEBUG
// Since this is a DEBUG-only verification, crash on OOM in the forEach ops
// below.
class ReadTempAttemptsVectorOp : public JS::ForEachTrackedOptimizationAttemptOp
{
TempOptimizationAttemptsVector *attempts_;
public:
explicit ReadTempAttemptsVectorOp(TempOptimizationAttemptsVector *attempts)
: attempts_(attempts)
{ }
void operator()(JS::TrackedStrategy strategy, JS::TrackedOutcome outcome) MOZ_OVERRIDE {
MOZ_ALWAYS_TRUE(attempts_->append(OptimizationAttempt(strategy, outcome)));
}
};
struct ReadTempTypeInfoVectorOp : public IonTrackedOptimizationsTypeInfo::ForEachOp
{
TempOptimizationTypeInfoVector *types_;
types::TypeSet::TypeList accTypes_;
public:
explicit ReadTempTypeInfoVectorOp(TempOptimizationTypeInfoVector *types)
: types_(types)
{ }
void readType(const IonTrackedTypeWithAddendum &tracked) MOZ_OVERRIDE {
MOZ_ALWAYS_TRUE(accTypes_.append(tracked.type));
}
void operator()(JS::TrackedTypeSite site, MIRType mirType) MOZ_OVERRIDE {
OptimizationTypeInfo ty(site, mirType);
for (uint32_t i = 0; i < accTypes_.length(); i++)
MOZ_ALWAYS_TRUE(ty.trackType(accTypes_[i]));
MOZ_ALWAYS_TRUE(types_->append(mozilla::Move(ty)));
accTypes_.clear();
}
};
#endif // DEBUG
void
CodeGeneratorShared::verifyCompactTrackedOptimizationsMap(JitCode *code, uint32_t numRegions,
const UniqueTrackedOptimizations &unique,
const types::TypeSet::TypeList *allTypes)
const IonTrackedTypeVector *allTypes)
{
#ifdef DEBUG
MOZ_ASSERT(trackedOptimizationsMap_ != nullptr);
@ -912,16 +954,18 @@ CodeGeneratorShared::verifyCompactTrackedOptimizationsMap(JitCode *code, uint32_
MOZ_ASSERT(endOffset == entry.endOffset.offset());
MOZ_ASSERT(index == unique.indexOf(entry.optimizations));
// Assert that the type info and attempts vector are correctly
// decoded. Since this is a DEBUG-only verification, crash on OOM.
// Assert that the type info and attempts vectors are correctly
// decoded.
IonTrackedOptimizationsTypeInfo typeInfo = typesTable->entry(index);
TempTrackedTypeInfoVector tvec(alloc());
MOZ_ALWAYS_TRUE(typeInfo.readVector(&tvec, allTypes));
TempOptimizationTypeInfoVector tvec(alloc());
ReadTempTypeInfoVectorOp top(&tvec);
typeInfo.forEach(top, allTypes);
MOZ_ASSERT(entry.optimizations->matchTypes(tvec));
IonTrackedOptimizationsAttempts attempts = attemptsTable->entry(index);
TempAttemptsVector avec(alloc());
MOZ_ALWAYS_TRUE(attempts.readVector(&avec));
TempOptimizationAttemptsVector avec(alloc());
ReadTempAttemptsVectorOp aop(&avec);
attempts.forEach(aop);
MOZ_ASSERT(entry.optimizations->matchAttempts(avec));
}
}

View File

@ -14,6 +14,7 @@
#include "jit/MacroAssembler.h"
#include "jit/MIRGenerator.h"
#include "jit/MIRGraph.h"
#include "jit/OptimizationTracking.h"
#include "jit/Safepoints.h"
#include "jit/Snapshots.h"
#include "jit/VMFunctions.h"
@ -25,7 +26,6 @@ class OutOfLineCode;
class CodeGenerator;
class MacroAssembler;
class IonCache;
class UniqueTrackedOptimizations;
template <class ArgSeq, class StoreOutputTo>
class OutOfLineCallVM;
@ -48,6 +48,17 @@ struct ReciprocalMulConstants {
int32_t shiftAmount;
};
// This should be nested in CodeGeneratorShared, but it is used in
// optimization tracking implementation and nested classes cannot be
// forward-declared.
struct NativeToTrackedOptimizations
{
// [startOffset, endOffset]
CodeOffsetLabel startOffset;
CodeOffsetLabel endOffset;
const TrackedOptimizations *optimizations;
};
class CodeGeneratorShared : public LElementVisitor
{
js::Vector<OutOfLineCode *, 0, SystemAllocPolicy> outOfLineCode_;
@ -115,15 +126,6 @@ class CodeGeneratorShared : public LElementVisitor
return gen->isProfilerInstrumentationEnabled();
}
public:
struct NativeToTrackedOptimizations {
// [startOffset, endOffset)
CodeOffsetLabel startOffset;
CodeOffsetLabel endOffset;
const TrackedOptimizations *optimizations;
};
protected:
js::Vector<NativeToTrackedOptimizations, 0, SystemAllocPolicy> trackedOptimizations_;
uint8_t *trackedOptimizationsMap_;
uint32_t trackedOptimizationsMapSize_;
@ -338,10 +340,10 @@ class CodeGeneratorShared : public LElementVisitor
void verifyCompactNativeToBytecodeMap(JitCode *code);
bool generateCompactTrackedOptimizationsMap(JSContext *cx, JitCode *code,
types::TypeSet::TypeList *allTypes);
IonTrackedTypeVector *allTypes);
void verifyCompactTrackedOptimizationsMap(JitCode *code, uint32_t numRegions,
const UniqueTrackedOptimizations &unique,
const types::TypeSet::TypeList *allTypes);
const IonTrackedTypeVector *allTypes);
// Mark the safepoint on |ins| as corresponding to the current assembler location.
// The location should be just after a call.

View File

@ -31,6 +31,7 @@
#include "js/SliceBudget.h"
#include "js/StructuredClone.h"
#include "js/TracingAPI.h"
#include "js/TrackedOptimizationInfo.h"
#include "js/TypeDecls.h"
#include "js/UbiNode.h"
#include "js/Utility.h"

View File

@ -304,6 +304,10 @@ class ExclusiveContext : public ContextFriendFields,
types::ObjectGroup *getNewGroup(const Class *clasp, TaggedProto proto,
JSObject *associated = nullptr);
types::ObjectGroup *getLazySingletonGroup(const Class *clasp, TaggedProto proto);
// Returns false if not found.
bool findAllocationSiteForType(types::Type ty, JSScript **script, uint32_t *offset) const;
inline js::LifoAlloc &typeLifoAlloc();
// Current global. This is only safe to use within the scope of the

View File

@ -97,6 +97,38 @@ types::TypeIdStringImpl(jsid id)
// Logging
/////////////////////////////////////////////////////////////////////
const char *
types::NonObjectTypeString(Type type)
{
if (type.isPrimitive()) {
switch (type.primitive()) {
case JSVAL_TYPE_UNDEFINED:
return "void";
case JSVAL_TYPE_NULL:
return "null";
case JSVAL_TYPE_BOOLEAN:
return "bool";
case JSVAL_TYPE_INT32:
return "int";
case JSVAL_TYPE_DOUBLE:
return "float";
case JSVAL_TYPE_STRING:
return "string";
case JSVAL_TYPE_SYMBOL:
return "symbol";
case JSVAL_TYPE_MAGIC:
return "lazyargs";
default:
MOZ_CRASH("Bad type");
}
}
if (type.isUnknown())
return "unknown";
MOZ_ASSERT(type.isAnyObject());
return "object";
}
#ifdef DEBUG
static bool InferSpewActive(SpewChannel channel)
@ -172,32 +204,8 @@ types::InferSpewColor(TypeSet *types)
const char *
types::TypeString(Type type)
{
if (type.isPrimitive()) {
switch (type.primitive()) {
case JSVAL_TYPE_UNDEFINED:
return "void";
case JSVAL_TYPE_NULL:
return "null";
case JSVAL_TYPE_BOOLEAN:
return "bool";
case JSVAL_TYPE_INT32:
return "int";
case JSVAL_TYPE_DOUBLE:
return "float";
case JSVAL_TYPE_STRING:
return "string";
case JSVAL_TYPE_SYMBOL:
return "symbol";
case JSVAL_TYPE_MAGIC:
return "lazyargs";
default:
MOZ_CRASH("Bad type");
}
}
if (type.isUnknown())
return "unknown";
if (type.isAnyObject())
return " object";
if (type.isPrimitive() || type.isUnknown() || type.isAnyObject())
return NonObjectTypeString(type);
static char bufs[4][40];
static unsigned which = 0;
@ -4710,6 +4718,30 @@ ExclusiveContext::getLazySingletonGroup(const Class *clasp, TaggedProto proto)
return group;
}
bool
ExclusiveContext::findAllocationSiteForType(Type type, JSScript **script, uint32_t *offset) const
{
*script = nullptr;
*offset = 0;
if (type.isUnknown() || type.isAnyObject() || !type.isGroup())
return false;
ObjectGroup *obj = type.group();
const AllocationSiteTable *table = compartment()->types.allocationSiteTable;
if (!table)
return false;
for (AllocationSiteTable::Range r = table->all(); !r.empty(); r.popFront()) {
if (obj == r.front().value()) {
*script = r.front().key().script;
*offset = r.front().key().offset;
return true;
}
}
return false;
}
/////////////////////////////////////////////////////////////////////
// Tracing
/////////////////////////////////////////////////////////////////////

View File

@ -1790,6 +1790,8 @@ enum SpewChannel {
SPEW_COUNT
};
const char *NonObjectTypeString(Type type);
#ifdef DEBUG
const char * InferSpewColorReset();

View File

@ -84,6 +84,7 @@ EXPORTS.js += [
'../public/SliceBudget.h',
'../public/StructuredClone.h',
'../public/TracingAPI.h',
'../public/TrackedOptimizationInfo.h',
'../public/TypeDecls.h',
'../public/UbiNode.h',
'../public/UbiNodeTraverse.h',

View File

@ -1866,6 +1866,8 @@ JS::ProfilingFrameIterator::extractStack(Frame *frames, uint32_t offset, uint32_
frames[offset].returnAddress = nullptr;
frames[offset].activation = activation_;
frames[offset].label = asmJSIter().label();
frames[offset].hasTrackedOptimizations = false;
frames[offset].trackedOptimizationIndex = 0;
return 1;
}