| /* -*- 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_shared_CodeGenerator_shared_h |
| #define jit_shared_CodeGenerator_shared_h |
| |
| #include "mozilla/Alignment.h" |
| #include "mozilla/Move.h" |
| #include "mozilla/TypeTraits.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 IonCache; |
| |
| template <class ArgSeq, class StoreOutputTo> |
| class OutOfLineCallVM; |
| |
| class OutOfLineTruncateSlow; |
| |
| struct PatchableBackedgeInfo |
| { |
| CodeOffsetJump backedge; |
| Label* loopHeader; |
| Label* interruptCheck; |
| |
| PatchableBackedgeInfo(CodeOffsetJump backedge, Label* loopHeader, Label* interruptCheck) |
| : backedge(backedge), loopHeader(loopHeader), interruptCheck(interruptCheck) |
| {} |
| }; |
| |
| struct ReciprocalMulConstants { |
| int64_t multiplier; |
| 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] |
| CodeOffset startOffset; |
| CodeOffset endOffset; |
| const TrackedOptimizations* optimizations; |
| }; |
| |
| class CodeGeneratorShared : public LElementVisitor |
| { |
| js::Vector<OutOfLineCode*, 0, SystemAllocPolicy> outOfLineCode_; |
| |
| MacroAssembler& ensureMasm(MacroAssembler* masm); |
| mozilla::Maybe<MacroAssembler> maybeMasm_; |
| |
| public: |
| MacroAssembler& masm; |
| |
| protected: |
| MIRGenerator* gen; |
| LIRGraph& graph; |
| LBlock* current; |
| SnapshotWriter snapshots_; |
| RecoverWriter recovers_; |
| JitCode* deoptTable_; |
| #ifdef DEBUG |
| uint32_t pushedArgs_; |
| #endif |
| uint32_t lastOsiPointOffset_; |
| SafepointWriter safepoints_; |
| Label invalidate_; |
| CodeOffset invalidateEpilogueData_; |
| |
| // Label for the common return path. |
| NonAssertingLabel returnLabel_; |
| |
| FallbackICStubSpace stubSpace_; |
| |
| js::Vector<SafepointIndex, 0, SystemAllocPolicy> safepointIndices_; |
| js::Vector<OsiIndex, 0, SystemAllocPolicy> osiIndices_; |
| |
| // Mapping from bailout table ID to an offset in the snapshot buffer. |
| js::Vector<SnapshotOffset, 0, SystemAllocPolicy> bailouts_; |
| |
| // Allocated data space needed at runtime. |
| js::Vector<uint8_t, 0, SystemAllocPolicy> runtimeData_; |
| |
| // Vector of information about generated polymorphic inline caches. |
| js::Vector<uint32_t, 0, SystemAllocPolicy> cacheList_; |
| |
| // Patchable backedges generated for loops. |
| Vector<PatchableBackedgeInfo, 0, SystemAllocPolicy> patchableBackedges_; |
| |
| #ifdef JS_TRACE_LOGGING |
| js::Vector<CodeOffset, 0, SystemAllocPolicy> patchableTraceLoggers_; |
| js::Vector<CodeOffset, 0, SystemAllocPolicy> patchableTLScripts_; |
| #endif |
| |
| public: |
| struct NativeToBytecode { |
| CodeOffset nativeOffset; |
| InlineScriptTree* tree; |
| jsbytecode* pc; |
| }; |
| |
| 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(); |
| } |
| |
| 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: |
| // The offset of the first instruction of the OSR entry block from the |
| // beginning of the code buffer. |
| 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_; |
| } |
| |
| // The offset of the first instruction of the body. |
| // This skips the arguments type checks. |
| 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 |
| // See JitOptions.checkOsiPointRegisters. We set this here to avoid |
| // races when enableOsiPointRegisterChecks is called while we're generating |
| // code off-thread. |
| bool checkOsiPointRegisters; |
| #endif |
| |
| // The initial size of the frame in bytes. These are bytes beyond the |
| // constant header present for every Ion frame, used for pre-determined |
| // spills. |
| int32_t frameDepth_; |
| |
| // In some cases, we force stack alignment to platform boundaries, see |
| // also CodeGeneratorShared constructor. This value records the adjustment |
| // we've done. |
| int32_t frameInitialAdjustment_; |
| |
| // Frame class this frame's size falls into (see IonFrame.h). |
| FrameSizeClass frameClass_; |
| |
| // For arguments to the current function. |
| inline int32_t ArgToStackOffset(int32_t slot) const; |
| |
| // For the callee of the current function. |
| inline int32_t CalleeStackOffset() const; |
| |
| inline int32_t SlotToStackOffset(int32_t slot) const; |
| inline int32_t StackOffsetToSlot(int32_t offset) const; |
| |
| // For argument construction for calls. Argslots are Value-sized. |
| inline int32_t StackOffsetOfPassedArg(int32_t slot) const; |
| |
| inline int32_t ToStackOffset(LAllocation a) const; |
| inline int32_t ToStackOffset(const LAllocation* a) const; |
| |
| uint32_t frameSize() const { |
| return frameClass_ == FrameSizeClass::None() ? frameDepth_ : frameClass_.frameSize(); |
| } |
| |
| inline Operand ToOperand(const LAllocation& a); |
| inline Operand ToOperand(const LAllocation* a); |
| inline Operand ToOperand(const LDefinition* def); |
| |
| protected: |
| #ifdef CHECK_OSIPOINT_REGISTERS |
| void resetOsiPointRegs(LSafepoint* safepoint); |
| bool shouldVerifyOsiPointRegs(LSafepoint* safepoint); |
| void verifyOsiPointRegs(LSafepoint* safepoint); |
| #endif |
| |
| 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; |
| } |
| |
| // When appending to runtimeData_, the vector might realloc, leaving pointers |
| // int the origianl vector stale and unusable. DataPtr acts like a pointer, |
| // but allows safety in the face of potentially realloc'ing vector appends. |
| 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_WARN_UNUSED_RESULT |
| 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(); |
| } |
| |
| // Ensure the cache is an IonCache while expecting the size of the derived |
| // class. We only need the cache list at GC time. Everyone else can just take |
| // runtimeData offsets. |
| template <typename T> |
| inline size_t allocateCache(const T& cache) { |
| static_assert(mozilla::IsBaseOf<IonCache, T>::value, "T must inherit from IonCache"); |
| size_t index; |
| masm.propagateOOM(allocateData(sizeof(mozilla::AlignedStorage2<T>), &index)); |
| masm.propagateOOM(cacheList_.append(index)); |
| if (masm.oom()) |
| return SIZE_MAX; |
| // Use the copy constructor on the allocated space. |
| MOZ_ASSERT(index == cacheList_.back()); |
| new (&runtimeData_[index]) T(cache); |
| return index; |
| } |
| |
| protected: |
| // Encodes an LSnapshot into the compressed snapshot buffer. |
| void encode(LRecoverInfo* recover); |
| void encode(LSnapshot* snapshot); |
| void encodeAllocation(LSnapshot* snapshot, MDefinition* def, uint32_t* startIndex); |
| |
| // Attempts to assign a BailoutId to a snapshot, if one isn't already set. |
| // If the bailout table is full, this returns false, which is not a fatal |
| // error (the code generator may use a slower bailout mechanism). |
| bool assignBailoutId(LSnapshot* snapshot); |
| |
| // Encode all encountered safepoints in CG-order, and resolve |indices| for |
| // safepoint offsets. |
| bool encodeSafepoints(); |
| |
| // Fixup offsets of native-to-bytecode map. |
| 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); |
| |
| // Mark the safepoint on |ins| as corresponding to the current assembler location. |
| // The location should be just after a call. |
| void markSafepoint(LInstruction* ins); |
| void markSafepointAt(uint32_t offset, LInstruction* ins); |
| |
| // Mark the OSI point |ins| as corresponding to the current |
| // assembler location inside the |osiIndices_|. Return the assembler |
| // location for the OSI point return location. |
| uint32_t markOsiPoint(LOsiPoint* ins); |
| |
| // Ensure that there is enough room between the last OSI point and the |
| // current instruction, such that: |
| // (1) Invalidation will not overwrite the current instruction, and |
| // (2) Overwriting the current instruction will not overwrite |
| // an invalidation marker. |
| void ensureOsiSpace(); |
| |
| OutOfLineCode* oolTruncateDouble(FloatRegister src, Register dest, MInstruction* mir); |
| void emitTruncateDouble(FloatRegister src, Register dest, MInstruction* mir); |
| void emitTruncateFloat32(FloatRegister src, Register dest, MInstruction* mir); |
| |
| void emitAsmJSCall(LAsmJSCall* ins); |
| |
| void emitPreBarrier(Register base, const LAllocation* index); |
| void emitPreBarrier(Address address); |
| |
| // We don't emit code for trivial blocks, so if we want to branch to the |
| // given block, and it's trivial, return the ultimate block we should |
| // actually branch directly to. |
| MBasicBlock* skipTrivialBlocks(MBasicBlock* block) { |
| while (block->lir()->isTrivial()) { |
| MOZ_ASSERT(block->lir()->rbegin()->numSuccessors() == 1); |
| block = block->lir()->rbegin()->getSuccessor(0); |
| } |
| return block; |
| } |
| |
| // Test whether the given block can be reached via fallthrough from the |
| // current 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; |
| // Trivial blocks can be crossed via fallthrough. |
| for (; i != target; ++i) { |
| if (!graph.getBlock(i)->isTrivial()) |
| return false; |
| } |
| return true; |
| } |
| |
| public: |
| // Save and restore all volatile registers to/from the stack, excluding the |
| // specified register(s), before a function call made using callWithABI and |
| // after storing the function call's return value to an output register. |
| // (The only registers that don't need to be saved/restored are 1) the |
| // temporary register used to store the return value of the function call, |
| // if there is one [otherwise that stored value would be overwritten]; and |
| // 2) temporary registers whose values aren't needed in the rest of the LIR |
| // instruction [this is purely an optimization]. All other volatiles must |
| // be saved and restored in case future LIR instructions need those values.) |
| 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())); |
| } |
| |
| // These functions have to be called before and after any callVM and before |
| // any modifications of the stack. Modification of the stack made after |
| // these calls should update the framePushed variable, needed by the exit |
| // frame produced by callVM. |
| inline void saveLive(LInstruction* ins); |
| inline void restoreLive(LInstruction* ins); |
| inline void restoreLiveIgnore(LInstruction* ins, LiveRegisterSet reg); |
| |
| // Save/restore all registers that are both live and volatile. |
| inline void saveLiveVolatile(LInstruction* ins); |
| inline void restoreLiveVolatile(LInstruction* ins); |
| |
| template <typename T> |
| void pushArg(const T& t) { |
| masm.Push(t); |
| #ifdef DEBUG |
| pushedArgs_++; |
| #endif |
| } |
| |
| void storeResultTo(Register reg) { |
| masm.storeCallResult(reg); |
| } |
| |
| void storeFloatResultTo(FloatRegister reg) { |
| masm.storeCallFloatResult(reg); |
| } |
| |
| template <typename T> |
| void storeResultValueTo(const T& t) { |
| masm.storeCallResultValue(t); |
| } |
| |
| void callVM(const VMFunction& f, LInstruction* ins, const Register* dynStack = nullptr); |
| |
| template <class ArgSeq, class StoreOutputTo> |
| inline OutOfLineCode* oolCallVM(const VMFunction& fun, LInstruction* ins, const ArgSeq& args, |
| const StoreOutputTo& out); |
| |
| void addCache(LInstruction* lir, size_t cacheIndex); |
| bool addCacheLocations(const CacheLocationList& locs, size_t* numLocs, size_t* offset); |
| 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* labelForBackedgeWithImplicitCheck(MBasicBlock* mir); |
| |
| // Generate a jump to the start of the specified block, adding information |
| // if this is a loop backedge. Use this in place of jumping directly to |
| // mir->lir()->label(), or use getJumpLabelForBranch() if a label to use |
| // directly is needed. |
| void jumpToBlock(MBasicBlock* mir); |
| |
| // This function is not used for MIPS. MIPS has branchToBlock. |
| #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: |
| template <class ArgSeq, class StoreOutputTo> |
| void visitOutOfLineCallVM(OutOfLineCallVM<ArgSeq, StoreOutputTo>* ool); |
| |
| void visitOutOfLineTruncateSlow(OutOfLineTruncateSlow* ool); |
| |
| bool omitOverRecursedCheck() const; |
| |
| #ifdef JS_TRACE_LOGGING |
| protected: |
| void emitTracelogScript(bool isStart); |
| void emitTracelogTree(bool isStart, uint32_t textId); |
| |
| public: |
| void emitTracelogScriptStart() { |
| emitTracelogScript(/* isStart =*/ true); |
| } |
| void emitTracelogScriptStop() { |
| emitTracelogScript(/* isStart =*/ false); |
| } |
| void emitTracelogStartEvent(uint32_t textId) { |
| emitTracelogTree(/* isStart =*/ true, textId); |
| } |
| void emitTracelogStopEvent(uint32_t textId) { |
| emitTracelogTree(/* isStart =*/ false, textId); |
| } |
| #endif |
| void emitTracelogIonStart() { |
| #ifdef JS_TRACE_LOGGING |
| emitTracelogScriptStart(); |
| emitTracelogStartEvent(TraceLogger_IonMonkey); |
| #endif |
| } |
| void emitTracelogIonStop() { |
| #ifdef JS_TRACE_LOGGING |
| emitTracelogStopEvent(TraceLogger_IonMonkey); |
| emitTracelogScriptStop(); |
| #endif |
| } |
| |
| inline void verifyHeapAccessDisassembly(uint32_t begin, uint32_t end, bool isLoad, |
| Scalar::Type type, unsigned numElems, |
| const Operand& mem, LAllocation alloc); |
| }; |
| |
| // An out-of-line path is generated at the end of the function. |
| 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(); |
| } |
| }; |
| |
| // For OOL paths that want a specific-typed code generator. |
| template <typename T> |
| class OutOfLineCodeBase : public OutOfLineCode |
| { |
| public: |
| virtual void generate(CodeGeneratorShared* codegen) { |
| accept(static_cast<T*>(codegen)); |
| } |
| |
| public: |
| virtual void accept(T* codegen) = 0; |
| }; |
| |
| // ArgSeq store arguments for OutOfLineCallVM. |
| // |
| // OutOfLineCallVM are created with "oolCallVM" function. The third argument of |
| // this function is an instance of a class which provides a "generate" in charge |
| // of pushing the argument, with "pushArg", for a VMFunction. |
| // |
| // Such list of arguments can be created by using the "ArgList" function which |
| // creates one instance of "ArgSeq", where the type of the arguments are inferred |
| // from the type of the arguments. |
| // |
| // The list of arguments must be written in the same order as if you were |
| // calling the function in C++. |
| // |
| // Example: |
| // ArgList(ToRegister(lir->lhs()), ToRegister(lir->rhs())) |
| |
| template <typename... ArgTypes> |
| class ArgSeq; |
| |
| template <> |
| class ArgSeq<> |
| { |
| public: |
| ArgSeq() { } |
| |
| inline void generate(CodeGeneratorShared* codegen) const { |
| } |
| }; |
| |
| template <typename HeadType, typename... TailTypes> |
| class ArgSeq<HeadType, TailTypes...> : public ArgSeq<TailTypes...> |
| { |
| private: |
| HeadType head_; |
| |
| public: |
| explicit ArgSeq(HeadType&& head, TailTypes&&... tail) |
| : ArgSeq<TailTypes...>(mozilla::Move(tail)...), |
| head_(mozilla::Move(head)) |
| { } |
| |
| // Arguments are pushed in reverse order, from last argument to first |
| // argument. |
| inline void generate(CodeGeneratorShared* codegen) const { |
| this->ArgSeq<TailTypes...>::generate(codegen); |
| codegen->pushArg(head_); |
| } |
| }; |
| |
| template <typename... ArgTypes> |
| inline ArgSeq<ArgTypes...> |
| ArgList(ArgTypes... args) |
| { |
| return ArgSeq<ArgTypes...>(mozilla::Move(args)...); |
| } |
| |
| // Store wrappers, to generate the right move of data after the VM call. |
| |
| struct StoreNothing |
| { |
| inline void generate(CodeGeneratorShared* codegen) const { |
| } |
| inline LiveRegisterSet clobbered() const { |
| return LiveRegisterSet(); // No register gets clobbered |
| } |
| }; |
| |
| class StoreRegisterTo |
| { |
| private: |
| Register out_; |
| |
| public: |
| explicit StoreRegisterTo(Register out) |
| : out_(out) |
| { } |
| |
| inline void generate(CodeGeneratorShared* codegen) const { |
| codegen->storeResultTo(out_); |
| } |
| inline LiveRegisterSet clobbered() const { |
| LiveRegisterSet set; |
| set.add(out_); |
| return set; |
| } |
| }; |
| |
| class StoreFloatRegisterTo |
| { |
| private: |
| FloatRegister out_; |
| |
| public: |
| explicit StoreFloatRegisterTo(FloatRegister out) |
| : out_(out) |
| { } |
| |
| inline void generate(CodeGeneratorShared* codegen) const { |
| codegen->storeFloatResultTo(out_); |
| } |
| inline LiveRegisterSet clobbered() const { |
| LiveRegisterSet set; |
| set.add(out_); |
| return set; |
| } |
| }; |
| |
| template <typename Output> |
| class StoreValueTo_ |
| { |
| private: |
| Output out_; |
| |
| public: |
| explicit StoreValueTo_(const Output& out) |
| : out_(out) |
| { } |
| |
| inline void generate(CodeGeneratorShared* codegen) const { |
| codegen->storeResultValueTo(out_); |
| } |
| inline LiveRegisterSet clobbered() const { |
| LiveRegisterSet set; |
| set.add(out_); |
| return set; |
| } |
| }; |
| |
| template <typename Output> |
| StoreValueTo_<Output> StoreValueTo(const Output& out) |
| { |
| return StoreValueTo_<Output>(out); |
| } |
| |
| template <class ArgSeq, class StoreOutputTo> |
| class OutOfLineCallVM : public OutOfLineCodeBase<CodeGeneratorShared> |
| { |
| private: |
| LInstruction* lir_; |
| const VMFunction& fun_; |
| ArgSeq args_; |
| StoreOutputTo out_; |
| |
| public: |
| OutOfLineCallVM(LInstruction* lir, const VMFunction& fun, const ArgSeq& args, |
| const StoreOutputTo& out) |
| : lir_(lir), |
| fun_(fun), |
| args_(args), |
| out_(out) |
| { } |
| |
| void accept(CodeGeneratorShared* codegen) { |
| codegen->visitOutOfLineCallVM(this); |
| } |
| |
| LInstruction* lir() const { return lir_; } |
| const VMFunction& function() const { return fun_; } |
| const ArgSeq& args() const { return args_; } |
| const StoreOutputTo& out() const { return out_; } |
| }; |
| |
| template <class ArgSeq, class StoreOutputTo> |
| inline OutOfLineCode* |
| CodeGeneratorShared::oolCallVM(const VMFunction& fun, LInstruction* lir, const ArgSeq& args, |
| const StoreOutputTo& out) |
| { |
| MOZ_ASSERT(lir->mirRaw()); |
| MOZ_ASSERT(lir->mirRaw()->isInstruction()); |
| |
| OutOfLineCode* ool = new(alloc()) OutOfLineCallVM<ArgSeq, StoreOutputTo>(lir, fun, args, out); |
| addOutOfLineCode(ool, lir->mirRaw()->toInstruction()); |
| return ool; |
| } |
| |
| template <class ArgSeq, class StoreOutputTo> |
| void |
| CodeGeneratorShared::visitOutOfLineCallVM(OutOfLineCallVM<ArgSeq, StoreOutputTo>* ool) |
| { |
| LInstruction* lir = ool->lir(); |
| |
| saveLive(lir); |
| ool->args().generate(this); |
| callVM(ool->function(), lir); |
| ool->out().generate(this); |
| restoreLiveIgnore(lir, ool->out().clobbered()); |
| masm.jump(ool->rejoin()); |
| } |
| |
| } // namespace jit |
| } // namespace js |
| |
| #endif /* jit_shared_CodeGenerator_shared_h */ |