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

#include <assembler/wtf/Platform.h>

#if ENABLE_ASSEMBLER && WTF_CPU_SPARC

#include "SparcAssembler.h"
#include "AbstractMacroAssembler.h"

namespace JSC {

    class MacroAssemblerSparc : public AbstractMacroAssembler<SparcAssembler> {
    public:
        enum Condition {
            Equal = SparcAssembler::ConditionE,
            NotEqual = SparcAssembler::ConditionNE,
            Above = SparcAssembler::ConditionGU,
            AboveOrEqual = SparcAssembler::ConditionCC,
            Below = SparcAssembler::ConditionCS,
            BelowOrEqual = SparcAssembler::ConditionLEU,
            GreaterThan = SparcAssembler::ConditionG,
            GreaterThanOrEqual = SparcAssembler::ConditionGE,
            LessThan = SparcAssembler::ConditionL,
            LessThanOrEqual = SparcAssembler::ConditionLE,
            Overflow = SparcAssembler::ConditionVS,
            Signed = SparcAssembler::ConditionNEG,
            Zero = SparcAssembler::ConditionE,
            NonZero = SparcAssembler::ConditionNE
        };

        enum DoubleCondition {
            // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN.
            DoubleEqual = SparcAssembler::DoubleConditionE,
            DoubleNotEqual = SparcAssembler::DoubleConditionNE,
            DoubleGreaterThan = SparcAssembler::DoubleConditionG,
            DoubleGreaterThanOrEqual = SparcAssembler::DoubleConditionGE,
            DoubleLessThan = SparcAssembler::DoubleConditionL,
            DoubleLessThanOrEqual = SparcAssembler::DoubleConditionLE,
            // If either operand is NaN, these conditions always evaluate to true.
            DoubleEqualOrUnordered = SparcAssembler::DoubleConditionUE,
            DoubleNotEqualOrUnordered = SparcAssembler::DoubleConditionNE,
            DoubleGreaterThanOrUnordered = SparcAssembler::DoubleConditionUG,
            DoubleGreaterThanOrEqualOrUnordered = SparcAssembler::DoubleConditionUGE,
            DoubleLessThanOrUnordered = SparcAssembler::DoubleConditionUL,
            DoubleLessThanOrEqualOrUnordered = SparcAssembler::DoubleConditionULE
        };

        static const RegisterID stackPointerRegister = SparcRegisters::sp;

        static const Scale ScalePtr = TimesFour;
        static const unsigned int TotalRegisters = 24;

        void add32(RegisterID src, RegisterID dest)
        {
            m_assembler.addcc_r(dest, src, dest);
        }

        void add32(TrustedImm32 imm, Address address)
        {
            load32(address, SparcRegisters::g2);
            add32(imm, SparcRegisters::g2);
            store32(SparcRegisters::g2, address);
        }

        void add32(TrustedImm32 imm, RegisterID dest)
        {
            if (m_assembler.isimm13(imm.m_value))
                m_assembler.addcc_imm(dest, imm.m_value, dest);
            else {
                m_assembler.move_nocheck(imm.m_value, SparcRegisters::g3);
                m_assembler.addcc_r(dest, SparcRegisters::g3, dest);
            }
        }

        void add32(Address src, RegisterID dest)
        {
            load32(src, SparcRegisters::g2);
            m_assembler.addcc_r(dest, SparcRegisters::g2, dest);
        }

        void and32(Address src, RegisterID dest)
        {
            load32(src, SparcRegisters::g2);
            m_assembler.andcc_r(dest, SparcRegisters::g2, dest);
        }

        void add32(TrustedImm32 imm, RegisterID src, RegisterID dest)
        {
            if (m_assembler.isimm13(imm.m_value))
                m_assembler.addcc_imm(src, imm.m_value, dest);
            else {
                m_assembler.move_nocheck(imm.m_value, SparcRegisters::g3);
                m_assembler.addcc_r(src, SparcRegisters::g3, dest);
            }
        }

        void and32(RegisterID src, RegisterID dest)
        {
            m_assembler.andcc_r(dest, src, dest);
        }

        void and32(Imm32 imm, RegisterID dest)
        {
            if (m_assembler.isimm13(imm.m_value))
                m_assembler.andcc_imm(dest, imm.m_value, dest);
            else {
                m_assembler.move_nocheck(imm.m_value, SparcRegisters::g3);
                m_assembler.andcc_r(dest, SparcRegisters::g3, dest);
            }
        }


        void lshift32(RegisterID shift_amount, RegisterID dest)
        {
            m_assembler.sll_r(dest, shift_amount, dest);
        }

        void lshift32(Imm32 imm, RegisterID dest)
        {
            // No need to check if imm.m_value.
            // The last 5 bit of imm.m_value will be used anyway.
            m_assembler.sll_imm(dest, imm.m_value, dest);
        }

        void mul32(RegisterID src, RegisterID dest)
        {
            m_assembler.smulcc_r(dest, src, dest);
        }

        void mul32(Imm32 imm, RegisterID src, RegisterID dest)
        {
            if (m_assembler.isimm13(imm.m_value))
                m_assembler.smulcc_imm(dest, imm.m_value, dest);
            else {
                m_assembler.move_nocheck(imm.m_value, SparcRegisters::g3);
                m_assembler.smulcc_r(SparcRegisters::g3, dest, dest);
            }
        }

        void neg32(RegisterID srcDest)
        {
            m_assembler.subcc_r(SparcRegisters::g0, srcDest, srcDest);
        }

        void not32(RegisterID dest)
        {
            m_assembler.xnorcc_r(dest, SparcRegisters::g0, dest);
        }

        void or32(RegisterID src, RegisterID dest)
        {
            m_assembler.orcc_r(dest, src, dest);
        }

        void or32(TrustedImm32 imm, RegisterID dest)
        {
            if (m_assembler.isimm13(imm.m_value))
                m_assembler.orcc_imm(dest, imm.m_value, dest);
            else {
                m_assembler.move_nocheck(imm.m_value, SparcRegisters::g3);
                m_assembler.or_r(SparcRegisters::g3, dest, dest);
            }
        }


        void or32(Address address, RegisterID dest)
        {
            load32(address, SparcRegisters::g2);
            or32(SparcRegisters::g2, dest);
        }

        void rshift32(RegisterID shift_amount, RegisterID dest)
        {
            m_assembler.sra_r(dest, shift_amount, dest);
        }

        void rshift32(Imm32 imm, RegisterID dest)
        {
            // No need to check if imm.m_value.
            // The last 5 bit of imm.m_value will be used anyway.
            m_assembler.sra_imm(dest, imm.m_value, dest);
        }
    
        void urshift32(RegisterID shift_amount, RegisterID dest)
        {
            m_assembler.srl_r(dest, shift_amount, dest);
        }
    
        void urshift32(Imm32 imm, RegisterID dest)
        {
            // No need to check if imm.m_value.
            // The last 5 bit of imm.m_value will be used anyway.
            m_assembler.srl_imm(dest, imm.m_value, dest);
        }

        void sub32(RegisterID src, RegisterID dest)
        {
            m_assembler.subcc_r(dest, src, dest);
        }

        void sub32(TrustedImm32 imm, RegisterID dest)
        {
            if (m_assembler.isimm13(imm.m_value))
                m_assembler.subcc_imm(dest, imm.m_value, dest);
            else {
                m_assembler.move_nocheck(imm.m_value, SparcRegisters::g3);
                m_assembler.subcc_r(dest, SparcRegisters::g3, dest);
            }
        }

        void sub32(TrustedImm32 imm, Address address)
        {
            load32(address, SparcRegisters::g2);
            sub32(imm, SparcRegisters::g2);
            store32(SparcRegisters::g2, address);
        }

        void sub32(Address src, RegisterID dest)
        {
            load32(src, SparcRegisters::g2);
            sub32(SparcRegisters::g2, dest);
        }

        void xor32(RegisterID src, RegisterID dest)
        {
            m_assembler.xorcc_r(src, dest, dest);
        }

        void xor32(TrustedImm32 imm, RegisterID dest)
        {
            if (m_assembler.isimm13(imm.m_value))
                m_assembler.xorcc_imm(dest, imm.m_value, dest);
            else {
                m_assembler.move_nocheck(imm.m_value, SparcRegisters::g3);
                m_assembler.xorcc_r(dest, SparcRegisters::g3, dest);
            }
        }

        void xor32(Address src, RegisterID dest)
        {
            load32(src, SparcRegisters::g2);
            xor32(SparcRegisters::g2, dest);
        }

        void load8(ImplicitAddress address, RegisterID dest)
        {
            if (m_assembler.isimm13(address.offset))
                m_assembler.ldub_imm(address.base, address.offset, dest);
            else {
                m_assembler.move_nocheck(address.offset, SparcRegisters::g3);
                m_assembler.ldub_r(address.base, SparcRegisters::g3, dest);
            }
        }

        void load32(ImplicitAddress address, RegisterID dest)
        {
            if (m_assembler.isimm13(address.offset))
                m_assembler.lduw_imm(address.base, address.offset, dest);
            else {
                m_assembler.move_nocheck(address.offset, SparcRegisters::g3);
                m_assembler.lduw_r(address.base, SparcRegisters::g3, dest);
            }
        }

        void load32(BaseIndex address, RegisterID dest)
        {
            m_assembler.sll_imm(address.index, address.scale, SparcRegisters::g2);
            add32(Imm32(address.offset), SparcRegisters::g2);
            m_assembler.lduw_r(address.base, SparcRegisters::g2, dest);
        }

        void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest)
        {
            m_assembler.sll_imm(address.index, address.scale, SparcRegisters::g2);
            add32(Imm32(address.offset+3), SparcRegisters::g2);
            m_assembler.ldub_r(address.base, SparcRegisters::g2, dest);
            m_assembler.subcc_imm(SparcRegisters::g2, 1, SparcRegisters::g2);
            m_assembler.ldub_r(address.base, SparcRegisters::g2, SparcRegisters::g3);
            m_assembler.sll_imm(SparcRegisters::g3, 8, SparcRegisters::g3);
            m_assembler.or_r(SparcRegisters::g3, dest, dest);
            m_assembler.subcc_imm(SparcRegisters::g2, 1, SparcRegisters::g2);
            m_assembler.ldub_r(address.base, SparcRegisters::g2, SparcRegisters::g3);
            m_assembler.sll_imm(SparcRegisters::g3, 16, SparcRegisters::g3);
            m_assembler.or_r(SparcRegisters::g3, dest, dest);
            m_assembler.subcc_imm(SparcRegisters::g2, 1, SparcRegisters::g2);
            m_assembler.ldub_r(address.base, SparcRegisters::g2, SparcRegisters::g3);
            m_assembler.sll_imm(SparcRegisters::g3, 24, SparcRegisters::g3);
            m_assembler.or_r(SparcRegisters::g3, dest, dest);
        }

        DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest)
        {
            DataLabel32 dataLabel(this);
            m_assembler.move_nocheck(0, SparcRegisters::g3);
            m_assembler.lduw_r(address.base, SparcRegisters::g3, dest);
            return dataLabel;
        }

        DataLabel32 load64WithAddressOffsetPatch(Address address, RegisterID hi, RegisterID lo)
        {
            DataLabel32 dataLabel(this);
            m_assembler.move_nocheck(0, SparcRegisters::g3);
            m_assembler.add_imm(SparcRegisters::g3, 4, SparcRegisters::g2);
            m_assembler.lduw_r(address.base, SparcRegisters::g3, hi);
            m_assembler.lduw_r(address.base, SparcRegisters::g2, lo);
            return dataLabel;
        }

        Label loadPtrWithPatchToLEA(Address address, RegisterID dest)
        {
            Label label(this);
            load32(address, dest);
            return label;
        }

        void load16(BaseIndex address, RegisterID dest)
        {
            m_assembler.sll_imm(address.index, address.scale, SparcRegisters::g2);
            add32(Imm32(address.offset), SparcRegisters::g2);
            m_assembler.lduh_r(address.base, SparcRegisters::g2, dest);
        }
    
        void load16(ImplicitAddress address, RegisterID dest)
        {
            if (m_assembler.isimm13(address.offset))
                m_assembler.lduh_imm(address.base, address.offset, dest);
            else {
                m_assembler.move_nocheck(address.offset, SparcRegisters::g3);
                m_assembler.lduh_r(address.base, SparcRegisters::g3, dest);
            }
        }

        void store8(RegisterID src, ImplicitAddress address)
        {
            if (m_assembler.isimm13(address.offset))
                m_assembler.stb_imm(src, address.base, address.offset);
            else {
                m_assembler.move_nocheck(address.offset, SparcRegisters::g3);
                m_assembler.stb_r(src, address.base, SparcRegisters::g3);
            }
        }

        void store8(RegisterID src, BaseIndex address)
        {
            m_assembler.sll_imm(address.index, address.scale, SparcRegisters::g2);
            add32(Imm32(address.offset), SparcRegisters::g2);
            m_assembler.stb_r(src, address.base, SparcRegisters::g2);
        }

        void store8(Imm32 imm, ImplicitAddress address)
        {
            m_assembler.move_nocheck(imm.m_value, SparcRegisters::g2);
            store8(SparcRegisters::g2, address);
        }

        void store8(Imm32 imm, BaseIndex address)
        {
            m_assembler.sll_imm(address.index, address.scale, SparcRegisters::g2);
            add32(Imm32(address.offset), SparcRegisters::g2);
            move(imm, SparcRegisters::g3);
            m_assembler.stb_r(SparcRegisters::g3, SparcRegisters::g2, address.base);
        }

        void store16(RegisterID src, ImplicitAddress address)
        {
            if (m_assembler.isimm13(address.offset))
                m_assembler.sth_imm(src, address.base, address.offset);
            else {
                m_assembler.move_nocheck(address.offset, SparcRegisters::g3);
                m_assembler.sth_r(src, address.base, SparcRegisters::g3);
            }
        }

        void store16(RegisterID src, BaseIndex address)
        {
            m_assembler.sll_imm(address.index, address.scale, SparcRegisters::g2);
            add32(Imm32(address.offset), SparcRegisters::g2);
            m_assembler.sth_r(src, address.base, SparcRegisters::g2);
        }

        void store16(Imm32 imm, ImplicitAddress address)
        {
            m_assembler.move_nocheck(imm.m_value, SparcRegisters::g2);
            store16(SparcRegisters::g2, address);
        }

        void store16(Imm32 imm, BaseIndex address)
        {
            m_assembler.sll_imm(address.index, address.scale, SparcRegisters::g2);
            add32(Imm32(address.offset), SparcRegisters::g2);
            move(imm, SparcRegisters::g3);
            m_assembler.sth_r(SparcRegisters::g3, SparcRegisters::g2, address.base);
        }

        void load8ZeroExtend(BaseIndex address, RegisterID dest)
        {
            m_assembler.sll_imm(address.index, address.scale, SparcRegisters::g2);
            add32(Imm32(address.offset), SparcRegisters::g2);
            m_assembler.ldub_r(address.base, SparcRegisters::g2, dest);
        }

        void load8ZeroExtend(Address address, RegisterID dest)
        {
            if (m_assembler.isimm13(address.offset))
                m_assembler.ldub_imm(address.base, address.offset, dest);
            else {
                m_assembler.move_nocheck(address.offset, SparcRegisters::g3);
                m_assembler.ldub_r(address.base, SparcRegisters::g3, dest);
            }
        }

        void load8SignExtend(BaseIndex address, RegisterID dest)
        {
            m_assembler.sll_imm(address.index, address.scale, SparcRegisters::g2);
            add32(Imm32(address.offset), SparcRegisters::g2);
            m_assembler.ldsb_r(address.base, SparcRegisters::g2, dest);
        }

        void load8SignExtend(Address address, RegisterID dest)
        {
            if (m_assembler.isimm13(address.offset))
                m_assembler.ldsb_imm(address.base, address.offset, dest);
            else {
                m_assembler.move_nocheck(address.offset, SparcRegisters::g3);
                m_assembler.ldsb_r(address.base, SparcRegisters::g3, dest);
            }
        }

        void load16SignExtend(BaseIndex address, RegisterID dest)
        {
            m_assembler.sll_imm(address.index, address.scale, SparcRegisters::g2);
            add32(Imm32(address.offset), SparcRegisters::g2);
            m_assembler.ldsh_r(address.base, SparcRegisters::g2, dest);
        }

        void load16SignExtend(Address address, RegisterID dest)
        {
            if (m_assembler.isimm13(address.offset))
                m_assembler.ldsh_imm(address.base, address.offset, dest);
            else {
                m_assembler.move_nocheck(address.offset, SparcRegisters::g3);
                m_assembler.ldsh_r(address.base, SparcRegisters::g3, dest);
            }
        }

        DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address)
        {
            DataLabel32 dataLabel(this);
            // Since this is for patch, we don't check is offset is imm13.
            m_assembler.move_nocheck(0, SparcRegisters::g3);
            m_assembler.stw_r(src, address.base, SparcRegisters::g3);
            return dataLabel;
        }


        DataLabel32 store64WithAddressOffsetPatch(RegisterID hi, RegisterID lo, Address address)
        {
            DataLabel32 dataLabel(this);
            m_assembler.move_nocheck(address.offset, SparcRegisters::g3);
            m_assembler.add_r(SparcRegisters::g3, address.base, SparcRegisters::g3);
            m_assembler.stw_imm(lo, SparcRegisters::g3, 4);
            m_assembler.stw_imm(hi, SparcRegisters::g3, 0);
            return dataLabel;
        }

        DataLabel32 store64WithAddressOffsetPatch(Imm32 hi, RegisterID lo, Address address)
        {
            DataLabel32 dataLabel(this);
            m_assembler.move_nocheck(address.offset, SparcRegisters::g3);
            m_assembler.add_r(SparcRegisters::g3, address.base, SparcRegisters::g3);
            m_assembler.stw_imm(lo, SparcRegisters::g3, 4);
            move(hi, SparcRegisters::g2);
            m_assembler.stw_imm(SparcRegisters::g2, SparcRegisters::g3, 0);

            return dataLabel;
        }

        DataLabel32 store64WithAddressOffsetPatch(Imm32 hi, Imm32 lo, Address address)
        {
            DataLabel32 dataLabel(this);
            m_assembler.move_nocheck(address.offset, SparcRegisters::g3);
            m_assembler.add_r(SparcRegisters::g3, address.base, SparcRegisters::g3);
            move(lo, SparcRegisters::g2);
            m_assembler.stw_imm(SparcRegisters::g2, SparcRegisters::g3, 4);
            move(hi, SparcRegisters::g2);
            m_assembler.stw_imm(SparcRegisters::g2, SparcRegisters::g3, 0);

            return dataLabel;
        }


        void store32(RegisterID src, ImplicitAddress address)
        {
            if (m_assembler.isimm13(address.offset))
                m_assembler.stw_imm(src, address.base, address.offset);
            else {
                m_assembler.move_nocheck(address.offset, SparcRegisters::g3);
                m_assembler.stw_r(src, address.base, SparcRegisters::g3);
            }
        }

        void store32(RegisterID src, BaseIndex address)
        {
            m_assembler.sll_imm(address.index, address.scale, SparcRegisters::g2);
            add32(Imm32(address.offset), SparcRegisters::g2);
            m_assembler.stw_r(src, address.base, SparcRegisters::g2);
        }

        void store32(TrustedImm32 imm, BaseIndex address)
        {
            m_assembler.sll_imm(address.index, address.scale, SparcRegisters::g2);
            add32(Imm32(address.offset), SparcRegisters::g2);
            move(imm, SparcRegisters::g3);
            m_assembler.stw_r(SparcRegisters::g3, SparcRegisters::g2, address.base);
        }

        void store32(TrustedImm32 imm, ImplicitAddress address)
        {
            m_assembler.move_nocheck(imm.m_value, SparcRegisters::g2);
            store32(SparcRegisters::g2, address);
        }

        void store32(RegisterID src, void* address)
        {
            m_assembler.move_nocheck((int)address, SparcRegisters::g3);
            m_assembler.stw_r(src, SparcRegisters::g0, SparcRegisters::g3);
        }

        void store32(TrustedImm32 imm, void* address)
        {
            move(imm, SparcRegisters::g2);
            store32(SparcRegisters::g2, address);
        }

        void pop(RegisterID dest)
        {
            m_assembler.lduw_imm(SparcRegisters::sp, 0x68, dest);
            m_assembler.addcc_imm(SparcRegisters::sp, 4, SparcRegisters::sp);
        }

        void push(RegisterID src)
        {
            m_assembler.subcc_imm(SparcRegisters::sp, 4, SparcRegisters::sp);
            m_assembler.stw_imm(src, SparcRegisters::sp, 0x68);
        }

        void push(Address address)
        {
            load32(address, SparcRegisters::g2);
            push(SparcRegisters::g2);
        }

        void push(Imm32 imm)
        {
            move(imm, SparcRegisters::g2);
            push(SparcRegisters::g2);
        }

        void move(TrustedImm32 imm, RegisterID dest)
        {
            if (m_assembler.isimm13(imm.m_value))
                m_assembler.or_imm(SparcRegisters::g0, imm.m_value, dest);
            else
                m_assembler.move_nocheck(imm.m_value, dest);
        }

        void move(RegisterID src, RegisterID dest)
        {
            m_assembler.or_r(src, SparcRegisters::g0, dest);
        }

        void move(TrustedImmPtr imm, RegisterID dest)
        {
            move(Imm32(imm), dest);
        }

        void swap(RegisterID reg1, RegisterID reg2)
        {
            m_assembler.or_r(reg1, SparcRegisters::g0, SparcRegisters::g3);
            m_assembler.or_r(reg2, SparcRegisters::g0, reg1);
            m_assembler.or_r(SparcRegisters::g3, SparcRegisters::g0, reg2);
        }

        void signExtend32ToPtr(RegisterID src, RegisterID dest)
        {
            if (src != dest)
                move(src, dest);
        }

        void zeroExtend32ToPtr(RegisterID src, RegisterID dest)
        {
            if (src != dest)
                move(src, dest);
        }

        Jump branch8(Condition cond, Address left, Imm32 right)
        {
            load8(left, SparcRegisters::g2);
            return branch32(cond, SparcRegisters::g2, right);
        }

        Jump branch32_force32(Condition cond, RegisterID left, TrustedImm32 right)
        {
            m_assembler.move_nocheck(right.m_value, SparcRegisters::g3);
            m_assembler.subcc_r(left, SparcRegisters::g3, SparcRegisters::g0);
            return Jump(m_assembler.branch(SparcCondition(cond)));
        }

        Jump branch32FixedLength(Condition cond, RegisterID left, TrustedImm32 right)
        {
            m_assembler.move_nocheck(right.m_value, SparcRegisters::g2);
            return branch32(cond, left, SparcRegisters::g2);
        }

        Jump branch32WithPatch(Condition cond, RegisterID left, TrustedImm32 right, DataLabel32 &dataLabel)
        {
            // Always use move_nocheck, since the value is to be patched.
            dataLabel = DataLabel32(this);
            m_assembler.move_nocheck(right.m_value, SparcRegisters::g3);
            m_assembler.subcc_r(left, SparcRegisters::g3, SparcRegisters::g0);
            return Jump(m_assembler.branch(SparcCondition(cond)));
        }

        Jump branch32(Condition cond, RegisterID left, RegisterID right)
        {
            m_assembler.subcc_r(left, right, SparcRegisters::g0);
            return Jump(m_assembler.branch(SparcCondition(cond)));
        }

        Jump branch32(Condition cond, RegisterID left, TrustedImm32 right)
        {
            if (m_assembler.isimm13(right.m_value))
                m_assembler.subcc_imm(left, right.m_value, SparcRegisters::g0);
            else {
                m_assembler.move_nocheck(right.m_value, SparcRegisters::g3);
                m_assembler.subcc_r(left, SparcRegisters::g3, SparcRegisters::g0);
            }
            return Jump(m_assembler.branch(SparcCondition(cond)));
        }

        Jump branch32(Condition cond, RegisterID left, Address right)
        {
            load32(right, SparcRegisters::g2);
            return branch32(cond, left, SparcRegisters::g2);
        }

        Jump branch32(Condition cond, Address left, RegisterID right)
        {
            load32(left, SparcRegisters::g2);
            return branch32(cond, SparcRegisters::g2, right);
        }

        Jump branch32(Condition cond, Address left, TrustedImm32 right)
        {
            load32(left, SparcRegisters::g2);
            return branch32(cond, SparcRegisters::g2, right);
        }

        Jump branch32(Condition cond, BaseIndex left, TrustedImm32 right)
        {

            load32(left, SparcRegisters::g2);
            return branch32(cond, SparcRegisters::g2, right);
        }

        Jump branch32WithUnalignedHalfWords(Condition cond, BaseIndex left, TrustedImm32 right)
        {
            load32WithUnalignedHalfWords(left, SparcRegisters::g4);
            return branch32(cond, SparcRegisters::g4, right);
        }

        Jump branch16(Condition cond, BaseIndex left, RegisterID right)
        {
            (void)(cond);
            (void)(left);
            (void)(right);
            ASSERT_NOT_REACHED();
            return jump();
        }

        Jump branch16(Condition cond, BaseIndex left, Imm32 right)
        {
            load16(left, SparcRegisters::g3);
            move(right, SparcRegisters::g2);
            m_assembler.subcc_r(SparcRegisters::g3, SparcRegisters::g2, SparcRegisters::g0);
            return Jump(m_assembler.branch(SparcCondition(cond)));
        }

        Jump branchTest8(Condition cond, Address address, Imm32 mask = Imm32(-1))
        {
            load8(address, SparcRegisters::g2);
            return branchTest32(cond, SparcRegisters::g2, mask);
        }

        Jump branchTest32(Condition cond, RegisterID reg, RegisterID mask)
        {
            m_assembler.andcc_r(reg, mask, SparcRegisters::g0);
            return Jump(m_assembler.branch(SparcCondition(cond)));
        }

        Jump branchTest32(Condition cond, RegisterID reg, Imm32 mask = Imm32(-1))
        {
            if (m_assembler.isimm13(mask.m_value))
                m_assembler.andcc_imm(reg, mask.m_value, SparcRegisters::g0);
            else {
                m_assembler.move_nocheck(mask.m_value, SparcRegisters::g3);
                m_assembler.andcc_r(reg, SparcRegisters::g3, SparcRegisters::g0);
            }
            return Jump(m_assembler.branch(SparcCondition(cond)));
        }

        Jump branchTest32(Condition cond, Address address, Imm32 mask = Imm32(-1))
        {
            load32(address, SparcRegisters::g2);
            return branchTest32(cond, SparcRegisters::g2, mask);
        }

        Jump branchTest32(Condition cond, BaseIndex address, Imm32 mask = Imm32(-1))
        {
            // FIXME. branchTest32 only used by PolyIC.
            // PolyIC is not enabled for sparc now.
            ASSERT(0);
            return jump();
        }

        Jump jump()
        {
            return Jump(m_assembler.jmp());
        }

        void jump(RegisterID target)
        {
            m_assembler.jmpl_r(SparcRegisters::g0, target, SparcRegisters::g0);
            m_assembler.nop();
        }

        void jump(Address address)
        {
            load32(address, SparcRegisters::g2);
            m_assembler.jmpl_r(SparcRegisters::g2, SparcRegisters::g0, SparcRegisters::g0);
            m_assembler.nop();
        }

        void jump(BaseIndex address)
        {
            load32(address, SparcRegisters::g2);
            m_assembler.jmpl_r(SparcRegisters::g2, SparcRegisters::g0, SparcRegisters::g0);
            m_assembler.nop();
        }

        Jump branchAdd32(Condition cond, RegisterID src, RegisterID dest)
        {
            ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
            m_assembler.addcc_r(src, dest, dest);
            return Jump(m_assembler.branch(SparcCondition(cond)));
        }

        Jump branchAdd32(Condition cond, Imm32 imm, RegisterID dest)
        {
            ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
            if (m_assembler.isimm13(imm.m_value))
                m_assembler.addcc_imm(dest, imm.m_value, dest);
            else {
                m_assembler.move_nocheck(imm.m_value, SparcRegisters::g3);
                m_assembler.addcc_r(dest, SparcRegisters::g3, dest);
            }
            return Jump(m_assembler.branch(SparcCondition(cond)));
        }

        Jump branchAdd32(Condition cond, Address src, RegisterID dest)
        {
            ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
            load32(src, SparcRegisters::g2);
            return branchAdd32(cond, SparcRegisters::g2, dest);
        }

        void mull32(RegisterID src1, RegisterID src2, RegisterID dest)
        {
            m_assembler.smulcc_r(src1, src2, dest);
        }

        Jump branchMul32(Condition cond, RegisterID src, RegisterID dest)
        {
            ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
            m_assembler.smulcc_r(src, dest, dest);
            if (cond == Overflow) {
                m_assembler.rdy(SparcRegisters::g2);
                m_assembler.sra_imm(dest, 31, SparcRegisters::g3);
                m_assembler.subcc_r(SparcRegisters::g2, SparcRegisters::g3, SparcRegisters::g2);
                cond = NotEqual;
            }
            return Jump(m_assembler.branch(SparcCondition(cond)));
        }

        Jump branchMul32(Condition cond, Imm32 imm, RegisterID src, RegisterID dest)
        {
            ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
            if (m_assembler.isimm13(imm.m_value))
                m_assembler.smulcc_imm(src, imm.m_value, dest);
            else {
                m_assembler.move_nocheck(imm.m_value, SparcRegisters::g3);
                m_assembler.smulcc_r(src, SparcRegisters::g3, dest);
            }
            if (cond == Overflow) {
                m_assembler.rdy(SparcRegisters::g2);
                m_assembler.sra_imm(dest, 31, SparcRegisters::g3);
                m_assembler.subcc_r(SparcRegisters::g2, SparcRegisters::g3, SparcRegisters::g2);
                cond = NotEqual;
            }
            return Jump(m_assembler.branch(SparcCondition(cond)));
        }

        Jump branchMul32(Condition cond, Address src, RegisterID dest)
        {
            ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
            load32(src, SparcRegisters::g2);
            return branchMul32(cond, SparcRegisters::g2, dest);
        }

        Jump branchSub32(Condition cond, RegisterID src, RegisterID dest)
        {
            ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
            m_assembler.subcc_r(dest, src, dest);
            return Jump(m_assembler.branch(SparcCondition(cond)));
        }

        Jump branchSub32(Condition cond, Imm32 imm, RegisterID dest)
        {
            ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
            sub32(imm, dest);
            return Jump(m_assembler.branch(SparcCondition(cond)));
        }

        Jump branchSub32(Condition cond, Address src, RegisterID dest)
        {
            ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
            load32(src, SparcRegisters::g2);
            return branchSub32(cond, SparcRegisters::g2, dest);
        }

        Jump branchSub32(Condition cond, Imm32 imm, Address dest)
        {
            ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
            sub32(imm, dest);
            return Jump(m_assembler.branch(SparcCondition(cond)));
        }

        Jump branchNeg32(Condition cond, RegisterID srcDest)
        {
            ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
            neg32(srcDest);
            return Jump(m_assembler.branch(SparcCondition(cond)));
        }

        Jump branchOr32(Condition cond, RegisterID src, RegisterID dest)
        {
            ASSERT((cond == Signed) || (cond == Zero) || (cond == NonZero));
            m_assembler.orcc_r(src, dest, dest);
            return Jump(m_assembler.branch(SparcCondition(cond)));
        }

        void breakpoint()
        {
            m_assembler.ta_imm(8);
        }

        Call nearCall()
        {
            return Call(m_assembler.call(), Call::LinkableNear);
        }

        Call call(RegisterID target)
        {
            m_assembler.jmpl_r(target, SparcRegisters::g0, SparcRegisters::o7);
            m_assembler.nop();
            JmpSrc jmpSrc;
            return Call(jmpSrc, Call::None);
        }

        void call(Address address)
        {
            if (m_assembler.isimm13(address.offset)) {
                m_assembler.jmpl_imm(address.base, address.offset, SparcRegisters::o7);
                m_assembler.nop();
            } else {
                m_assembler.move_nocheck(address.offset, SparcRegisters::g3);
                m_assembler.jmpl_r(address.base, SparcRegisters::g3, SparcRegisters::o7);
                m_assembler.nop();
            }
        }

        void ret()
        {
            m_assembler.jmpl_imm(SparcRegisters::i7, 8, SparcRegisters::g0);
            m_assembler.nop();
        }

        void ret_and_restore()
        {
            m_assembler.jmpl_imm(SparcRegisters::i7, 8, SparcRegisters::g0);
            m_assembler.restore_r(SparcRegisters::g0, SparcRegisters::g0, SparcRegisters::g0);
        }

        void save(Imm32 size)
        {
            if (m_assembler.isimm13(size.m_value)) {
                m_assembler.save_imm(SparcRegisters::sp, size.m_value, SparcRegisters::sp);
            } else {
                m_assembler.move_nocheck(size.m_value, SparcRegisters::g3);
                m_assembler.save_r(SparcRegisters::sp, SparcRegisters::g3, SparcRegisters::sp);
            }
        }

        void set32(Condition cond, Address left, RegisterID right, RegisterID dest)
        {
            load32(left, SparcRegisters::g2);
            set32(cond, SparcRegisters::g2, right, dest);
        }

        void set32(Condition cond, RegisterID left, Address right, RegisterID dest)
        {
            load32(right, SparcRegisters::g2);
            set32(cond, left, SparcRegisters::g2, dest);
        }

        void set32(Condition cond, RegisterID left, RegisterID right, RegisterID dest)
        {
            m_assembler.subcc_r(left, right, SparcRegisters::g0);
            m_assembler.or_imm(SparcRegisters::g0, 0, dest);
            m_assembler.movcc_imm(1, dest, SparcCondition(cond));
        }

        void set32(Condition cond, RegisterID left, Imm32 right, RegisterID dest)
        {
            if (m_assembler.isimm13(right.m_value))
                m_assembler.subcc_imm(left, right.m_value, SparcRegisters::g0);
            else {
                m_assembler.move_nocheck(right.m_value, SparcRegisters::g3);
                m_assembler.subcc_r(left, SparcRegisters::g3, SparcRegisters::g0);
            }
            m_assembler.or_imm(SparcRegisters::g0, 0, dest);
            m_assembler.movcc_imm(1, dest, SparcCondition(cond));
        }

        void set32(Condition cond, Address left, Imm32 right, RegisterID dest)
        {
            load32(left, SparcRegisters::g2);
            set32(cond, SparcRegisters::g2, right, dest);
        }

        void set8(Condition cond, RegisterID left, RegisterID right, RegisterID dest)
        {
            // Sparc does not have byte register.
            set32(cond, left, right, dest);
        }

        void set8(Condition cond, Address left, RegisterID right, RegisterID dest)
        {
            // Sparc doesn't have byte registers
            load32(left, SparcRegisters::g2);
            set32(cond, SparcRegisters::g2, right, dest);
        }

        void set8(Condition cond, RegisterID left, Imm32 right, RegisterID dest)
        {
            // Sparc does not have byte register.
            set32(cond, left, right, dest);
        }

        void setTest32(Condition cond, Address address, Imm32 mask, RegisterID dest)
        {
            load32(address, SparcRegisters::g2);
            if (m_assembler.isimm13(mask.m_value))
                m_assembler.andcc_imm(SparcRegisters::g2, mask.m_value, SparcRegisters::g0);
            else {
                m_assembler.move_nocheck(mask.m_value, SparcRegisters::g3);
                m_assembler.andcc_r(SparcRegisters::g3, SparcRegisters::g2, SparcRegisters::g0);
            }
            m_assembler.or_imm(SparcRegisters::g0, 0, dest);
            m_assembler.movcc_imm(1, dest, SparcCondition(cond));
        }

        void setTest8(Condition cond, Address address, Imm32 mask, RegisterID dest)
        {
            // Sparc does not have byte register.
            setTest32(cond, address, mask, dest);
        }

        void lea(Address address, RegisterID dest)
        {
            if (m_assembler.isimm13(address.offset))
                m_assembler.add_imm(address.base, address.offset, dest);
            else {
                m_assembler.move_nocheck(address.offset, SparcRegisters::g3);
                m_assembler.add_r(address.base, SparcRegisters::g3, dest);
            }
        }

        void lea(BaseIndex address, RegisterID dest)
        {
            // lea only used by PolyIC.
            // PolyIC is not enabled for sparc now.
            ASSERT(0);
        }

        void add32(Imm32 imm, AbsoluteAddress address)
        {
            load32(address.m_ptr, SparcRegisters::g2);
            add32(imm, SparcRegisters::g2);
            store32(SparcRegisters::g2, address.m_ptr);
        }

        void sub32(TrustedImm32 imm, AbsoluteAddress address)
        {
            load32(address.m_ptr, SparcRegisters::g2);
            sub32(imm, SparcRegisters::g2);
            store32(SparcRegisters::g2, address.m_ptr);
        }

        void load32(const void* address, RegisterID dest)
        {
            m_assembler.move_nocheck((int)address, SparcRegisters::g3);
            m_assembler.lduw_r(SparcRegisters::g3, SparcRegisters::g0, dest);
        }

        Jump branch32(Condition cond, AbsoluteAddress left, RegisterID right)
        {
            load32(left.m_ptr, SparcRegisters::g2);
            return branch32(cond, SparcRegisters::g2, right);
        }

        Jump branch32(Condition cond, AbsoluteAddress left, TrustedImm32 right)
        {
            load32(left.m_ptr, SparcRegisters::g2);
            return branch32(cond, SparcRegisters::g2, right);
        }

        Call call()
        {
            m_assembler.rdpc(SparcRegisters::g2);
            m_assembler.add_imm(SparcRegisters::g2, 32, SparcRegisters::g2);
            m_assembler.stw_imm(SparcRegisters::g2, SparcRegisters::fp, -8);
            Call cl = Call(m_assembler.call(), Call::Linkable);
            m_assembler.lduw_imm(SparcRegisters::fp, -8, SparcRegisters::g2);
            m_assembler.jmpl_imm(SparcRegisters::g2, 0, SparcRegisters::g0);
            m_assembler.nop();
            return cl;
        }

        Call tailRecursiveCall()
        {
            return Call::fromTailJump(jump());
        }

        Call makeTailRecursiveCall(Jump oldJump)
        {
            return Call::fromTailJump(oldJump);
        }

        DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest)
        {
            DataLabelPtr dataLabel(this);
            Imm32 imm = Imm32(initialValue);
            m_assembler.move_nocheck(imm.m_value, dest);
            return dataLabel;
        }

        DataLabel32 moveWithPatch(TrustedImm32 initialValue, RegisterID dest)
        {
            DataLabel32 dataLabel(this);
            m_assembler.move_nocheck(initialValue.m_value, dest);
            return dataLabel;
        }

        Jump branchPtrWithPatch(Condition cond, RegisterID left, DataLabelPtr& dataLabel, ImmPtr initialRightValue = ImmPtr(0))
        {
            dataLabel = moveWithPatch(initialRightValue, SparcRegisters::g2);
            Jump jump = branch32(cond, left, SparcRegisters::g2);
            return jump;
        }

        Jump branchPtrWithPatch(Condition cond, Address left, DataLabelPtr& dataLabel, ImmPtr initialRightValue = ImmPtr(0))
        {
            load32(left, SparcRegisters::g2);
            dataLabel = moveWithPatch(initialRightValue, SparcRegisters::g3);
            Jump jump = branch32(cond, SparcRegisters::g3, SparcRegisters::g2);
            return jump;
        }

        DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address)
        {
            DataLabelPtr dataLabel = moveWithPatch(initialValue, SparcRegisters::g2);
            store32(SparcRegisters::g2, address);
            return dataLabel;
        }

        DataLabelPtr storePtrWithPatch(ImplicitAddress address)
        {
            return storePtrWithPatch(ImmPtr(0), address);
        }

        // Floating point operators
        bool supportsFloatingPoint() const
        {
            return true;
        }

        bool supportsFloatingPointTruncate() const
        {
            return true;
        }

        bool supportsFloatingPointSqrt() const
        {
            return true;
        }

        void moveDouble(FPRegisterID src, FPRegisterID dest)
        {
            m_assembler.fmovd_r(src, dest);
        }

        void loadFloat(BaseIndex address, FPRegisterID dest)
        {
            m_assembler.sll_imm(address.index, address.scale, SparcRegisters::g2);
            add32(Imm32(address.offset), SparcRegisters::g2);
            m_assembler.ldf_r(address.base, SparcRegisters::g2, dest);
            m_assembler.fstod_r(dest, dest);
        }

        void loadFloat(ImplicitAddress address, FPRegisterID dest)
        {
            if (m_assembler.isimm13(address.offset))
                m_assembler.ldf_imm(address.base, address.offset, dest);
            else {
                m_assembler.move_nocheck(address.offset, SparcRegisters::g3);
                m_assembler.ldf_r(address.base, SparcRegisters::g3, dest);
            }
            m_assembler.fstod_r(dest, dest);
        }

        void loadFloat(const void* address, FPRegisterID dest)
        {
            m_assembler.move_nocheck((int)address, SparcRegisters::g3);
            m_assembler.ldf_r(SparcRegisters::g3, SparcRegisters::g0, dest);
            m_assembler.fstod_r(dest, dest);
        }

        void loadDouble(BaseIndex address, FPRegisterID dest)
        {
            m_assembler.sll_imm(address.index, address.scale, SparcRegisters::g2);
            add32(Imm32(address.offset), SparcRegisters::g2);
            m_assembler.ldf_r(address.base, SparcRegisters::g2, dest);
            m_assembler.add_imm(SparcRegisters::g2, 4, SparcRegisters::g2);
            m_assembler.ldf_r(address.base, SparcRegisters::g2, dest + 1);
        }

        void loadDouble(ImplicitAddress address, FPRegisterID dest)
        {
            m_assembler.move_nocheck(address.offset, SparcRegisters::g3);
            m_assembler.ldf_r(address.base, SparcRegisters::g3, dest);
            m_assembler.add_imm(SparcRegisters::g3, 4, SparcRegisters::g3);
            m_assembler.ldf_r(address.base, SparcRegisters::g3, dest + 1);
        }

        DataLabelPtr loadDouble(const void* address, FPRegisterID dest)
        {
            DataLabelPtr dataLabel(this);
            m_assembler.move_nocheck((int)address, SparcRegisters::g3);
            m_assembler.ldf_imm(SparcRegisters::g3, 0, dest);
            m_assembler.ldf_imm(SparcRegisters::g3, 4, dest + 1);
            return dataLabel;
        }

        void storeFloat(FPRegisterID src, BaseIndex address)
        {
            m_assembler.sll_imm(address.index, address.scale, SparcRegisters::g2);
            add32(Imm32(address.offset), SparcRegisters::g2);
            m_assembler.stf_r(src, address.base, SparcRegisters::g2);
        }

        void storeFloat(FPRegisterID src, ImplicitAddress address)
        {
            if (m_assembler.isimm13(address.offset))
                m_assembler.stf_imm(src, address.base, address.offset);
            else {
                m_assembler.move_nocheck(address.offset, SparcRegisters::g3);
                m_assembler.stf_r(src, address.base, SparcRegisters::g3);
            }
        }

        void storeFloat(ImmDouble imm, Address address)
        {
            union {
                float f;
                uint32_t u32;
            } u;
            u.f = imm.u.d;
            store32(Imm32(u.u32), address);
        }

        void storeFloat(ImmDouble imm, BaseIndex address)
        {
            union {
                float f;
                uint32_t u32;
            } u;
            u.f = imm.u.d;
            store32(Imm32(u.u32), address);
        }

        void storeDouble(FPRegisterID src, BaseIndex address)
        {
            m_assembler.sll_imm(address.index, address.scale, SparcRegisters::g2);
            add32(Imm32(address.offset), SparcRegisters::g2);
            m_assembler.stf_r(src, address.base, SparcRegisters::g2);
            m_assembler.add_imm(SparcRegisters::g2, 4, SparcRegisters::g2);
            m_assembler.stf_r(src + 1, address.base, SparcRegisters::g2);
        }

        void storeDouble(FPRegisterID src, ImplicitAddress address)
        {
            if (m_assembler.isimm13(address.offset + 4)) {
                m_assembler.stf_imm(src, address.base, address.offset);
                m_assembler.stf_imm(src + 1, address.base, address.offset + 4);
            } else {
                m_assembler.move_nocheck(address.offset, SparcRegisters::g3);
                m_assembler.stf_r(src, address.base, SparcRegisters::g3);
                m_assembler.add_imm(SparcRegisters::g3, 4, SparcRegisters::g3);
                m_assembler.stf_r(src + 1, address.base, SparcRegisters::g3);
            }
        }

        void storeDouble(ImmDouble imm, Address address)
        {
            store32(Imm32(imm.u.s.msb), address);
            store32(Imm32(imm.u.s.lsb), Address(address.base, address.offset + 4));
        }

        void storeDouble(ImmDouble imm, BaseIndex address)
        {
            store32(Imm32(imm.u.s.msb), address);
            store32(Imm32(imm.u.s.lsb),
                    BaseIndex(address.base, address.index, address.scale, address.offset + 4));
        }

        void addDouble(FPRegisterID src, FPRegisterID dest)
        {
            m_assembler.faddd_r(src, dest, dest);
        }

        void addDouble(Address src, FPRegisterID dest)
        {
            loadDouble(src, SparcRegisters::f30);
            m_assembler.faddd_r(SparcRegisters::f30, dest, dest);
        }

        void divDouble(FPRegisterID src, FPRegisterID dest)
        {
            m_assembler.fdivd_r(dest, src, dest);
        }

        void divDouble(Address src, FPRegisterID dest)
        {
            loadDouble(src, SparcRegisters::f30);
            m_assembler.fdivd_r(dest, SparcRegisters::f30, dest);
        }

        void subDouble(FPRegisterID src, FPRegisterID dest)
        {
            m_assembler.fsubd_r(dest, src, dest);
        }

        void subDouble(Address src, FPRegisterID dest)
        {
            loadDouble(src, SparcRegisters::f30);
            m_assembler.fsubd_r(dest, SparcRegisters::f30, dest);
        }

        void mulDouble(FPRegisterID src, FPRegisterID dest)
        {
            m_assembler.fmuld_r(src, dest, dest);
        }

        void mulDouble(Address src, FPRegisterID dest)
        {
            loadDouble(src, SparcRegisters::f30);
            m_assembler.fmuld_r(SparcRegisters::f30, dest, dest);
        }

        void absDouble(FPRegisterID src, FPRegisterID dest)
        {
            m_assembler.fabsd_r(src, dest);
        }

        void sqrtDouble(FPRegisterID src, FPRegisterID dest)
        {
            m_assembler.fsqrtd_r(src, dest);
        }

        void negDouble(FPRegisterID src, FPRegisterID dest)
        {
            m_assembler.fnegd_r(src, dest);
        }

        void convertUInt32ToDouble(RegisterID src, FPRegisterID dest)
        {
            m_assembler.move_nocheck(0x43300000, SparcRegisters::g1);
            m_assembler.stw_imm(SparcRegisters::g1, SparcRegisters::sp, 0x60);
            m_assembler.stw_imm(src, SparcRegisters::sp, 0x64);
            m_assembler.ldf_imm(SparcRegisters::sp, 0x60, SparcRegisters::f30);
            m_assembler.ldf_imm(SparcRegisters::sp, 0x64, SparcRegisters::f31);
            m_assembler.stw_imm(SparcRegisters::g0, SparcRegisters::sp, 0x64);
            m_assembler.ldf_imm(SparcRegisters::sp, 0x60, dest);
            m_assembler.ldf_imm(SparcRegisters::sp, 0x64, dest + 1);
            m_assembler.fsubd_r(SparcRegisters::f30, dest, dest);
            m_assembler.fabss_r(dest, dest);
        }

        void convertInt32ToDouble(RegisterID src, FPRegisterID dest)
        {
            m_assembler.stw_imm(src, SparcRegisters::sp, 0x60);
            m_assembler.ldf_imm(SparcRegisters::sp, 0x60, dest);
            m_assembler.fitod_r(dest, dest);
        }

        void convertInt32ToDouble(Address address, FPRegisterID dest)
        {
            if (m_assembler.isimm13(address.offset))
                m_assembler.ldf_imm(address.base, address.offset, dest);
            else {
                m_assembler.move_nocheck(address.offset, SparcRegisters::g3);
                m_assembler.ldf_r(address.base, SparcRegisters::g3, dest);
            }
            m_assembler.fitod_r(dest, dest);
        }

        void convertInt32ToDouble(AbsoluteAddress src, FPRegisterID dest)
        {
            m_assembler.move_nocheck((int)src.m_ptr, SparcRegisters::g3);
            m_assembler.ldf_r(SparcRegisters::g3, SparcRegisters::g0, dest);
            m_assembler.fitod_r(dest, dest);
        }

        void fastLoadDouble(RegisterID lo, RegisterID hi, FPRegisterID fpReg)
        {
            m_assembler.stw_imm(lo, SparcRegisters::sp, 0x64);
            m_assembler.stw_imm(hi, SparcRegisters::sp, 0x60);
            m_assembler.ldf_imm(SparcRegisters::sp, 0x60, fpReg);
            m_assembler.ldf_imm(SparcRegisters::sp, 0x64, fpReg + 1);
        }

        void convertDoubleToFloat(FPRegisterID src, FPRegisterID dest)
        {
            m_assembler.fdtos_r(src, dest);
        }

        void breakDoubleTo32(FPRegisterID srcDest, RegisterID typeReg, RegisterID dataReg) {
            // We don't assume stack is aligned to 8.
            // Always using stf, ldf instead of stdf, lddf.
            m_assembler.stf_imm(srcDest, SparcRegisters::sp, 0x60);
            m_assembler.stf_imm(srcDest + 1, SparcRegisters::sp, 0x64);
            m_assembler.lduw_imm(SparcRegisters::sp, 0x60, typeReg);
            m_assembler.lduw_imm(SparcRegisters::sp, 0x64, dataReg);
        }

        Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right)
        {
            m_assembler.fcmpd_r(left, right);
            return Jump(m_assembler.fbranch(SparcDoubleCondition(cond)));
        }

        // Truncates 'src' to an integer, and places the resulting 'dest'.
        // If the result is not representable as a 32 bit value, branch.
        // May also branch for some values that are representable in 32 bits
        // (specifically, in this case, INT_MIN).
        Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest)
        {
            m_assembler.fdtoi_r(src, SparcRegisters::f30);
            m_assembler.stf_imm(SparcRegisters::f30, SparcRegisters::sp, 0x60);
            m_assembler.lduw_imm(SparcRegisters::sp, 0x60, dest);

            m_assembler.or_r(SparcRegisters::g0, SparcRegisters::g0, SparcRegisters::g2);
            m_assembler.move_nocheck(0x80000000, SparcRegisters::g3);
            m_assembler.subcc_r(SparcRegisters::g3, dest, SparcRegisters::g0);
            m_assembler.movcc_imm(1, SparcRegisters::g2, SparcCondition(Equal));
            m_assembler.move_nocheck(0x7fffffff, SparcRegisters::g3);
            m_assembler.subcc_r(SparcRegisters::g3, dest, SparcRegisters::g0);
            m_assembler.movcc_imm(1, SparcRegisters::g2, SparcCondition(Equal));

            return branch32(Equal, SparcRegisters::g2, Imm32(1));
        }

        // Convert 'src' to an integer, and places the resulting 'dest'.
        // If the result is not representable as a 32 bit value, branch.
        // May also branch for some values that are representable in 32 bits
        // (specifically, in this case, 0).
        void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp)
        {
            m_assembler.fdtoi_r(src, SparcRegisters::f30);
            m_assembler.stf_imm(SparcRegisters::f30, SparcRegisters::sp, 0x60);
            m_assembler.lduw_imm(SparcRegisters::sp, 0x60, dest);

            // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump.
            m_assembler.fitod_r(SparcRegisters::f30, SparcRegisters::f30);
            failureCases.append(branchDouble(DoubleNotEqualOrUnordered, src, SparcRegisters::f30));

            // If the result is zero, it might have been -0.0, and 0.0 equals to -0.0
            failureCases.append(branchTest32(Zero, dest));
        }

        void zeroDouble(FPRegisterID srcDest)
        {
            fastLoadDouble(SparcRegisters::g0, SparcRegisters::g0, srcDest);
        }

    protected:
        SparcAssembler::Condition SparcCondition(Condition cond)
        {
            return static_cast<SparcAssembler::Condition>(cond);
        }

        SparcAssembler::DoubleCondition SparcDoubleCondition(DoubleCondition cond)
        {
            return static_cast<SparcAssembler::DoubleCondition>(cond);
        }

    private:
        friend class LinkBuffer;
        friend class RepatchBuffer;

        static void linkCall(void* code, Call call, FunctionPtr function)
        {
            SparcAssembler::linkCall(code, call.m_jmp, function.value());
        }

        static void repatchCall(CodeLocationCall call, CodeLocationLabel destination)
        {
            SparcAssembler::relinkCall(call.dataLocation(), destination.executableAddress());
        }

        static void repatchCall(CodeLocationCall call, FunctionPtr destination)
        {
            SparcAssembler::relinkCall(call.dataLocation(), destination.executableAddress());
        }

    };

}


#endif // ENABLE(ASSEMBLER) && CPU(SPARC)

#endif /* assembler_assembler_MacroAssemblerSparc_h */
