blob: 3b244763168f33beb90fb4d50755f023ad8c9f5b [file] [log] [blame]
/* -*- 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_IonCode_h
#define jit_IonCode_h
#include "mozilla/PodOperations.h"
#include "IonTypes.h"
#include "AsmJS.h"
#include "gc/Heap.h"
// For RecompileInfo
#include "jsinfer.h"
namespace JSC {
class ExecutablePool;
}
class JSScript;
namespace js {
namespace jit {
// The maximum size of any buffer associated with an assembler or code object.
// This is chosen to not overflow a signed integer, leaving room for an extra
// bit on offsets.
static const uint32_t MAX_BUFFER_SIZE = (1 << 30) - 1;
// Maximum number of scripted arg slots.
static const uint32_t SNAPSHOT_MAX_NARGS = 127;
class MacroAssembler;
class CodeOffsetLabel;
class IonCode : public gc::Cell
{
protected:
uint8_t *code_;
JSC::ExecutablePool *pool_;
uint32_t bufferSize_; // Total buffer size.
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.
uint32_t preBarrierTableBytes_; // Size of the prebarrier table.
JSBool invalidated_; // Whether the code object has been invalidated.
// This is necessary to prevent GC tracing.
#if JS_BITS_PER_WORD == 32
// Ensure IonCode is gc::Cell aligned.
uint32_t padding_;
#endif
IonCode()
: code_(NULL),
pool_(NULL)
{ }
IonCode(uint8_t *code, uint32_t bufferSize, JSC::ExecutablePool *pool)
: code_(code),
pool_(pool),
bufferSize_(bufferSize),
insnSize_(0),
dataSize_(0),
jumpRelocTableBytes_(0),
dataRelocTableBytes_(0),
preBarrierTableBytes_(0),
invalidated_(false)
{ }
uint32_t dataOffset() const {
return insnSize_;
}
uint32_t jumpRelocTableOffset() const {
return dataOffset() + dataSize_;
}
uint32_t dataRelocTableOffset() const {
return jumpRelocTableOffset() + jumpRelocTableBytes_;
}
uint32_t preBarrierTableOffset() const {
return dataRelocTableOffset() + dataRelocTableBytes_;
}
public:
uint8_t *raw() const {
return code_;
}
size_t instructionsSize() const {
return insnSize_;
}
void trace(JSTracer *trc);
void finalize(FreeOp *fop);
void setInvalidated() {
invalidated_ = true;
}
void togglePreBarriers(bool enabled);
// If this IonCode 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 IonCode *FromExecutable(uint8_t *buffer) {
IonCode *code = *(IonCode **)(buffer - sizeof(IonCode *));
JS_ASSERT(code->raw() == buffer);
return code;
}
static size_t offsetOfCode() {
return offsetof(IonCode, code_);
}
uint8_t *jumpRelocTable() {
return code_ + jumpRelocTableOffset();
}
// Allocates a new IonCode object which will be managed by the GC. If no
// object can be allocated, NULL is returned. On failure, |pool| is
// automatically released, so the code may be freed.
static IonCode *New(JSContext *cx, uint8_t *code, uint32_t bufferSize, JSC::ExecutablePool *pool);
public:
JS::Zone *zone() const { return tenuredZone(); }
static void readBarrier(IonCode *code);
static void writeBarrierPre(IonCode *code);
static void writeBarrierPost(IonCode *code, void *addr);
static inline ThingRootKind rootKind() { return THING_ROOT_ION_CODE; }
};
class SnapshotWriter;
class SafepointWriter;
class SafepointIndex;
class OsiIndex;
class IonCache;
// An IonScript attaches Ion-generated information to a JSScript.
struct IonScript
{
private:
// Code pointer containing the actual method.
EncapsulatedPtr<IonCode> method_;
// Deoptimization table used by this method.
EncapsulatedPtr<IonCode> deoptTable_;
// Entrypoint for OSR, or NULL.
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 when it is likely that one of our (transitive) call
// targets is not compiled. Used in ForkJoin.cpp to decide when
// we should add call targets to the worklist.
bool hasUncompiledCallTarget_;
// Flag set if IonScript was compiled with SPS profiling enabled.
bool hasSPSInstrumentation_;
// 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 cacheIndex which give a
// relative offset in the runtimeData array.
uint32_t cacheIndex_;
uint32_t cacheEntries_;
// 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 STACK_SLOT_SIZE-length slots this function reserves on the
// stack.
uint32_t frameSlots_;
// Frame size is the value that can be added to the StackPointer along
// with the frame prefix to get a valid IonJSFrameLayout.
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 snapshotsSize_;
// Constant table for constants stored in snapshots.
uint32_t constantTable_;
uint32_t constantEntries_;
// List of compiled/inlined JSScript's.
uint32_t scriptList_;
uint32_t scriptEntries_;
// List of scripts that we call.
//
// Currently this is only non-NULL for parallel IonScripts.
uint32_t callTargetList_;
uint32_t callTargetEntries_;
// Number of references from invalidation records.
size_t refcount_;
// Identifier of the compilation which produced this code.
types::RecompileInfo recompileInfo_;
// Number of times we tried to enter this script via OSR but failed due to
// a LOOPENTRY pc other than osrPc_.
uint32_t osrPcMismatchCounter_;
// If non-null, the list of AsmJSModules
// that contain an optimized call directly into this IonScript.
Vector<DependentAsmJSModuleExit> *dependentAsmJSModules;
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_];
}
EncapsulatedValue *constants() {
return (EncapsulatedValue *) &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 *cacheIndex() {
return (uint32_t *) &bottomBuffer()[cacheIndex_];
}
uint8_t *runtimeData() {
return &bottomBuffer()[runtimeData_];
}
JSScript **scriptList() const {
return (JSScript **) &bottomBuffer()[scriptList_];
}
JSScript **callTargetList() {
return (JSScript **) &bottomBuffer()[callTargetList_];
}
bool addDependentAsmJSModule(JSContext *cx, DependentAsmJSModuleExit exit);
void removeDependentAsmJSModule(DependentAsmJSModuleExit exit) {
JS_ASSERT(dependentAsmJSModules);
for (size_t i = 0; i < dependentAsmJSModules->length(); i++) {
if (dependentAsmJSModules->begin()[i].module == exit.module &&
dependentAsmJSModules->begin()[i].exitIndex == exit.exitIndex)
{
dependentAsmJSModules->erase(dependentAsmJSModules->begin() + i);
break;
}
}
}
void detachDependentAsmJSModules(FreeOp *fop);
private:
void trace(JSTracer *trc);
public:
// Do not call directly, use IonScript::New. This is public for cx->new_.
IonScript();
static IonScript *New(JSContext *cx, uint32_t frameLocals, uint32_t frameSize,
size_t snapshotsSize, size_t snapshotEntries,
size_t constants, size_t safepointIndexEntries, size_t osiIndexEntries,
size_t cacheEntries, size_t runtimeSize,
size_t safepointsSize, size_t scriptEntries,
size_t callTargetEntries);
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_);
}
public:
IonCode *method() const {
return method_;
}
void setMethod(IonCode *code) {
JS_ASSERT(!invalidated());
method_ = code;
}
void setDeoptTable(IonCode *code) {
deoptTable_ = code;
}
void setOsrPc(jsbytecode *osrPc) {
osrPc_ = osrPc;
}
jsbytecode *osrPc() const {
return osrPc_;
}
void setOsrEntryOffset(uint32_t offset) {
JS_ASSERT(!osrEntryOffset_);
osrEntryOffset_ = offset;
}
uint32_t osrEntryOffset() const {
return osrEntryOffset_;
}
void setSkipArgCheckEntryOffset(uint32_t offset) {
JS_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) {
JS_ASSERT(!invalidateEpilogueOffset_);
invalidateEpilogueOffset_ = offset;
}
uint32_t invalidateEpilogueOffset() const {
JS_ASSERT(invalidateEpilogueOffset_);
return invalidateEpilogueOffset_;
}
void setInvalidationEpilogueDataOffset(uint32_t offset) {
JS_ASSERT(!invalidateEpilogueDataOffset_);
invalidateEpilogueDataOffset_ = offset;
}
uint32_t invalidateEpilogueDataOffset() const {
JS_ASSERT(invalidateEpilogueDataOffset_);
return invalidateEpilogueDataOffset_;
}
void incNumBailouts() {
numBailouts_++;
}
uint32_t numBailouts() const {
return numBailouts_;
}
bool bailoutExpected() const {
return numBailouts_ > 0;
}
void setHasUncompiledCallTarget() {
hasUncompiledCallTarget_ = true;
}
void clearHasUncompiledCallTarget() {
hasUncompiledCallTarget_ = false;
}
bool hasUncompiledCallTarget() const {
return hasUncompiledCallTarget_;
}
void setHasSPSInstrumentation() {
hasSPSInstrumentation_ = true;
}
void clearHasSPSInstrumentation() {
hasSPSInstrumentation_ = false;
}
bool hasSPSInstrumentation() const {
return hasSPSInstrumentation_;
}
const uint8_t *snapshots() const {
return reinterpret_cast<const uint8_t *>(this) + snapshots_;
}
size_t snapshotsSize() const {
return snapshotsSize_;
}
const uint8_t *safepoints() const {
return reinterpret_cast<const uint8_t *>(this) + safepointsStart_;
}
size_t safepointsSize() const {
return safepointsSize_;
}
JSScript *getScript(size_t i) const {
JS_ASSERT(i < scriptEntries_);
return scriptList()[i];
}
size_t scriptEntries() const {
return scriptEntries_;
}
size_t callTargetEntries() const {
return callTargetEntries_;
}
size_t sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf) const {
return mallocSizeOf(this);
}
EncapsulatedValue &getConstant(size_t index) {
JS_ASSERT(index < numConstants());
return constants()[index];
}
size_t numConstants() const {
return constantEntries_;
}
uint32_t frameSlots() const {
return frameSlots_;
}
uint32_t frameSize() const {
return frameSize_;
}
SnapshotOffset bailoutToSnapshot(uint32_t bailoutId) {
JS_ASSERT(bailoutId < bailoutEntries_);
return bailoutTable()[bailoutId];
}
const SafepointIndex *getSafepointIndex(uint32_t disp) const;
const SafepointIndex *getSafepointIndex(uint8_t *retAddr) const {
JS_ASSERT(containsCodeAddress(retAddr));
return getSafepointIndex(retAddr - method()->raw());
}
const OsiIndex *getOsiIndex(uint32_t disp) const;
const OsiIndex *getOsiIndex(uint8_t *retAddr) const;
inline IonCache &getCache(uint32_t index) {
JS_ASSERT(index < cacheEntries_);
uint32_t offset = cacheIndex()[index];
JS_ASSERT(offset < runtimeSize_);
return *(IonCache *) &runtimeData()[offset];
}
size_t numCaches() const {
return cacheEntries_;
}
size_t runtimeSize() const {
return runtimeSize_;
}
void toggleBarriers(bool enabled);
void purgeCaches(JS::Zone *zone);
void destroyCaches();
void copySnapshots(const SnapshotWriter *writer);
void copyBailoutTable(const SnapshotOffset *table);
void copyConstants(const Value *vp);
void copySafepointIndices(const SafepointIndex *firstSafepointIndex, MacroAssembler &masm);
void copyOsiIndices(const OsiIndex *firstOsiIndex, MacroAssembler &masm);
void copyRuntimeData(const uint8_t *data);
void copyCacheEntries(const uint32_t *caches, MacroAssembler &masm);
void copySafepoints(const SafepointWriter *writer);
void copyScriptEntries(JSScript **scripts);
void copyCallTargetEntries(JSScript **callTargets);
bool invalidated() const {
return refcount_ != 0;
}
size_t refcount() const {
return refcount_;
}
void incref() {
refcount_++;
}
void decref(FreeOp *fop) {
JS_ASSERT(refcount_);
refcount_--;
if (!refcount_)
Destroy(fop, this);
}
const types::RecompileInfo& recompileInfo() const {
return recompileInfo_;
}
uint32_t incrOsrPcMismatchCounter() {
return ++osrPcMismatchCounter_;
}
void resetOsrPcMismatchCounter() {
osrPcMismatchCounter_ = 0;
}
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_;
// 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_;
// Number of bytes of code generated in this block. Spill code is counted
// separately from other, instruction implementing code.
uint32_t instructionBytes_;
uint32_t spillBytes_;
public:
bool init(uint32_t id, uint32_t offset, uint32_t numSuccessors) {
id_ = id;
offset_ = offset;
numSuccessors_ = numSuccessors;
if (numSuccessors) {
successors_ = js_pod_calloc<uint32_t>(numSuccessors);
if (!successors_)
return false;
}
return true;
}
void destroy() {
if (successors_)
js_free(successors_);
if (code_)
js_free(code_);
}
uint32_t id() const {
return id_;
}
uint32_t offset() const {
return offset_;
}
size_t numSuccessors() const {
return numSuccessors_;
}
void setSuccessor(size_t i, uint32_t id) {
JS_ASSERT(i < numSuccessors_);
successors_[i] = id;
}
uint32_t successor(size_t i) const {
JS_ASSERT(i < numSuccessors_);
return successors_[i];
}
uint64_t *addressOfHitCount() {
return &hitCount_;
}
uint64_t hitCount() const {
return hitCount_;
}
void setCode(const char *code) {
char *ncode = (char *) js_malloc(strlen(code) + 1);
if (ncode) {
strcpy(ncode, code);
code_ = ncode;
}
}
const char *code() const {
return code_;
}
void setInstructionBytes(uint32_t bytes) {
instructionBytes_ = bytes;
}
uint32_t instructionBytes() const {
return instructionBytes_;
}
void setSpillBytes(uint32_t bytes) {
spillBytes_ = bytes;
}
uint32_t spillBytes() const {
return spillBytes_;
}
};
// 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_;
// Information about basic blocks in this script.
size_t numBlocks_;
IonBlockCounts *blocks_;
public:
IonScriptCounts() {
mozilla::PodZero(this);
}
~IonScriptCounts() {
for (size_t i = 0; i < numBlocks_; i++)
blocks_[i].destroy();
js_free(blocks_);
if (previous_)
js_delete(previous_);
}
bool init(size_t numBlocks) {
numBlocks_ = numBlocks;
blocks_ = js_pod_calloc<IonBlockCounts>(numBlocks);
return blocks_ != NULL;
}
size_t numBlocks() const {
return numBlocks_;
}
IonBlockCounts &block(size_t i) {
JS_ASSERT(i < numBlocks_);
return blocks_[i];
}
void setPrevious(IonScriptCounts *previous) {
previous_ = previous;
}
IonScriptCounts *previous() const {
return previous_;
}
};
struct VMFunction;
class IonCompartment;
class IonRuntime;
struct AutoFlushCache
{
private:
uintptr_t start_;
uintptr_t stop_;
const char *name_;
IonRuntime *runtime_;
bool used_;
public:
void update(uintptr_t p, size_t len);
static void updateTop(uintptr_t p, size_t len);
~AutoFlushCache();
AutoFlushCache(const char *nonce, IonRuntime *rt = NULL);
void flushAnyway();
};
// If you are currently in the middle of modifing Ion-compiled code, which
// is going to be flushed at *some* point, but determine that you *must*
// call a function *right* *now*, two things can go wrong:
// 1) The flusher that you were using is still active, but you are about to
// enter jitted code, so it needs to be flushed
// 2) the called function can re-enter a compilation/modification path which
// will use your AFC, and thus not flush when his compilation is done
struct AutoFlushInhibitor {
private:
IonCompartment *ic_;
AutoFlushCache *afc;
public:
AutoFlushInhibitor(IonCompartment *ic);
~AutoFlushInhibitor();
};
} // namespace jit
namespace gc {
inline bool
IsMarked(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
#endif /* jit_IonCode_h */