summaryrefslogtreecommitdiffstats
path: root/js/src/jit/shared/CodeGenerator-shared.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit/shared/CodeGenerator-shared.h')
-rw-r--r--js/src/jit/shared/CodeGenerator-shared.h850
1 files changed, 850 insertions, 0 deletions
diff --git a/js/src/jit/shared/CodeGenerator-shared.h b/js/src/jit/shared/CodeGenerator-shared.h
new file mode 100644
index 000000000..c96808c2d
--- /dev/null
+++ b/js/src/jit/shared/CodeGenerator-shared.h
@@ -0,0 +1,850 @@
+/* -*- 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;
+class OutOfLineWasmTruncateCheck;
+
+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
+ struct PatchableTLEvent {
+ CodeOffset offset;
+ const char* event;
+ PatchableTLEvent(CodeOffset offset, const char* event)
+ : offset(offset), event(event)
+ {}
+ };
+ js::Vector<CodeOffset, 0, SystemAllocPolicy> patchableTraceLoggers_;
+ js::Vector<PatchableTLEvent, 0, SystemAllocPolicy> patchableTLEvents_;
+ 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;
+
+ inline Address ToAddress(const LAllocation& a);
+ inline Address ToAddress(const LAllocation* a);
+
+ uint32_t frameSize() const {
+ return frameClass_ == FrameSizeClass::None() ? frameDepth_ : frameClass_.frameSize();
+ }
+
+ 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_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();
+ }
+
+ // 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 emitWasmCallBase(LWasmCallBase* ins);
+
+ void emitPreBarrier(Register base, const LAllocation* index, int32_t offsetAdjustment);
+ 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 storePointerResultTo(Register reg) {
+ masm.storeCallPointerResult(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* getJumpLabelForBranch(MBasicBlock* block);
+
+ // 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);
+
+ // Get a label for the start of block which can be used for jumping, in
+ // place of jumpToBlock.
+ Label* labelForBackedgeWithImplicitCheck(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
+
+ template <class T>
+ wasm::TrapDesc trap(T* mir, wasm::Trap trap) {
+ return wasm::TrapDesc(mir->trapOffset(), trap, masm.framePushed());
+ }
+
+ 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);
+
+ virtual void visitOutOfLineWasmTruncateCheck(OutOfLineWasmTruncateCheck* ool) {
+ MOZ_CRASH("NYI");
+ }
+
+ 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);
+
+ 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);
+ }
+ // Log an arbitrary text. The TraceloggerTextId is used to toggle the
+ // logging on and off.
+ // Note: the text is not copied and need to be kept alive until linking.
+ void emitTracelogStartEvent(const char* text, TraceLoggerTextId enabledTextId) {
+ emitTracelogTree(/* isStart =*/ true, text, enabledTextId);
+ }
+ void emitTracelogStopEvent(const char* text, TraceLoggerTextId enabledTextId) {
+ emitTracelogTree(/* isStart =*/ false, text, enabledTextId);
+ }
+#endif
+ void emitTracelogIonStart() {
+#ifdef JS_TRACE_LOGGING
+ emitTracelogScriptStart();
+ emitTracelogStartEvent(TraceLogger_IonMonkey);
+#endif
+ }
+ void emitTracelogIonStop() {
+#ifdef JS_TRACE_LOGGING
+ emitTracelogStopEvent(TraceLogger_IonMonkey);
+ emitTracelogScriptStop();
+#endif
+ }
+
+ protected:
+ inline void verifyHeapAccessDisassembly(uint32_t begin, uint32_t end, bool isLoad,
+ Scalar::Type type, Operand mem, LAllocation alloc);
+
+ public:
+ inline void verifyLoadDisassembly(uint32_t begin, uint32_t end, Scalar::Type type,
+ Operand mem, LAllocation alloc);
+ inline void verifyStoreDisassembly(uint32_t begin, uint32_t end, Scalar::Type type,
+ Operand mem, LAllocation alloc);
+
+ bool isGlobalObject(JSObject* object);
+};
+
+// 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:
+ using RawHeadType = typename mozilla::RemoveReference<HeadType>::Type;
+ RawHeadType head_;
+
+ public:
+ template <typename ProvidedHead, typename... ProvidedTail>
+ explicit ArgSeq(ProvidedHead&& head, ProvidedTail&&... tail)
+ : ArgSeq<TailTypes...>(mozilla::Forward<ProvidedTail>(tail)...),
+ head_(mozilla::Forward<ProvidedHead>(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::Forward<ArgTypes>(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 {
+ // It's okay to use storePointerResultTo here - the VMFunction wrapper
+ // ensures the upper bytes are zero for bool/int32 return values.
+ codegen->storePointerResultTo(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());
+}
+
+class OutOfLineWasmTruncateCheck : public OutOfLineCodeBase<CodeGeneratorShared>
+{
+ MIRType fromType_;
+ MIRType toType_;
+ FloatRegister input_;
+ bool isUnsigned_;
+ wasm::TrapOffset trapOffset_;
+
+ public:
+ OutOfLineWasmTruncateCheck(MWasmTruncateToInt32* mir, FloatRegister input)
+ : fromType_(mir->input()->type()), toType_(MIRType::Int32), input_(input),
+ isUnsigned_(mir->isUnsigned()), trapOffset_(mir->trapOffset())
+ { }
+
+ OutOfLineWasmTruncateCheck(MWasmTruncateToInt64* mir, FloatRegister input)
+ : fromType_(mir->input()->type()), toType_(MIRType::Int64), input_(input),
+ isUnsigned_(mir->isUnsigned()), trapOffset_(mir->trapOffset())
+ { }
+
+ void accept(CodeGeneratorShared* codegen) {
+ codegen->visitOutOfLineWasmTruncateCheck(this);
+ }
+
+ FloatRegister input() const { return input_; }
+ MIRType toType() const { return toType_; }
+ MIRType fromType() const { return fromType_; }
+ bool isUnsigned() const { return isUnsigned_; }
+ wasm::TrapOffset trapOffset() const { return trapOffset_; }
+};
+
+} // namespace jit
+} // namespace js
+
+#endif /* jit_shared_CodeGenerator_shared_h */