summaryrefslogtreecommitdiffstats
path: root/js/src/jit/MacroAssembler.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit/MacroAssembler.h')
-rw-r--r--js/src/jit/MacroAssembler.h2233
1 files changed, 2233 insertions, 0 deletions
diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h
new file mode 100644
index 000000000..b6616321c
--- /dev/null
+++ b/js/src/jit/MacroAssembler.h
@@ -0,0 +1,2233 @@
+/* -*- 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_MacroAssembler_h
+#define jit_MacroAssembler_h
+
+#include "mozilla/MacroForEach.h"
+#include "mozilla/MathAlgorithms.h"
+
+#include "jscompartment.h"
+
+#if defined(JS_CODEGEN_X86)
+# include "jit/x86/MacroAssembler-x86.h"
+#elif defined(JS_CODEGEN_X64)
+# include "jit/x64/MacroAssembler-x64.h"
+#elif defined(JS_CODEGEN_ARM)
+# include "jit/arm/MacroAssembler-arm.h"
+#elif defined(JS_CODEGEN_ARM64)
+# include "jit/arm64/MacroAssembler-arm64.h"
+#elif defined(JS_CODEGEN_MIPS32)
+# include "jit/mips32/MacroAssembler-mips32.h"
+#elif defined(JS_CODEGEN_MIPS64)
+# include "jit/mips64/MacroAssembler-mips64.h"
+#elif defined(JS_CODEGEN_NONE)
+# include "jit/none/MacroAssembler-none.h"
+#else
+# error "Unknown architecture!"
+#endif
+#include "jit/AtomicOp.h"
+#include "jit/IonInstrumentation.h"
+#include "jit/JitCompartment.h"
+#include "jit/VMFunctions.h"
+#include "vm/ProxyObject.h"
+#include "vm/Shape.h"
+#include "vm/TypedArrayObject.h"
+#include "vm/UnboxedObject.h"
+
+using mozilla::FloatingPoint;
+
+// * How to read/write MacroAssembler method declarations:
+//
+// The following macros are made to avoid #ifdef around each method declarations
+// of the Macro Assembler, and they are also used as an hint on the location of
+// the implementations of each method. For example, the following declaration
+//
+// void Pop(FloatRegister t) DEFINED_ON(x86_shared, arm);
+//
+// suggests the MacroAssembler::Pop(FloatRegister) method is implemented in
+// x86-shared/MacroAssembler-x86-shared.h, and also in arm/MacroAssembler-arm.h.
+//
+// - If there is no annotation, then there is only one generic definition in
+// MacroAssembler.cpp.
+//
+// - If the declaration is "inline", then the method definition(s) would be in
+// the "-inl.h" variant of the same file(s).
+//
+// The script check_macroassembler_style.py (check-masm target of the Makefile)
+// is used to verify that method definitions are matching the annotation added
+// to the method declarations. If there is any difference, then you either
+// forgot to define the method in one of the macro assembler, or you forgot to
+// update the annotation of the macro assembler declaration.
+//
+// Some convenient short-cuts are used to avoid repeating the same list of
+// architectures on each method declaration, such as PER_ARCH and
+// PER_SHARED_ARCH.
+
+# define ALL_ARCH mips32, mips64, arm, arm64, x86, x64
+# define ALL_SHARED_ARCH arm, arm64, x86_shared, mips_shared
+
+// * How this macro works:
+//
+// DEFINED_ON is a macro which check if, for the current architecture, the
+// method is defined on the macro assembler or not.
+//
+// For each architecture, we have a macro named DEFINED_ON_arch. This macro is
+// empty if this is not the current architecture. Otherwise it must be either
+// set to "define" or "crash" (only use for the none target so-far).
+//
+// The DEFINED_ON macro maps the list of architecture names given as argument to
+// a list of macro names. For example,
+//
+// DEFINED_ON(arm, x86_shared)
+//
+// is expanded to
+//
+// DEFINED_ON_none DEFINED_ON_arm DEFINED_ON_x86_shared
+//
+// which are later expanded on ARM, x86, x64 by DEFINED_ON_EXPAND_ARCH_RESULTS
+// to
+//
+// define
+//
+// or if the JIT is disabled or set to no architecture to
+//
+// crash
+//
+// or to nothing, if the current architecture is not listed in the list of
+// arguments of DEFINED_ON. Note, only one of the DEFINED_ON_arch macro
+// contributes to the non-empty result, which is the macro of the current
+// architecture if it is listed in the arguments of DEFINED_ON.
+//
+// This result is appended to DEFINED_ON_RESULT_ before expanding the macro,
+// which result is either no annotation, a MOZ_CRASH(), or a "= delete"
+// annotation on the method declaration.
+
+# define DEFINED_ON_x86
+# define DEFINED_ON_x64
+# define DEFINED_ON_x86_shared
+# define DEFINED_ON_arm
+# define DEFINED_ON_arm64
+# define DEFINED_ON_mips32
+# define DEFINED_ON_mips64
+# define DEFINED_ON_mips_shared
+# define DEFINED_ON_none
+
+// Specialize for each architecture.
+#if defined(JS_CODEGEN_X86)
+# undef DEFINED_ON_x86
+# define DEFINED_ON_x86 define
+# undef DEFINED_ON_x86_shared
+# define DEFINED_ON_x86_shared define
+#elif defined(JS_CODEGEN_X64)
+# undef DEFINED_ON_x64
+# define DEFINED_ON_x64 define
+# undef DEFINED_ON_x86_shared
+# define DEFINED_ON_x86_shared define
+#elif defined(JS_CODEGEN_ARM)
+# undef DEFINED_ON_arm
+# define DEFINED_ON_arm define
+#elif defined(JS_CODEGEN_ARM64)
+# undef DEFINED_ON_arm64
+# define DEFINED_ON_arm64 define
+#elif defined(JS_CODEGEN_MIPS32)
+# undef DEFINED_ON_mips32
+# define DEFINED_ON_mips32 define
+# undef DEFINED_ON_mips_shared
+# define DEFINED_ON_mips_shared define
+#elif defined(JS_CODEGEN_MIPS64)
+# undef DEFINED_ON_mips64
+# define DEFINED_ON_mips64 define
+# undef DEFINED_ON_mips_shared
+# define DEFINED_ON_mips_shared define
+#elif defined(JS_CODEGEN_NONE)
+# undef DEFINED_ON_none
+# define DEFINED_ON_none crash
+#else
+# error "Unknown architecture!"
+#endif
+
+# define DEFINED_ON_RESULT_crash { MOZ_CRASH(); }
+# define DEFINED_ON_RESULT_define
+# define DEFINED_ON_RESULT_ = delete
+
+# define DEFINED_ON_DISPATCH_RESULT_2(Macro, Result) \
+ Macro ## Result
+# define DEFINED_ON_DISPATCH_RESULT(...) \
+ DEFINED_ON_DISPATCH_RESULT_2(DEFINED_ON_RESULT_, __VA_ARGS__)
+
+// We need to let the evaluation of MOZ_FOR_EACH terminates.
+# define DEFINED_ON_EXPAND_ARCH_RESULTS_3(ParenResult) \
+ DEFINED_ON_DISPATCH_RESULT ParenResult
+# define DEFINED_ON_EXPAND_ARCH_RESULTS_2(ParenResult) \
+ DEFINED_ON_EXPAND_ARCH_RESULTS_3 (ParenResult)
+# define DEFINED_ON_EXPAND_ARCH_RESULTS(ParenResult) \
+ DEFINED_ON_EXPAND_ARCH_RESULTS_2 (ParenResult)
+
+# define DEFINED_ON_FWDARCH(Arch) DEFINED_ON_ ## Arch
+# define DEFINED_ON_MAP_ON_ARCHS(ArchList) \
+ DEFINED_ON_EXPAND_ARCH_RESULTS( \
+ (MOZ_FOR_EACH(DEFINED_ON_FWDARCH, (), ArchList)))
+
+# define DEFINED_ON(...) \
+ DEFINED_ON_MAP_ON_ARCHS((none, __VA_ARGS__))
+
+# define PER_ARCH DEFINED_ON(ALL_ARCH)
+# define PER_SHARED_ARCH DEFINED_ON(ALL_SHARED_ARCH)
+
+
+#if MOZ_LITTLE_ENDIAN
+#define IMM32_16ADJ(X) X << 16
+#else
+#define IMM32_16ADJ(X) X
+#endif
+
+namespace js {
+namespace jit {
+
+// Defined in JitFrames.h
+enum ExitFrameTokenValues;
+
+// The public entrypoint for emitting assembly. Note that a MacroAssembler can
+// use cx->lifoAlloc, so take care not to interleave masm use with other
+// lifoAlloc use if one will be destroyed before the other.
+class MacroAssembler : public MacroAssemblerSpecific
+{
+ MacroAssembler* thisFromCtor() {
+ return this;
+ }
+
+ public:
+ class AutoRooter : public JS::AutoGCRooter
+ {
+ MacroAssembler* masm_;
+
+ public:
+ AutoRooter(JSContext* cx, MacroAssembler* masm)
+ : JS::AutoGCRooter(cx, IONMASM),
+ masm_(masm)
+ { }
+
+ MacroAssembler* masm() const {
+ return masm_;
+ }
+ };
+
+ /*
+ * Base class for creating a branch.
+ */
+ class Branch
+ {
+ bool init_;
+ Condition cond_;
+ Label* jump_;
+ Register reg_;
+
+ public:
+ Branch()
+ : init_(false),
+ cond_(Equal),
+ jump_(nullptr),
+ reg_(Register::FromCode(0)) // Quell compiler warnings.
+ { }
+
+ Branch(Condition cond, Register reg, Label* jump)
+ : init_(true),
+ cond_(cond),
+ jump_(jump),
+ reg_(reg)
+ { }
+
+ bool isInitialized() const {
+ return init_;
+ }
+
+ Condition cond() const {
+ return cond_;
+ }
+
+ Label* jump() const {
+ return jump_;
+ }
+
+ Register reg() const {
+ return reg_;
+ }
+
+ void invertCondition() {
+ cond_ = InvertCondition(cond_);
+ }
+
+ void relink(Label* jump) {
+ jump_ = jump;
+ }
+
+ virtual void emit(MacroAssembler& masm) = 0;
+ };
+
+ /*
+ * Creates a branch based on a specific TypeSet::Type.
+ * Note: emits number test (int/double) for TypeSet::DoubleType()
+ */
+ class BranchType : public Branch
+ {
+ TypeSet::Type type_;
+
+ public:
+ BranchType()
+ : Branch(),
+ type_(TypeSet::UnknownType())
+ { }
+
+ BranchType(Condition cond, Register reg, TypeSet::Type type, Label* jump)
+ : Branch(cond, reg, jump),
+ type_(type)
+ { }
+
+ void emit(MacroAssembler& masm);
+ };
+
+ /*
+ * Creates a branch based on a GCPtr.
+ */
+ class BranchGCPtr : public Branch
+ {
+ ImmGCPtr ptr_;
+
+ public:
+ BranchGCPtr()
+ : Branch(),
+ ptr_(ImmGCPtr(nullptr))
+ { }
+
+ BranchGCPtr(Condition cond, Register reg, ImmGCPtr ptr, Label* jump)
+ : Branch(cond, reg, jump),
+ ptr_(ptr)
+ { }
+
+ void emit(MacroAssembler& masm);
+ };
+
+ mozilla::Maybe<AutoRooter> autoRooter_;
+ mozilla::Maybe<JitContext> jitContext_;
+ mozilla::Maybe<AutoJitContextAlloc> alloc_;
+
+ private:
+ // Labels for handling exceptions and failures.
+ NonAssertingLabel failureLabel_;
+
+ public:
+ MacroAssembler()
+ : framePushed_(0),
+#ifdef DEBUG
+ inCall_(false),
+#endif
+ emitProfilingInstrumentation_(false)
+ {
+ JitContext* jcx = GetJitContext();
+ JSContext* cx = jcx->cx;
+ if (cx)
+ constructRoot(cx);
+
+ if (!jcx->temp) {
+ MOZ_ASSERT(cx);
+ alloc_.emplace(cx);
+ }
+
+ moveResolver_.setAllocator(*jcx->temp);
+
+#if defined(JS_CODEGEN_ARM)
+ initWithAllocator();
+ m_buffer.id = jcx->getNextAssemblerId();
+#elif defined(JS_CODEGEN_ARM64)
+ initWithAllocator();
+ armbuffer_.id = jcx->getNextAssemblerId();
+#endif
+ }
+
+ // This constructor should only be used when there is no JitContext active
+ // (for example, Trampoline-$(ARCH).cpp and IonCaches.cpp).
+ explicit MacroAssembler(JSContext* cx, IonScript* ion = nullptr,
+ JSScript* script = nullptr, jsbytecode* pc = nullptr);
+
+ // wasm compilation handles its own JitContext-pushing
+ struct WasmToken {};
+ explicit MacroAssembler(WasmToken, TempAllocator& alloc)
+ : framePushed_(0),
+#ifdef DEBUG
+ inCall_(false),
+#endif
+ emitProfilingInstrumentation_(false)
+ {
+ moveResolver_.setAllocator(alloc);
+
+#if defined(JS_CODEGEN_ARM)
+ initWithAllocator();
+ m_buffer.id = 0;
+#elif defined(JS_CODEGEN_ARM64)
+ initWithAllocator();
+ armbuffer_.id = 0;
+#endif
+ }
+
+ void constructRoot(JSContext* cx) {
+ autoRooter_.emplace(cx, this);
+ }
+
+ MoveResolver& moveResolver() {
+ return moveResolver_;
+ }
+
+ size_t instructionsSize() const {
+ return size();
+ }
+
+ //{{{ check_macroassembler_style
+ public:
+ // ===============================================================
+ // MacroAssembler high-level usage.
+
+ // Flushes the assembly buffer, on platforms that need it.
+ void flush() PER_SHARED_ARCH;
+
+ // Add a comment that is visible in the pretty printed assembly code.
+ void comment(const char* msg) PER_SHARED_ARCH;
+
+ // ===============================================================
+ // Frame manipulation functions.
+
+ inline uint32_t framePushed() const;
+ inline void setFramePushed(uint32_t framePushed);
+ inline void adjustFrame(int32_t value);
+
+ // Adjust the frame, to account for implicit modification of the stack
+ // pointer, such that callee can remove arguments on the behalf of the
+ // caller.
+ inline void implicitPop(uint32_t bytes);
+
+ private:
+ // This field is used to statically (at compilation time) emulate a frame
+ // pointer by keeping track of stack manipulations.
+ //
+ // It is maintained by all stack manipulation functions below.
+ uint32_t framePushed_;
+
+ public:
+ // ===============================================================
+ // Stack manipulation functions.
+
+ void PushRegsInMask(LiveRegisterSet set)
+ DEFINED_ON(arm, arm64, mips32, mips64, x86_shared);
+ void PushRegsInMask(LiveGeneralRegisterSet set);
+
+ void PopRegsInMask(LiveRegisterSet set);
+ void PopRegsInMask(LiveGeneralRegisterSet set);
+ void PopRegsInMaskIgnore(LiveRegisterSet set, LiveRegisterSet ignore)
+ DEFINED_ON(arm, arm64, mips32, mips64, x86_shared);
+
+ void Push(const Operand op) DEFINED_ON(x86_shared);
+ void Push(Register reg) PER_SHARED_ARCH;
+ void Push(Register reg1, Register reg2, Register reg3, Register reg4) DEFINED_ON(arm64);
+ void Push(const Imm32 imm) PER_SHARED_ARCH;
+ void Push(const ImmWord imm) PER_SHARED_ARCH;
+ void Push(const ImmPtr imm) PER_SHARED_ARCH;
+ void Push(const ImmGCPtr ptr) PER_SHARED_ARCH;
+ void Push(FloatRegister reg) PER_SHARED_ARCH;
+ void Push(jsid id, Register scratchReg);
+ void Push(TypedOrValueRegister v);
+ void Push(const ConstantOrRegister& v);
+ void Push(const ValueOperand& val);
+ void Push(const Value& val);
+ void Push(JSValueType type, Register reg);
+ void PushValue(const Address& addr);
+ void PushEmptyRooted(VMFunction::RootType rootType);
+ inline CodeOffset PushWithPatch(ImmWord word);
+ inline CodeOffset PushWithPatch(ImmPtr imm);
+
+ void Pop(const Operand op) DEFINED_ON(x86_shared);
+ void Pop(Register reg) PER_SHARED_ARCH;
+ void Pop(FloatRegister t) PER_SHARED_ARCH;
+ void Pop(const ValueOperand& val) PER_SHARED_ARCH;
+ void popRooted(VMFunction::RootType rootType, Register cellReg, const ValueOperand& valueReg);
+
+ // Move the stack pointer based on the requested amount.
+ void adjustStack(int amount);
+ void freeStack(uint32_t amount);
+
+ // Warning: This method does not update the framePushed() counter.
+ void freeStack(Register amount);
+
+ private:
+ // ===============================================================
+ // Register allocation fields.
+#ifdef DEBUG
+ friend AutoRegisterScope;
+ friend AutoFloatRegisterScope;
+ // Used to track register scopes for debug builds.
+ // Manipulated by the AutoGenericRegisterScope class.
+ AllocatableRegisterSet debugTrackedRegisters_;
+#endif // DEBUG
+
+ public:
+ // ===============================================================
+ // Simple call functions.
+
+ CodeOffset call(Register reg) PER_SHARED_ARCH;
+ CodeOffset call(Label* label) PER_SHARED_ARCH;
+ void call(const Address& addr) DEFINED_ON(x86_shared);
+ void call(ImmWord imm) PER_SHARED_ARCH;
+ // Call a target native function, which is neither traceable nor movable.
+ void call(ImmPtr imm) PER_SHARED_ARCH;
+ void call(wasm::SymbolicAddress imm) PER_SHARED_ARCH;
+ // Call a target JitCode, which must be traceable, and may be movable.
+ void call(JitCode* c) PER_SHARED_ARCH;
+
+ inline void call(const wasm::CallSiteDesc& desc, const Register reg);
+ inline void call(const wasm::CallSiteDesc& desc, uint32_t funcDefIndex);
+ inline void call(const wasm::CallSiteDesc& desc, wasm::Trap trap);
+
+ CodeOffset callWithPatch() PER_SHARED_ARCH;
+ void patchCall(uint32_t callerOffset, uint32_t calleeOffset) PER_SHARED_ARCH;
+
+ // Push the return address and make a call. On platforms where this function
+ // is not defined, push the link register (pushReturnAddress) at the entry
+ // point of the callee.
+ void callAndPushReturnAddress(Register reg) DEFINED_ON(x86_shared);
+ void callAndPushReturnAddress(Label* label) DEFINED_ON(x86_shared);
+
+ void pushReturnAddress() DEFINED_ON(mips_shared, arm, arm64);
+ void popReturnAddress() DEFINED_ON(mips_shared, arm, arm64);
+
+ public:
+ // ===============================================================
+ // Patchable near/far jumps.
+
+ // "Far jumps" provide the ability to jump to any uint32_t offset from any
+ // other uint32_t offset without using a constant pool (thus returning a
+ // simple CodeOffset instead of a CodeOffsetJump).
+ CodeOffset farJumpWithPatch() PER_SHARED_ARCH;
+ void patchFarJump(CodeOffset farJump, uint32_t targetOffset) PER_SHARED_ARCH;
+ static void repatchFarJump(uint8_t* code, uint32_t farJumpOffset, uint32_t targetOffset) PER_SHARED_ARCH;
+
+ // Emit a nop that can be patched to and from a nop and a jump with an int8
+ // relative displacement.
+ CodeOffset nopPatchableToNearJump() PER_SHARED_ARCH;
+ static void patchNopToNearJump(uint8_t* jump, uint8_t* target) PER_SHARED_ARCH;
+ static void patchNearJumpToNop(uint8_t* jump) PER_SHARED_ARCH;
+
+ public:
+ // ===============================================================
+ // ABI function calls.
+
+ // Setup a call to C/C++ code, given the assumption that the framePushed
+ // accruately define the state of the stack, and that the top of the stack
+ // was properly aligned. Note that this only supports cdecl.
+ void setupAlignedABICall(); // CRASH_ON(arm64)
+
+ // Setup an ABI call for when the alignment is not known. This may need a
+ // scratch register.
+ void setupUnalignedABICall(Register scratch) PER_ARCH;
+
+ // Arguments must be assigned to a C/C++ call in order. They are moved
+ // in parallel immediately before performing the call. This process may
+ // temporarily use more stack, in which case esp-relative addresses will be
+ // automatically adjusted. It is extremely important that esp-relative
+ // addresses are computed *after* setupABICall(). Furthermore, no
+ // operations should be emitted while setting arguments.
+ void passABIArg(const MoveOperand& from, MoveOp::Type type);
+ inline void passABIArg(Register reg);
+ inline void passABIArg(FloatRegister reg, MoveOp::Type type);
+
+ template <typename T>
+ inline void callWithABI(const T& fun, MoveOp::Type result = MoveOp::GENERAL);
+
+ private:
+ // Reinitialize the variables which have to be cleared before making a call
+ // with callWithABI.
+ void setupABICall();
+
+ // Reserve the stack and resolve the arguments move.
+ void callWithABIPre(uint32_t* stackAdjust, bool callFromWasm = false) PER_ARCH;
+
+ // Emits a call to a C/C++ function, resolving all argument moves.
+ void callWithABINoProfiler(void* fun, MoveOp::Type result);
+ void callWithABINoProfiler(wasm::SymbolicAddress imm, MoveOp::Type result);
+ void callWithABINoProfiler(Register fun, MoveOp::Type result) PER_ARCH;
+ void callWithABINoProfiler(const Address& fun, MoveOp::Type result) PER_ARCH;
+
+ // Restore the stack to its state before the setup function call.
+ void callWithABIPost(uint32_t stackAdjust, MoveOp::Type result) PER_ARCH;
+
+ // Create the signature to be able to decode the arguments of a native
+ // function, when calling a function within the simulator.
+ inline void appendSignatureType(MoveOp::Type type);
+ inline ABIFunctionType signature() const;
+
+ // Private variables used to handle moves between registers given as
+ // arguments to passABIArg and the list of ABI registers expected for the
+ // signature of the function.
+ MoveResolver moveResolver_;
+
+ // Architecture specific implementation which specify how registers & stack
+ // offsets are used for calling a function.
+ ABIArgGenerator abiArgs_;
+
+#ifdef DEBUG
+ // Flag use to assert that we use ABI function in the right context.
+ bool inCall_;
+#endif
+
+ // If set by setupUnalignedABICall then callWithABI will pop the stack
+ // register which is on the stack.
+ bool dynamicAlignment_;
+
+#ifdef JS_SIMULATOR
+ // The signature is used to accumulate all types of arguments which are used
+ // by the caller. This is used by the simulators to decode the arguments
+ // properly, and cast the function pointer to the right type.
+ uint32_t signature_;
+#endif
+
+ public:
+ // ===============================================================
+ // Jit Frames.
+ //
+ // These functions are used to build the content of the Jit frames. See
+ // CommonFrameLayout class, and all its derivatives. The content should be
+ // pushed in the opposite order as the fields of the structures, such that
+ // the structures can be used to interpret the content of the stack.
+
+ // Call the Jit function, and push the return address (or let the callee
+ // push the return address).
+ //
+ // These functions return the offset of the return address, in order to use
+ // the return address to index the safepoints, which are used to list all
+ // live registers.
+ inline uint32_t callJitNoProfiler(Register callee);
+ inline uint32_t callJit(Register callee);
+ inline uint32_t callJit(JitCode* code);
+
+ // The frame descriptor is the second field of all Jit frames, pushed before
+ // calling the Jit function. It is a composite value defined in JitFrames.h
+ inline void makeFrameDescriptor(Register frameSizeReg, FrameType type, uint32_t headerSize);
+
+ // Push the frame descriptor, based on the statically known framePushed.
+ inline void pushStaticFrameDescriptor(FrameType type, uint32_t headerSize);
+
+ // Push the callee token of a JSFunction which pointer is stored in the
+ // |callee| register. The callee token is packed with a |constructing| flag
+ // which correspond to the fact that the JS function is called with "new" or
+ // not.
+ inline void PushCalleeToken(Register callee, bool constructing);
+
+ // Unpack a callee token located at the |token| address, and return the
+ // JSFunction pointer in the |dest| register.
+ inline void loadFunctionFromCalleeToken(Address token, Register dest);
+
+ // This function emulates a call by pushing an exit frame on the stack,
+ // except that the fake-function is inlined within the body of the caller.
+ //
+ // This function assumes that the current frame is an IonJS frame.
+ //
+ // This function returns the offset of the /fake/ return address, in order to use
+ // the return address to index the safepoints, which are used to list all
+ // live registers.
+ //
+ // This function should be balanced with a call to adjustStack, to pop the
+ // exit frame and emulate the return statement of the inlined function.
+ inline uint32_t buildFakeExitFrame(Register scratch);
+
+ private:
+ // This function is used by buildFakeExitFrame to push a fake return address
+ // on the stack. This fake return address should never be used for resuming
+ // any execution, and can even be an invalid pointer into the instruction
+ // stream, as long as it does not alias any other.
+ uint32_t pushFakeReturnAddress(Register scratch) PER_SHARED_ARCH;
+
+ public:
+ // ===============================================================
+ // Exit frame footer.
+ //
+ // When calling outside the Jit we push an exit frame. To mark the stack
+ // correctly, we have to push additional information, called the Exit frame
+ // footer, which is used to identify how the stack is marked.
+ //
+ // See JitFrames.h, and MarkJitExitFrame in JitFrames.cpp.
+
+ // If the current piece of code might be garbage collected, then the exit
+ // frame footer must contain a pointer to the current JitCode, such that the
+ // garbage collector can keep the code alive as long this code is on the
+ // stack. This function pushes a placeholder which is replaced when the code
+ // is linked.
+ inline void PushStubCode();
+
+ // Return true if the code contains a self-reference which needs to be
+ // patched when the code is linked.
+ inline bool hasSelfReference() const;
+
+ // Push stub code and the VMFunction pointer.
+ inline void enterExitFrame(const VMFunction* f = nullptr);
+
+ // Push an exit frame token to identify which fake exit frame this footer
+ // corresponds to.
+ inline void enterFakeExitFrame(enum ExitFrameTokenValues token);
+
+ // Push an exit frame token for a native call.
+ inline void enterFakeExitFrameForNative(bool isConstructing);
+
+ // Pop ExitFrame footer in addition to the extra frame.
+ inline void leaveExitFrame(size_t extraFrame = 0);
+
+ private:
+ // Save the top of the stack into PerThreadData::jitTop of the main thread,
+ // which should be the location of the latest exit frame.
+ void linkExitFrame();
+
+ // Patch the value of PushStubCode with the pointer to the finalized code.
+ void linkSelfReference(JitCode* code);
+
+ // If the JitCode that created this assembler needs to transition into the VM,
+ // we want to store the JitCode on the stack in order to mark it during a GC.
+ // This is a reference to a patch location where the JitCode* will be written.
+ CodeOffset selfReferencePatch_;
+
+ public:
+ // ===============================================================
+ // Move instructions
+
+ inline void move64(Imm64 imm, Register64 dest) PER_ARCH;
+ inline void move64(Register64 src, Register64 dest) PER_ARCH;
+
+ inline void moveFloat32ToGPR(FloatRegister src, Register dest) PER_SHARED_ARCH;
+ inline void moveGPRToFloat32(Register src, FloatRegister dest) PER_SHARED_ARCH;
+
+ inline void move8SignExtend(Register src, Register dest) PER_SHARED_ARCH;
+ inline void move16SignExtend(Register src, Register dest) PER_SHARED_ARCH;
+
+ // ===============================================================
+ // Logical instructions
+
+ inline void not32(Register reg) PER_SHARED_ARCH;
+
+ inline void and32(Register src, Register dest) PER_SHARED_ARCH;
+ inline void and32(Imm32 imm, Register dest) PER_SHARED_ARCH;
+ inline void and32(Imm32 imm, Register src, Register dest) DEFINED_ON(arm64);
+ inline void and32(Imm32 imm, const Address& dest) PER_SHARED_ARCH;
+ inline void and32(const Address& src, Register dest) PER_SHARED_ARCH;
+
+ inline void andPtr(Register src, Register dest) PER_ARCH;
+ inline void andPtr(Imm32 imm, Register dest) PER_ARCH;
+
+ inline void and64(Imm64 imm, Register64 dest) PER_ARCH;
+ inline void or64(Imm64 imm, Register64 dest) PER_ARCH;
+ inline void xor64(Imm64 imm, Register64 dest) PER_ARCH;
+
+ inline void or32(Register src, Register dest) PER_SHARED_ARCH;
+ inline void or32(Imm32 imm, Register dest) PER_SHARED_ARCH;
+ inline void or32(Imm32 imm, const Address& dest) PER_SHARED_ARCH;
+
+ inline void orPtr(Register src, Register dest) PER_ARCH;
+ inline void orPtr(Imm32 imm, Register dest) PER_ARCH;
+
+ inline void and64(Register64 src, Register64 dest) PER_ARCH;
+ inline void or64(Register64 src, Register64 dest) PER_ARCH;
+ inline void xor64(Register64 src, Register64 dest) PER_ARCH;
+
+ inline void xor32(Register src, Register dest) PER_SHARED_ARCH;
+ inline void xor32(Imm32 imm, Register dest) PER_SHARED_ARCH;
+
+ inline void xorPtr(Register src, Register dest) PER_ARCH;
+ inline void xorPtr(Imm32 imm, Register dest) PER_ARCH;
+
+ inline void and64(const Operand& src, Register64 dest) DEFINED_ON(x64, mips64);
+ inline void or64(const Operand& src, Register64 dest) DEFINED_ON(x64, mips64);
+ inline void xor64(const Operand& src, Register64 dest) DEFINED_ON(x64, mips64);
+
+ // ===============================================================
+ // Arithmetic functions
+
+ inline void add32(Register src, Register dest) PER_SHARED_ARCH;
+ inline void add32(Imm32 imm, Register dest) PER_SHARED_ARCH;
+ inline void add32(Imm32 imm, const Address& dest) PER_SHARED_ARCH;
+ inline void add32(Imm32 imm, const AbsoluteAddress& dest) DEFINED_ON(x86_shared);
+
+ inline void addPtr(Register src, Register dest) PER_ARCH;
+ inline void addPtr(Register src1, Register src2, Register dest) DEFINED_ON(arm64);
+ inline void addPtr(Imm32 imm, Register dest) PER_ARCH;
+ inline void addPtr(Imm32 imm, Register src, Register dest) DEFINED_ON(arm64);
+ inline void addPtr(ImmWord imm, Register dest) PER_ARCH;
+ inline void addPtr(ImmPtr imm, Register dest);
+ inline void addPtr(Imm32 imm, const Address& dest) DEFINED_ON(mips_shared, arm, arm64, x86, x64);
+ inline void addPtr(Imm32 imm, const AbsoluteAddress& dest) DEFINED_ON(x86, x64);
+ inline void addPtr(const Address& src, Register dest) DEFINED_ON(mips_shared, arm, arm64, x86, x64);
+
+ inline void add64(Register64 src, Register64 dest) PER_ARCH;
+ inline void add64(Imm32 imm, Register64 dest) PER_ARCH;
+ inline void add64(Imm64 imm, Register64 dest) DEFINED_ON(x86, x64, arm, mips32, mips64);
+ inline void add64(const Operand& src, Register64 dest) DEFINED_ON(x64, mips64);
+
+ inline void addFloat32(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH;
+
+ inline void addDouble(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH;
+ inline void addConstantDouble(double d, FloatRegister dest) DEFINED_ON(x86);
+
+ inline void sub32(const Address& src, Register dest) PER_SHARED_ARCH;
+ inline void sub32(Register src, Register dest) PER_SHARED_ARCH;
+ inline void sub32(Imm32 imm, Register dest) PER_SHARED_ARCH;
+
+ inline void subPtr(Register src, Register dest) PER_ARCH;
+ inline void subPtr(Register src, const Address& dest) DEFINED_ON(mips_shared, arm, arm64, x86, x64);
+ inline void subPtr(Imm32 imm, Register dest) PER_ARCH;
+ inline void subPtr(ImmWord imm, Register dest) DEFINED_ON(x64);
+ inline void subPtr(const Address& addr, Register dest) DEFINED_ON(mips_shared, arm, arm64, x86, x64);
+
+ inline void sub64(Register64 src, Register64 dest) PER_ARCH;
+ inline void sub64(Imm64 imm, Register64 dest) DEFINED_ON(x86, x64, arm, mips32, mips64);
+ inline void sub64(const Operand& src, Register64 dest) DEFINED_ON(x64, mips64);
+
+ inline void subFloat32(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH;
+
+ inline void subDouble(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH;
+
+ // On x86-shared, srcDest must be eax and edx will be clobbered.
+ inline void mul32(Register rhs, Register srcDest) PER_SHARED_ARCH;
+
+ inline void mul32(Register src1, Register src2, Register dest, Label* onOver, Label* onZero) DEFINED_ON(arm64);
+
+ inline void mul64(const Operand& src, const Register64& dest) DEFINED_ON(x64);
+ inline void mul64(const Operand& src, const Register64& dest, const Register temp)
+ DEFINED_ON(x64, mips64);
+ inline void mul64(Imm64 imm, const Register64& dest) PER_ARCH;
+ inline void mul64(Imm64 imm, const Register64& dest, const Register temp)
+ DEFINED_ON(x86, x64, arm, mips32, mips64);
+ inline void mul64(const Register64& src, const Register64& dest, const Register temp)
+ PER_ARCH;
+
+ inline void mulBy3(Register src, Register dest) PER_ARCH;
+
+ inline void mulFloat32(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH;
+ inline void mulDouble(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH;
+
+ inline void mulDoublePtr(ImmPtr imm, Register temp, FloatRegister dest) DEFINED_ON(mips_shared, arm, arm64, x86, x64);
+
+ // Perform an integer division, returning the integer part rounded toward zero.
+ // rhs must not be zero, and the division must not overflow.
+ //
+ // On x86_shared, srcDest must be eax and edx will be clobbered.
+ // On ARM, the chip must have hardware division instructions.
+ inline void quotient32(Register rhs, Register srcDest, bool isUnsigned) PER_SHARED_ARCH;
+
+ // Perform an integer division, returning the remainder part.
+ // rhs must not be zero, and the division must not overflow.
+ //
+ // On x86_shared, srcDest must be eax and edx will be clobbered.
+ // On ARM, the chip must have hardware division instructions.
+ inline void remainder32(Register rhs, Register srcDest, bool isUnsigned) PER_SHARED_ARCH;
+
+ inline void divFloat32(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH;
+ inline void divDouble(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH;
+
+ inline void inc32(RegisterOrInt32Constant* key);
+ inline void inc64(AbsoluteAddress dest) PER_ARCH;
+
+ inline void dec32(RegisterOrInt32Constant* key);
+
+ inline void neg32(Register reg) PER_SHARED_ARCH;
+ inline void neg64(Register64 reg) DEFINED_ON(x86, x64, arm, mips32, mips64);
+
+ inline void negateFloat(FloatRegister reg) PER_SHARED_ARCH;
+
+ inline void negateDouble(FloatRegister reg) PER_SHARED_ARCH;
+
+ inline void absFloat32(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH;
+ inline void absDouble(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH;
+
+ inline void sqrtFloat32(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH;
+ inline void sqrtDouble(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH;
+
+ // srcDest = {min,max}{Float32,Double}(srcDest, other)
+ // For min and max, handle NaN specially if handleNaN is true.
+
+ inline void minFloat32(FloatRegister other, FloatRegister srcDest, bool handleNaN) PER_SHARED_ARCH;
+ inline void minDouble(FloatRegister other, FloatRegister srcDest, bool handleNaN) PER_SHARED_ARCH;
+
+ inline void maxFloat32(FloatRegister other, FloatRegister srcDest, bool handleNaN) PER_SHARED_ARCH;
+ inline void maxDouble(FloatRegister other, FloatRegister srcDest, bool handleNaN) PER_SHARED_ARCH;
+
+ // ===============================================================
+ // Shift functions
+
+ // For shift-by-register there may be platform-specific
+ // variations, for example, x86 will perform the shift mod 32 but
+ // ARM will perform the shift mod 256.
+ //
+ // For shift-by-immediate the platform assembler may restrict the
+ // immediate, for example, the ARM assembler requires the count
+ // for 32-bit shifts to be in the range [0,31].
+
+ inline void lshift32(Imm32 shift, Register srcDest) PER_SHARED_ARCH;
+ inline void rshift32(Imm32 shift, Register srcDest) PER_SHARED_ARCH;
+ inline void rshift32Arithmetic(Imm32 shift, Register srcDest) PER_SHARED_ARCH;
+
+ inline void lshiftPtr(Imm32 imm, Register dest) PER_ARCH;
+ inline void rshiftPtr(Imm32 imm, Register dest) PER_ARCH;
+ inline void rshiftPtr(Imm32 imm, Register src, Register dest) DEFINED_ON(arm64);
+ inline void rshiftPtrArithmetic(Imm32 imm, Register dest) PER_ARCH;
+
+ inline void lshift64(Imm32 imm, Register64 dest) PER_ARCH;
+ inline void rshift64(Imm32 imm, Register64 dest) PER_ARCH;
+ inline void rshift64Arithmetic(Imm32 imm, Register64 dest) PER_ARCH;
+
+ // On x86_shared these have the constraint that shift must be in CL.
+ inline void lshift32(Register shift, Register srcDest) PER_SHARED_ARCH;
+ inline void rshift32(Register shift, Register srcDest) PER_SHARED_ARCH;
+ inline void rshift32Arithmetic(Register shift, Register srcDest) PER_SHARED_ARCH;
+
+ inline void lshift64(Register shift, Register64 srcDest) PER_ARCH;
+ inline void rshift64(Register shift, Register64 srcDest) PER_ARCH;
+ inline void rshift64Arithmetic(Register shift, Register64 srcDest) PER_ARCH;
+
+ // ===============================================================
+ // Rotation functions
+ // Note: - on x86 and x64 the count register must be in CL.
+ // - on x64 the temp register should be InvalidReg.
+
+ inline void rotateLeft(Imm32 count, Register input, Register dest) PER_SHARED_ARCH;
+ inline void rotateLeft(Register count, Register input, Register dest) PER_SHARED_ARCH;
+ inline void rotateLeft64(Imm32 count, Register64 input, Register64 dest) DEFINED_ON(x64);
+ inline void rotateLeft64(Register count, Register64 input, Register64 dest) DEFINED_ON(x64);
+ inline void rotateLeft64(Imm32 count, Register64 input, Register64 dest, Register temp)
+ DEFINED_ON(x86, x64, arm, mips32, mips64);
+ inline void rotateLeft64(Register count, Register64 input, Register64 dest, Register temp)
+ PER_ARCH;
+
+ inline void rotateRight(Imm32 count, Register input, Register dest) PER_SHARED_ARCH;
+ inline void rotateRight(Register count, Register input, Register dest) PER_SHARED_ARCH;
+ inline void rotateRight64(Imm32 count, Register64 input, Register64 dest) DEFINED_ON(x64);
+ inline void rotateRight64(Register count, Register64 input, Register64 dest) DEFINED_ON(x64);
+ inline void rotateRight64(Imm32 count, Register64 input, Register64 dest, Register temp)
+ DEFINED_ON(x86, x64, arm, mips32, mips64);
+ inline void rotateRight64(Register count, Register64 input, Register64 dest, Register temp)
+ PER_ARCH;
+
+ // ===============================================================
+ // Bit counting functions
+
+ // knownNotZero may be true only if the src is known not to be zero.
+ inline void clz32(Register src, Register dest, bool knownNotZero) PER_SHARED_ARCH;
+ inline void ctz32(Register src, Register dest, bool knownNotZero) PER_SHARED_ARCH;
+
+ inline void clz64(Register64 src, Register dest) PER_ARCH;
+ inline void ctz64(Register64 src, Register dest) PER_ARCH;
+
+ // On x86_shared, temp may be Invalid only if the chip has the POPCNT instruction.
+ // On ARM, temp may never be Invalid.
+ inline void popcnt32(Register src, Register dest, Register temp) PER_SHARED_ARCH;
+
+ // temp may be invalid only if the chip has the POPCNT instruction.
+ inline void popcnt64(Register64 src, Register64 dest, Register temp) PER_ARCH;
+
+ // ===============================================================
+ // Condition functions
+
+ template <typename T1, typename T2>
+ inline void cmp32Set(Condition cond, T1 lhs, T2 rhs, Register dest)
+ DEFINED_ON(x86_shared, arm, arm64, mips32, mips64);
+
+ template <typename T1, typename T2>
+ inline void cmpPtrSet(Condition cond, T1 lhs, T2 rhs, Register dest)
+ PER_ARCH;
+
+ // ===============================================================
+ // Branch functions
+
+ template <class L>
+ inline void branch32(Condition cond, Register lhs, Register rhs, L label) PER_SHARED_ARCH;
+ template <class L>
+ inline void branch32(Condition cond, Register lhs, Imm32 rhs, L label) PER_SHARED_ARCH;
+ inline void branch32(Condition cond, Register length, const RegisterOrInt32Constant& key,
+ Label* label);
+
+ inline void branch32(Condition cond, const Address& lhs, Register rhs, Label* label) PER_SHARED_ARCH;
+ inline void branch32(Condition cond, const Address& lhs, Imm32 rhs, Label* label) PER_SHARED_ARCH;
+ inline void branch32(Condition cond, const Address& length, const RegisterOrInt32Constant& key,
+ Label* label);
+
+ inline void branch32(Condition cond, const AbsoluteAddress& lhs, Register rhs, Label* label)
+ DEFINED_ON(arm, arm64, mips_shared, x86, x64);
+ inline void branch32(Condition cond, const AbsoluteAddress& lhs, Imm32 rhs, Label* label)
+ DEFINED_ON(arm, arm64, mips_shared, x86, x64);
+
+ inline void branch32(Condition cond, const BaseIndex& lhs, Register rhs, Label* label)
+ DEFINED_ON(x86_shared);
+ inline void branch32(Condition cond, const BaseIndex& lhs, Imm32 rhs, Label* label) PER_SHARED_ARCH;
+
+ inline void branch32(Condition cond, const Operand& lhs, Register rhs, Label* label) DEFINED_ON(x86_shared);
+ inline void branch32(Condition cond, const Operand& lhs, Imm32 rhs, Label* label) DEFINED_ON(x86_shared);
+
+ inline void branch32(Condition cond, wasm::SymbolicAddress lhs, Imm32 rhs, Label* label)
+ DEFINED_ON(arm, arm64, mips_shared, x86, x64);
+
+ // The supported condition are Equal, NotEqual, LessThan(orEqual), GreaterThan(orEqual),
+ // Below(orEqual) and Above(orEqual).
+ // When a fail label is not defined it will fall through to next instruction,
+ // else jump to the fail label.
+ inline void branch64(Condition cond, Register64 lhs, Imm64 val, Label* success,
+ Label* fail = nullptr) PER_ARCH;
+ inline void branch64(Condition cond, Register64 lhs, Register64 rhs, Label* success,
+ Label* fail = nullptr) DEFINED_ON(x86, x64, arm, mips32, mips64);
+ // On x86 and x64 NotEqual and Equal conditions are allowed for the branch64 variants
+ // with Address as lhs. On others only the NotEqual condition.
+ inline void branch64(Condition cond, const Address& lhs, Imm64 val, Label* label) PER_ARCH;
+
+ // Compare the value at |lhs| with the value at |rhs|. The scratch
+ // register *must not* be the base of |lhs| or |rhs|.
+ inline void branch64(Condition cond, const Address& lhs, const Address& rhs, Register scratch,
+ Label* label) PER_ARCH;
+
+ template <class L>
+ inline void branchPtr(Condition cond, Register lhs, Register rhs, L label) PER_SHARED_ARCH;
+ inline void branchPtr(Condition cond, Register lhs, Imm32 rhs, Label* label) PER_SHARED_ARCH;
+ inline void branchPtr(Condition cond, Register lhs, ImmPtr rhs, Label* label) PER_SHARED_ARCH;
+ inline void branchPtr(Condition cond, Register lhs, ImmGCPtr rhs, Label* label) PER_SHARED_ARCH;
+ inline void branchPtr(Condition cond, Register lhs, ImmWord rhs, Label* label) PER_SHARED_ARCH;
+
+ template <class L>
+ inline void branchPtr(Condition cond, const Address& lhs, Register rhs, L label) PER_SHARED_ARCH;
+ inline void branchPtr(Condition cond, const Address& lhs, ImmPtr rhs, Label* label) PER_SHARED_ARCH;
+ inline void branchPtr(Condition cond, const Address& lhs, ImmGCPtr rhs, Label* label) PER_SHARED_ARCH;
+ inline void branchPtr(Condition cond, const Address& lhs, ImmWord rhs, Label* label) PER_SHARED_ARCH;
+
+ inline void branchPtr(Condition cond, const AbsoluteAddress& lhs, Register rhs, Label* label)
+ DEFINED_ON(arm, arm64, mips_shared, x86, x64);
+ inline void branchPtr(Condition cond, const AbsoluteAddress& lhs, ImmWord rhs, Label* label)
+ DEFINED_ON(arm, arm64, mips_shared, x86, x64);
+
+ inline void branchPtr(Condition cond, wasm::SymbolicAddress lhs, Register rhs, Label* label)
+ DEFINED_ON(arm, arm64, mips_shared, x86, x64);
+
+ template <typename T>
+ inline CodeOffsetJump branchPtrWithPatch(Condition cond, Register lhs, T rhs, RepatchLabel* label) PER_SHARED_ARCH;
+ template <typename T>
+ inline CodeOffsetJump branchPtrWithPatch(Condition cond, Address lhs, T rhs, RepatchLabel* label) PER_SHARED_ARCH;
+
+ void branchPtrInNurseryChunk(Condition cond, Register ptr, Register temp, Label* label)
+ DEFINED_ON(arm, arm64, mips_shared, x86, x64);
+ void branchPtrInNurseryChunk(Condition cond, const Address& address, Register temp, Label* label)
+ DEFINED_ON(x86);
+ void branchValueIsNurseryObject(Condition cond, const Address& address, Register temp, Label* label) PER_ARCH;
+ void branchValueIsNurseryObject(Condition cond, ValueOperand value, Register temp, Label* label) PER_ARCH;
+
+ // This function compares a Value (lhs) which is having a private pointer
+ // boxed inside a js::Value, with a raw pointer (rhs).
+ inline void branchPrivatePtr(Condition cond, const Address& lhs, Register rhs, Label* label) PER_ARCH;
+
+ inline void branchFloat(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs,
+ Label* label) PER_SHARED_ARCH;
+
+ // Truncate a double/float32 to int32 and when it doesn't fit an int32 it will jump to
+ // the failure label. This particular variant is allowed to return the value module 2**32,
+ // which isn't implemented on all architectures.
+ // E.g. the x64 variants will do this only in the int64_t range.
+ inline void branchTruncateFloat32MaybeModUint32(FloatRegister src, Register dest, Label* fail)
+ DEFINED_ON(arm, arm64, mips_shared, x86, x64);
+ inline void branchTruncateDoubleMaybeModUint32(FloatRegister src, Register dest, Label* fail)
+ DEFINED_ON(arm, arm64, mips_shared, x86, x64);
+
+ // Truncate a double/float32 to intptr and when it doesn't fit jump to the failure label.
+ inline void branchTruncateFloat32ToPtr(FloatRegister src, Register dest, Label* fail)
+ DEFINED_ON(x86, x64);
+ inline void branchTruncateDoubleToPtr(FloatRegister src, Register dest, Label* fail)
+ DEFINED_ON(x86, x64);
+
+ // Truncate a double/float32 to int32 and when it doesn't fit jump to the failure label.
+ inline void branchTruncateFloat32ToInt32(FloatRegister src, Register dest, Label* fail)
+ DEFINED_ON(arm, arm64, mips_shared, x86, x64);
+ inline void branchTruncateDoubleToInt32(FloatRegister src, Register dest, Label* fail)
+ DEFINED_ON(arm, arm64, mips_shared, x86, x64);
+
+ inline void branchDouble(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs,
+ Label* label) PER_SHARED_ARCH;
+
+ inline void branchDoubleNotInInt64Range(Address src, Register temp, Label* fail);
+ inline void branchDoubleNotInUInt64Range(Address src, Register temp, Label* fail);
+ inline void branchFloat32NotInInt64Range(Address src, Register temp, Label* fail);
+ inline void branchFloat32NotInUInt64Range(Address src, Register temp, Label* fail);
+
+ template <typename T, typename L>
+ inline void branchAdd32(Condition cond, T src, Register dest, L label) PER_SHARED_ARCH;
+ template <typename T>
+ inline void branchSub32(Condition cond, T src, Register dest, Label* label) PER_SHARED_ARCH;
+
+ inline void decBranchPtr(Condition cond, Register lhs, Imm32 rhs, Label* label) PER_SHARED_ARCH;
+
+ template <class L>
+ inline void branchTest32(Condition cond, Register lhs, Register rhs, L label) PER_SHARED_ARCH;
+ template <class L>
+ inline void branchTest32(Condition cond, Register lhs, Imm32 rhs, L label) PER_SHARED_ARCH;
+ inline void branchTest32(Condition cond, const Address& lhs, Imm32 rhh, Label* label) PER_SHARED_ARCH;
+ inline void branchTest32(Condition cond, const AbsoluteAddress& lhs, Imm32 rhs, Label* label)
+ DEFINED_ON(arm, arm64, mips_shared, x86, x64);
+
+ template <class L>
+ inline void branchTestPtr(Condition cond, Register lhs, Register rhs, L label) PER_SHARED_ARCH;
+ inline void branchTestPtr(Condition cond, Register lhs, Imm32 rhs, Label* label) PER_SHARED_ARCH;
+ inline void branchTestPtr(Condition cond, const Address& lhs, Imm32 rhs, Label* label) PER_SHARED_ARCH;
+
+ template <class L>
+ inline void branchTest64(Condition cond, Register64 lhs, Register64 rhs, Register temp,
+ L label) PER_ARCH;
+
+ // Branches to |label| if |reg| is false. |reg| should be a C++ bool.
+ template <class L>
+ inline void branchIfFalseBool(Register reg, L label);
+
+ // Branches to |label| if |reg| is true. |reg| should be a C++ bool.
+ inline void branchIfTrueBool(Register reg, Label* label);
+
+ inline void branchIfRope(Register str, Label* label);
+ inline void branchIfRopeOrExternal(Register str, Register temp, Label* label);
+
+ inline void branchLatin1String(Register string, Label* label);
+ inline void branchTwoByteString(Register string, Label* label);
+
+ inline void branchIfFunctionHasNoScript(Register fun, Label* label);
+ inline void branchIfInterpreted(Register fun, Label* label);
+
+ inline void branchFunctionKind(Condition cond, JSFunction::FunctionKind kind, Register fun,
+ Register scratch, Label* label);
+
+ void branchIfNotInterpretedConstructor(Register fun, Register scratch, Label* label);
+
+ inline void branchTestObjClass(Condition cond, Register obj, Register scratch, const js::Class* clasp,
+ Label* label);
+ inline void branchTestObjShape(Condition cond, Register obj, const Shape* shape, Label* label);
+ inline void branchTestObjShape(Condition cond, Register obj, Register shape, Label* label);
+ inline void branchTestObjGroup(Condition cond, Register obj, ObjectGroup* group, Label* label);
+ inline void branchTestObjGroup(Condition cond, Register obj, Register group, Label* label);
+
+ inline void branchTestObjectTruthy(bool truthy, Register objReg, Register scratch,
+ Label* slowCheck, Label* checked);
+
+ inline void branchTestClassIsProxy(bool proxy, Register clasp, Label* label);
+
+ inline void branchTestObjectIsProxy(bool proxy, Register object, Register scratch, Label* label);
+
+ inline void branchTestProxyHandlerFamily(Condition cond, Register proxy, Register scratch,
+ const void* handlerp, Label* label);
+
+ template <typename Value>
+ inline void branchTestMIRType(Condition cond, const Value& val, MIRType type, Label* label);
+
+ // Emit type case branch on tag matching if the type tag in the definition
+ // might actually be that type.
+ void maybeBranchTestType(MIRType type, MDefinition* maybeDef, Register tag, Label* label);
+
+ inline void branchTestNeedsIncrementalBarrier(Condition cond, Label* label);
+
+ // Perform a type-test on a tag of a Value (32bits boxing), or the tagged
+ // value (64bits boxing).
+ inline void branchTestUndefined(Condition cond, Register tag, Label* label) PER_SHARED_ARCH;
+ inline void branchTestInt32(Condition cond, Register tag, Label* label) PER_SHARED_ARCH;
+ inline void branchTestDouble(Condition cond, Register tag, Label* label)
+ DEFINED_ON(arm, arm64, mips32, mips64, x86_shared);
+ inline void branchTestNumber(Condition cond, Register tag, Label* label) PER_SHARED_ARCH;
+ inline void branchTestBoolean(Condition cond, Register tag, Label* label) PER_SHARED_ARCH;
+ inline void branchTestString(Condition cond, Register tag, Label* label) PER_SHARED_ARCH;
+ inline void branchTestSymbol(Condition cond, Register tag, Label* label) PER_SHARED_ARCH;
+ inline void branchTestNull(Condition cond, Register tag, Label* label) PER_SHARED_ARCH;
+ inline void branchTestObject(Condition cond, Register tag, Label* label) PER_SHARED_ARCH;
+ inline void branchTestPrimitive(Condition cond, Register tag, Label* label) PER_SHARED_ARCH;
+ inline void branchTestMagic(Condition cond, Register tag, Label* label) PER_SHARED_ARCH;
+
+ // Perform a type-test on a Value, addressed by Address or BaseIndex, or
+ // loaded into ValueOperand.
+ // BaseIndex and ValueOperand variants clobber the ScratchReg on x64.
+ // All Variants clobber the ScratchReg on arm64.
+ inline void branchTestUndefined(Condition cond, const Address& address, Label* label) PER_SHARED_ARCH;
+ inline void branchTestUndefined(Condition cond, const BaseIndex& address, Label* label) PER_SHARED_ARCH;
+ inline void branchTestUndefined(Condition cond, const ValueOperand& value, Label* label)
+ DEFINED_ON(arm, arm64, mips32, mips64, x86_shared);
+
+ inline void branchTestInt32(Condition cond, const Address& address, Label* label) PER_SHARED_ARCH;
+ inline void branchTestInt32(Condition cond, const BaseIndex& address, Label* label) PER_SHARED_ARCH;
+ inline void branchTestInt32(Condition cond, const ValueOperand& value, Label* label)
+ DEFINED_ON(arm, arm64, mips32, mips64, x86_shared);
+
+ inline void branchTestDouble(Condition cond, const Address& address, Label* label) PER_SHARED_ARCH;
+ inline void branchTestDouble(Condition cond, const BaseIndex& address, Label* label) PER_SHARED_ARCH;
+ inline void branchTestDouble(Condition cond, const ValueOperand& value, Label* label)
+ DEFINED_ON(arm, arm64, mips32, mips64, x86_shared);
+
+ inline void branchTestNumber(Condition cond, const ValueOperand& value, Label* label)
+ DEFINED_ON(arm, arm64, mips32, mips64, x86_shared);
+
+ inline void branchTestBoolean(Condition cond, const Address& address, Label* label) PER_SHARED_ARCH;
+ inline void branchTestBoolean(Condition cond, const BaseIndex& address, Label* label) PER_SHARED_ARCH;
+ inline void branchTestBoolean(Condition cond, const ValueOperand& value, Label* label)
+ DEFINED_ON(arm, arm64, mips32, mips64, x86_shared);
+
+ inline void branchTestString(Condition cond, const BaseIndex& address, Label* label) PER_SHARED_ARCH;
+ inline void branchTestString(Condition cond, const ValueOperand& value, Label* label)
+ DEFINED_ON(arm, arm64, mips32, mips64, x86_shared);
+
+ inline void branchTestSymbol(Condition cond, const BaseIndex& address, Label* label) PER_SHARED_ARCH;
+ inline void branchTestSymbol(Condition cond, const ValueOperand& value, Label* label)
+ DEFINED_ON(arm, arm64, mips32, mips64, x86_shared);
+
+ inline void branchTestNull(Condition cond, const Address& address, Label* label) PER_SHARED_ARCH;
+ inline void branchTestNull(Condition cond, const BaseIndex& address, Label* label) PER_SHARED_ARCH;
+ inline void branchTestNull(Condition cond, const ValueOperand& value, Label* label)
+ DEFINED_ON(arm, arm64, mips32, mips64, x86_shared);
+
+ // Clobbers the ScratchReg on x64.
+ inline void branchTestObject(Condition cond, const Address& address, Label* label) PER_SHARED_ARCH;
+ inline void branchTestObject(Condition cond, const BaseIndex& address, Label* label) PER_SHARED_ARCH;
+ inline void branchTestObject(Condition cond, const ValueOperand& value, Label* label)
+ DEFINED_ON(arm, arm64, mips32, mips64, x86_shared);
+
+ inline void branchTestGCThing(Condition cond, const Address& address, Label* label) PER_SHARED_ARCH;
+ inline void branchTestGCThing(Condition cond, const BaseIndex& address, Label* label) PER_SHARED_ARCH;
+
+ inline void branchTestPrimitive(Condition cond, const ValueOperand& value, Label* label)
+ DEFINED_ON(arm, arm64, mips32, mips64, x86_shared);
+
+ inline void branchTestMagic(Condition cond, const Address& address, Label* label) PER_SHARED_ARCH;
+ inline void branchTestMagic(Condition cond, const BaseIndex& address, Label* label) PER_SHARED_ARCH;
+ template <class L>
+ inline void branchTestMagic(Condition cond, const ValueOperand& value, L label)
+ DEFINED_ON(arm, arm64, mips32, mips64, x86_shared);
+
+ inline void branchTestMagic(Condition cond, const Address& valaddr, JSWhyMagic why, Label* label) PER_ARCH;
+
+ inline void branchTestMagicValue(Condition cond, const ValueOperand& val, JSWhyMagic why,
+ Label* label);
+
+ void branchTestValue(Condition cond, const ValueOperand& lhs,
+ const Value& rhs, Label* label) PER_ARCH;
+
+ // Checks if given Value is evaluated to true or false in a condition.
+ // The type of the value should match the type of the method.
+ inline void branchTestInt32Truthy(bool truthy, const ValueOperand& value, Label* label)
+ DEFINED_ON(arm, arm64, mips32, mips64, x86_shared);
+ inline void branchTestDoubleTruthy(bool truthy, FloatRegister reg, Label* label) PER_SHARED_ARCH;
+ inline void branchTestBooleanTruthy(bool truthy, const ValueOperand& value, Label* label) PER_ARCH;
+ inline void branchTestStringTruthy(bool truthy, const ValueOperand& value, Label* label)
+ DEFINED_ON(arm, arm64, mips32, mips64, x86_shared);
+
+ private:
+
+ // Implementation for branch* methods.
+ template <typename T>
+ inline void branch32Impl(Condition cond, const T& length, const RegisterOrInt32Constant& key,
+ Label* label);
+
+ template <typename T, typename S, typename L>
+ inline void branchPtrImpl(Condition cond, const T& lhs, const S& rhs, L label)
+ DEFINED_ON(x86_shared);
+
+ void branchPtrInNurseryChunkImpl(Condition cond, Register ptr, Label* label)
+ DEFINED_ON(x86);
+ template <typename T>
+ void branchValueIsNurseryObjectImpl(Condition cond, const T& value, Register temp, Label* label)
+ DEFINED_ON(arm64, mips64, x64);
+
+ template <typename T>
+ inline void branchTestUndefinedImpl(Condition cond, const T& t, Label* label)
+ DEFINED_ON(arm, arm64, x86_shared);
+ template <typename T>
+ inline void branchTestInt32Impl(Condition cond, const T& t, Label* label)
+ DEFINED_ON(arm, arm64, x86_shared);
+ template <typename T>
+ inline void branchTestDoubleImpl(Condition cond, const T& t, Label* label)
+ DEFINED_ON(arm, arm64, x86_shared);
+ template <typename T>
+ inline void branchTestNumberImpl(Condition cond, const T& t, Label* label)
+ DEFINED_ON(arm, arm64, x86_shared);
+ template <typename T>
+ inline void branchTestBooleanImpl(Condition cond, const T& t, Label* label)
+ DEFINED_ON(arm, arm64, x86_shared);
+ template <typename T>
+ inline void branchTestStringImpl(Condition cond, const T& t, Label* label)
+ DEFINED_ON(arm, arm64, x86_shared);
+ template <typename T>
+ inline void branchTestSymbolImpl(Condition cond, const T& t, Label* label)
+ DEFINED_ON(arm, arm64, x86_shared);
+ template <typename T>
+ inline void branchTestNullImpl(Condition cond, const T& t, Label* label)
+ DEFINED_ON(arm, arm64, x86_shared);
+ template <typename T>
+ inline void branchTestObjectImpl(Condition cond, const T& t, Label* label)
+ DEFINED_ON(arm, arm64, x86_shared);
+ template <typename T>
+ inline void branchTestGCThingImpl(Condition cond, const T& t, Label* label)
+ DEFINED_ON(arm, arm64, x86_shared);
+ template <typename T>
+ inline void branchTestPrimitiveImpl(Condition cond, const T& t, Label* label)
+ DEFINED_ON(arm, arm64, x86_shared);
+ template <typename T, class L>
+ inline void branchTestMagicImpl(Condition cond, const T& t, L label)
+ DEFINED_ON(arm, arm64, x86_shared);
+
+ public:
+ // ========================================================================
+ // Canonicalization primitives.
+ inline void canonicalizeDouble(FloatRegister reg);
+ inline void canonicalizeDoubleIfDeterministic(FloatRegister reg);
+
+ inline void canonicalizeFloat(FloatRegister reg);
+ inline void canonicalizeFloatIfDeterministic(FloatRegister reg);
+
+ inline void canonicalizeFloat32x4(FloatRegister reg, FloatRegister scratch)
+ DEFINED_ON(x86_shared);
+
+ public:
+ // ========================================================================
+ // Memory access primitives.
+ inline void storeUncanonicalizedDouble(FloatRegister src, const Address& dest)
+ DEFINED_ON(x86_shared, arm, arm64, mips32, mips64);
+ inline void storeUncanonicalizedDouble(FloatRegister src, const BaseIndex& dest)
+ DEFINED_ON(x86_shared, arm, arm64, mips32, mips64);
+ inline void storeUncanonicalizedDouble(FloatRegister src, const Operand& dest)
+ DEFINED_ON(x86_shared);
+
+ template<class T>
+ inline void storeDouble(FloatRegister src, const T& dest);
+
+ inline void storeUncanonicalizedFloat32(FloatRegister src, const Address& dest)
+ DEFINED_ON(x86_shared, arm, arm64, mips32, mips64);
+ inline void storeUncanonicalizedFloat32(FloatRegister src, const BaseIndex& dest)
+ DEFINED_ON(x86_shared, arm, arm64, mips32, mips64);
+ inline void storeUncanonicalizedFloat32(FloatRegister src, const Operand& dest)
+ DEFINED_ON(x86_shared);
+
+ template<class T>
+ inline void storeFloat32(FloatRegister src, const T& dest);
+
+ inline void storeFloat32x3(FloatRegister src, const Address& dest) PER_SHARED_ARCH;
+ inline void storeFloat32x3(FloatRegister src, const BaseIndex& dest) PER_SHARED_ARCH;
+
+ template <typename T>
+ void storeUnboxedValue(const ConstantOrRegister& value, MIRType valueType, const T& dest,
+ MIRType slotType) PER_ARCH;
+
+ inline void memoryBarrier(MemoryBarrierBits barrier) PER_SHARED_ARCH;
+
+ public:
+ // ========================================================================
+ // Truncate floating point.
+
+ // Undefined behaviour when truncation is outside Int64 range.
+ // Needs a temp register if SSE3 is not present.
+ inline void truncateFloat32ToInt64(Address src, Address dest, Register temp)
+ DEFINED_ON(x86_shared);
+ inline void truncateFloat32ToUInt64(Address src, Address dest, Register temp,
+ FloatRegister floatTemp)
+ DEFINED_ON(x86, x64);
+ inline void truncateDoubleToInt64(Address src, Address dest, Register temp)
+ DEFINED_ON(x86_shared);
+ inline void truncateDoubleToUInt64(Address src, Address dest, Register temp,
+ FloatRegister floatTemp)
+ DEFINED_ON(x86, x64);
+
+ public:
+ // ========================================================================
+ // wasm support
+
+ // Emit a bounds check against the (dynamically-patched) wasm bounds check
+ // limit, jumping to 'label' if 'cond' holds.
+ template <class L>
+ inline void wasmBoundsCheck(Condition cond, Register index, L label) PER_ARCH;
+
+ // Called after compilation completes to patch the given limit into the
+ // given instruction's immediate.
+ static inline void wasmPatchBoundsCheck(uint8_t* patchAt, uint32_t limit) PER_ARCH;
+
+ // On x86, each instruction adds its own wasm::MemoryAccess's to the
+ // wasm::MemoryAccessVector (there can be multiple when i64 is involved).
+ // On x64, only some asm.js accesses need a wasm::MemoryAccess so the caller
+ // is responsible for doing this instead.
+ void wasmLoad(const wasm::MemoryAccessDesc& access, Operand srcAddr, AnyRegister out) DEFINED_ON(x86, x64);
+ void wasmLoadI64(const wasm::MemoryAccessDesc& access, Operand srcAddr, Register64 out) DEFINED_ON(x86, x64);
+ void wasmStore(const wasm::MemoryAccessDesc& access, AnyRegister value, Operand dstAddr) DEFINED_ON(x86, x64);
+ void wasmStoreI64(const wasm::MemoryAccessDesc& access, Register64 value, Operand dstAddr) DEFINED_ON(x86);
+
+ // wasm specific methods, used in both the wasm baseline compiler and ion.
+ void wasmTruncateDoubleToUInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86, x64, arm);
+ void wasmTruncateDoubleToInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86_shared, arm);
+ void outOfLineWasmTruncateDoubleToInt32(FloatRegister input, bool isUnsigned, wasm::TrapOffset off, Label* rejoin) DEFINED_ON(x86_shared);
+
+ void wasmTruncateFloat32ToUInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86, x64, arm);
+ void wasmTruncateFloat32ToInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86_shared, arm);
+ void outOfLineWasmTruncateFloat32ToInt32(FloatRegister input, bool isUnsigned, wasm::TrapOffset off, Label* rejoin) DEFINED_ON(x86_shared);
+
+ void outOfLineWasmTruncateDoubleToInt64(FloatRegister input, bool isUnsigned, wasm::TrapOffset off, Label* rejoin) DEFINED_ON(x86_shared);
+ void outOfLineWasmTruncateFloat32ToInt64(FloatRegister input, bool isUnsigned, wasm::TrapOffset off, Label* rejoin) DEFINED_ON(x86_shared);
+
+ // This function takes care of loading the callee's TLS and pinned regs but
+ // it is the caller's responsibility to save/restore TLS or pinned regs.
+ void wasmCallImport(const wasm::CallSiteDesc& desc, const wasm::CalleeDesc& callee);
+
+ // WasmTableCallIndexReg must contain the index of the indirect call.
+ void wasmCallIndirect(const wasm::CallSiteDesc& desc, const wasm::CalleeDesc& callee);
+
+ // This function takes care of loading the pointer to the current instance
+ // as the implicit first argument. It preserves TLS and pinned registers.
+ // (TLS & pinned regs are non-volatile registers in the system ABI).
+ void wasmCallBuiltinInstanceMethod(const ABIArg& instanceArg,
+ wasm::SymbolicAddress builtin);
+
+ // Emit the out-of-line trap code to which trapping jumps/branches are
+ // bound. This should be called once per function after all other codegen,
+ // including "normal" OutOfLineCode.
+ void wasmEmitTrapOutOfLineCode();
+
+ public:
+ // ========================================================================
+ // Clamping functions.
+
+ inline void clampIntToUint8(Register reg) PER_SHARED_ARCH;
+
+ //}}} check_macroassembler_style
+ public:
+
+ // Emits a test of a value against all types in a TypeSet. A scratch
+ // register is required.
+ template <typename Source>
+ void guardTypeSet(const Source& address, const TypeSet* types, BarrierKind kind, Register scratch, Label* miss);
+
+ void guardObjectType(Register obj, const TypeSet* types, Register scratch, Label* miss);
+
+ template <typename TypeSet>
+ void guardTypeSetMightBeIncomplete(TypeSet* types, Register obj, Register scratch, Label* label);
+
+ void loadObjShape(Register objReg, Register dest) {
+ loadPtr(Address(objReg, ShapedObject::offsetOfShape()), dest);
+ }
+ void loadObjGroup(Register objReg, Register dest) {
+ loadPtr(Address(objReg, JSObject::offsetOfGroup()), dest);
+ }
+ void loadBaseShape(Register objReg, Register dest) {
+ loadObjShape(objReg, dest);
+ loadPtr(Address(dest, Shape::offsetOfBase()), dest);
+ }
+ void loadObjClass(Register objReg, Register dest) {
+ loadObjGroup(objReg, dest);
+ loadPtr(Address(dest, ObjectGroup::offsetOfClasp()), dest);
+ }
+
+ void loadObjPrivate(Register obj, uint32_t nfixed, Register dest) {
+ loadPtr(Address(obj, NativeObject::getPrivateDataOffset(nfixed)), dest);
+ }
+
+ void loadObjProto(Register obj, Register dest) {
+ loadPtr(Address(obj, JSObject::offsetOfGroup()), dest);
+ loadPtr(Address(dest, ObjectGroup::offsetOfProto()), dest);
+ }
+
+ void loadStringLength(Register str, Register dest) {
+ load32(Address(str, JSString::offsetOfLength()), dest);
+ }
+
+ void loadStringChars(Register str, Register dest);
+ void loadStringChar(Register str, Register index, Register output);
+
+ void loadJSContext(Register dest) {
+ movePtr(ImmPtr(GetJitContext()->runtime->getJSContext()), dest);
+ }
+ void loadJitActivation(Register dest) {
+ loadPtr(AbsoluteAddress(GetJitContext()->runtime->addressOfActivation()), dest);
+ }
+ void loadWasmActivationFromTls(Register dest) {
+ loadPtr(Address(WasmTlsReg, offsetof(wasm::TlsData, cx)), dest);
+ loadPtr(Address(dest, JSContext::offsetOfWasmActivation()), dest);
+ }
+ void loadWasmActivationFromSymbolicAddress(Register dest) {
+ movePtr(wasm::SymbolicAddress::Context, dest);
+ loadPtr(Address(dest, JSContext::offsetOfWasmActivation()), dest);
+ }
+
+ template<typename T>
+ void loadTypedOrValue(const T& src, TypedOrValueRegister dest) {
+ if (dest.hasValue())
+ loadValue(src, dest.valueReg());
+ else
+ loadUnboxedValue(src, dest.type(), dest.typedReg());
+ }
+
+ template<typename T>
+ void loadElementTypedOrValue(const T& src, TypedOrValueRegister dest, bool holeCheck,
+ Label* hole) {
+ if (dest.hasValue()) {
+ loadValue(src, dest.valueReg());
+ if (holeCheck)
+ branchTestMagic(Assembler::Equal, dest.valueReg(), hole);
+ } else {
+ if (holeCheck)
+ branchTestMagic(Assembler::Equal, src, hole);
+ loadUnboxedValue(src, dest.type(), dest.typedReg());
+ }
+ }
+
+ template <typename T>
+ void storeTypedOrValue(TypedOrValueRegister src, const T& dest) {
+ if (src.hasValue()) {
+ storeValue(src.valueReg(), dest);
+ } else if (IsFloatingPointType(src.type())) {
+ FloatRegister reg = src.typedReg().fpu();
+ if (src.type() == MIRType::Float32) {
+ convertFloat32ToDouble(reg, ScratchDoubleReg);
+ reg = ScratchDoubleReg;
+ }
+ storeDouble(reg, dest);
+ } else {
+ storeValue(ValueTypeFromMIRType(src.type()), src.typedReg().gpr(), dest);
+ }
+ }
+
+ template <typename T>
+ inline void storeObjectOrNull(Register src, const T& dest);
+
+ template <typename T>
+ void storeConstantOrRegister(const ConstantOrRegister& src, const T& dest) {
+ if (src.constant())
+ storeValue(src.value(), dest);
+ else
+ storeTypedOrValue(src.reg(), dest);
+ }
+
+ void storeCallPointerResult(Register reg) {
+ if (reg != ReturnReg)
+ mov(ReturnReg, reg);
+ }
+
+ inline void storeCallBoolResult(Register reg);
+ inline void storeCallInt32Result(Register reg);
+
+ void storeCallFloatResult(FloatRegister reg) {
+ if (reg != ReturnDoubleReg)
+ moveDouble(ReturnDoubleReg, reg);
+ }
+
+ inline void storeCallResultValue(AnyRegister dest);
+
+ void storeCallResultValue(ValueOperand dest) {
+#if defined(JS_NUNBOX32)
+ // reshuffle the return registers used for a call result to store into
+ // dest, using ReturnReg as a scratch register if necessary. This must
+ // only be called after returning from a call, at a point when the
+ // return register is not live. XXX would be better to allow wrappers
+ // to store the return value to different places.
+ if (dest.typeReg() == JSReturnReg_Data) {
+ if (dest.payloadReg() == JSReturnReg_Type) {
+ // swap the two registers.
+ mov(JSReturnReg_Type, ReturnReg);
+ mov(JSReturnReg_Data, JSReturnReg_Type);
+ mov(ReturnReg, JSReturnReg_Data);
+ } else {
+ mov(JSReturnReg_Data, dest.payloadReg());
+ mov(JSReturnReg_Type, dest.typeReg());
+ }
+ } else {
+ mov(JSReturnReg_Type, dest.typeReg());
+ mov(JSReturnReg_Data, dest.payloadReg());
+ }
+#elif defined(JS_PUNBOX64)
+ if (dest.valueReg() != JSReturnReg)
+ mov(JSReturnReg, dest.valueReg());
+#else
+#error "Bad architecture"
+#endif
+ }
+
+ inline void storeCallResultValue(TypedOrValueRegister dest);
+
+ template <typename T>
+ Register extractString(const T& source, Register scratch) {
+ return extractObject(source, scratch);
+ }
+ using MacroAssemblerSpecific::store32;
+ void store32(const RegisterOrInt32Constant& key, const Address& dest) {
+ if (key.isRegister())
+ store32(key.reg(), dest);
+ else
+ store32(Imm32(key.constant()), dest);
+ }
+
+ template <typename T>
+ void callPreBarrier(const T& address, MIRType type) {
+ Label done;
+
+ if (type == MIRType::Value)
+ branchTestGCThing(Assembler::NotEqual, address, &done);
+
+ Push(PreBarrierReg);
+ computeEffectiveAddress(address, PreBarrierReg);
+
+ const JitRuntime* rt = GetJitContext()->runtime->jitRuntime();
+ JitCode* preBarrier = rt->preBarrier(type);
+
+ call(preBarrier);
+ Pop(PreBarrierReg);
+
+ bind(&done);
+ }
+
+ template <typename T>
+ void patchableCallPreBarrier(const T& address, MIRType type) {
+ Label done;
+
+ // All barriers are off by default.
+ // They are enabled if necessary at the end of CodeGenerator::generate().
+ CodeOffset nopJump = toggledJump(&done);
+ writePrebarrierOffset(nopJump);
+
+ callPreBarrier(address, type);
+ jump(&done);
+
+ haltingAlign(8);
+ bind(&done);
+ }
+
+ template<typename T>
+ void loadFromTypedArray(Scalar::Type arrayType, const T& src, AnyRegister dest, Register temp, Label* fail,
+ bool canonicalizeDoubles = true, unsigned numElems = 0);
+
+ template<typename T>
+ void loadFromTypedArray(Scalar::Type arrayType, const T& src, const ValueOperand& dest, bool allowDouble,
+ Register temp, Label* fail);
+
+ template<typename S, typename T>
+ void storeToTypedIntArray(Scalar::Type arrayType, const S& value, const T& dest) {
+ switch (arrayType) {
+ case Scalar::Int8:
+ case Scalar::Uint8:
+ case Scalar::Uint8Clamped:
+ store8(value, dest);
+ break;
+ case Scalar::Int16:
+ case Scalar::Uint16:
+ store16(value, dest);
+ break;
+ case Scalar::Int32:
+ case Scalar::Uint32:
+ store32(value, dest);
+ break;
+ default:
+ MOZ_CRASH("Invalid typed array type");
+ }
+ }
+
+ void storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value, const BaseIndex& dest,
+ unsigned numElems = 0);
+ void storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value, const Address& dest,
+ unsigned numElems = 0);
+
+ // Load a property from an UnboxedPlainObject or UnboxedArrayObject.
+ template <typename T>
+ void loadUnboxedProperty(T address, JSValueType type, TypedOrValueRegister output);
+
+ // Store a property to an UnboxedPlainObject, without triggering barriers.
+ // If failure is null, the value definitely has a type suitable for storing
+ // in the property.
+ template <typename T>
+ void storeUnboxedProperty(T address, JSValueType type,
+ const ConstantOrRegister& value, Label* failure);
+
+ void checkUnboxedArrayCapacity(Register obj, const RegisterOrInt32Constant& index,
+ Register temp, Label* failure);
+
+ Register extractString(const Address& address, Register scratch) {
+ return extractObject(address, scratch);
+ }
+ Register extractString(const ValueOperand& value, Register scratch) {
+ return extractObject(value, scratch);
+ }
+
+ using MacroAssemblerSpecific::extractTag;
+ Register extractTag(const TypedOrValueRegister& reg, Register scratch) {
+ if (reg.hasValue())
+ return extractTag(reg.valueReg(), scratch);
+ mov(ImmWord(MIRTypeToTag(reg.type())), scratch);
+ return scratch;
+ }
+
+ using MacroAssemblerSpecific::extractObject;
+ Register extractObject(const TypedOrValueRegister& reg, Register scratch) {
+ if (reg.hasValue())
+ return extractObject(reg.valueReg(), scratch);
+ MOZ_ASSERT(reg.type() == MIRType::Object);
+ return reg.typedReg().gpr();
+ }
+
+ // Inline version of js_TypedArray_uint8_clamp_double.
+ // This function clobbers the input register.
+ void clampDoubleToUint8(FloatRegister input, Register output) PER_ARCH;
+
+ using MacroAssemblerSpecific::ensureDouble;
+
+ template <typename S>
+ void ensureDouble(const S& source, FloatRegister dest, Label* failure) {
+ Label isDouble, done;
+ branchTestDouble(Assembler::Equal, source, &isDouble);
+ branchTestInt32(Assembler::NotEqual, source, failure);
+
+ convertInt32ToDouble(source, dest);
+ jump(&done);
+
+ bind(&isDouble);
+ unboxDouble(source, dest);
+
+ bind(&done);
+ }
+
+ // Inline allocation.
+ private:
+ void checkAllocatorState(Label* fail);
+ bool shouldNurseryAllocate(gc::AllocKind allocKind, gc::InitialHeap initialHeap);
+ void nurseryAllocate(Register result, Register temp, gc::AllocKind allocKind,
+ size_t nDynamicSlots, gc::InitialHeap initialHeap, Label* fail);
+ void freeListAllocate(Register result, Register temp, gc::AllocKind allocKind, Label* fail);
+ void allocateObject(Register result, Register temp, gc::AllocKind allocKind,
+ uint32_t nDynamicSlots, gc::InitialHeap initialHeap, Label* fail);
+ void allocateNonObject(Register result, Register temp, gc::AllocKind allocKind, Label* fail);
+ void copySlotsFromTemplate(Register obj, const NativeObject* templateObj,
+ uint32_t start, uint32_t end);
+ void fillSlotsWithConstantValue(Address addr, Register temp, uint32_t start, uint32_t end,
+ const Value& v);
+ void fillSlotsWithUndefined(Address addr, Register temp, uint32_t start, uint32_t end);
+ void fillSlotsWithUninitialized(Address addr, Register temp, uint32_t start, uint32_t end);
+
+ void initGCSlots(Register obj, Register temp, NativeObject* templateObj, bool initContents);
+
+ public:
+ void callMallocStub(size_t nbytes, Register result, Label* fail);
+ void callFreeStub(Register slots);
+ void createGCObject(Register result, Register temp, JSObject* templateObj,
+ gc::InitialHeap initialHeap, Label* fail, bool initContents = true,
+ bool convertDoubleElements = false);
+
+ void initGCThing(Register obj, Register temp, JSObject* templateObj,
+ bool initContents = true, bool convertDoubleElements = false);
+ void initTypedArraySlots(Register obj, Register temp, Register lengthReg,
+ LiveRegisterSet liveRegs, Label* fail,
+ TypedArrayObject* templateObj, TypedArrayLength lengthKind);
+
+ void initUnboxedObjectContents(Register object, UnboxedPlainObject* templateObject);
+
+ void newGCString(Register result, Register temp, Label* fail);
+ void newGCFatInlineString(Register result, Register temp, Label* fail);
+
+ // Compares two strings for equality based on the JSOP.
+ // This checks for identical pointers, atoms and length and fails for everything else.
+ void compareStrings(JSOp op, Register left, Register right, Register result,
+ Label* fail);
+
+ public:
+ // Generates code used to complete a bailout.
+ void generateBailoutTail(Register scratch, Register bailoutInfo);
+
+ public:
+#ifndef JS_CODEGEN_ARM64
+ // StackPointer manipulation functions.
+ // On ARM64, the StackPointer is implemented as two synchronized registers.
+ // Code shared across platforms must use these functions to be valid.
+ template <typename T> inline void addToStackPtr(T t);
+ template <typename T> inline void addStackPtrTo(T t);
+
+ void subFromStackPtr(Imm32 imm32) DEFINED_ON(mips32, mips64, arm, x86, x64);
+ void subFromStackPtr(Register reg);
+
+ template <typename T>
+ void subStackPtrFrom(T t) { subPtr(getStackPointer(), t); }
+
+ template <typename T>
+ void andToStackPtr(T t) { andPtr(t, getStackPointer()); }
+ template <typename T>
+ void andStackPtrTo(T t) { andPtr(getStackPointer(), t); }
+
+ template <typename T>
+ void moveToStackPtr(T t) { movePtr(t, getStackPointer()); }
+ template <typename T>
+ void moveStackPtrTo(T t) { movePtr(getStackPointer(), t); }
+
+ template <typename T>
+ void loadStackPtr(T t) { loadPtr(t, getStackPointer()); }
+ template <typename T>
+ void storeStackPtr(T t) { storePtr(getStackPointer(), t); }
+
+ // StackPointer testing functions.
+ // On ARM64, sp can function as the zero register depending on context.
+ // Code shared across platforms must use these functions to be valid.
+ template <typename T>
+ inline void branchTestStackPtr(Condition cond, T t, Label* label);
+ template <typename T>
+ inline void branchStackPtr(Condition cond, T rhs, Label* label);
+ template <typename T>
+ inline void branchStackPtrRhs(Condition cond, T lhs, Label* label);
+
+ // Move the stack pointer based on the requested amount.
+ inline void reserveStack(uint32_t amount);
+#else // !JS_CODEGEN_ARM64
+ void reserveStack(uint32_t amount);
+#endif
+
+ public:
+ void enableProfilingInstrumentation() {
+ emitProfilingInstrumentation_ = true;
+ }
+
+ private:
+ // This class is used to surround call sites throughout the assembler. This
+ // is used by callWithABI, and callJit functions, except if suffixed by
+ // NoProfiler.
+ class AutoProfilerCallInstrumentation {
+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
+
+ public:
+ explicit AutoProfilerCallInstrumentation(MacroAssembler& masm
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
+ ~AutoProfilerCallInstrumentation() {}
+ };
+ friend class AutoProfilerCallInstrumentation;
+
+ void appendProfilerCallSite(CodeOffset label) {
+ propagateOOM(profilerCallSites_.append(label));
+ }
+
+ // Fix up the code pointers to be written for locations where profilerCallSite
+ // emitted moves of RIP to a register.
+ void linkProfilerCallSites(JitCode* code);
+
+ // This field is used to manage profiling instrumentation output. If
+ // provided and enabled, then instrumentation will be emitted around call
+ // sites.
+ bool emitProfilingInstrumentation_;
+
+ // Record locations of the call sites.
+ Vector<CodeOffset, 0, SystemAllocPolicy> profilerCallSites_;
+
+ public:
+ void loadBaselineOrIonRaw(Register script, Register dest, Label* failure);
+ void loadBaselineOrIonNoArgCheck(Register callee, Register dest, Label* failure);
+
+ void loadBaselineFramePtr(Register framePtr, Register dest);
+
+ void pushBaselineFramePtr(Register framePtr, Register scratch) {
+ loadBaselineFramePtr(framePtr, scratch);
+ push(scratch);
+ }
+
+ void PushBaselineFramePtr(Register framePtr, Register scratch) {
+ loadBaselineFramePtr(framePtr, scratch);
+ Push(scratch);
+ }
+
+ private:
+ void handleFailure();
+
+ public:
+ Label* exceptionLabel() {
+ // Exceptions are currently handled the same way as sequential failures.
+ return &failureLabel_;
+ }
+
+ Label* failureLabel() {
+ return &failureLabel_;
+ }
+
+ void finish();
+ void link(JitCode* code);
+
+ void assumeUnreachable(const char* output);
+
+ template<typename T>
+ void assertTestInt32(Condition cond, const T& value, const char* output);
+
+ void printf(const char* output);
+ void printf(const char* output, Register value);
+
+#ifdef JS_TRACE_LOGGING
+ void tracelogStartId(Register logger, uint32_t textId, bool force = false);
+ void tracelogStartId(Register logger, Register textId);
+ void tracelogStartEvent(Register logger, Register event);
+ void tracelogStopId(Register logger, uint32_t textId, bool force = false);
+ void tracelogStopId(Register logger, Register textId);
+#endif
+
+#define DISPATCH_FLOATING_POINT_OP(method, type, arg1d, arg1f, arg2) \
+ MOZ_ASSERT(IsFloatingPointType(type)); \
+ if (type == MIRType::Double) \
+ method##Double(arg1d, arg2); \
+ else \
+ method##Float32(arg1f, arg2); \
+
+ void loadConstantFloatingPoint(double d, float f, FloatRegister dest, MIRType destType) {
+ DISPATCH_FLOATING_POINT_OP(loadConstant, destType, d, f, dest);
+ }
+ void boolValueToFloatingPoint(ValueOperand value, FloatRegister dest, MIRType destType) {
+ DISPATCH_FLOATING_POINT_OP(boolValueTo, destType, value, value, dest);
+ }
+ void int32ValueToFloatingPoint(ValueOperand value, FloatRegister dest, MIRType destType) {
+ DISPATCH_FLOATING_POINT_OP(int32ValueTo, destType, value, value, dest);
+ }
+ void convertInt32ToFloatingPoint(Register src, FloatRegister dest, MIRType destType) {
+ DISPATCH_FLOATING_POINT_OP(convertInt32To, destType, src, src, dest);
+ }
+
+#undef DISPATCH_FLOATING_POINT_OP
+
+ void convertValueToFloatingPoint(ValueOperand value, FloatRegister output, Label* fail,
+ MIRType outputType);
+ MOZ_MUST_USE bool convertValueToFloatingPoint(JSContext* cx, const Value& v,
+ FloatRegister output, Label* fail,
+ MIRType outputType);
+ MOZ_MUST_USE bool convertConstantOrRegisterToFloatingPoint(JSContext* cx,
+ const ConstantOrRegister& src,
+ FloatRegister output, Label* fail,
+ MIRType outputType);
+ void convertTypedOrValueToFloatingPoint(TypedOrValueRegister src, FloatRegister output,
+ Label* fail, MIRType outputType);
+
+ void outOfLineTruncateSlow(FloatRegister src, Register dest, bool widenFloatToDouble,
+ bool compilingWasm);
+
+ void convertInt32ValueToDouble(const Address& address, Register scratch, Label* done);
+ void convertValueToDouble(ValueOperand value, FloatRegister output, Label* fail) {
+ convertValueToFloatingPoint(value, output, fail, MIRType::Double);
+ }
+ MOZ_MUST_USE bool convertValueToDouble(JSContext* cx, const Value& v, FloatRegister output,
+ Label* fail) {
+ return convertValueToFloatingPoint(cx, v, output, fail, MIRType::Double);
+ }
+ MOZ_MUST_USE bool convertConstantOrRegisterToDouble(JSContext* cx,
+ const ConstantOrRegister& src,
+ FloatRegister output, Label* fail)
+ {
+ return convertConstantOrRegisterToFloatingPoint(cx, src, output, fail, MIRType::Double);
+ }
+ void convertTypedOrValueToDouble(TypedOrValueRegister src, FloatRegister output, Label* fail) {
+ convertTypedOrValueToFloatingPoint(src, output, fail, MIRType::Double);
+ }
+
+ void convertValueToFloat(ValueOperand value, FloatRegister output, Label* fail) {
+ convertValueToFloatingPoint(value, output, fail, MIRType::Float32);
+ }
+ MOZ_MUST_USE bool convertValueToFloat(JSContext* cx, const Value& v, FloatRegister output,
+ Label* fail) {
+ return convertValueToFloatingPoint(cx, v, output, fail, MIRType::Float32);
+ }
+ MOZ_MUST_USE bool convertConstantOrRegisterToFloat(JSContext* cx,
+ const ConstantOrRegister& src,
+ FloatRegister output, Label* fail)
+ {
+ return convertConstantOrRegisterToFloatingPoint(cx, src, output, fail, MIRType::Float32);
+ }
+ void convertTypedOrValueToFloat(TypedOrValueRegister src, FloatRegister output, Label* fail) {
+ convertTypedOrValueToFloatingPoint(src, output, fail, MIRType::Float32);
+ }
+
+ enum IntConversionBehavior {
+ IntConversion_Normal,
+ IntConversion_NegativeZeroCheck,
+ IntConversion_Truncate,
+ IntConversion_ClampToUint8,
+ };
+
+ enum IntConversionInputKind {
+ IntConversion_NumbersOnly,
+ IntConversion_NumbersOrBoolsOnly,
+ IntConversion_Any
+ };
+
+ //
+ // Functions for converting values to int.
+ //
+ void convertDoubleToInt(FloatRegister src, Register output, FloatRegister temp,
+ Label* truncateFail, Label* fail, IntConversionBehavior behavior);
+
+ // Strings may be handled by providing labels to jump to when the behavior
+ // is truncation or clamping. The subroutine, usually an OOL call, is
+ // passed the unboxed string in |stringReg| and should convert it to a
+ // double store into |temp|.
+ void convertValueToInt(ValueOperand value, MDefinition* input,
+ Label* handleStringEntry, Label* handleStringRejoin,
+ Label* truncateDoubleSlow,
+ Register stringReg, FloatRegister temp, Register output,
+ Label* fail, IntConversionBehavior behavior,
+ IntConversionInputKind conversion = IntConversion_Any);
+ void convertValueToInt(ValueOperand value, FloatRegister temp, Register output, Label* fail,
+ IntConversionBehavior behavior)
+ {
+ convertValueToInt(value, nullptr, nullptr, nullptr, nullptr, InvalidReg, temp, output,
+ fail, behavior);
+ }
+ MOZ_MUST_USE bool convertValueToInt(JSContext* cx, const Value& v, Register output, Label* fail,
+ IntConversionBehavior behavior);
+ MOZ_MUST_USE bool convertConstantOrRegisterToInt(JSContext* cx,
+ const ConstantOrRegister& src,
+ FloatRegister temp, Register output,
+ Label* fail, IntConversionBehavior behavior);
+ void convertTypedOrValueToInt(TypedOrValueRegister src, FloatRegister temp, Register output,
+ Label* fail, IntConversionBehavior behavior);
+
+ //
+ // Convenience functions for converting values to int32.
+ //
+ void convertValueToInt32(ValueOperand value, FloatRegister temp, Register output, Label* fail,
+ bool negativeZeroCheck)
+ {
+ convertValueToInt(value, temp, output, fail, negativeZeroCheck
+ ? IntConversion_NegativeZeroCheck
+ : IntConversion_Normal);
+ }
+ void convertValueToInt32(ValueOperand value, MDefinition* input,
+ FloatRegister temp, Register output, Label* fail,
+ bool negativeZeroCheck, IntConversionInputKind conversion = IntConversion_Any)
+ {
+ convertValueToInt(value, input, nullptr, nullptr, nullptr, InvalidReg, temp, output, fail,
+ negativeZeroCheck
+ ? IntConversion_NegativeZeroCheck
+ : IntConversion_Normal,
+ conversion);
+ }
+ MOZ_MUST_USE bool convertValueToInt32(JSContext* cx, const Value& v, Register output,
+ Label* fail, bool negativeZeroCheck)
+ {
+ return convertValueToInt(cx, v, output, fail, negativeZeroCheck
+ ? IntConversion_NegativeZeroCheck
+ : IntConversion_Normal);
+ }
+ MOZ_MUST_USE bool convertConstantOrRegisterToInt32(JSContext* cx,
+ const ConstantOrRegister& src,
+ FloatRegister temp, Register output,
+ Label* fail, bool negativeZeroCheck)
+ {
+ return convertConstantOrRegisterToInt(cx, src, temp, output, fail, negativeZeroCheck
+ ? IntConversion_NegativeZeroCheck
+ : IntConversion_Normal);
+ }
+ void convertTypedOrValueToInt32(TypedOrValueRegister src, FloatRegister temp, Register output,
+ Label* fail, bool negativeZeroCheck)
+ {
+ convertTypedOrValueToInt(src, temp, output, fail, negativeZeroCheck
+ ? IntConversion_NegativeZeroCheck
+ : IntConversion_Normal);
+ }
+
+ //
+ // Convenience functions for truncating values to int32.
+ //
+ void truncateValueToInt32(ValueOperand value, FloatRegister temp, Register output, Label* fail) {
+ convertValueToInt(value, temp, output, fail, IntConversion_Truncate);
+ }
+ void truncateValueToInt32(ValueOperand value, MDefinition* input,
+ Label* handleStringEntry, Label* handleStringRejoin,
+ Label* truncateDoubleSlow,
+ Register stringReg, FloatRegister temp, Register output, Label* fail)
+ {
+ convertValueToInt(value, input, handleStringEntry, handleStringRejoin, truncateDoubleSlow,
+ stringReg, temp, output, fail, IntConversion_Truncate);
+ }
+ void truncateValueToInt32(ValueOperand value, MDefinition* input,
+ FloatRegister temp, Register output, Label* fail)
+ {
+ convertValueToInt(value, input, nullptr, nullptr, nullptr, InvalidReg, temp, output, fail,
+ IntConversion_Truncate);
+ }
+ MOZ_MUST_USE bool truncateValueToInt32(JSContext* cx, const Value& v, Register output,
+ Label* fail) {
+ return convertValueToInt(cx, v, output, fail, IntConversion_Truncate);
+ }
+ MOZ_MUST_USE bool truncateConstantOrRegisterToInt32(JSContext* cx,
+ const ConstantOrRegister& src,
+ FloatRegister temp, Register output,
+ Label* fail)
+ {
+ return convertConstantOrRegisterToInt(cx, src, temp, output, fail, IntConversion_Truncate);
+ }
+ void truncateTypedOrValueToInt32(TypedOrValueRegister src, FloatRegister temp, Register output,
+ Label* fail)
+ {
+ convertTypedOrValueToInt(src, temp, output, fail, IntConversion_Truncate);
+ }
+
+ // Convenience functions for clamping values to uint8.
+ void clampValueToUint8(ValueOperand value, FloatRegister temp, Register output, Label* fail) {
+ convertValueToInt(value, temp, output, fail, IntConversion_ClampToUint8);
+ }
+ void clampValueToUint8(ValueOperand value, MDefinition* input,
+ Label* handleStringEntry, Label* handleStringRejoin,
+ Register stringReg, FloatRegister temp, Register output, Label* fail)
+ {
+ convertValueToInt(value, input, handleStringEntry, handleStringRejoin, nullptr,
+ stringReg, temp, output, fail, IntConversion_ClampToUint8);
+ }
+ void clampValueToUint8(ValueOperand value, MDefinition* input,
+ FloatRegister temp, Register output, Label* fail)
+ {
+ convertValueToInt(value, input, nullptr, nullptr, nullptr, InvalidReg, temp, output, fail,
+ IntConversion_ClampToUint8);
+ }
+ MOZ_MUST_USE bool clampValueToUint8(JSContext* cx, const Value& v, Register output,
+ Label* fail) {
+ return convertValueToInt(cx, v, output, fail, IntConversion_ClampToUint8);
+ }
+ MOZ_MUST_USE bool clampConstantOrRegisterToUint8(JSContext* cx,
+ const ConstantOrRegister& src,
+ FloatRegister temp, Register output,
+ Label* fail)
+ {
+ return convertConstantOrRegisterToInt(cx, src, temp, output, fail,
+ IntConversion_ClampToUint8);
+ }
+ void clampTypedOrValueToUint8(TypedOrValueRegister src, FloatRegister temp, Register output,
+ Label* fail)
+ {
+ convertTypedOrValueToInt(src, temp, output, fail, IntConversion_ClampToUint8);
+ }
+
+ public:
+ class AfterICSaveLive {
+ friend class MacroAssembler;
+ explicit AfterICSaveLive(uint32_t initialStack)
+#ifdef JS_DEBUG
+ : initialStack(initialStack)
+#endif
+ {}
+
+ public:
+#ifdef JS_DEBUG
+ uint32_t initialStack;
+#endif
+ uint32_t alignmentPadding;
+ };
+
+ void alignFrameForICArguments(AfterICSaveLive& aic) PER_ARCH;
+ void restoreFrameAlignmentForICArguments(AfterICSaveLive& aic) PER_ARCH;
+
+ AfterICSaveLive icSaveLive(LiveRegisterSet& liveRegs);
+ MOZ_MUST_USE bool icBuildOOLFakeExitFrame(void* fakeReturnAddr, AfterICSaveLive& aic);
+ void icRestoreLive(LiveRegisterSet& liveRegs, AfterICSaveLive& aic);
+
+ // Align the stack pointer based on the number of arguments which are pushed
+ // on the stack, such that the JitFrameLayout would be correctly aligned on
+ // the JitStackAlignment.
+ void alignJitStackBasedOnNArgs(Register nargs);
+ void alignJitStackBasedOnNArgs(uint32_t nargs);
+
+ inline void assertStackAlignment(uint32_t alignment, int32_t offset = 0);
+};
+
+static inline Assembler::DoubleCondition
+JSOpToDoubleCondition(JSOp op)
+{
+ switch (op) {
+ case JSOP_EQ:
+ case JSOP_STRICTEQ:
+ return Assembler::DoubleEqual;
+ case JSOP_NE:
+ case JSOP_STRICTNE:
+ return Assembler::DoubleNotEqualOrUnordered;
+ case JSOP_LT:
+ return Assembler::DoubleLessThan;
+ case JSOP_LE:
+ return Assembler::DoubleLessThanOrEqual;
+ case JSOP_GT:
+ return Assembler::DoubleGreaterThan;
+ case JSOP_GE:
+ return Assembler::DoubleGreaterThanOrEqual;
+ default:
+ MOZ_CRASH("Unexpected comparison operation");
+ }
+}
+
+// Note: the op may have been inverted during lowering (to put constants in a
+// position where they can be immediates), so it is important to use the
+// lir->jsop() instead of the mir->jsop() when it is present.
+static inline Assembler::Condition
+JSOpToCondition(JSOp op, bool isSigned)
+{
+ if (isSigned) {
+ switch (op) {
+ case JSOP_EQ:
+ case JSOP_STRICTEQ:
+ return Assembler::Equal;
+ case JSOP_NE:
+ case JSOP_STRICTNE:
+ return Assembler::NotEqual;
+ case JSOP_LT:
+ return Assembler::LessThan;
+ case JSOP_LE:
+ return Assembler::LessThanOrEqual;
+ case JSOP_GT:
+ return Assembler::GreaterThan;
+ case JSOP_GE:
+ return Assembler::GreaterThanOrEqual;
+ default:
+ MOZ_CRASH("Unrecognized comparison operation");
+ }
+ } else {
+ switch (op) {
+ case JSOP_EQ:
+ case JSOP_STRICTEQ:
+ return Assembler::Equal;
+ case JSOP_NE:
+ case JSOP_STRICTNE:
+ return Assembler::NotEqual;
+ case JSOP_LT:
+ return Assembler::Below;
+ case JSOP_LE:
+ return Assembler::BelowOrEqual;
+ case JSOP_GT:
+ return Assembler::Above;
+ case JSOP_GE:
+ return Assembler::AboveOrEqual;
+ default:
+ MOZ_CRASH("Unrecognized comparison operation");
+ }
+ }
+}
+
+static inline size_t
+StackDecrementForCall(uint32_t alignment, size_t bytesAlreadyPushed, size_t bytesToPush)
+{
+ return bytesToPush +
+ ComputeByteAlignment(bytesAlreadyPushed + bytesToPush, alignment);
+}
+
+static inline MIRType
+ToMIRType(MIRType t)
+{
+ return t;
+}
+
+template <class VecT>
+class ABIArgIter
+{
+ ABIArgGenerator gen_;
+ const VecT& types_;
+ unsigned i_;
+
+ void settle() { if (!done()) gen_.next(ToMIRType(types_[i_])); }
+
+ public:
+ explicit ABIArgIter(const VecT& types) : types_(types), i_(0) { settle(); }
+ void operator++(int) { MOZ_ASSERT(!done()); i_++; settle(); }
+ bool done() const { return i_ == types_.length(); }
+
+ ABIArg* operator->() { MOZ_ASSERT(!done()); return &gen_.current(); }
+ ABIArg& operator*() { MOZ_ASSERT(!done()); return gen_.current(); }
+
+ unsigned index() const { MOZ_ASSERT(!done()); return i_; }
+ MIRType mirType() const { MOZ_ASSERT(!done()); return ToMIRType(types_[i_]); }
+ uint32_t stackBytesConsumedSoFar() const { return gen_.stackBytesConsumedSoFar(); }
+};
+
+} // namespace jit
+} // namespace js
+
+#endif /* jit_MacroAssembler_h */