mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-06-10 02:29:43 +00:00
576 lines
20 KiB
C++
576 lines
20 KiB
C++
/* -*- 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 jit_OptimizationTracking_h
|
|
#define jit_OptimizationTracking_h
|
|
|
|
#include "mozilla/Maybe.h"
|
|
|
|
#include "jit/CompactBuffer.h"
|
|
#include "jit/CompileInfo.h"
|
|
#include "jit/JitAllocPolicy.h"
|
|
#include "js/TrackedOptimizationInfo.h"
|
|
#include "vm/TypeInference.h"
|
|
|
|
namespace js {
|
|
|
|
namespace jit {
|
|
|
|
struct NativeToTrackedOptimizations;
|
|
|
|
class OptimizationAttempt
|
|
{
|
|
JS::TrackedStrategy strategy_;
|
|
JS::TrackedOutcome outcome_;
|
|
|
|
public:
|
|
OptimizationAttempt(JS::TrackedStrategy strategy, JS::TrackedOutcome outcome)
|
|
: strategy_(strategy),
|
|
outcome_(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_;
|
|
}
|
|
bool operator !=(const OptimizationAttempt& other) const {
|
|
return strategy_ != other.strategy_ || outcome_ != other.outcome_;
|
|
}
|
|
HashNumber hash() const {
|
|
return (HashNumber(strategy_) << 8) + HashNumber(outcome_);
|
|
}
|
|
|
|
void writeCompact(CompactBufferWriter& writer) const;
|
|
};
|
|
|
|
typedef Vector<OptimizationAttempt, 4, JitAllocPolicy> TempOptimizationAttemptsVector;
|
|
typedef Vector<TypeSet::Type, 1, JitAllocPolicy> TempTypeList;
|
|
|
|
class UniqueTrackedTypes;
|
|
|
|
class OptimizationTypeInfo
|
|
{
|
|
JS::TrackedTypeSite site_;
|
|
MIRType mirType_;
|
|
TempTypeList types_;
|
|
|
|
public:
|
|
OptimizationTypeInfo(OptimizationTypeInfo&& other)
|
|
: site_(other.site_),
|
|
mirType_(other.mirType_),
|
|
types_(mozilla::Move(other.types_))
|
|
{ }
|
|
|
|
OptimizationTypeInfo(TempAllocator& alloc, JS::TrackedTypeSite site, MIRType mirType)
|
|
: site_(site),
|
|
mirType_(mirType),
|
|
types_(alloc)
|
|
{ }
|
|
|
|
bool trackTypeSet(TemporaryTypeSet* typeSet);
|
|
bool trackType(TypeSet::Type type);
|
|
|
|
JS::TrackedTypeSite site() const { return site_; }
|
|
MIRType mirType() const { return mirType_; }
|
|
const TempTypeList& types() const { return types_; }
|
|
|
|
bool operator ==(const OptimizationTypeInfo& other) const;
|
|
bool operator !=(const OptimizationTypeInfo& other) const;
|
|
|
|
HashNumber hash() const;
|
|
|
|
bool writeCompact(JSContext* cx, CompactBufferWriter& writer,
|
|
UniqueTrackedTypes& uniqueTypes) const;
|
|
};
|
|
|
|
typedef Vector<OptimizationTypeInfo, 1, JitAllocPolicy> TempOptimizationTypeInfoVector;
|
|
|
|
// Tracks the optimization attempts made at a bytecode location.
|
|
class TrackedOptimizations : public TempObject
|
|
{
|
|
friend class UniqueTrackedOptimizations;
|
|
TempOptimizationTypeInfoVector types_;
|
|
TempOptimizationAttemptsVector attempts_;
|
|
uint32_t currentAttempt_;
|
|
|
|
public:
|
|
explicit TrackedOptimizations(TempAllocator& alloc)
|
|
: types_(alloc),
|
|
attempts_(alloc),
|
|
currentAttempt_(UINT32_MAX)
|
|
{ }
|
|
|
|
void clear() {
|
|
types_.clear();
|
|
attempts_.clear();
|
|
currentAttempt_ = UINT32_MAX;
|
|
}
|
|
|
|
bool trackTypeInfo(OptimizationTypeInfo&& ty);
|
|
|
|
bool trackAttempt(JS::TrackedStrategy strategy);
|
|
void amendAttempt(uint32_t index);
|
|
void trackOutcome(JS::TrackedOutcome outcome);
|
|
void trackSuccess();
|
|
|
|
bool matchTypes(const TempOptimizationTypeInfoVector& other) const;
|
|
bool matchAttempts(const TempOptimizationAttemptsVector& other) const;
|
|
|
|
void spew() const;
|
|
};
|
|
|
|
// Assigns each unique sequence of optimization attempts an index; outputs a
|
|
// compact table.
|
|
class UniqueTrackedOptimizations
|
|
{
|
|
public:
|
|
struct SortEntry
|
|
{
|
|
const TempOptimizationTypeInfoVector* types;
|
|
const TempOptimizationAttemptsVector* attempts;
|
|
uint32_t frequency;
|
|
};
|
|
typedef Vector<SortEntry, 4> SortedVector;
|
|
|
|
private:
|
|
struct Key
|
|
{
|
|
const TempOptimizationTypeInfoVector* types;
|
|
const TempOptimizationAttemptsVector* attempts;
|
|
|
|
typedef Key Lookup;
|
|
static HashNumber hash(const Lookup& lookup);
|
|
static bool match(const Key& key, const Lookup& lookup);
|
|
static void rekey(Key& key, const Key& newKey) {
|
|
key = newKey;
|
|
}
|
|
};
|
|
|
|
struct Entry
|
|
{
|
|
uint8_t index;
|
|
uint32_t frequency;
|
|
};
|
|
|
|
// Map of unique (TempOptimizationTypeInfoVector,
|
|
// TempOptimizationAttemptsVector) pairs to indices.
|
|
typedef HashMap<Key, Entry, Key> AttemptsMap;
|
|
AttemptsMap map_;
|
|
|
|
// TempOptimizationAttemptsVectors sorted by frequency.
|
|
SortedVector sorted_;
|
|
|
|
public:
|
|
explicit UniqueTrackedOptimizations(JSContext* cx)
|
|
: map_(cx),
|
|
sorted_(cx)
|
|
{ }
|
|
|
|
bool init() { return map_.init(); }
|
|
bool add(const TrackedOptimizations* optimizations);
|
|
|
|
bool sortByFrequency(JSContext* cx);
|
|
bool sorted() const { return !sorted_.empty(); }
|
|
uint32_t count() const { MOZ_ASSERT(sorted()); return sorted_.length(); }
|
|
const SortedVector& sortedVector() const { MOZ_ASSERT(sorted()); return sorted_; }
|
|
uint8_t indexOf(const TrackedOptimizations* optimizations) const;
|
|
};
|
|
|
|
// A compact table of tracked optimization information. Pictorially,
|
|
//
|
|
// +------------------------------------------------+
|
|
// | Region 1 | |
|
|
// |------------------------------------------------| |
|
|
// | Region 2 | |
|
|
// |------------------------------------------------| |-- PayloadR of list-of-list of
|
|
// | ... | | range triples (see below)
|
|
// |------------------------------------------------| |
|
|
// | Region M | |
|
|
// +================================================+ <- IonTrackedOptimizationsRegionTable
|
|
// | uint32_t numRegions_ = M | |
|
|
// +------------------------------------------------+ |
|
|
// | Region 1 | |
|
|
// | uint32_t regionOffset = size(PayloadR) | |
|
|
// +------------------------------------------------+ |-- Table
|
|
// | ... | |
|
|
// +------------------------------------------------+ |
|
|
// | Region M | |
|
|
// | uint32_t regionOffset | |
|
|
// +================================================+
|
|
// | Optimization type info 1 | |
|
|
// |------------------------------------------------| |
|
|
// | Optimization type info 2 | |-- PayloadT of list of
|
|
// |------------------------------------------------| | OptimizationTypeInfo in
|
|
// | ... | | order of decreasing frequency
|
|
// |------------------------------------------------| |
|
|
// | Optimization type info N | |
|
|
// +================================================+ <- IonTrackedOptimizationsTypesTable
|
|
// | uint32_t numEntries_ = N | |
|
|
// +------------------------------------------------+ |
|
|
// | Optimization type info 1 | |
|
|
// | uint32_t entryOffset = size(PayloadT) | |
|
|
// +------------------------------------------------+ |-- Table
|
|
// | ... | |
|
|
// +------------------------------------------------+ |
|
|
// | Optimization type info N | |
|
|
// | uint32_t entryOffset | |
|
|
// +================================================+
|
|
// | Optimization attempts 1 | |
|
|
// |------------------------------------------------| |
|
|
// | Optimization attempts 2 | |-- PayloadA of list of
|
|
// |------------------------------------------------| | OptimizationAttempts in
|
|
// | ... | | order of decreasing frequency
|
|
// |------------------------------------------------| |
|
|
// | Optimization attempts N | |
|
|
// +================================================+ <- IonTrackedOptimizationsAttemptsTable
|
|
// | uint32_t numEntries_ = N | |
|
|
// +------------------------------------------------+ |
|
|
// | Optimization attempts 1 | |
|
|
// | uint32_t entryOffset = size(PayloadA) | |
|
|
// +------------------------------------------------+ |-- Table
|
|
// | ... | |
|
|
// +------------------------------------------------+ |
|
|
// | Optimization attempts N | |
|
|
// | uint32_t entryOffset | |
|
|
// +------------------------------------------------+
|
|
//
|
|
// Abstractly, each region in the PayloadR section is a list of triples of the
|
|
// following, in order of ascending startOffset:
|
|
//
|
|
// (startOffset, endOffset, optimization attempts index)
|
|
//
|
|
// The range of [startOffset, endOffset) is the native machine code offsets
|
|
// for which the optimization attempts referred to by the index applies.
|
|
//
|
|
// Concretely, each region starts with a header of:
|
|
//
|
|
// { startOffset : 32, endOffset : 32 }
|
|
//
|
|
// followed by an (endOffset, index) pair, then by delta-encoded variants
|
|
// triples described below.
|
|
//
|
|
// Each list of type infos in the PayloadT section is a list of triples:
|
|
//
|
|
// (kind, MIR type, type set)
|
|
//
|
|
// The type set is separately in another vector, and what is encoded instead
|
|
// is the (offset, length) pair needed to index into that vector.
|
|
//
|
|
// Each list of optimization attempts in the PayloadA section is a list of
|
|
// pairs:
|
|
//
|
|
// (strategy, outcome)
|
|
//
|
|
// Both tail tables for PayloadR and PayloadA use reverse offsets from the
|
|
// table pointers.
|
|
|
|
class IonTrackedOptimizationsRegion
|
|
{
|
|
const uint8_t* start_;
|
|
const uint8_t* end_;
|
|
|
|
// Unpacked state.
|
|
uint32_t startOffset_;
|
|
uint32_t endOffset_;
|
|
const uint8_t* rangesStart_;
|
|
|
|
void unpackHeader();
|
|
|
|
public:
|
|
IonTrackedOptimizationsRegion(const uint8_t* start, const uint8_t* end)
|
|
: start_(start), end_(end),
|
|
startOffset_(0), endOffset_(0), rangesStart_(nullptr)
|
|
{
|
|
MOZ_ASSERT(start < end);
|
|
unpackHeader();
|
|
}
|
|
|
|
// Offsets for the entire range that this region covers.
|
|
//
|
|
// This, as well as the offsets for the deltas, is open at the ending
|
|
// address: [startOffset, endOffset).
|
|
uint32_t startOffset() const { return startOffset_; }
|
|
uint32_t endOffset() const { return endOffset_; }
|
|
|
|
class RangeIterator
|
|
{
|
|
const uint8_t* cur_;
|
|
const uint8_t* start_;
|
|
const uint8_t* end_;
|
|
|
|
uint32_t firstStartOffset_;
|
|
uint32_t prevEndOffset_;
|
|
|
|
public:
|
|
RangeIterator(const uint8_t* start, const uint8_t* end, uint32_t startOffset)
|
|
: cur_(start), start_(start), end_(end),
|
|
firstStartOffset_(startOffset), prevEndOffset_(0)
|
|
{ }
|
|
|
|
bool more() const { return cur_ < end_; }
|
|
void readNext(uint32_t* startOffset, uint32_t* endOffset, uint8_t* index);
|
|
};
|
|
|
|
RangeIterator ranges() const { return RangeIterator(rangesStart_, end_, startOffset_); }
|
|
|
|
// 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, uint32_t* entryOffsetOut) const;
|
|
|
|
// For the variants below, S stands for startDelta, L for length, and I
|
|
// for index. These were automatically generated from training on the
|
|
// Octane benchmark.
|
|
//
|
|
// byte 1 byte 0
|
|
// SSSS-SSSL LLLL-LII0
|
|
// startDelta max 127, length max 63, index max 3
|
|
|
|
static const uint32_t ENC1_MASK = 0x1;
|
|
static const uint32_t ENC1_MASK_VAL = 0x0;
|
|
|
|
static const uint32_t ENC1_START_DELTA_MAX = 0x7f;
|
|
static const uint32_t ENC1_START_DELTA_SHIFT = 9;
|
|
|
|
static const uint32_t ENC1_LENGTH_MAX = 0x3f;
|
|
static const uint32_t ENC1_LENGTH_SHIFT = 3;
|
|
|
|
static const uint32_t ENC1_INDEX_MAX = 0x3;
|
|
static const uint32_t ENC1_INDEX_SHIFT = 1;
|
|
|
|
// byte 2 byte 1 byte 0
|
|
// SSSS-SSSS SSSS-LLLL LLII-II01
|
|
// startDelta max 4095, length max 63, index max 15
|
|
|
|
static const uint32_t ENC2_MASK = 0x3;
|
|
static const uint32_t ENC2_MASK_VAL = 0x1;
|
|
|
|
static const uint32_t ENC2_START_DELTA_MAX = 0xfff;
|
|
static const uint32_t ENC2_START_DELTA_SHIFT = 12;
|
|
|
|
static const uint32_t ENC2_LENGTH_MAX = 0x3f;
|
|
static const uint32_t ENC2_LENGTH_SHIFT = 6;
|
|
|
|
static const uint32_t ENC2_INDEX_MAX = 0xf;
|
|
static const uint32_t ENC2_INDEX_SHIFT = 2;
|
|
|
|
// byte 3 byte 2 byte 1 byte 0
|
|
// SSSS-SSSS SSSL-LLLL LLLL-LIII IIII-I011
|
|
// startDelta max 2047, length max 1023, index max 255
|
|
|
|
static const uint32_t ENC3_MASK = 0x7;
|
|
static const uint32_t ENC3_MASK_VAL = 0x3;
|
|
|
|
static const uint32_t ENC3_START_DELTA_MAX = 0x7ff;
|
|
static const uint32_t ENC3_START_DELTA_SHIFT = 21;
|
|
|
|
static const uint32_t ENC3_LENGTH_MAX = 0x3ff;
|
|
static const uint32_t ENC3_LENGTH_SHIFT = 11;
|
|
|
|
static const uint32_t ENC3_INDEX_MAX = 0xff;
|
|
static const uint32_t ENC3_INDEX_SHIFT = 3;
|
|
|
|
// byte 4 byte 3 byte 2 byte 1 byte 0
|
|
// SSSS-SSSS SSSS-SSSL LLLL-LLLL LLLL-LIII IIII-I111
|
|
// startDelta max 32767, length max 16383, index max 255
|
|
|
|
static const uint32_t ENC4_MASK = 0x7;
|
|
static const uint32_t ENC4_MASK_VAL = 0x7;
|
|
|
|
static const uint32_t ENC4_START_DELTA_MAX = 0x7fff;
|
|
static const uint32_t ENC4_START_DELTA_SHIFT = 25;
|
|
|
|
static const uint32_t ENC4_LENGTH_MAX = 0x3fff;
|
|
static const uint32_t ENC4_LENGTH_SHIFT = 11;
|
|
|
|
static const uint32_t ENC4_INDEX_MAX = 0xff;
|
|
static const uint32_t ENC4_INDEX_SHIFT = 3;
|
|
|
|
static bool IsDeltaEncodeable(uint32_t startDelta, uint32_t length) {
|
|
MOZ_ASSERT(length != 0);
|
|
return startDelta <= ENC4_START_DELTA_MAX && length <= ENC4_LENGTH_MAX;
|
|
}
|
|
|
|
static const uint32_t MAX_RUN_LENGTH = 100;
|
|
|
|
static uint32_t ExpectedRunLength(const NativeToTrackedOptimizations* start,
|
|
const NativeToTrackedOptimizations* end);
|
|
|
|
static void ReadDelta(CompactBufferReader& reader, uint32_t* startDelta, uint32_t* length,
|
|
uint8_t* index);
|
|
static void WriteDelta(CompactBufferWriter& writer, uint32_t startDelta, uint32_t length,
|
|
uint8_t index);
|
|
static bool WriteRun(CompactBufferWriter& writer,
|
|
const NativeToTrackedOptimizations* start,
|
|
const NativeToTrackedOptimizations* end,
|
|
const UniqueTrackedOptimizations& unique);
|
|
};
|
|
|
|
class IonTrackedOptimizationsAttempts
|
|
{
|
|
const uint8_t* start_;
|
|
const uint8_t* end_;
|
|
|
|
public:
|
|
IonTrackedOptimizationsAttempts(const uint8_t* start, const uint8_t* end)
|
|
: start_(start), end_(end)
|
|
{
|
|
// Cannot be empty.
|
|
MOZ_ASSERT(start < end);
|
|
}
|
|
|
|
void forEach(JS::ForEachTrackedOptimizationAttemptOp& op);
|
|
};
|
|
|
|
struct IonTrackedTypeWithAddendum
|
|
{
|
|
TypeSet::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(TypeSet::Type type)
|
|
: type(type),
|
|
hasAddendum(HasNothing)
|
|
{ }
|
|
|
|
IonTrackedTypeWithAddendum(TypeSet::Type type, JSScript* script, uint32_t offset)
|
|
: type(type),
|
|
hasAddendum(HasAllocationSite),
|
|
script(script),
|
|
offset(offset)
|
|
{ }
|
|
|
|
IonTrackedTypeWithAddendum(TypeSet::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_;
|
|
const uint8_t* end_;
|
|
|
|
public:
|
|
IonTrackedOptimizationsTypeInfo(const uint8_t* start, const uint8_t* end)
|
|
: start_(start), end_(end)
|
|
{
|
|
// Can be empty; i.e., no type info was tracked.
|
|
}
|
|
|
|
bool empty() const { return start_ == end_; }
|
|
|
|
// Unlike IonTrackedOptimizationAttempts,
|
|
// JS::ForEachTrackedOptimizationTypeInfoOp cannot be used directly. The
|
|
// internal API needs to deal with engine-internal data structures (e.g.,
|
|
// TypeSet::Type) directly.
|
|
//
|
|
// An adapter is provided below.
|
|
struct ForEachOp
|
|
{
|
|
virtual void readType(const IonTrackedTypeWithAddendum& tracked) = 0;
|
|
virtual void operator()(JS::TrackedTypeSite site, MIRType mirType) = 0;
|
|
};
|
|
|
|
class ForEachOpAdapter : public ForEachOp
|
|
{
|
|
JS::ForEachTrackedOptimizationTypeInfoOp& op_;
|
|
|
|
public:
|
|
explicit ForEachOpAdapter(JS::ForEachTrackedOptimizationTypeInfoOp& op)
|
|
: op_(op)
|
|
{ }
|
|
|
|
void readType(const IonTrackedTypeWithAddendum& tracked) override;
|
|
void operator()(JS::TrackedTypeSite site, MIRType mirType) override;
|
|
};
|
|
|
|
void forEach(ForEachOp& op, const IonTrackedTypeVector* allTypes);
|
|
};
|
|
|
|
template <class Entry>
|
|
class IonTrackedOptimizationsOffsetsTable
|
|
{
|
|
uint32_t padding_;
|
|
uint32_t numEntries_;
|
|
uint32_t entryOffsets_[1];
|
|
|
|
protected:
|
|
const uint8_t* payloadEnd() const {
|
|
return (uint8_t*)(this) - padding_;
|
|
}
|
|
|
|
public:
|
|
uint32_t numEntries() const { return numEntries_; }
|
|
uint32_t entryOffset(uint32_t index) const {
|
|
MOZ_ASSERT(index < numEntries());
|
|
return entryOffsets_[index];
|
|
}
|
|
|
|
Entry entry(uint32_t index) const {
|
|
const uint8_t* start = payloadEnd() - entryOffset(index);
|
|
const uint8_t* end = payloadEnd();
|
|
if (index < numEntries() - 1)
|
|
end -= entryOffset(index + 1);
|
|
return Entry(start, end);
|
|
}
|
|
};
|
|
|
|
class IonTrackedOptimizationsRegionTable
|
|
: public IonTrackedOptimizationsOffsetsTable<IonTrackedOptimizationsRegion>
|
|
{
|
|
public:
|
|
mozilla::Maybe<IonTrackedOptimizationsRegion> findRegion(uint32_t offset) const;
|
|
|
|
const uint8_t* payloadStart() const { return payloadEnd() - entryOffset(0); }
|
|
};
|
|
|
|
typedef IonTrackedOptimizationsOffsetsTable<IonTrackedOptimizationsAttempts>
|
|
IonTrackedOptimizationsAttemptsTable;
|
|
|
|
typedef IonTrackedOptimizationsOffsetsTable<IonTrackedOptimizationsTypeInfo>
|
|
IonTrackedOptimizationsTypesTable;
|
|
|
|
bool
|
|
WriteIonTrackedOptimizationsTable(JSContext* cx, CompactBufferWriter& writer,
|
|
const NativeToTrackedOptimizations* start,
|
|
const NativeToTrackedOptimizations* end,
|
|
const UniqueTrackedOptimizations& unique,
|
|
uint32_t* numRegions, uint32_t* regionTableOffsetp,
|
|
uint32_t* typesTableOffsetp, uint32_t* attemptsTableOffsetp,
|
|
IonTrackedTypeVector* allTypes);
|
|
|
|
} // namespace jit
|
|
} // namespace js
|
|
|
|
#endif // jit_OptimizationTracking_h
|