https://github.com/mozilla/gecko-dev
Tip revision: ed635421756a45e0fddd91f20f0cc7fa68aab395 authored by Mihai Tabara on 22 September 2020, 12:14:25 UTC
Bug 1666434 - remove all cron jobs because EOL.DONTBUILD CLOSED TREE r=bhearsum a=release
Bug 1666434 - remove all cron jobs because EOL.DONTBUILD CLOSED TREE r=bhearsum a=release
Tip revision: ed63542
IonCode.h
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* 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_IonCode_h
#define jit_IonCode_h
#include "mozilla/Atomics.h"
#include "mozilla/MemoryReporting.h"
#include "jstypes.h"
#include "gc/Heap.h"
#include "jit/ExecutableAllocator.h"
#include "jit/ICStubSpace.h"
#include "jit/IonOptimizationLevels.h"
#include "jit/IonTypes.h"
#include "js/UbiNode.h"
#include "vm/TraceLogging.h"
namespace js {
namespace jit {
class IonBuilder;
class JitAllocPolicy;
class JitCode;
class MacroAssembler;
using ObjectVector = Vector<JSObject*, 4, JitAllocPolicy>;
using TraceLoggerEventVector = Vector<TraceLoggerEvent, 0, SystemAllocPolicy>;
// Header at start of raw code buffer
struct JitCodeHeader {
// Link back to corresponding gcthing
JitCode* jitCode_;
// !!! NOTE !!!
// If we are running on AMD Bobcat, insert a NOP-slide at end of the JitCode
// header so we can try to recover when the CPU screws up the branch landing
// site. See Bug 1281759.
void* nops_;
void init(JitCode* jitCode);
static JitCodeHeader* FromExecutable(uint8_t* buffer) {
return (JitCodeHeader*)(buffer - sizeof(JitCodeHeader));
}
};
class JitCode : public gc::TenuredCell {
protected:
uint8_t* code_;
ExecutablePool* pool_;
uint32_t bufferSize_; // Total buffer size. Does not include headerSize_.
uint32_t insnSize_; // Instruction stream size.
uint32_t dataSize_; // Size of the read-only data area.
uint32_t jumpRelocTableBytes_; // Size of the jump relocation table.
uint32_t dataRelocTableBytes_; // Size of the data relocation table.
uint8_t headerSize_ : 5; // Number of bytes allocated before codeStart.
uint8_t kind_ : 3; // jit::CodeKind, for the memory reporters.
bool invalidated_ : 1; // Whether the code object has been invalidated.
// This is necessary to prevent GC tracing.
bool hasBytecodeMap_ : 1; // Whether the code object has been registered with
// native=>bytecode mapping tables.
JitCode() = delete;
JitCode(uint8_t* code, uint32_t bufferSize, uint32_t headerSize,
ExecutablePool* pool, CodeKind kind)
: code_(code),
pool_(pool),
bufferSize_(bufferSize),
insnSize_(0),
dataSize_(0),
jumpRelocTableBytes_(0),
dataRelocTableBytes_(0),
headerSize_(headerSize),
kind_(uint8_t(kind)),
invalidated_(false),
hasBytecodeMap_(false) {
MOZ_ASSERT(CodeKind(kind_) == kind);
MOZ_ASSERT(headerSize_ == headerSize);
}
uint32_t dataOffset() const { return insnSize_; }
uint32_t jumpRelocTableOffset() const { return dataOffset() + dataSize_; }
uint32_t dataRelocTableOffset() const {
return jumpRelocTableOffset() + jumpRelocTableBytes_;
}
public:
uint8_t* raw() const { return code_; }
uint8_t* rawEnd() const { return code_ + insnSize_; }
bool containsNativePC(const void* addr) const {
const uint8_t* addr_u8 = (const uint8_t*)addr;
return raw() <= addr_u8 && addr_u8 < rawEnd();
}
size_t instructionsSize() const { return insnSize_; }
size_t bufferSize() const { return bufferSize_; }
size_t headerSize() const { return headerSize_; }
void traceChildren(JSTracer* trc);
void finalize(FreeOp* fop);
void setInvalidated() { invalidated_ = true; }
void setHasBytecodeMap() { hasBytecodeMap_ = true; }
// If this JitCode object has been, effectively, corrupted due to
// invalidation patching, then we have to remember this so we don't try and
// trace relocation entries that may now be corrupt.
bool invalidated() const { return !!invalidated_; }
template <typename T>
T as() const {
return JS_DATA_TO_FUNC_PTR(T, raw());
}
void copyFrom(MacroAssembler& masm);
static JitCode* FromExecutable(uint8_t* buffer) {
JitCode* code = JitCodeHeader::FromExecutable(buffer)->jitCode_;
MOZ_ASSERT(code->raw() == buffer);
return code;
}
static size_t offsetOfCode() { return offsetof(JitCode, code_); }
uint8_t* jumpRelocTable() { return code_ + jumpRelocTableOffset(); }
// Allocates a new JitCode object which will be managed by the GC. If no
// object can be allocated, nullptr is returned. On failure, |pool| is
// automatically released, so the code may be freed.
template <AllowGC allowGC>
static JitCode* New(JSContext* cx, uint8_t* code, uint32_t bufferSize,
uint32_t headerSize, ExecutablePool* pool, CodeKind kind);
public:
static const JS::TraceKind TraceKind = JS::TraceKind::JitCode;
};
class SnapshotWriter;
class RecoverWriter;
class SafepointWriter;
class SafepointIndex;
class OsiIndex;
class IonIC;
// An IonScript attaches Ion-generated information to a JSScript.
struct IonScript {
private:
// Code pointer containing the actual method.
HeapPtrJitCode method_;
// Entrypoint for OSR, or nullptr.
jsbytecode* osrPc_;
// Offset to OSR entrypoint from method_->raw(), or 0.
uint32_t osrEntryOffset_;
// Offset to entrypoint skipping type arg check from method_->raw().
uint32_t skipArgCheckEntryOffset_;
// Offset of the invalidation epilogue (which pushes this IonScript
// and calls the invalidation thunk).
uint32_t invalidateEpilogueOffset_;
// The offset immediately after the IonScript immediate.
// NOTE: technically a constant delta from
// |invalidateEpilogueOffset_|, so we could hard-code this
// per-platform if we want.
uint32_t invalidateEpilogueDataOffset_;
// Number of times this script bailed out without invalidation.
uint32_t numBailouts_;
// Flag set if IonScript was compiled with profiling enabled.
bool hasProfilingInstrumentation_;
// Flag for if this script is getting recompiled.
uint32_t recompiling_;
// Any kind of data needed by the runtime, these can be either cache
// information or profiling info.
uint32_t runtimeData_;
uint32_t runtimeSize_;
// State for polymorphic caches in the compiled code. All caches are stored
// in the runtimeData buffer and indexed by the icIndex which gives a
// relative offset in the runtimeData array.
uint32_t icIndex_;
uint32_t icEntries_;
// Map code displacement to safepoint / OSI-patch-delta.
uint32_t safepointIndexOffset_;
uint32_t safepointIndexEntries_;
// Offset to and length of the safepoint table in bytes.
uint32_t safepointsStart_;
uint32_t safepointsSize_;
// Number of bytes this function reserves on the stack.
uint32_t frameSlots_;
// Number of bytes used passed in as formal arguments or |this|.
uint32_t argumentSlots_;
// Frame size is the value that can be added to the StackPointer along
// with the frame prefix to get a valid JitFrameLayout.
uint32_t frameSize_;
// Table mapping bailout IDs to snapshot offsets.
uint32_t bailoutTable_;
uint32_t bailoutEntries_;
// Map OSI-point displacement to snapshot.
uint32_t osiIndexOffset_;
uint32_t osiIndexEntries_;
// Offset from the start of the code buffer to its snapshot buffer.
uint32_t snapshots_;
uint32_t snapshotsListSize_;
uint32_t snapshotsRVATableSize_;
// List of instructions needed to recover stack frames.
uint32_t recovers_;
uint32_t recoversSize_;
// Constant table for constants stored in snapshots.
uint32_t constantTable_;
uint32_t constantEntries_;
// Number of references from invalidation records.
uint32_t invalidationCount_;
// Identifier of the compilation which produced this code.
IonCompilationId compilationId_;
// The optimization level this script was compiled in.
OptimizationLevel optimizationLevel_;
// Number of times we tried to enter this script via OSR but failed due to
// a LOOPENTRY pc other than osrPc_.
uint32_t osrPcMismatchCounter_;
// TraceLogger events that are baked into the IonScript.
TraceLoggerEventVector traceLoggerEvents_;
private:
inline uint8_t* bottomBuffer() { return reinterpret_cast<uint8_t*>(this); }
inline const uint8_t* bottomBuffer() const {
return reinterpret_cast<const uint8_t*>(this);
}
public:
SnapshotOffset* bailoutTable() {
return (SnapshotOffset*)&bottomBuffer()[bailoutTable_];
}
PreBarrieredValue* constants() {
// Nursery constants are manually barriered in CodeGenerator::link() so a
// post barrier is not required..
return (PreBarrieredValue*)&bottomBuffer()[constantTable_];
}
const SafepointIndex* safepointIndices() const {
return const_cast<IonScript*>(this)->safepointIndices();
}
SafepointIndex* safepointIndices() {
return (SafepointIndex*)&bottomBuffer()[safepointIndexOffset_];
}
const OsiIndex* osiIndices() const {
return const_cast<IonScript*>(this)->osiIndices();
}
OsiIndex* osiIndices() { return (OsiIndex*)&bottomBuffer()[osiIndexOffset_]; }
uint32_t* icIndex() { return (uint32_t*)&bottomBuffer()[icIndex_]; }
uint8_t* runtimeData() { return &bottomBuffer()[runtimeData_]; }
private:
void trace(JSTracer* trc);
public:
// Do not call directly, use IonScript::New. This is public for cx->new_.
explicit IonScript(IonCompilationId compilationId);
static IonScript* New(JSContext* cx, IonCompilationId compilationId,
uint32_t frameSlots, uint32_t argumentSlots,
uint32_t frameSize, size_t snapshotsListSize,
size_t snapshotsRVATableSize, size_t recoversSize,
size_t bailoutEntries, size_t constants,
size_t safepointIndexEntries, size_t osiIndexEntries,
size_t icEntries, size_t runtimeSize,
size_t safepointsSize,
OptimizationLevel optimizationLevel);
static void Trace(JSTracer* trc, IonScript* script);
static void Destroy(FreeOp* fop, IonScript* script);
static inline size_t offsetOfMethod() { return offsetof(IonScript, method_); }
static inline size_t offsetOfOsrEntryOffset() {
return offsetof(IonScript, osrEntryOffset_);
}
static inline size_t offsetOfSkipArgCheckEntryOffset() {
return offsetof(IonScript, skipArgCheckEntryOffset_);
}
static inline size_t offsetOfInvalidationCount() {
return offsetof(IonScript, invalidationCount_);
}
static inline size_t offsetOfRecompiling() {
return offsetof(IonScript, recompiling_);
}
public:
JitCode* method() const { return method_; }
void setMethod(JitCode* code) {
MOZ_ASSERT(!invalidated());
method_ = code;
}
void setOsrPc(jsbytecode* osrPc) { osrPc_ = osrPc; }
jsbytecode* osrPc() const { return osrPc_; }
void setOsrEntryOffset(uint32_t offset) {
MOZ_ASSERT(!osrEntryOffset_);
osrEntryOffset_ = offset;
}
uint32_t osrEntryOffset() const { return osrEntryOffset_; }
void setSkipArgCheckEntryOffset(uint32_t offset) {
MOZ_ASSERT(!skipArgCheckEntryOffset_);
skipArgCheckEntryOffset_ = offset;
}
uint32_t getSkipArgCheckEntryOffset() const {
return skipArgCheckEntryOffset_;
}
bool containsCodeAddress(uint8_t* addr) const {
return method()->raw() <= addr &&
addr <= method()->raw() + method()->instructionsSize();
}
bool containsReturnAddress(uint8_t* addr) const {
// This accounts for an off by one error caused by the return address of a
// bailout sitting outside the range of the containing function.
return method()->raw() <= addr &&
addr <= method()->raw() + method()->instructionsSize();
}
void setInvalidationEpilogueOffset(uint32_t offset) {
MOZ_ASSERT(!invalidateEpilogueOffset_);
invalidateEpilogueOffset_ = offset;
}
uint32_t invalidateEpilogueOffset() const {
MOZ_ASSERT(invalidateEpilogueOffset_);
return invalidateEpilogueOffset_;
}
void setInvalidationEpilogueDataOffset(uint32_t offset) {
MOZ_ASSERT(!invalidateEpilogueDataOffset_);
invalidateEpilogueDataOffset_ = offset;
}
uint32_t invalidateEpilogueDataOffset() const {
MOZ_ASSERT(invalidateEpilogueDataOffset_);
return invalidateEpilogueDataOffset_;
}
void incNumBailouts() { numBailouts_++; }
bool bailoutExpected() const {
return numBailouts_ >= JitOptions.frequentBailoutThreshold;
}
void setHasProfilingInstrumentation() { hasProfilingInstrumentation_ = true; }
void clearHasProfilingInstrumentation() {
hasProfilingInstrumentation_ = false;
}
bool hasProfilingInstrumentation() const {
return hasProfilingInstrumentation_;
}
MOZ_MUST_USE bool addTraceLoggerEvent(TraceLoggerEvent& event) {
MOZ_ASSERT(event.hasTextId());
return traceLoggerEvents_.append(std::move(event));
}
const uint8_t* snapshots() const {
return reinterpret_cast<const uint8_t*>(this) + snapshots_;
}
size_t snapshotsListSize() const { return snapshotsListSize_; }
size_t snapshotsRVATableSize() const { return snapshotsRVATableSize_; }
const uint8_t* recovers() const {
return reinterpret_cast<const uint8_t*>(this) + recovers_;
}
size_t recoversSize() const { return recoversSize_; }
const uint8_t* safepoints() const {
return reinterpret_cast<const uint8_t*>(this) + safepointsStart_;
}
size_t safepointsSize() const { return safepointsSize_; }
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
return mallocSizeOf(this);
}
PreBarrieredValue& getConstant(size_t index) {
MOZ_ASSERT(index < numConstants());
return constants()[index];
}
size_t numConstants() const { return constantEntries_; }
uint32_t frameSlots() const { return frameSlots_; }
uint32_t argumentSlots() const { return argumentSlots_; }
uint32_t frameSize() const { return frameSize_; }
SnapshotOffset bailoutToSnapshot(uint32_t bailoutId) {
MOZ_ASSERT(bailoutId < bailoutEntries_);
return bailoutTable()[bailoutId];
}
const SafepointIndex* getSafepointIndex(uint32_t disp) const;
const SafepointIndex* getSafepointIndex(uint8_t* retAddr) const {
MOZ_ASSERT(containsCodeAddress(retAddr));
return getSafepointIndex(retAddr - method()->raw());
}
const OsiIndex* getOsiIndex(uint32_t disp) const;
const OsiIndex* getOsiIndex(uint8_t* retAddr) const;
IonIC& getICFromIndex(uint32_t index) {
MOZ_ASSERT(index < icEntries_);
uint32_t offset = icIndex()[index];
return getIC(offset);
}
inline IonIC& getIC(uint32_t offset) {
MOZ_ASSERT(offset < runtimeSize_);
return *(IonIC*)&runtimeData()[offset];
}
size_t numICs() const { return icEntries_; }
size_t runtimeSize() const { return runtimeSize_; }
void purgeICs(Zone* zone);
void copySnapshots(const SnapshotWriter* writer);
void copyRecovers(const RecoverWriter* writer);
void copyBailoutTable(const SnapshotOffset* table);
void copyConstants(const Value* vp);
void copySafepointIndices(const SafepointIndex* firstSafepointIndex);
void copyOsiIndices(const OsiIndex* firstOsiIndex);
void copyRuntimeData(const uint8_t* data);
void copyICEntries(const uint32_t* caches);
void copySafepoints(const SafepointWriter* writer);
bool invalidated() const { return invalidationCount_ != 0; }
// Invalidate the current compilation.
void invalidate(JSContext* cx, JSScript* script, bool resetUses,
const char* reason);
size_t invalidationCount() const { return invalidationCount_; }
void incrementInvalidationCount() { invalidationCount_++; }
void decrementInvalidationCount(FreeOp* fop) {
MOZ_ASSERT(invalidationCount_);
invalidationCount_--;
if (!invalidationCount_) {
Destroy(fop, this);
}
}
IonCompilationId compilationId() const { return compilationId_; }
OptimizationLevel optimizationLevel() const { return optimizationLevel_; }
uint32_t incrOsrPcMismatchCounter() { return ++osrPcMismatchCounter_; }
void resetOsrPcMismatchCounter() { osrPcMismatchCounter_ = 0; }
void setRecompiling() { recompiling_ = true; }
bool isRecompiling() const { return recompiling_; }
void clearRecompiling() { recompiling_ = false; }
enum ShouldIncreaseAge { IncreaseAge = true, KeepAge = false };
static void writeBarrierPre(Zone* zone, IonScript* ionScript);
};
// Execution information for a basic block which may persist after the
// accompanying IonScript is destroyed, for use during profiling.
struct IonBlockCounts {
private:
uint32_t id_;
// Approximate bytecode in the outer (not inlined) script this block
// was generated from.
uint32_t offset_;
// File and line of the inner script this block was generated from.
char* description_;
// ids for successors of this block.
uint32_t numSuccessors_;
uint32_t* successors_;
// Hit count for this block.
uint64_t hitCount_;
// Text information about the code generated for this block.
char* code_;
public:
MOZ_MUST_USE bool init(uint32_t id, uint32_t offset, char* description,
uint32_t numSuccessors) {
id_ = id;
offset_ = offset;
description_ = description;
numSuccessors_ = numSuccessors;
if (numSuccessors) {
successors_ = js_pod_calloc<uint32_t>(numSuccessors);
if (!successors_) {
return false;
}
}
return true;
}
void destroy() {
js_free(description_);
js_free(successors_);
js_free(code_);
}
uint32_t id() const { return id_; }
uint32_t offset() const { return offset_; }
const char* description() const { return description_; }
size_t numSuccessors() const { return numSuccessors_; }
void setSuccessor(size_t i, uint32_t id) {
MOZ_ASSERT(i < numSuccessors_);
successors_[i] = id;
}
uint32_t successor(size_t i) const {
MOZ_ASSERT(i < numSuccessors_);
return successors_[i];
}
uint64_t* addressOfHitCount() { return &hitCount_; }
uint64_t hitCount() const { return hitCount_; }
void setCode(const char* code) {
char* ncode = js_pod_malloc<char>(strlen(code) + 1);
if (ncode) {
strcpy(ncode, code);
code_ = ncode;
}
}
const char* code() const { return code_; }
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
return mallocSizeOf(description_) + mallocSizeOf(successors_) +
mallocSizeOf(code_);
}
};
// Execution information for a compiled script which may persist after the
// IonScript is destroyed, for use during profiling.
struct IonScriptCounts {
private:
// Any previous invalidated compilation(s) for the script.
IonScriptCounts* previous_ = nullptr;
// Information about basic blocks in this script.
size_t numBlocks_ = 0;
IonBlockCounts* blocks_ = nullptr;
public:
IonScriptCounts() = default;
~IonScriptCounts() {
for (size_t i = 0; i < numBlocks_; i++) {
blocks_[i].destroy();
}
js_free(blocks_);
// The list can be long in some corner cases (bug 1140084), so
// unroll the recursion.
IonScriptCounts* victims = previous_;
while (victims) {
IonScriptCounts* victim = victims;
victims = victim->previous_;
victim->previous_ = nullptr;
js_delete(victim);
}
}
MOZ_MUST_USE bool init(size_t numBlocks) {
blocks_ = js_pod_calloc<IonBlockCounts>(numBlocks);
if (!blocks_) {
return false;
}
numBlocks_ = numBlocks;
return true;
}
size_t numBlocks() const { return numBlocks_; }
IonBlockCounts& block(size_t i) {
MOZ_ASSERT(i < numBlocks_);
return blocks_[i];
}
void setPrevious(IonScriptCounts* previous) { previous_ = previous; }
IonScriptCounts* previous() const { return previous_; }
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
size_t size = 0;
auto currCounts = this;
while (currCounts) {
const IonScriptCounts* currCount = currCounts;
currCounts = currCount->previous_;
size += currCount->sizeOfOneIncludingThis(mallocSizeOf);
}
return size;
}
size_t sizeOfOneIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
size_t size = mallocSizeOf(this) + mallocSizeOf(blocks_);
for (size_t i = 0; i < numBlocks_; i++) {
blocks_[i].sizeOfExcludingThis(mallocSizeOf);
}
return size;
}
};
struct VMFunction;
struct AutoFlushICache {
private:
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \
defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
uintptr_t start_;
uintptr_t stop_;
# ifdef JS_JITSPEW
const char* name_;
# endif
bool inhibit_;
AutoFlushICache* prev_;
#endif
public:
static void setRange(uintptr_t p, size_t len);
static void flush(uintptr_t p, size_t len);
static void setInhibit();
~AutoFlushICache();
explicit AutoFlushICache(const char* nonce, bool inhibit = false);
};
} // namespace jit
namespace gc {
inline bool IsMarked(JSRuntime* rt, const jit::VMFunction*) {
// VMFunction are only static objects which are used by WeakMaps as keys.
// It is considered as a root object which is always marked.
return true;
}
} // namespace gc
} // namespace js
// JS::ubi::Nodes can point to js::jit::JitCode instances; they're js::gc::Cell
// instances with no associated compartment.
namespace JS {
namespace ubi {
template <>
class Concrete<js::jit::JitCode> : TracerConcrete<js::jit::JitCode> {
protected:
explicit Concrete(js::jit::JitCode* ptr)
: TracerConcrete<js::jit::JitCode>(ptr) {}
public:
static void construct(void* storage, js::jit::JitCode* ptr) {
new (storage) Concrete(ptr);
}
CoarseType coarseType() const final { return CoarseType::Script; }
Size size(mozilla::MallocSizeOf mallocSizeOf) const override {
Size size = js::gc::Arena::thingSize(get().asTenured().getAllocKind());
size += get().bufferSize();
size += get().headerSize();
return size;
}
const char16_t* typeName() const override { return concreteTypeName; }
static const char16_t concreteTypeName[];
};
} // namespace ubi
template <>
struct DeletePolicy<js::jit::IonScript> {
explicit DeletePolicy(JSRuntime* rt) : rt_(rt) {}
void operator()(const js::jit::IonScript* script);
private:
JSRuntime* rt_;
};
} // namespace JS
#endif /* jit_IonCode_h */