js/src/jit/IonMacroAssembler.h
author Jan de Mooij <jdemooij@mozilla.com>
Tue, 28 Jan 2014 15:33:56 +0100
changeset 181628 fe720ff03f43f96d4142491820884c9d3dae5de6
parent 177281 49f4fd358fa12f76dfa076cbc15aabbb92933252
child 181923 488ab195f1ae21f123a446cb4bf76752599bbfa8
permissions -rw-r--r--
Bug 959597 part 2 - Use JS_CODEGEN_* instead of JS_CPU_* for JIT backend code. r=nbp

/* -*- 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_IonMacroAssembler_h
#define jit_IonMacroAssembler_h

#ifdef JS_ION

#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"
#endif
#include "jit/IonInstrumentation.h"
#include "jit/JitCompartment.h"
#include "jit/VMFunctions.h"
#include "vm/ProxyObject.h"
#include "vm/Shape.h"

namespace js {
namespace jit {

// 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 AutoGCRooter
    {
        MacroAssembler *masm_;

      public:
        AutoRooter(JSContext *cx, MacroAssembler *masm)
          : 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 types::Type.
     * Note: emits number test (int/double) for types::Type::DoubleType()
     */
    class BranchType : public Branch
    {
        types::Type type_;

      public:
        BranchType()
          : Branch(),
            type_(types::Type::UnknownType())
        { }

        BranchType(Condition cond, Register reg, types::Type type, Label *jump)
          : Branch(cond, reg, jump),
            type_(type)
        { }

        void emit(MacroAssembler &masm) {
            JS_ASSERT(isInitialized());
            MIRType mirType = MIRType_None;

            if (type_.isPrimitive())
                mirType = MIRTypeFromValueType(type_.primitive());
            else if (type_.isAnyObject())
                mirType = MIRType_Object;
            else
                MOZ_ASSUME_UNREACHABLE("Unknown conversion to mirtype");

            if (mirType == MIRType_Double)
                masm.branchTestNumber(cond(), reg(), jump());
            else
                masm.branchTestMIRType(cond(), reg(), mirType, jump());
        }

    };

    /*
     * 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) {
            JS_ASSERT(isInitialized());
            masm.branchPtr(cond(), reg(), ptr_, jump());
        }
    };

    mozilla::Maybe<AutoRooter> autoRooter_;
    mozilla::Maybe<IonContext> ionContext_;
    mozilla::Maybe<AutoIonContextAlloc> alloc_;
    bool enoughMemory_;
    bool embedsNurseryPointers_;

  private:
    // This field is used to manage profiling instrumentation output. If
    // provided and enabled, then instrumentation will be emitted around call
    // sites. The IonInstrumentation instance is hosted inside of
    // CodeGeneratorShared and is the manager of when instrumentation is
    // actually emitted or not. If nullptr, then no instrumentation is emitted.
    IonInstrumentation *sps_;

    // Labels for handling exceptions and failures.
    NonAssertingLabel sequentialFailureLabel_;
    NonAssertingLabel parallelFailureLabel_;

  public:
    // If instrumentation should be emitted, then the sps parameter should be
    // provided, but otherwise it can be safely omitted to prevent all
    // instrumentation from being emitted.
    MacroAssembler()
      : enoughMemory_(true),
        embedsNurseryPointers_(false),
        sps_(nullptr)
    {
        IonContext *icx = GetIonContext();
        JSContext *cx = icx->cx;
        if (cx)
            constructRoot(cx);

        if (!icx->temp) {
            JS_ASSERT(cx);
            alloc_.construct(cx);
        }

        moveResolver_.setAllocator(*icx->temp);
#ifdef JS_CODEGEN_ARM
        initWithAllocator();
        m_buffer.id = icx->getNextAssemblerId();
#endif
    }

    // This constructor should only be used when there is no IonContext active
    // (for example, Trampoline-$(ARCH).cpp and IonCaches.cpp).
    MacroAssembler(JSContext *cx, IonScript *ion = nullptr)
      : enoughMemory_(true),
        embedsNurseryPointers_(false),
        sps_(nullptr)
    {
        constructRoot(cx);
        ionContext_.construct(cx, (js::jit::TempAllocator *)nullptr);
        alloc_.construct(cx);
        moveResolver_.setAllocator(*ionContext_.ref().temp);
#ifdef JS_CODEGEN_ARM
        initWithAllocator();
        m_buffer.id = GetIonContext()->getNextAssemblerId();
#endif
        if (ion)
            setFramePushed(ion->frameSize());
    }

    // asm.js compilation handles its own IonContet-pushing
    struct AsmJSToken {};
    MacroAssembler(AsmJSToken)
      : enoughMemory_(true),
        embedsNurseryPointers_(false),
        sps_(nullptr)
    {
#ifdef JS_CODEGEN_ARM
        initWithAllocator();
        m_buffer.id = 0;
#endif
    }

    void setInstrumentation(IonInstrumentation *sps) {
        sps_ = sps;
    }

    void resetForNewCodeGenerator(TempAllocator &alloc) {
        setFramePushed(0);
        moveResolver_.clearTempObjectPool();
        moveResolver_.setAllocator(alloc);
    }

    void constructRoot(JSContext *cx) {
        autoRooter_.construct(cx, this);
    }

    MoveResolver &moveResolver() {
        return moveResolver_;
    }

    size_t instructionsSize() const {
        return size();
    }

    void propagateOOM(bool success) {
        enoughMemory_ &= success;
    }
    bool oom() const {
        return !enoughMemory_ || MacroAssemblerSpecific::oom();
    }

    bool embedsNurseryPointers() const {
        return embedsNurseryPointers_;
    }

    // Emits a test of a value against all types in a TypeSet. A scratch
    // register is required.
    template <typename Source, typename TypeSet>
    void guardTypeSet(const Source &address, const TypeSet *types, Register scratch, Label *miss);
    template <typename TypeSet>
    void guardObjectType(Register obj, const TypeSet *types, Register scratch, Label *miss);
    template <typename Source>
    void guardType(const Source &address, types::Type type, Register scratch, Label *miss);

    void loadObjShape(Register objReg, Register dest) {
        loadPtr(Address(objReg, JSObject::offsetOfShape()), dest);
    }
    void loadBaseShape(Register objReg, Register dest) {
        loadPtr(Address(objReg, JSObject::offsetOfShape()), dest);

        loadPtr(Address(dest, Shape::offsetOfBase()), dest);
    }
    void loadObjClass(Register objReg, Register dest) {
        loadPtr(Address(objReg, JSObject::offsetOfType()), dest);
        loadPtr(Address(dest, types::TypeObject::offsetOfClasp()), dest);
    }
    void branchTestObjClass(Condition cond, Register obj, Register scratch, const js::Class *clasp,
                            Label *label) {
        loadPtr(Address(obj, JSObject::offsetOfType()), scratch);
        branchPtr(cond, Address(scratch, types::TypeObject::offsetOfClasp()), ImmPtr(clasp), label);
    }
    void branchTestObjShape(Condition cond, Register obj, const Shape *shape, Label *label) {
        branchPtr(cond, Address(obj, JSObject::offsetOfShape()), ImmGCPtr(shape), label);
    }
    void branchTestObjShape(Condition cond, Register obj, Register shape, Label *label) {
        branchPtr(cond, Address(obj, JSObject::offsetOfShape()), shape, label);
    }
    void branchTestProxyHandlerFamily(Condition cond, Register proxy, Register scratch,
                                      const void *handlerp, Label *label) {
        Address handlerAddr(proxy, ProxyObject::offsetOfHandler());
        loadPrivate(handlerAddr, scratch);
        Address familyAddr(scratch, BaseProxyHandler::offsetOfFamily());
        branchPtr(cond, familyAddr, ImmPtr(handlerp), label);
    }

    template <typename Value>
    Condition testMIRType(Condition cond, const Value &val, MIRType type) {
        switch (type) {
          case MIRType_Null:        return testNull(cond, val);
          case MIRType_Undefined:   return testUndefined(cond, val);
          case MIRType_Boolean:     return testBoolean(cond, val);
          case MIRType_Int32:       return testInt32(cond, val);
          case MIRType_String:      return testString(cond, val);
          case MIRType_Object:      return testObject(cond, val);
          case MIRType_Double:      return testDouble(cond, val);
          case MIRType_Magic:       return testMagic(cond, val);
          default:
            MOZ_ASSUME_UNREACHABLE("Bad MIRType");
        }
    }

    template <typename Value>
    void branchTestMIRType(Condition cond, const Value &val, MIRType type, Label *label) {
        cond = testMIRType(cond, val, type);
        j(cond, label);
    }

    // Branches to |label| if |reg| is false. |reg| should be a C++ bool.
    void branchIfFalseBool(const Register &reg, Label *label) {
        // Note that C++ bool is only 1 byte, so ignore the higher-order bits.
        branchTest32(Assembler::Zero, reg, Imm32(0xFF), label);
    }

    // Branches to |label| if |reg| is true. |reg| should be a C++ bool.
    void branchIfTrueBool(const Register &reg, Label *label) {
        // Note that C++ bool is only 1 byte, so ignore the higher-order bits.
        branchTest32(Assembler::NonZero, reg, Imm32(0xFF), label);
    }

    void loadObjPrivate(Register obj, uint32_t nfixed, Register dest) {
        loadPtr(Address(obj, JSObject::getPrivateDataOffset(nfixed)), dest);
    }

    void loadObjProto(Register obj, Register dest) {
        loadPtr(Address(obj, JSObject::offsetOfType()), dest);
        loadPtr(Address(dest, types::TypeObject::offsetOfProto()), dest);
    }

    void loadStringLength(Register str, Register dest) {
        loadPtr(Address(str, JSString::offsetOfLengthAndFlags()), dest);
        rshiftPtr(Imm32(JSString::LENGTH_SHIFT), dest);
    }

    void loadJSContext(const Register &dest) {
        loadPtr(AbsoluteAddress(GetIonContext()->runtime->addressOfJSContext()), dest);
    }
    void loadJitActivation(const Register &dest) {
        loadPtr(AbsoluteAddress(GetIonContext()->runtime->addressOfActivation()), 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, ScratchFloatReg);
                reg = ScratchFloatReg;
            }
            storeDouble(reg, dest);
        } else {
            storeValue(ValueTypeFromMIRType(src.type()), src.typedReg().gpr(), dest);
        }
    }

    template <typename T>
    void storeConstantOrRegister(ConstantOrRegister src, const T &dest) {
        if (src.constant())
            storeValue(src.value(), dest);
        else
            storeTypedOrValue(src.reg(), dest);
    }

    void storeCallResult(Register reg) {
        if (reg != ReturnReg)
            mov(ReturnReg, reg);
    }

    void storeCallFloatResult(const FloatRegister &reg) {
        if (reg != ReturnFloatReg)
            moveDouble(ReturnFloatReg, reg);
    }

    void storeCallResultValue(AnyRegister dest) {
#if defined(JS_NUNBOX32)
        unboxValue(ValueOperand(JSReturnReg_Type, JSReturnReg_Data), dest);
#elif defined(JS_PUNBOX64)
        unboxValue(ValueOperand(JSReturnReg), dest);
#else
#error "Bad architecture"
#endif
    }

    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)
            movq(JSReturnReg, dest.valueReg());
#else
#error "Bad architecture"
#endif
    }

    void storeCallResultValue(TypedOrValueRegister dest) {
        if (dest.hasValue())
            storeCallResultValue(dest.valueReg());
        else
            storeCallResultValue(dest.typedReg());
    }

    template <typename T>
    Register extractString(const T &source, Register scratch) {
        return extractObject(source, scratch);
    }

    void PushRegsInMask(RegisterSet set);
    void PushRegsInMask(GeneralRegisterSet set) {
        PushRegsInMask(RegisterSet(set, FloatRegisterSet()));
    }
    void PopRegsInMask(RegisterSet set) {
        PopRegsInMaskIgnore(set, RegisterSet());
    }
    void PopRegsInMask(GeneralRegisterSet set) {
        PopRegsInMask(RegisterSet(set, FloatRegisterSet()));
    }
    void PopRegsInMaskIgnore(RegisterSet set, RegisterSet ignore);

    void branchIfFunctionHasNoScript(Register fun, Label *label) {
        // 16-bit loads are slow and unaligned 32-bit loads may be too so
        // perform an aligned 32-bit load and adjust the bitmask accordingly.
        JS_ASSERT(JSFunction::offsetOfNargs() % sizeof(uint32_t) == 0);
        JS_ASSERT(JSFunction::offsetOfFlags() == JSFunction::offsetOfNargs() + 2);
        JS_STATIC_ASSERT(IS_LITTLE_ENDIAN);
        Address address(fun, JSFunction::offsetOfNargs());
        uint32_t bit = JSFunction::INTERPRETED << 16;
        branchTest32(Assembler::Zero, address, Imm32(bit), label);
    }
    void branchIfInterpreted(Register fun, Label *label) {
        // 16-bit loads are slow and unaligned 32-bit loads may be too so
        // perform an aligned 32-bit load and adjust the bitmask accordingly.
        JS_ASSERT(JSFunction::offsetOfNargs() % sizeof(uint32_t) == 0);
        JS_ASSERT(JSFunction::offsetOfFlags() == JSFunction::offsetOfNargs() + 2);
        JS_STATIC_ASSERT(IS_LITTLE_ENDIAN);
        Address address(fun, JSFunction::offsetOfNargs());
        uint32_t bit = JSFunction::INTERPRETED << 16;
        branchTest32(Assembler::NonZero, address, Imm32(bit), label);
    }

    void branchIfNotInterpretedConstructor(Register fun, Register scratch, Label *label);

    using MacroAssemblerSpecific::Push;
    using MacroAssemblerSpecific::Pop;

    void Push(jsid id, Register scratchReg) {
        if (JSID_IS_GCTHING(id)) {
            // If we're pushing a gcthing, then we can't just push the tagged jsid
            // value since the GC won't have any idea that the push instruction
            // carries a reference to a gcthing.  Need to unpack the pointer,
            // push it using ImmGCPtr, and then rematerialize the id at runtime.

            // double-checking this here to ensure we don't lose sync
            // with implementation of JSID_IS_GCTHING.
            if (JSID_IS_OBJECT(id)) {
                JSObject *obj = JSID_TO_OBJECT(id);
                movePtr(ImmGCPtr(obj), scratchReg);
                JS_ASSERT(((size_t)obj & JSID_TYPE_MASK) == 0);
                orPtr(Imm32(JSID_TYPE_OBJECT), scratchReg);
                Push(scratchReg);
            } else {
                JSString *str = JSID_TO_STRING(id);
                JS_ASSERT(((size_t)str & JSID_TYPE_MASK) == 0);
                JS_ASSERT(JSID_TYPE_STRING == 0x0);
                Push(ImmGCPtr(str));
            }
        } else {
            Push(ImmWord(JSID_BITS(id)));
        }
    }

    void Push(TypedOrValueRegister v) {
        if (v.hasValue()) {
            Push(v.valueReg());
        } else if (IsFloatingPointType(v.type())) {
            FloatRegister reg = v.typedReg().fpu();
            if (v.type() == MIRType_Float32) {
                convertFloat32ToDouble(reg, ScratchFloatReg);
                reg = ScratchFloatReg;
            }
            Push(reg);
        } else {
            Push(ValueTypeFromMIRType(v.type()), v.typedReg().gpr());
        }
    }

    void Push(ConstantOrRegister v) {
        if (v.constant())
            Push(v.value());
        else
            Push(v.reg());
    }

    void Push(const ValueOperand &val) {
        pushValue(val);
        framePushed_ += sizeof(Value);
    }

    void Push(const Value &val) {
        pushValue(val);
        framePushed_ += sizeof(Value);
    }

    void Push(JSValueType type, Register reg) {
        pushValue(type, reg);
        framePushed_ += sizeof(Value);
    }

    void PushValue(const Address &addr) {
        JS_ASSERT(addr.base != StackPointer);
        pushValue(addr);
        framePushed_ += sizeof(Value);
    }

    void PushEmptyRooted(VMFunction::RootType rootType);
    void popRooted(VMFunction::RootType rootType, Register cellReg, const ValueOperand &valueReg);

    void adjustStack(int amount) {
        if (amount > 0)
            freeStack(amount);
        else if (amount < 0)
            reserveStack(-amount);
    }

    void bumpKey(Int32Key *key, int diff) {
        if (key->isRegister())
            add32(Imm32(diff), key->reg());
        else
            key->bumpConstant(diff);
    }

    void storeKey(const Int32Key &key, const Address &dest) {
        if (key.isRegister())
            store32(key.reg(), dest);
        else
            store32(Imm32(key.constant()), dest);
    }

    template<typename T>
    void branchKey(Condition cond, const T &length, const Int32Key &key, Label *label) {
        if (key.isRegister())
            branch32(cond, length, key.reg(), label);
        else
            branch32(cond, length, Imm32(key.constant()), label);
    }

    void branchTestNeedsBarrier(Condition cond, const Register &scratch, Label *label) {
        JS_ASSERT(cond == Zero || cond == NonZero);
        CompileZone *zone = GetIonContext()->compartment->zone();
        movePtr(ImmPtr(zone->addressOfNeedsBarrier()), scratch);
        Address needsBarrierAddr(scratch, 0);
        branchTest32(cond, needsBarrierAddr, Imm32(0x1), label);
    }

    template <typename T>
    void callPreBarrier(const T &address, MIRType type) {
        JS_ASSERT(type == MIRType_Value ||
                  type == MIRType_String ||
                  type == MIRType_Object ||
                  type == MIRType_Shape);
        Label done;

        if (type == MIRType_Value)
            branchTestGCThing(Assembler::NotEqual, address, &done);

        Push(PreBarrierReg);
        computeEffectiveAddress(address, PreBarrierReg);

        const JitRuntime *rt = GetIonContext()->runtime->jitRuntime();
        JitCode *preBarrier = (type == MIRType_Shape)
                              ? rt->shapePreBarrier()
                              : rt->valuePreBarrier();

        call(preBarrier);
        Pop(PreBarrierReg);

        bind(&done);
    }

    template <typename T>
    void patchableCallPreBarrier(const T &address, MIRType type) {
        JS_ASSERT(type == MIRType_Value ||
                  type == MIRType_String ||
                  type == MIRType_Object ||
                  type == MIRType_Shape);

        Label done;

        // All barriers are off by default.
        // They are enabled if necessary at the end of CodeGenerator::generate().
        CodeOffsetLabel nopJump = toggledJump(&done);
        writePrebarrierOffset(nopJump);

        callPreBarrier(address, type);
        jump(&done);

        align(8);
        bind(&done);
    }

    void branchNurseryPtr(Condition cond, const Address &ptr1, const ImmMaybeNurseryPtr &ptr2,
                          Label *label);
    void moveNurseryPtr(const ImmMaybeNurseryPtr &ptr, const Register &reg);

    void canonicalizeDouble(FloatRegister reg) {
        Label notNaN;
        branchDouble(DoubleOrdered, reg, reg, &notNaN);
        loadConstantDouble(JS::GenericNaN(), reg);
        bind(&notNaN);
    }

    void canonicalizeFloat(FloatRegister reg) {
        Label notNaN;
        branchFloat(DoubleOrdered, reg, reg, &notNaN);
        loadConstantFloat32(float(JS::GenericNaN()), reg);
        bind(&notNaN);
    }

    template<typename T>
    void loadFromTypedArray(int arrayType, const T &src, AnyRegister dest, Register temp, Label *fail);

    template<typename T>
    void loadFromTypedArray(int arrayType, const T &src, const ValueOperand &dest, bool allowDouble,
                            Register temp, Label *fail);

    template<typename S, typename T>
    void storeToTypedIntArray(int arrayType, const S &value, const T &dest) {
        switch (arrayType) {
          case ScalarTypeRepresentation::TYPE_INT8:
          case ScalarTypeRepresentation::TYPE_UINT8:
          case ScalarTypeRepresentation::TYPE_UINT8_CLAMPED:
            store8(value, dest);
            break;
          case ScalarTypeRepresentation::TYPE_INT16:
          case ScalarTypeRepresentation::TYPE_UINT16:
            store16(value, dest);
            break;
          case ScalarTypeRepresentation::TYPE_INT32:
          case ScalarTypeRepresentation::TYPE_UINT32:
            store32(value, dest);
            break;
          default:
            MOZ_ASSUME_UNREACHABLE("Invalid typed array type");
        }
    }

    void storeToTypedFloatArray(int arrayType, const FloatRegister &value, const BaseIndex &dest);
    void storeToTypedFloatArray(int arrayType, const FloatRegister &value, const Address &dest);

    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);
        JS_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);

    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);
    }

    // Emit type case branch on tag matching if the type tag in the definition
    // might actually be that type.
    void branchEqualTypeIfNeeded(MIRType type, MDefinition *maybeDef, const Register &tag,
                                 Label *label);

    // Inline allocation.
    void newGCThing(const Register &result, gc::AllocKind allocKind, Label *fail,
                    gc::InitialHeap initialHeap = gc::DefaultHeap);
    void newGCThing(const Register &result, JSObject *templateObject, Label *fail,
                    gc::InitialHeap initialHeap);
    void newGCString(const Register &result, Label *fail);
    void newGCShortString(const Register &result, Label *fail);

    void newGCThingPar(const Register &result, const Register &slice,
                       const Register &tempReg1, const Register &tempReg2,
                       gc::AllocKind allocKind, Label *fail);
    void newGCThingPar(const Register &result, const Register &slice,
                       const Register &tempReg1, const Register &tempReg2,
                       JSObject *templateObject, Label *fail);
    void newGCStringPar(const Register &result, const Register &slice,
                        const Register &tempReg1, const Register &tempReg2,
                        Label *fail);
    void newGCShortStringPar(const Register &result, const Register &slice,
                             const Register &tempReg1, const Register &tempReg2,
                             Label *fail);
    void initGCThing(const Register &obj, JSObject *templateObject);

    // 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,
                        Register temp, Label *fail);

    // Checks the flags that signal that parallel code may need to interrupt or
    // abort.  Branches to fail in that case.
    void checkInterruptFlagsPar(const Register &tempReg, Label *fail);

    // 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.
  private:
    CodeOffsetLabel exitCodePatch_;

  public:
    void enterExitFrame(const VMFunction *f = nullptr) {
        linkExitFrame();
        // Push the ioncode. (Bailout or VM wrapper)
        exitCodePatch_ = PushWithPatch(ImmWord(-1));
        // Push VMFunction pointer, to mark arguments.
        Push(ImmPtr(f));
    }
    void enterFakeExitFrame(JitCode *codeVal = nullptr) {
        linkExitFrame();
        Push(ImmPtr(codeVal));
        Push(ImmPtr(nullptr));
    }

    void loadForkJoinSlice(Register slice, Register scratch);
    void loadContext(Register cxReg, Register scratch, ExecutionMode executionMode);

    void enterParallelExitFrameAndLoadSlice(const VMFunction *f, Register slice,
                                            Register scratch);

    void enterExitFrameAndLoadContext(const VMFunction *f, Register cxReg, Register scratch,
                                      ExecutionMode executionMode);

    void enterFakeParallelExitFrame(Register slice, Register scratch,
                                    JitCode *codeVal = nullptr);

    void enterFakeExitFrame(Register cxReg, Register scratch,
                            ExecutionMode executionMode,
                            JitCode *codeVal = nullptr);

    void leaveExitFrame() {
        freeStack(IonExitFooterFrame::Size());
    }

    bool hasEnteredExitFrame() const {
        return exitCodePatch_.offset() != 0;
    }

    void link(JitCode *code) {
        JS_ASSERT(!oom());
        // If this code can transition to C++ code and witness a GC, then we need to store
        // the JitCode onto the stack in order to GC it correctly.  exitCodePatch should
        // be unset if the code never needed to push its JitCode*.
        if (hasEnteredExitFrame()) {
            patchDataWithValueCheck(CodeLocationLabel(code, exitCodePatch_),
                                    ImmPtr(code),
                                    ImmPtr((void*)-1));
        }

    }

    // Generates code used to complete a bailout.
    void generateBailoutTail(Register scratch, Register bailoutInfo);

    // These functions exist as small wrappers around sites where execution can
    // leave the currently running stream of instructions. They exist so that
    // instrumentation may be put in place around them if necessary and the
    // instrumentation is enabled. For the functions that return a uint32_t,
    // they are returning the offset of the assembler just after the call has
    // been made so that a safepoint can be made at that location.

    template <typename T>
    void callWithABINoProfiling(const T &fun, MoveOp::Type result = MoveOp::GENERAL) {
        MacroAssemblerSpecific::callWithABI(fun, result);
    }

    template <typename T>
    void callWithABI(const T &fun, MoveOp::Type result = MoveOp::GENERAL) {
        leaveSPSFrame();
        callWithABINoProfiling(fun, result);
        reenterSPSFrame();
    }

    // see above comment for what is returned
    uint32_t callIon(const Register &callee) {
        leaveSPSFrame();
        MacroAssemblerSpecific::callIon(callee);
        uint32_t ret = currentOffset();
        reenterSPSFrame();
        return ret;
    }

    // see above comment for what is returned
    uint32_t callWithExitFrame(JitCode *target) {
        leaveSPSFrame();
        MacroAssemblerSpecific::callWithExitFrame(target);
        uint32_t ret = currentOffset();
        reenterSPSFrame();
        return ret;
    }

    // see above comment for what is returned
    uint32_t callWithExitFrame(JitCode *target, Register dynStack) {
        leaveSPSFrame();
        MacroAssemblerSpecific::callWithExitFrame(target, dynStack);
        uint32_t ret = currentOffset();
        reenterSPSFrame();
        return ret;
    }

    Condition branchTestObjectTruthy(bool truthy, Register objReg, Register scratch,
                                     Label *slowCheck)
    {
        // The branches to out-of-line code here implement a conservative version
        // of the JSObject::isWrapper test performed in EmulatesUndefined.  If none
        // of the branches are taken, we can check class flags directly.
        loadObjClass(objReg, scratch);
        branchPtr(Assembler::Equal, scratch, ImmPtr(&ProxyObject::callableClass_), slowCheck);
        branchPtr(Assembler::Equal, scratch, ImmPtr(&ProxyObject::uncallableClass_), slowCheck);
        branchPtr(Assembler::Equal, scratch, ImmPtr(&OuterWindowProxyObject::class_), slowCheck);

        test32(Address(scratch, Class::offsetOfFlags()), Imm32(JSCLASS_EMULATES_UNDEFINED));
        return truthy ? Assembler::Zero : Assembler::NonZero;
    }

  private:
    // These two functions are helpers used around call sites throughout the
    // assembler. They are called from the above call wrappers to emit the
    // necessary instrumentation.
    void leaveSPSFrame() {
        if (!sps_ || !sps_->enabled())
            return;
        // No registers are guaranteed to be available, so push/pop a register
        // so we can use one
        push(CallTempReg0);
        sps_->leave(*this, CallTempReg0);
        pop(CallTempReg0);
    }

    void reenterSPSFrame() {
        if (!sps_ || !sps_->enabled())
            return;
        // Attempt to use a now-free register within a given set, but if the
        // architecture being built doesn't have an available register, resort
        // to push/pop
        GeneralRegisterSet regs(Registers::TempMask & ~Registers::JSCallMask &
                                                      ~Registers::CallMask);
        if (regs.empty()) {
            push(CallTempReg0);
            sps_->reenter(*this, CallTempReg0);
            pop(CallTempReg0);
        } else {
            sps_->reenter(*this, regs.getAny());
        }
    }

    void spsProfileEntryAddress(SPSProfiler *p, int offset, Register temp,
                                Label *full)
    {
        movePtr(ImmPtr(p->sizePointer()), temp);
        load32(Address(temp, 0), temp);
        if (offset != 0)
            add32(Imm32(offset), temp);
        branch32(Assembler::GreaterThanOrEqual, temp, Imm32(p->maxSize()), full);

        // 4 * sizeof(void*) * idx = idx << (2 + log(sizeof(void*)))
        JS_STATIC_ASSERT(sizeof(ProfileEntry) == 4 * sizeof(void*));
        lshiftPtr(Imm32(2 + (sizeof(void*) == 4 ? 2 : 3)), temp);
        addPtr(ImmPtr(p->stack()), temp);
    }

    // The safe version of the above method refrains from assuming that the fields
    // of the SPSProfiler class are going to stay the same across different runs of
    // the jitcode.  Ion can use the more efficient unsafe version because ion jitcode
    // will not survive changes to to the profiler settings.  Baseline jitcode, however,
    // can span these changes, so any hardcoded field values will be incorrect afterwards.
    // All the sps-related methods used by baseline call |spsProfileEntryAddressSafe|.
    void spsProfileEntryAddressSafe(SPSProfiler *p, int offset, Register temp,
                                    Label *full)
    {
        // Load size pointer
        loadPtr(AbsoluteAddress(p->addressOfSizePointer()), temp);

        // Load size
        load32(Address(temp, 0), temp);
        if (offset != 0)
            add32(Imm32(offset), temp);

        // Test against max size.
        branch32(Assembler::LessThanOrEqual, AbsoluteAddress(p->addressOfMaxSize()), temp, full);

        // 4 * sizeof(void*) * idx = idx << (2 + log(sizeof(void*)))
        JS_STATIC_ASSERT(sizeof(ProfileEntry) == 4 * sizeof(void*));
        lshiftPtr(Imm32(2 + (sizeof(void*) == 4 ? 2 : 3)), temp);
        push(temp);
        loadPtr(AbsoluteAddress(p->addressOfStack()), temp);
        addPtr(Address(StackPointer, 0), temp);
        addPtr(Imm32(sizeof(size_t)), StackPointer);
    }

  public:

    // These functions are needed by the IonInstrumentation interface defined in
    // vm/SPSProfiler.h.  They will modify the pseudostack provided to SPS to
    // perform the actual instrumentation.

    void spsUpdatePCIdx(SPSProfiler *p, int32_t idx, Register temp) {
        Label stackFull;
        spsProfileEntryAddress(p, -1, temp, &stackFull);
        store32(Imm32(idx), Address(temp, ProfileEntry::offsetOfPCIdx()));
        bind(&stackFull);
    }

    void spsUpdatePCIdx(SPSProfiler *p, Register idx, Register temp) {
        Label stackFull;
        spsProfileEntryAddressSafe(p, -1, temp, &stackFull);
        store32(idx, Address(temp, ProfileEntry::offsetOfPCIdx()));
        bind(&stackFull);
    }

    void spsPushFrame(SPSProfiler *p, const char *str, JSScript *s, Register temp) {
        Label stackFull;
        spsProfileEntryAddress(p, 0, temp, &stackFull);

        storePtr(ImmPtr(str),  Address(temp, ProfileEntry::offsetOfString()));
        storePtr(ImmGCPtr(s),  Address(temp, ProfileEntry::offsetOfScript()));
        storePtr(ImmPtr(nullptr), Address(temp, ProfileEntry::offsetOfStackAddress()));
        store32(Imm32(ProfileEntry::NullPCIndex), Address(temp, ProfileEntry::offsetOfPCIdx()));

        /* Always increment the stack size, whether or not we actually pushed. */
        bind(&stackFull);
        movePtr(ImmPtr(p->sizePointer()), temp);
        add32(Imm32(1), Address(temp, 0));
    }

    void spsPushFrame(SPSProfiler *p, const Address &str, const Address &script,
                      Register temp, Register temp2)
    {
        Label stackFull;
        spsProfileEntryAddressSafe(p, 0, temp, &stackFull);

        loadPtr(str, temp2);
        storePtr(temp2, Address(temp, ProfileEntry::offsetOfString()));

        loadPtr(script, temp2);
        storePtr(temp2, Address(temp, ProfileEntry::offsetOfScript()));

        storePtr(ImmPtr(nullptr), Address(temp, ProfileEntry::offsetOfStackAddress()));

        // Store 0 for PCIdx because that's what interpreter does.
        // (See probes::EnterScript, which calls spsProfiler.enter, which pushes an entry
        //  with 0 pcIdx).
        store32(Imm32(0), Address(temp, ProfileEntry::offsetOfPCIdx()));

        /* Always increment the stack size, whether or not we actually pushed. */
        bind(&stackFull);
        movePtr(ImmPtr(p->addressOfSizePointer()), temp);
        loadPtr(Address(temp, 0), temp);
        add32(Imm32(1), Address(temp, 0));
    }

    void spsPopFrame(SPSProfiler *p, Register temp) {
        movePtr(ImmPtr(p->sizePointer()), temp);
        add32(Imm32(-1), Address(temp, 0));
    }

    // spsPropFrameSafe does not assume |profiler->sizePointer()| will stay constant.
    void spsPopFrameSafe(SPSProfiler *p, Register temp) {
        loadPtr(AbsoluteAddress(p->addressOfSizePointer()), temp);
        add32(Imm32(-1), Address(temp, 0));
    }

    void loadBaselineOrIonRaw(Register script, Register dest, ExecutionMode mode, Label *failure);
    void loadBaselineOrIonNoArgCheck(Register callee, Register dest, ExecutionMode mode, Label *failure);

    void loadBaselineFramePtr(Register framePtr, Register dest);

    void pushBaselineFramePtr(Register framePtr, Register scratch) {
        loadBaselineFramePtr(framePtr, scratch);
        push(scratch);
    }

  private:
    void handleFailure(ExecutionMode executionMode);

  public:
    Label *exceptionLabel() {
        // Exceptions are currently handled the same way as sequential failures.
        return &sequentialFailureLabel_;
    }

    Label *failureLabel(ExecutionMode executionMode) {
        switch (executionMode) {
          case SequentialExecution: return &sequentialFailureLabel_;
          case ParallelExecution: return &parallelFailureLabel_;
          default: MOZ_ASSUME_UNREACHABLE("Unexpected execution mode");
        }
    }

    void finish();

    void assumeUnreachable(const char *output);
    void printf(const char *output);
    void printf(const char *output, Register value);

#if JS_TRACE_LOGGING
    void tracelogStart(JSScript *script);
    void tracelogStop();
    void tracelogLog(TraceLogging::Type type);
#endif

#define DISPATCH_FLOATING_POINT_OP(method, type, arg1d, arg1f, arg2)    \
    JS_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);
    bool convertValueToFloatingPoint(JSContext *cx, const Value &v, FloatRegister output,
                                     Label *fail, MIRType outputType);
    bool convertConstantOrRegisterToFloatingPoint(JSContext *cx, ConstantOrRegister src,
                                                  FloatRegister output, Label *fail,
                                                  MIRType outputType);
    void convertTypedOrValueToFloatingPoint(TypedOrValueRegister src, FloatRegister output,
                                            Label *fail, MIRType outputType);

    void convertInt32ValueToDouble(const Address &address, Register scratch, Label *done);
    void convertValueToDouble(ValueOperand value, FloatRegister output, Label *fail) {
        convertValueToFloatingPoint(value, output, fail, MIRType_Double);
    }
    bool convertValueToDouble(JSContext *cx, const Value &v, FloatRegister output, Label *fail) {
        return convertValueToFloatingPoint(cx, v, output, fail, MIRType_Double);
    }
    bool convertConstantOrRegisterToDouble(JSContext *cx, 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);
    }
    bool convertValueToFloat(JSContext *cx, const Value &v, FloatRegister output, Label *fail) {
        return convertValueToFloatingPoint(cx, v, output, fail, MIRType_Float32);
    }
    bool convertConstantOrRegisterToFloat(JSContext *cx, 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_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);
    }
    bool convertValueToInt(JSContext *cx, const Value &v, Register output, Label *fail,
                           IntConversionBehavior behavior);
    bool convertConstantOrRegisterToInt(JSContext *cx, 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);
    }
    bool convertValueToInt32(JSContext *cx, const Value &v, Register output, Label *fail,
                             bool negativeZeroCheck)
    {
        return convertValueToInt(cx, v, output, fail, negativeZeroCheck
                                 ? IntConversion_NegativeZeroCheck
                                 : IntConversion_Normal);
    }
    bool convertConstantOrRegisterToInt32(JSContext *cx, 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);
    }
    bool truncateValueToInt32(JSContext *cx, const Value &v, Register output, Label *fail) {
        return convertValueToInt(cx, v, output, fail, IntConversion_Truncate);
    }
    bool truncateConstantOrRegisterToInt32(JSContext *cx, 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);
    }
    bool clampValueToUint8(JSContext *cx, const Value &v, Register output, Label *fail) {
        return convertValueToInt(cx, v, output, fail, IntConversion_ClampToUint8);
    }
    bool clampConstantOrRegisterToUint8(JSContext *cx, 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;
        AfterICSaveLive(uint32_t initialStack)
#ifdef JS_DEBUG
          : initialStack(initialStack)
#endif
        {}

#ifdef JS_DEBUG
      public:
        uint32_t initialStack;
#endif
    };

    AfterICSaveLive icSaveLive(RegisterSet &liveRegs) {
        PushRegsInMask(liveRegs);
        return AfterICSaveLive(framePushed());
    }

    bool icBuildOOLFakeExitFrame(void *fakeReturnAddr, AfterICSaveLive &aic) {
        return buildOOLFakeExitFrame(fakeReturnAddr);
    }

    void icRestoreLive(RegisterSet &liveRegs, AfterICSaveLive &aic) {
        JS_ASSERT(framePushed() == aic.initialStack);
        PopRegsInMask(liveRegs);
    }
};

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_ASSUME_UNREACHABLE("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_ASSUME_UNREACHABLE("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_ASSUME_UNREACHABLE("Unrecognized comparison operation");
        }
    }
}

} // namespace jit
} // namespace js

#endif // JS_ION

#endif /* jit_IonMacroAssembler_h */