#ifndef jit_shared_CodeGenerator_shared_h
#define jit_shared_CodeGenerator_shared_h
#include "mozilla/Alignment.h"
#include "mozilla/Move.h"
#include "mozilla/TypeTraits.h"
#include "jit/JitcodeMap.h"
#include "jit/JitFrames.h"
#include "jit/LIR.h"
#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"
namespace js {
namespace jit {
class OutOfLineCode;
class CodeGenerator;
class MacroAssembler;
class IonIC;
class OutOfLineTruncateSlow;
struct ReciprocalMulConstants {
int64_t multiplier;
int32_t shiftAmount;
};
class CodeGeneratorShared : public LElementVisitor {
js::Vector<OutOfLineCode*, 0, SystemAllocPolicy> outOfLineCode_;
MacroAssembler& ensureMasm(MacroAssembler* masm);
mozilla::Maybe<IonHeapMacroAssembler> maybeMasm_;
public:
MacroAssembler& masm;
protected:
MIRGenerator* gen;
LIRGraph& graph;
LBlock* current;
SnapshotWriter snapshots_;
RecoverWriter recovers_;
mozilla::Maybe<TrampolinePtr> deoptTable_;
#ifdef DEBUG
uint32_t pushedArgs_;
#endif
uint32_t lastOsiPointOffset_;
SafepointWriter safepoints_;
Label invalidate_;
CodeOffset invalidateEpilogueData_;
NonAssertingLabel returnLabel_;
js::Vector<SafepointIndex, 0, SystemAllocPolicy> safepointIndices_;
js::Vector<OsiIndex, 0, SystemAllocPolicy> osiIndices_;
js::Vector<SnapshotOffset, 0, SystemAllocPolicy> bailouts_;
js::Vector<uint8_t, 0, SystemAllocPolicy> runtimeData_;
js::Vector<uint32_t, 0, SystemAllocPolicy> icList_;
struct CompileTimeICInfo {
CodeOffset icOffsetForJump;
CodeOffset icOffsetForPush;
};
js::Vector<CompileTimeICInfo, 0, SystemAllocPolicy> icInfo_;
#ifdef JS_TRACE_LOGGING
struct PatchableTLEvent {
CodeOffset offset;
const char* event;
PatchableTLEvent(CodeOffset offset, const char* event)
: offset(offset), event(event) {}
};
js::Vector<PatchableTLEvent, 0, SystemAllocPolicy> patchableTLEvents_;
js::Vector<CodeOffset, 0, SystemAllocPolicy> patchableTLScripts_;
#endif
protected:
js::Vector<NativeToBytecode, 0, SystemAllocPolicy> nativeToBytecodeList_;
uint8_t* nativeToBytecodeMap_;
uint32_t nativeToBytecodeMapSize_;
uint32_t nativeToBytecodeTableOffset_;
uint32_t nativeToBytecodeNumRegions_;
JSScript** nativeToBytecodeScriptList_;
uint32_t nativeToBytecodeScriptListLength_;
bool isProfilerInstrumentationEnabled() {
return gen->isProfilerInstrumentationEnabled();
}
bool stringsCanBeInNursery() const { return gen->stringsCanBeInNursery(); }
js::Vector<NativeToTrackedOptimizations, 0, SystemAllocPolicy>
trackedOptimizations_;
uint8_t* trackedOptimizationsMap_;
uint32_t trackedOptimizationsMapSize_;
uint32_t trackedOptimizationsRegionTableOffset_;
uint32_t trackedOptimizationsTypesTableOffset_;
uint32_t trackedOptimizationsAttemptsTableOffset_;
bool isOptimizationTrackingEnabled() {
return gen->isOptimizationTrackingEnabled();
}
protected:
size_t osrEntryOffset_;
TempAllocator& alloc() const { return graph.mir().alloc(); }
inline void setOsrEntryOffset(size_t offset) {
MOZ_ASSERT(osrEntryOffset_ == 0);
osrEntryOffset_ = offset;
}
inline size_t getOsrEntryOffset() const { return osrEntryOffset_; }
size_t skipArgCheckEntryOffset_;
inline void setSkipArgCheckEntryOffset(size_t offset) {
MOZ_ASSERT(skipArgCheckEntryOffset_ == 0);
skipArgCheckEntryOffset_ = offset;
}
inline size_t getSkipArgCheckEntryOffset() const {
return skipArgCheckEntryOffset_;
}
typedef js::Vector<SafepointIndex, 8, SystemAllocPolicy> SafepointIndices;
protected:
#ifdef CHECK_OSIPOINT_REGISTERS
bool checkOsiPointRegisters;
#endif
int32_t frameDepth_;
FrameSizeClass frameClass_;
inline int32_t ArgToStackOffset(int32_t slot) const;
inline int32_t SlotToStackOffset(int32_t slot) const;
inline int32_t StackOffsetToSlot(int32_t offset) const;
inline int32_t StackOffsetOfPassedArg(int32_t slot) const;
inline int32_t ToStackOffset(LAllocation a) const;
inline int32_t ToStackOffset(const LAllocation* a) const;
inline Address ToAddress(const LAllocation& a);
inline Address ToAddress(const LAllocation* a);
uint32_t frameSize() const {
return frameClass_ == FrameSizeClass::None() ? frameDepth_
: frameClass_.frameSize();
}
protected:
bool addNativeToBytecodeEntry(const BytecodeSite* site);
void dumpNativeToBytecodeEntries();
void dumpNativeToBytecodeEntry(uint32_t idx);
bool addTrackedOptimizationsEntry(const TrackedOptimizations* optimizations);
void extendTrackedOptimizationsEntry(
const TrackedOptimizations* optimizations);
public:
MIRGenerator& mirGen() const { return *gen; }
friend class DataPtr;
template <typename T>
class DataPtr {
CodeGeneratorShared* cg_;
size_t index_;
T* lookup() { return reinterpret_cast<T*>(&cg_->runtimeData_[index_]); }
public:
DataPtr(CodeGeneratorShared* cg, size_t index) : cg_(cg), index_(index) {}
T* operator->() { return lookup(); }
T* operator*() { return lookup(); }
};
protected:
MOZ_MUST_USE
bool allocateData(size_t size, size_t* offset) {
MOZ_ASSERT(size % sizeof(void*) == 0);
*offset = runtimeData_.length();
masm.propagateOOM(runtimeData_.appendN(0, size));
return !masm.oom();
}
template <typename T>
inline size_t allocateIC(const T& cache) {
static_assert(mozilla::IsBaseOf<IonIC, T>::value,
"T must inherit from IonIC");
size_t index;
masm.propagateOOM(
allocateData(sizeof(mozilla::AlignedStorage2<T>), &index));
masm.propagateOOM(icList_.append(index));
masm.propagateOOM(icInfo_.append(CompileTimeICInfo()));
if (masm.oom()) {
return SIZE_MAX;
}
MOZ_ASSERT(index == icList_.back());
new (&runtimeData_[index]) T(cache);
return index;
}
protected:
void encode(LRecoverInfo* recover);
void encode(LSnapshot* snapshot);
void encodeAllocation(LSnapshot* snapshot, MDefinition* def,
uint32_t* startIndex);
bool assignBailoutId(LSnapshot* snapshot);
bool encodeSafepoints();
bool createNativeToBytecodeScriptList(JSContext* cx);
bool generateCompactNativeToBytecodeMap(JSContext* cx, JitCode* code);
void verifyCompactNativeToBytecodeMap(JitCode* code);
bool generateCompactTrackedOptimizationsMap(JSContext* cx, JitCode* code,
IonTrackedTypeVector* allTypes);
void verifyCompactTrackedOptimizationsMap(
JitCode* code, uint32_t numRegions,
const UniqueTrackedOptimizations& unique,
const IonTrackedTypeVector* allTypes);
void markSafepoint(LInstruction* ins);
void markSafepointAt(uint32_t offset, LInstruction* ins);
uint32_t markOsiPoint(LOsiPoint* ins);
void ensureOsiSpace();
OutOfLineCode* oolTruncateDouble(
FloatRegister src, Register dest, MInstruction* mir,
wasm::BytecodeOffset callOffset = wasm::BytecodeOffset());
void emitTruncateDouble(FloatRegister src, Register dest,
MTruncateToInt32* mir);
void emitTruncateFloat32(FloatRegister src, Register dest,
MTruncateToInt32* mir);
void emitPreBarrier(Register elements, const LAllocation* index,
int32_t offsetAdjustment);
void emitPreBarrier(Address address);
MBasicBlock* skipTrivialBlocks(MBasicBlock* block) {
while (block->lir()->isTrivial()) {
LGoto* ins = block->lir()->rbegin()->toGoto();
MOZ_ASSERT(ins->numSuccessors() == 1);
block = ins->getSuccessor(0);
}
return block;
}
inline bool isNextBlock(LBlock* block) {
uint32_t target = skipTrivialBlocks(block->mir())->id();
uint32_t i = current->mir()->id() + 1;
if (target < i) {
return false;
}
for (; i != target; ++i) {
if (!graph.getBlock(i)->isTrivial()) {
return false;
}
}
return true;
}
protected:
void saveVolatile(Register output) {
LiveRegisterSet regs(RegisterSet::Volatile());
regs.takeUnchecked(output);
masm.PushRegsInMask(regs);
}
void restoreVolatile(Register output) {
LiveRegisterSet regs(RegisterSet::Volatile());
regs.takeUnchecked(output);
masm.PopRegsInMask(regs);
}
void saveVolatile(FloatRegister output) {
LiveRegisterSet regs(RegisterSet::Volatile());
regs.takeUnchecked(output);
masm.PushRegsInMask(regs);
}
void restoreVolatile(FloatRegister output) {
LiveRegisterSet regs(RegisterSet::Volatile());
regs.takeUnchecked(output);
masm.PopRegsInMask(regs);
}
void saveVolatile(LiveRegisterSet temps) {
masm.PushRegsInMask(LiveRegisterSet(RegisterSet::VolatileNot(temps.set())));
}
void restoreVolatile(LiveRegisterSet temps) {
masm.PopRegsInMask(LiveRegisterSet(RegisterSet::VolatileNot(temps.set())));
}
void saveVolatile() {
masm.PushRegsInMask(LiveRegisterSet(RegisterSet::Volatile()));
}
void restoreVolatile() {
masm.PopRegsInMask(LiveRegisterSet(RegisterSet::Volatile()));
}
inline void saveLive(LInstruction* ins);
inline void restoreLive(LInstruction* ins);
inline void restoreLiveIgnore(LInstruction* ins, LiveRegisterSet reg);
inline void saveLiveVolatile(LInstruction* ins);
inline void restoreLiveVolatile(LInstruction* ins);
public:
template <typename T>
void pushArg(const T& t) {
masm.Push(t);
#ifdef DEBUG
pushedArgs_++;
#endif
}
template <typename T>
CodeOffset pushArgWithPatch(const T& t) {
#ifdef DEBUG
pushedArgs_++;
#endif
return masm.PushWithPatch(t);
}
void storePointerResultTo(Register reg) { masm.storeCallPointerResult(reg); }
void storeFloatResultTo(FloatRegister reg) { masm.storeCallFloatResult(reg); }
template <typename T>
void storeResultValueTo(const T& t) {
masm.storeCallResultValue(t);
}
protected:
void addIC(LInstruction* lir, size_t cacheIndex);
ReciprocalMulConstants computeDivisionConstants(uint32_t d, int maxLog);
protected:
bool generatePrologue();
bool generateEpilogue();
void addOutOfLineCode(OutOfLineCode* code, const MInstruction* mir);
void addOutOfLineCode(OutOfLineCode* code, const BytecodeSite* site);
bool generateOutOfLineCode();
Label* getJumpLabelForBranch(MBasicBlock* block);
void jumpToBlock(MBasicBlock* mir);
#if !defined(JS_CODEGEN_MIPS32) && !defined(JS_CODEGEN_MIPS64)
void jumpToBlock(MBasicBlock* mir, Assembler::Condition cond);
#endif
private:
void generateInvalidateEpilogue();
public:
CodeGeneratorShared(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm);
public:
void visitOutOfLineTruncateSlow(OutOfLineTruncateSlow* ool);
bool omitOverRecursedCheck() const;
#ifdef JS_TRACE_LOGGING
protected:
void emitTracelogScript(bool isStart);
void emitTracelogTree(bool isStart, uint32_t textId);
void emitTracelogTree(bool isStart, const char* text,
TraceLoggerTextId enabledTextId);
#endif
public:
#ifdef JS_TRACE_LOGGING
void emitTracelogScriptStart() { emitTracelogScript(true); }
void emitTracelogScriptStop() { emitTracelogScript(false); }
void emitTracelogStartEvent(uint32_t textId) {
emitTracelogTree(true, textId);
}
void emitTracelogStopEvent(uint32_t textId) {
emitTracelogTree(false, textId);
}
void emitTracelogStartEvent(const char* text,
TraceLoggerTextId enabledTextId) {
emitTracelogTree(true, text, enabledTextId);
}
void emitTracelogStopEvent(const char* text,
TraceLoggerTextId enabledTextId) {
emitTracelogTree(false, text, enabledTextId);
}
void emitTracelogIonStart() {
emitTracelogScriptStart();
emitTracelogStartEvent(TraceLogger_IonMonkey);
}
void emitTracelogIonStop() {
emitTracelogStopEvent(TraceLogger_IonMonkey);
emitTracelogScriptStop();
}
#else
void emitTracelogScriptStart() {}
void emitTracelogScriptStop() {}
void emitTracelogStartEvent(uint32_t textId) {}
void emitTracelogStopEvent(uint32_t textId) {}
void emitTracelogStartEvent(const char* text,
TraceLoggerTextId enabledTextId) {}
void emitTracelogStopEvent(const char* text,
TraceLoggerTextId enabledTextId) {}
void emitTracelogIonStart() {}
void emitTracelogIonStop() {}
#endif
bool isGlobalObject(JSObject* object);
};
class OutOfLineCode : public TempObject {
Label entry_;
Label rejoin_;
uint32_t framePushed_;
const BytecodeSite* site_;
public:
OutOfLineCode() : framePushed_(0), site_() {}
virtual void generate(CodeGeneratorShared* codegen) = 0;
Label* entry() { return &entry_; }
virtual void bind(MacroAssembler* masm) { masm->bind(entry()); }
Label* rejoin() { return &rejoin_; }
void setFramePushed(uint32_t framePushed) { framePushed_ = framePushed; }
uint32_t framePushed() const { return framePushed_; }
void setBytecodeSite(const BytecodeSite* site) { site_ = site; }
const BytecodeSite* bytecodeSite() const { return site_; }
jsbytecode* pc() const { return site_->pc(); }
JSScript* script() const { return site_->script(); }
};
template <typename T>
class OutOfLineCodeBase : public OutOfLineCode {
public:
virtual void generate(CodeGeneratorShared* codegen) override {
accept(static_cast<T*>(codegen));
}
public:
virtual void accept(T* codegen) = 0;
};
template <class CodeGen>
class OutOfLineWasmTruncateCheckBase : public OutOfLineCodeBase<CodeGen> {
MIRType fromType_;
MIRType toType_;
FloatRegister input_;
Register output_;
Register64 output64_;
TruncFlags flags_;
wasm::BytecodeOffset bytecodeOffset_;
public:
OutOfLineWasmTruncateCheckBase(MWasmTruncateToInt32* mir, FloatRegister input,
Register output)
: fromType_(mir->input()->type()),
toType_(MIRType::Int32),
input_(input),
output_(output),
output64_(Register64::Invalid()),
flags_(mir->flags()),
bytecodeOffset_(mir->bytecodeOffset()) {}
OutOfLineWasmTruncateCheckBase(MWasmTruncateToInt64* mir, FloatRegister input,
Register64 output)
: fromType_(mir->input()->type()),
toType_(MIRType::Int64),
input_(input),
output_(Register::Invalid()),
output64_(output),
flags_(mir->flags()),
bytecodeOffset_(mir->bytecodeOffset()) {}
void accept(CodeGen* codegen) override {
codegen->visitOutOfLineWasmTruncateCheck(this);
}
FloatRegister input() const { return input_; }
Register output() const { return output_; }
Register64 output64() const { return output64_; }
MIRType toType() const { return toType_; }
MIRType fromType() const { return fromType_; }
bool isUnsigned() const { return flags_ & TRUNC_UNSIGNED; }
bool isSaturating() const { return flags_ & TRUNC_SATURATING; }
TruncFlags flags() const { return flags_; }
wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; }
};
} }
#endif