<commit_before>/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is [Open Source Virtual Machine].
 *
 * The Initial Developer of the Original Code is
 * Adobe System Incorporated.
 * Portions created by the Initial Developer are Copyright (C) 2004-2007
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Adobe AS3 Team
 *   <NAME> <<EMAIL>>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include "nanojit.h"

#ifdef UNDER_CE
#include <cmnintrin.h>
extern "C" bool blx_lr_broken();
#endif

#if defined(FEATURE_NANOJIT) && defined(NANOJIT_ARM)

namespace nanojit
{

#ifdef NJ_VERBOSE
const char* regNames[] = {"r0","r1","r2","r3","r4","r5","r6","r7","r8","r9","r10","fp","ip","sp","lr","pc",
                          "d0","d1","d2","d3","d4","d5","d6","d7","s14"};
const char* condNames[] = {"eq","ne","cs","cc","mi","pl","vs","vc","hi","ls","ge","lt","gt","le",""/*al*/,"nv"};
const char* shiftNames[] = { "lsl", "lsl", "lsr", "lsr", "asr", "asr", "ror", "ror" };
#endif

const Register Assembler::argRegs[] = { R0, R1, R2, R3 };
const Register Assembler::retRegs[] = { R0, R1 };
const Register Assembler::savedRegs[] = { R4, R5, R6, R7, R8, R9, R10 };

// --------------------------------
// ARM-specific utility functions.
// --------------------------------

// NB the thumb2 check (i.e using > v6) is technically 
// not correct, but its close enough for now).
#ifdef JS_TRACER
#   define IS_ARM_ARCH_GT_V4()    (AvmCore::config.arch > 4)
#   define IS_ARM_ARCH_GT_V5()    (AvmCore::config.arch > 5)
#   define IS_ARM_ARCH_VFP()      (AvmCore::config.vfp)
#   define IS_ARM_ARCH_THUMB2()   (AvmCore::config.thumb2)
#else 
#   if NJ_ARM_ARCH > NJ_ARM_V4
#      define IS_ARM_ARCH_GT_V4() (1)
#   else
#      define IS_ARM_ARCH_GT_V4() (0)
#   endif
#   if NJ_ARM_ARCH > NJ_ARM_V5
#      define IS_ARM_ARCH_GT_V5() (1)
#   else
#      define IS_ARM_ARCH_GT_V5() (0)
#   endif
#   if NJ_ARM_ARCH > NJ_ARM_V6
#      define IS_ARM_ARCH_THUMB2() (1)
#   else
#      define IS_ARM_ARCH_THUMB2() (0)
#   endif
#   ifdef NJ_ARM_VFP
#      define IS_ARM_ARCH_VFP()   (1)
#   else
#      define IS_ARM_ARCH_VFP()   (0)
#   endif
#endif // JS_TRACER

#ifdef DEBUG
// Return true if enc is a valid Operand 2 encoding and thus can be used as-is
// in an ARM arithmetic operation that accepts such encoding.
//
// This utility does not know (or determine) the actual value that the encoded
// value represents, and thus cannot be used to ensure the correct operation of
// encOp2Imm, but it does ensure that the encoded value can be used to encode a
// valid ARM instruction. decOp2Imm can be used if you also need to check that
// a literal is correctly encoded (and thus that encOp2Imm is working
// correctly).
inline bool
Assembler::isOp2Imm(uint32_t enc)
{
    return ((enc & 0xfff) == enc);
}

// Decodes operand 2 immediate values (for debug output and assertions).
inline uint32_t
Assembler::decOp2Imm(uint32_t enc)
{
    NanoAssert(isOp2Imm(enc));

    uint32_t    imm8 = enc & 0xff;
    uint32_t    rot = 32 - ((enc >> 7) & 0x1e);

    return imm8 << (rot & 0x1f);
}
#endif

// Calculate the number of leading zeroes in data.
inline uint32_t
Assembler::CountLeadingZeroes(uint32_t data)
{
    uint32_t    leading_zeroes;

    // We can't do CLZ on anything earlier than ARMv5. Architectures as early
    // as that aren't supported, but assert that we aren't running on one
    // anyway.
    // If ARMv4 support is required in the future for some reason, we can do a
    // run-time check on config.arch and fall back to the C routine, but for
    // now we can avoid the cost of the check as we don't intend to support
    // ARMv4 anyway.
#ifdef TM_MERGE    
    NanoAssert(IS_ARM_ARCH_GT_V5());
#endif

#if defined(__ARMCC__)
    // ARMCC can do this with an intrinsic.
    leading_zeroes = __clz(data);

// current Android GCC compiler incorrectly refuses to compile 'clz' for armv5
// (even though this is a legal instruction there). Since we currently only compile for ARMv5
// for emulation, we don't care too much (but we DO care for ARMv6+ since those are "real"
// devices).
#elif defined(__GNUC__) && !(defined(ANDROID) && __ARM_ARCH__ <= 5) 
    // GCC can use inline assembler to insert a CLZ instruction.
    __asm (
        "   clz     %0, %1  \n"
        :   "=r"    (leading_zeroes)
        :   "r"     (data)
    );
#elif defined(WINCE)
    // WinCE can do this with an intrinsic.
    leading_zeroes = _CountLeadingZeros(data);
#else
    // Other platforms must fall back to a C routine. This won't be as
    // efficient as the CLZ instruction, but it is functional.
    uint32_t    try_shift;

    leading_zeroes = 0;

    // This loop does a bisection search rather than the obvious rotation loop.
    // This should be faster, though it will still be no match for CLZ.
    for (try_shift = 16; try_shift != 0; try_shift /= 2) {
        uint32_t    shift = leading_zeroes + try_shift;
        if (((data << shift) >> shift) == data) {
            leading_zeroes = shift;
        }
    }
#endif

    // Assert that the operation worked!
    NanoAssert(((0xffffffff >> leading_zeroes) & data) == data);

    return leading_zeroes;
}

// The ARM instruction set allows some flexibility to the second operand of
// most arithmetic operations. When operand 2 is an immediate value, it takes
// the form of an 8-bit value rotated by an even value in the range 0-30.
//
// Some values that can be encoded this scheme — such as 0xf000000f — are
// probably fairly rare in practice and require extra code to detect, so this
// function implements a fast CLZ-based heuristic to detect any value that can
// be encoded using just a shift, and not a full rotation. For example,
// 0xff000000 and 0x000000ff are both detected, but 0xf000000f is not.
//
// This function will return true to indicate that the encoding was successful,
// or false to indicate that the literal could not be encoded as an operand 2
// immediate. If successful, the encoded value will be written to *enc.
inline bool
Assembler::encOp2Imm(uint32_t literal, uint32_t * enc)
{
    // The number of leading zeroes in the literal. This is used to calculate
    // the rotation component of the encoding.
    uint32_t    leading_zeroes;

    // Components of the operand 2 encoding.
    int32_t    rot;
    uint32_t    imm8;

    // Check the literal to see if it is a simple 8-bit value. I suspect that
    // most literals are in fact small values, so doing this check early should
    // give a decent speed-up.
    if (literal < 256)
    {
        *enc = literal;
        return true;
    }

    // Determine the number of leading zeroes in the literal. This is used to
    // calculate the required rotation.
    leading_zeroes = CountLeadingZeroes(literal);

    // We've already done a check to see if the literal is an 8-bit value, so
    // leading_zeroes must be less than (and not equal to) (32-8)=24. However,
    // if it is greater than 24, this algorithm will break, so debug code
    // should use an assertion here to check that we have a value that we
    // expect.
    NanoAssert(leading_zeroes < 24);

    // Assuming that we have a field of no more than 8 bits for a valid
    // literal, we can calculate the required rotation by subtracting
    // leading_zeroes from (32-8):
    //
    // Example:
    //      0: Known to be zero.
    //      1: Known to be one.
    //      X: Either zero or one.
    //      .: Zero in a valid operand 2 literal.
    //
    //  Literal:     [ 1XXXXXXX ........ ........ ........ ]
    //  leading_zeroes = 0
    //  Therefore rot (left) = 24.
    //  Encoded 8-bit literal:                  [ 1XXXXXXX ]
    //
    //  Literal:     [ ........ ..1XXXXX XX...... ........ ]
    //  leading_zeroes = 10
    //  Therefore rot (left) = 14.
    //  Encoded 8-bit literal:                  [ 1XXXXXXX ]
    //
    // Note, however, that we can only encode even shifts, and so
    // "rot=24-leading_zeroes" is not sufficient by itself. By ignoring
    // zero-bits in odd bit positions, we can ensure that we get a valid
    // encoding.
    //
    // Example:
    //  Literal:     [ 01XXXXXX ........ ........ ........ ]
    //  leading_zeroes = 1
    //  Therefore rot (left) = round_up(23) = 24.
    //  Encoded 8-bit literal:                  [ 01XXXXXX ]
    rot = 24 - (leading_zeroes & ~1);

    // The imm8 component of the operand 2 encoding can be calculated from the
    // rot value.
    imm8 = literal >> rot;

    // The validity of the literal can be checked by reversing the
    // calculation. It is much easier to decode the immediate than it is to
    // encode it!
    if (literal != (imm8 << rot)) {
        // The encoding is not valid, so report the failure. Calling code
        // should use some other method of loading the value (such as LDR).
        return false;
    }

    // The operand is valid, so encode it.
    // Note that the ARM encoding is actually described by a rotate to the
    // _right_, so rot must be negated here. Calculating a left shift (rather
    // than calculating a right rotation) simplifies the above code.
    *enc = ((-rot << 7) & 0xf00) | imm8;

    // Assert that the operand was properly encoded.
    NanoAssert(decOp2Imm(*enc) == literal);

    return true;
}

// Encode "rd = rn + imm" using an appropriate instruction sequence.
// Set stat to 1 to update the status flags. Otherwise, set it to 0 or omit it.
// (The declaration in NativeARM.h defines the default value of stat as 0.)
//
// It is not valid to call this function if:
//   (rd == IP) AND (rn == IP) AND !encOp2Imm(imm) AND !encOp2Imm(-imm)
// Where: if (encOp2Imm(imm)), imm can be encoded as an ARM operand 2 using the
// encOp2Imm method.
void
Assembler::asm_add_imm(Register rd, Register rn, int32_t imm, int stat /* =0 */)
{
    // Operand 2 encoding of the immediate.
    uint32_t    op2imm;

    NanoAssert(IsGpReg(rd));
    NanoAssert(IsGpReg(rn));
    NanoAssert((stat & 1) == stat);

    // Try to encode the value directly as an operand 2 immediate value, then
    // fall back to loading the value into a register.
    if (encOp2Imm(imm, &op2imm)) {
        ADDis(rd, rn, op2imm, stat);
    } else if (encOp2Imm(-imm, &op2imm)) {
        // We could not encode the value for ADD, so try to encode it for SUB.
        // Note that this is valid even if stat is set, _unless_ imm is 0, but
        // that case is caught above.
        NanoAssert(imm != 0);
        SUBis(rd, rn, op2imm, stat);
    } else {
        // We couldn't encode the value directly, so use an intermediate
        // register to encode the value. We will use IP to do this unless rn is
        // IP; in that case we can reuse rd. This allows every case other than
        // "ADD IP, IP, =#imm".
        Register    rm = (rn == IP) ? (rd) : (IP);
        NanoAssert(rn != rm);

        ADDs(rd, rn, rm, stat);
        asm_ld_imm(rm, imm);
    }
}

// Encode "rd = rn - imm" using an appropriate instruction sequence.
// Set stat to 1 to update the status flags. Otherwise, set it to 0 or omit it.
// (The declaration in NativeARM.h defines the default value of stat as 0.)
//
// It is not valid to call this function if:
//   (rd == IP) AND (rn == IP) AND !encOp2Imm(imm) AND !encOp2Imm(-imm)
// Where: if (encOp2Imm(imm)), imm can be encoded as an ARM operand 2 using the
// encOp2Imm method.
void
Assembler::asm_sub_imm(Register rd, Register rn, int32_t imm, int stat /* =0 */)
{
    // Operand 2 encoding of the immediate.
    uint32_t    op2imm;

    NanoAssert(IsGpReg(rd));
    NanoAssert(IsGpReg(rn));
    NanoAssert((stat & 1) == stat);

    // Try to encode the value directly as an operand 2 immediate value, then
    // fall back to loading the value into a register.
    if (encOp2Imm(imm, &op2imm)) {
        SUBis(rd, rn, op2imm, stat);
    } else if (encOp2Imm(-imm, &op2imm)) {
        // We could not encode the value for SUB, so try to encode it for ADD.
        // Note that this is valid even if stat is set, _unless_ imm is 0, but
        // that case is caught above.
        NanoAssert(imm != 0);
        ADDis(rd, rn, op2imm, stat);
    } else {
        // We couldn't encode the value directly, so use an intermediate
        // register to encode the value. We will use IP to do this unless rn is
        // IP; in that case we can reuse rd. This allows every case other than
        // "SUB IP, IP, =#imm".
        Register    rm = (rn == IP) ? (rd) : (IP);
        NanoAssert(rn != rm);

        SUBs(rd, rn, rm, stat);
        asm_ld_imm(rm, imm);
    }
}

// Encode "rd = rn & imm" using an appropriate instruction sequence.
// Set stat to 1 to update the status flags. Otherwise, set it to 0 or omit it.
// (The declaration in NativeARM.h defines the default value of stat as 0.)
//
// It is not valid to call this function if:
//   (rd == IP) AND (rn == IP) AND !encOp2Imm(imm) AND !encOp2Imm(~imm)
// Where: if (encOp2Imm(imm)), imm can be encoded as an ARM operand 2 using the
// encOp2Imm method.
void
Assembler::asm_and_imm(Register rd, Register rn, int32_t imm, int stat /* =0 */)
{
    // Operand 2 encoding of the immediate.
    uint32_t    op2imm;

    NanoAssert(IsGpReg(rd));
    NanoAssert(IsGpReg(rn));
    NanoAssert((stat & 1) == stat);

    // Try to encode the value directly as an operand 2 immediate value, then
    // fall back to loading the value into a register.
    if (encOp2Imm(imm, &op2imm)) {
        ANDis(rd, rn, op2imm, stat);
    } else if (encOp2Imm(~imm, &op2imm)) {
        // Use BIC with the inverted immediate.
        BICis(rd, rn, op2imm, stat);
    } else {
        // We couldn't encode the value directly, so use an intermediate
        // register to encode the value. We will use IP to do this unless rn is
        // IP; in that case we can reuse rd. This allows every case other than
        // "AND IP, IP, =#imm".
        Register    rm = (rn == IP) ? (rd) : (IP);
        NanoAssert(rn != rm);

        ANDs(rd, rn, rm, stat);
        asm_ld_imm(rm, imm);
    }
}

// Encode "rd = rn | imm" using an appropriate instruction sequence.
// Set stat to 1 to update the status flags. Otherwise, set it to 0 or omit it.
// (The declaration in NativeARM.h defines the default value of stat as 0.)
//
// It is not valid to call this function if:
//   (rd == IP) AND (rn == IP) AND !encOp2Imm(imm)
// Where: if (encOp2Imm(imm)), imm can be encoded as an ARM operand 2 using the
// encOp2Imm method.
void
Assembler::asm_orr_imm(Register rd, Register rn, int32_t imm, int stat /* =0 */)
{
    // Operand 2 encoding of the immediate.
    uint32_t    op2imm;

    NanoAssert(IsGpReg(rd));
    NanoAssert(IsGpReg(rn));
    NanoAssert((stat & 1) == stat);

    // Try to encode the value directly as an operand 2 immediate value, then
    // fall back to loading the value into a register.
    if (encOp2Imm(imm, &op2imm)) {
        ORRis(rd, rn, op2imm, stat);
    } else {
        // We couldn't encode the value directly, so use an intermediate
        // register to encode the value. We will use IP to do this unless rn is
        // IP; in that case we can reuse rd. This allows every case other than
        // "ORR IP, IP, =#imm".
        Register    rm = (rn == IP) ? (rd) : (IP);
        NanoAssert(rn != rm);

        ORRs(rd, rn, rm, stat);
        asm_ld_imm(rm, imm);
    }
}

// Encode "rd = rn ^ imm" using an appropriate instruction sequence.
// Set stat to 1 to update the status flags. Otherwise, set it to 0 or omit it.
// (The declaration in NativeARM.h defines the default value of stat as 0.)
//
// It is not valid to call this function if:
//   (rd == IP) AND (rn == IP) AND !encOp2Imm(imm)
// Where: if (encOp2Imm(imm)), imm can be encoded as an ARM operand 2 using the
// encOp2Imm method.
void
Assembler::asm_eor_imm(Register rd, Register rn, int32_t imm, int stat /* =0 */)
{
    // Operand 2 encoding of the immediate.
    uint32_t    op2imm;

    NanoAssert(IsGpReg(rd));
    NanoAssert(IsGpReg(rn));
    NanoAssert((stat & 1) == stat);

    // Try to encode the value directly as an operand 2 immediate value, then
    // fall back to loading the value into a register.
    if (encOp2Imm(imm, &op2imm)) {
        EORis(rd, rn, op2imm, stat);
    } else {
        // We couldn't encoder the value directly, so use an intermediate
        // register to encode the value. We will use IP to do this unless rn is
        // IP; in that case we can reuse rd. This allows every case other than
        // "EOR IP, IP, =#imm".
        Register    rm = (rn == IP) ? (rd) : (IP);
        NanoAssert(rn != rm);

        EORs(rd, rn, rm, stat);
        asm_ld_imm(rm, imm);
    }
}

// --------------------------------
// Assembler functions.
// --------------------------------

void
Assembler::nInit(AvmCore*)
{
#ifdef UNDER_CE
    blx_lr_bug = blx_lr_broken();
#else
    blx_lr_bug = 0;
#endif
}

void Assembler::nBeginAssembly()
{
}

NIns*
Assembler::genPrologue()
{
    /**
     * Prologue
     */

    // NJ_RESV_OFFSET is space at the top of the stack for us
    // to use for parameter passing (8 bytes at the moment)
    uint32_t stackNeeded = max_out_args + STACK_GRANULARITY * _activation.tos;
    uint32_t savingCount = NumSavedRegs + 2;
    uint32_t savingMask = SavedRegs | rmask(FP) | rmask(LR);

    // so for alignment purposes we've pushed  return addr, fp, and savingCount registers
    uint32_t stackPushed = STACK_GRANULARITY * savingCount;
    uint32_t aligned = alignUp(stackNeeded + stackPushed, NJ_ALIGN_STACK);
    int32_t amt = aligned - stackPushed;

    // Make room on stack for what we are doing
    if (amt)
        asm_sub_imm(SP, SP, amt);

    verbose_only( asm_output("## %p:",(void*)_nIns); )
    verbose_only( asm_output("## patch entry"); )
    NIns *patchEntry = _nIns;

    MOV(FP, SP);
    PUSH_mask(savingMask);
    return patchEntry;
}

void
Assembler::nFragExit(LInsp guard)
{
    (void)guard;
#if NJ_MERGE
    SideExit *  exit = guard->record()->exit;
    Fragment *  frag = exit->target;
    bool        target_is_known = frag && frag->fragEntry;

    if (target_is_known) {
        // The target exists so we can simply emit a branch to its location.
        JMP_far(frag->fragEntry);
    } else {
        // The target doesn't exit yet, so emit a jump to the epilogue. If the
        // target is created later on, the jump will be patched.

        GuardRecord *gr = guard->record();

        if (!_epilogue)
            _epilogue = genEpilogue();

        // Jump to the epilogue. This may get patched later, but JMP_far always
        // emits two instructions even when only one is required, so patching
        // will work correctly.
        JMP_far(_epilogue);

        asm_ld_imm(R0, int(gr));

        // Set the jmp pointer to the start of the sequence so that patched
        // branches can skip the LDi sequence.
        gr->jmp = _nIns;
    }

#ifdef NJ_VERBOSE
    if (config.show_stats) {
        // load R1 with Fragment *fromFrag, target fragment
        // will make use of this when calling fragenter().
        int fromfrag = int((Fragment*)_thisfrag);
        asm_ld_imm(argRegs[1], fromfrag);
    }
#endif

    // Pop the stack frame.
    MOV(SP, FP);
#endif // TM_MERGE
}

NIns*
Assembler::genEpilogue()
{
    // On v5 and above, this generates the following sequence:
    //   MOV SP,FP
    //   LDMFD SP!,{SavedRegs,FP,PC}
    // which will always handle interworking correctly.
    //
    // On v4T, LR is loaded instead of PC, and then a BX LR is executed.
    //
    // On v4 (and below), interworking is not required, and the v5+ code
    // sequence is used.
#if NJ_ARM_ARCH != NJ_ARM_V4T
    RegisterMask savingMask = SavedRegs | rmask(FP) | rmask(PC);
#else
    RegisterMask savingMask = SavedRegs | rmask(FP) | rmask(LR);
    BX(LR);
#endif
    POP_mask(savingMask);
    MOV(SP,FP);
    return _nIns;
}

/*
 * asm_arg will encode the specified argument according to the current ABI, and
 * will update r and stkd as appropriate so that the next argument can be
 * encoded.
 *
 * Linux has used ARM's EABI for some time. Windows CE uses the legacy ABI.
 *
 * Under EABI:
 * - doubles are 64-bit aligned both in registers and on the stack.
 *   If the next available argument register is R1, it is skipped
 *   and the double is placed in R2:R3.  If R0:R1 or R2:R3 are not
 *   available, the double is placed on the stack, 64-bit aligned.
 * - 32-bit arguments are placed in registers and 32-bit aligned
 *   on the stack.
 *
 * Under legacy ABI:
 * - doubles are placed in subsequent arg registers; if the next
 *   available register is r3, the low order word goes into r3
 *   and the high order goes on the stack.
 * - 32-bit arguments are placed in the next available arg register,
 * - both doubles and 32-bit arguments are placed on stack with 32-bit
 *   alignment.
 */
void 
Assembler::asm_arg(ArgSize sz, LInsp arg, Register& r, int& stkd)
{
    // The stack pointer must always be at least aligned to 4 bytes.
    NanoAssert((stkd & 3) == 0);

    if (sz == ARGSIZE_F) {
        // This task is fairly complex and so is delegated to asm_arg_64.
        asm_arg_64(arg, r, stkd);
    } else if (sz & ARGSIZE_MASK_INT) {
        // pre-assign registers R0-R3 for arguments (if they fit)
        if (r < R4) {
            asm_regarg(sz, arg, r);
            r = nextreg(r);
        } else {
            asm_stkarg(arg, stkd);
            stkd += 4;
        }
    } else {
        NanoAssert(sz == ARGSIZE_Q);
        // shouldn't have 64 bit int params on ARM
        NanoAssert(false);
    }
}

// Encode a 64-bit floating-point argument using the appropriate ABI.
// This function operates in the same way as asm_arg, except that it will only
// handle arguments where (ArgSize)sz == ARGSIZE_F.
void
Assembler::asm_arg_64(LInsp arg, Register& r, int& stkd)
{
    // The stack pointer must always be at least aligned to 4 bytes.
    NanoAssert((stkd & 3) == 0);
    // The only use for this function when we are using soft floating-point
    // is for LIR_qjoin.
    NanoAssert(IS_ARM_ARCH_VFP() || arg->isop(LIR_qjoin));

    Register    fp_reg = UnknownReg;

    if (IS_ARM_ARCH_VFP()) {
        fp_reg = findRegFor(arg, FpRegs);
        NanoAssert(fp_reg != UnknownReg);
    }

    if (r < R3) {
        Register    ra = r;
        Register    rb = nextreg(r);
        r = nextreg(rb);
            
#ifdef NJ_ARM_EABI
        // EABI requires that 64-bit arguments are aligned on even-numbered
        // registers, as R0:R1 or R2:R3.
        NanoAssert( ((ra == R0) && (rb == R1)) || ((ra == R2) && (rb == R3)) );
#endif

        // Put the argument in ra and rb. If the argument is in a VFP register,
        // use FMRRD to move it to ra and rb. Otherwise, let asm_regarg deal
        // with the argument as if it were two 32-bit arguments.
        if (IS_ARM_ARCH_VFP()) {
            FMRRD(ra, rb, fp_reg);
        } else {
            asm_regarg(ARGSIZE_LO, arg->oprnd1(), ra);
            asm_regarg(ARGSIZE_LO, arg->oprnd2(), rb);
        }
        
#ifndef NJ_ARM_EABI
    } else if (r == R3) {
        // We only have one register left, but the legacy ABI requires that we
        // put 32 bits of the argument in the register (R3) and the remaining
        // 32 bits on the stack.
        Register    ra = r;
        r = nextreg(r);

        // This really just checks that nextreg() works properly, as we know
        // that r was previously R3.
        NanoAssert(r == R4);

        // We're splitting the argument between registers and the stack.  This
        // must be the first time that the stack is used, so stkd must be at 0.
        NanoAssert(stkd == 0);

        if (IS_ARM_ARCH_VFP()) {
            // TODO: We could optimize the this to store directly from
            // the VFP register to memory using "FMRRD ra, fp_reg[31:0]" and
            // "STR fp_reg[63:32], [SP, #stkd]".

            // Load from the floating-point register as usual, but use IP
            // as a swap register.
            STR(IP, SP, 0);
            stkd += 4;
            FMRRD(ra, IP, fp_reg);
        } else {
            // Without VFP, we can simply use asm_regarg and asm_stkarg to
            // encode the two 32-bit words as we don't need to load from a VFP
            // register.
            asm_regarg(ARGSIZE_LO, arg->oprnd1(), ra);
            asm_stkarg(arg->oprnd2(), 0);
            stkd += 4;
        }
#endif
    } else {
        // The argument won't fit in registers, so pass on to asm_stkarg.
#ifdef NJ_ARM_EABI
        // EABI requires that 64-bit arguments are 64-bit aligned.
        if ((stkd & 7) != 0) {
            // stkd will always be aligned to at least 4 bytes; this was
            // asserted on entry to this function.
            stkd += 4;
        }
#endif
        asm_stkarg(arg, stkd);
        stkd += 8;
    }
}

void 
Assembler::asm_regarg(ArgSize sz, LInsp p, Register r)
{
    NanoAssert(r != UnknownReg);
    if (sz & ARGSIZE_MASK_INT)
    {
        // arg goes in specific register
        if (p->isconst()) {
            asm_ld_imm(r, p->imm32());
        } else {
            Reservation* rA = getresv(p);
            if (rA) {
                if (rA->reg == UnknownReg) {
                    // load it into the arg reg
                    int d = findMemFor(p);
                    if (p->isop(LIR_alloc)) {
                        asm_add_imm(r, FP, d, 0);
                    } else {
                        LDR(r, FP, d);
                    }
                } else {
                    // it must be in a saved reg
                    MOV(r, rA->reg);
                }
            }
            else {
                // this is the last use, so fine to assign it
                // to the scratch reg, it's dead after this point.
                findSpecificRegFor(p, r);
            }
        }
    }
    else if (sz == ARGSIZE_Q) {
        // 64 bit integer argument - should never happen on ARM
        NanoAssert(false);
    }
    else
    {
        NanoAssert(sz == ARGSIZE_F);
        // fpu argument in register - should never happen since FPU
        // args are converted to two 32-bit ints on ARM
        NanoAssert(false);
    }
}

void
Assembler::asm_stkarg(LInsp arg, int stkd)
{
    Reservation* argRes = getresv(arg);
    bool isQuad = arg->isQuad();

    if (argRes && (argRes->reg != UnknownReg)) {
        // The argument resides somewhere in registers, so we simply need to
        // push it onto the stack.    
        if (!IS_ARM_ARCH_VFP() || !isQuad) {
            NanoAssert(IsGpReg(argRes->reg));
                    
            STR(argRes->reg, SP, stkd);
        } else {
            // According to the comments in asm_arg_64, LIR_qjoin
            // can have a 64-bit argument even if VFP is disabled. However,
            // asm_arg_64 will split the argument and issue two 32-bit
            // arguments to asm_stkarg so we can ignore that case here and
            // assert that we will never get 64-bit arguments unless VFP is
            // available.
            NanoAssert(IS_ARM_ARCH_VFP());
            NanoAssert(IsFpReg(argRes->reg));

#ifdef NJ_ARM_EABI
            // EABI requires that 64-bit arguments are 64-bit aligned.
            NanoAssert((stkd & 7) == 0);
#endif

            FSTD(argRes->reg, SP, stkd);
        }
    } else {
        // The argument does not reside in registers, so we need to get some
        // memory for it and then copy it onto the stack.
        int d = findMemFor(arg);
        if (!isQuad) {
            STR(IP, SP, stkd);
            if (arg->isop(LIR_alloc)) {
                asm_add_imm(IP, FP, d);
            } else {
                LDR(IP, FP, d);
            }
        } else {
#ifdef NJ_ARM_EABI
            // EABI requires that 64-bit arguments are 64-bit aligned.
            NanoAssert((stkd & 7) == 0);
#endif

            STR_preindex(IP, SP, stkd+4);
            LDR(IP, FP, d+4);
            STR_preindex(IP, SP, stkd);
            LDR(IP, FP, d);
        }
    }
}

void
Assembler::asm_call(LInsp ins)
{
    const CallInfo* call = ins->callInfo();
    bool needToLoadAddr = false;
    ArgSize sizes[MAXARGS];
    uint32_t argc = call->get_sizes(sizes);
    bool indirect = call->isIndirect();

#ifdef NJ_SOFTFLOAT
    NanoAssert(ins->isop(LIR_icall));
#endif

#ifdef NJ_ARM_VFP
    // Sort out where a VFP result goes. See comments in
    // asm_prep_fcall() for more details as to why this is
    // necessary here for floating point calls, but not for
    // integer calls.
    Reservation *callRes = getresv(ins);
    if (callRes) { // If callRes==NULL, the result is discarded
        Register rr = callRes->reg;
        if (rr == UnknownReg) {
            // If the result doesn't have a register allocated, then
            // store R0,R1 directly to the stack slot
        int d = disp(callRes);
            NanoAssert(d != 0);
            freeRsrcOf(ins, false);
            STR(R1, FP, d+4);
            STR(R0, FP, d+0);
        } else {
            // If the result does have a register allocated, move it
            // into the appropriate register, and allow prepResultReg()
            // to work out if any other action (e.g. storing to stack)
            // is necessary.
            NanoAssert(IsFpReg(rr));
            prepResultReg(ins, rmask(rr));
            FMDRR(rr,R0,R1);
        }
    }
#endif

    if (!indirect) {
        verbose_only(if (_logc->lcbits & LC_Assembly)
            outputf("        %p:", _nIns);
        )
        // Direct call: on v5 and above (where the calling sequence doesn't
        // corrupt LR until the actual branch instruction), we can avoid an
        // interlock in the "long" branch sequence by manually loading the
        // target address into LR ourselves before setting up the parameters
        // in other registers.
#if NJ_ARM_ARCH >= NJ_ARM_V5
        needToLoadAddr = BL_noload((NIns*)call->_address, LR);
#else
        BL((NIns*)call->_address);
#endif
    } else {
        // Indirect call: we assign the address arg to LR since it's not
        // used for regular arguments, and is otherwise scratch since it's
        // clobberred by the call. On v4/v4T, where we have to manually do
        // the equivalent of a BLX, move LR into IP before corrupting LR
        // with the return address.
#if NJ_ARM_ARCH >= NJ_ARM_V5
        if (blx_lr_bug) {
            // workaround for msft device emulator bug (blx lr emulated as no-op)
            underrunProtect(8);
            BLX(IP);
            MOV(IP,LR);
        } else {
            BLX(LR);
        }
#else
        underrunProtect(8); // keep next instr (BX or MOV PC,IP) and MOV LR,PC in the same page
    #if NJ_ARM_ARCH == NJ_ARM_V4T
        BX(IP);
    #else
        MOV(PC,IP);
    #endif
        MOV(LR,PC);
        MOV(IP,LR);
#endif
        asm_regarg(ARGSIZE_LO, ins->arg(--argc), LR);
    }

    Register r = R0;
    int stkd = 0;

    // XXX TODO we should go through the args and figure out how much
    // stack space we'll need, allocate it up front, and then do
    // SP-relative stores using stkd instead of doing STR_preindex for
    // every stack write like we currently do in asm_arg.

    for(uint32_t i = 0; i < argc; i++) {
        uint32_t j = argc - i - 1;
        ArgSize sz = sizes[j];
        LInsp arg = ins->arg(j);

#ifdef TM_MERGE
        NanoAssert(r < R4 || r == UnknownReg);
#endif

#ifdef NJ_ARM_EABI
        // arm eabi puts doubles only in R0:1 or R2:3, and 64bit aligned on the stack.
        if (sz == ARGSIZE_F) {
            if (r == R1)
                r = R2;
            else if (r == R3)
                r = nextreg(r);
        }
#endif
        asm_arg(sz, arg, r, stkd);
    }
    if (stkd > max_out_args)
        max_out_args = stkd;

    if (needToLoadAddr)
        LDi(LR, (int32_t)call->_address);
}

Register
Assembler::nRegisterAllocFromSet(int set)
{
    NanoAssert(set != 0);

    // The CountLeadingZeroes function will use the CLZ instruction where
    // available. In other cases, it will fall back to a (slower) C
    // implementation.
    Register r = (Register)(31-CountLeadingZeroes(set));
    _allocator.free &= ~rmask(r);

    NanoAssert(IsGpReg(r) || IsFpReg(r));
    NanoAssert((rmask(r) & set) == rmask(r));

    return r;
}

void<commit_msg>Bug 506138 - Merge tracemonkey NativeARM changes into tamarin-redux (asm_call)<commit_after>/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is [Open Source Virtual Machine].
 *
 * The Initial Developer of the Original Code is
 * Adobe System Incorporated.
 * Portions created by the Initial Developer are Copyright (C) 2004-2007
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Adobe AS3 Team
 *   <NAME> <<EMAIL>>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include "nanojit.h"

#ifdef UNDER_CE
#include <cmnintrin.h>
extern "C" bool blx_lr_broken();
#endif

#if defined(FEATURE_NANOJIT) && defined(NANOJIT_ARM)

namespace nanojit
{

#ifdef NJ_VERBOSE
const char* regNames[] = {"r0","r1","r2","r3","r4","r5","r6","r7","r8","r9","r10","fp","ip","sp","lr","pc",
                          "d0","d1","d2","d3","d4","d5","d6","d7","s14"};
const char* condNames[] = {"eq","ne","cs","cc","mi","pl","vs","vc","hi","ls","ge","lt","gt","le",""/*al*/,"nv"};
const char* shiftNames[] = { "lsl", "lsl", "lsr", "lsr", "asr", "asr", "ror", "ror" };
#endif

const Register Assembler::argRegs[] = { R0, R1, R2, R3 };
const Register Assembler::retRegs[] = { R0, R1 };
const Register Assembler::savedRegs[] = { R4, R5, R6, R7, R8, R9, R10 };

// --------------------------------
// ARM-specific utility functions.
// --------------------------------

// NB the thumb2 check (i.e using > v6) is technically 
// not correct, but its close enough for now).
#ifdef JS_TRACER
#   define IS_ARM_ARCH_GT_V4()    (AvmCore::config.arch > 4)
#   define IS_ARM_ARCH_GT_V5()    (AvmCore::config.arch > 5)
#   define IS_ARM_ARCH_VFP()      (AvmCore::config.vfp)
#   define IS_ARM_ARCH_THUMB2()   (AvmCore::config.thumb2)
#else 
#   if NJ_ARM_ARCH > NJ_ARM_V4
#      define IS_ARM_ARCH_GT_V4() (1)
#   else
#      define IS_ARM_ARCH_GT_V4() (0)
#   endif
#   if NJ_ARM_ARCH > NJ_ARM_V5
#      define IS_ARM_ARCH_GT_V5() (1)
#   else
#      define IS_ARM_ARCH_GT_V5() (0)
#   endif
#   if NJ_ARM_ARCH > NJ_ARM_V6
#      define IS_ARM_ARCH_THUMB2() (1)
#   else
#      define IS_ARM_ARCH_THUMB2() (0)
#   endif
#   ifdef NJ_ARM_VFP
#      define IS_ARM_ARCH_VFP()   (1)
#   else
#      define IS_ARM_ARCH_VFP()   (0)
#   endif
#endif // JS_TRACER

#ifdef DEBUG
// Return true if enc is a valid Operand 2 encoding and thus can be used as-is
// in an ARM arithmetic operation that accepts such encoding.
//
// This utility does not know (or determine) the actual value that the encoded
// value represents, and thus cannot be used to ensure the correct operation of
// encOp2Imm, but it does ensure that the encoded value can be used to encode a
// valid ARM instruction. decOp2Imm can be used if you also need to check that
// a literal is correctly encoded (and thus that encOp2Imm is working
// correctly).
inline bool
Assembler::isOp2Imm(uint32_t enc)
{
    return ((enc & 0xfff) == enc);
}

// Decodes operand 2 immediate values (for debug output and assertions).
inline uint32_t
Assembler::decOp2Imm(uint32_t enc)
{
    NanoAssert(isOp2Imm(enc));

    uint32_t    imm8 = enc & 0xff;
    uint32_t    rot = 32 - ((enc >> 7) & 0x1e);

    return imm8 << (rot & 0x1f);
}
#endif

// Calculate the number of leading zeroes in data.
inline uint32_t
Assembler::CountLeadingZeroes(uint32_t data)
{
    uint32_t    leading_zeroes;

    // We can't do CLZ on anything earlier than ARMv5. Architectures as early
    // as that aren't supported, but assert that we aren't running on one
    // anyway.
    // If ARMv4 support is required in the future for some reason, we can do a
    // run-time check on config.arch and fall back to the C routine, but for
    // now we can avoid the cost of the check as we don't intend to support
    // ARMv4 anyway.
#ifdef TM_MERGE    
    NanoAssert(IS_ARM_ARCH_GT_V5());
#endif

#if defined(__ARMCC__)
    // ARMCC can do this with an intrinsic.
    leading_zeroes = __clz(data);

// current Android GCC compiler incorrectly refuses to compile 'clz' for armv5
// (even though this is a legal instruction there). Since we currently only compile for ARMv5
// for emulation, we don't care too much (but we DO care for ARMv6+ since those are "real"
// devices).
#elif defined(__GNUC__) && !(defined(ANDROID) && __ARM_ARCH__ <= 5) 
    // GCC can use inline assembler to insert a CLZ instruction.
    __asm (
        "   clz     %0, %1  \n"
        :   "=r"    (leading_zeroes)
        :   "r"     (data)
    );
#elif defined(WINCE)
    // WinCE can do this with an intrinsic.
    leading_zeroes = _CountLeadingZeros(data);
#else
    // Other platforms must fall back to a C routine. This won't be as
    // efficient as the CLZ instruction, but it is functional.
    uint32_t    try_shift;

    leading_zeroes = 0;

    // This loop does a bisection search rather than the obvious rotation loop.
    // This should be faster, though it will still be no match for CLZ.
    for (try_shift = 16; try_shift != 0; try_shift /= 2) {
        uint32_t    shift = leading_zeroes + try_shift;
        if (((data << shift) >> shift) == data) {
            leading_zeroes = shift;
        }
    }
#endif

    // Assert that the operation worked!
    NanoAssert(((0xffffffff >> leading_zeroes) & data) == data);

    return leading_zeroes;
}

// The ARM instruction set allows some flexibility to the second operand of
// most arithmetic operations. When operand 2 is an immediate value, it takes
// the form of an 8-bit value rotated by an even value in the range 0-30.
//
// Some values that can be encoded this scheme — such as 0xf000000f — are
// probably fairly rare in practice and require extra code to detect, so this
// function implements a fast CLZ-based heuristic to detect any value that can
// be encoded using just a shift, and not a full rotation. For example,
// 0xff000000 and 0x000000ff are both detected, but 0xf000000f is not.
//
// This function will return true to indicate that the encoding was successful,
// or false to indicate that the literal could not be encoded as an operand 2
// immediate. If successful, the encoded value will be written to *enc.
inline bool
Assembler::encOp2Imm(uint32_t literal, uint32_t * enc)
{
    // The number of leading zeroes in the literal. This is used to calculate
    // the rotation component of the encoding.
    uint32_t    leading_zeroes;

    // Components of the operand 2 encoding.
    int32_t    rot;
    uint32_t    imm8;

    // Check the literal to see if it is a simple 8-bit value. I suspect that
    // most literals are in fact small values, so doing this check early should
    // give a decent speed-up.
    if (literal < 256)
    {
        *enc = literal;
        return true;
    }

    // Determine the number of leading zeroes in the literal. This is used to
    // calculate the required rotation.
    leading_zeroes = CountLeadingZeroes(literal);

    // We've already done a check to see if the literal is an 8-bit value, so
    // leading_zeroes must be less than (and not equal to) (32-8)=24. However,
    // if it is greater than 24, this algorithm will break, so debug code
    // should use an assertion here to check that we have a value that we
    // expect.
    NanoAssert(leading_zeroes < 24);

    // Assuming that we have a field of no more than 8 bits for a valid
    // literal, we can calculate the required rotation by subtracting
    // leading_zeroes from (32-8):
    //
    // Example:
    //      0: Known to be zero.
    //      1: Known to be one.
    //      X: Either zero or one.
    //      .: Zero in a valid operand 2 literal.
    //
    //  Literal:     [ 1XXXXXXX ........ ........ ........ ]
    //  leading_zeroes = 0
    //  Therefore rot (left) = 24.
    //  Encoded 8-bit literal:                  [ 1XXXXXXX ]
    //
    //  Literal:     [ ........ ..1XXXXX XX...... ........ ]
    //  leading_zeroes = 10
    //  Therefore rot (left) = 14.
    //  Encoded 8-bit literal:                  [ 1XXXXXXX ]
    //
    // Note, however, that we can only encode even shifts, and so
    // "rot=24-leading_zeroes" is not sufficient by itself. By ignoring
    // zero-bits in odd bit positions, we can ensure that we get a valid
    // encoding.
    //
    // Example:
    //  Literal:     [ 01XXXXXX ........ ........ ........ ]
    //  leading_zeroes = 1
    //  Therefore rot (left) = round_up(23) = 24.
    //  Encoded 8-bit literal:                  [ 01XXXXXX ]
    rot = 24 - (leading_zeroes & ~1);

    // The imm8 component of the operand 2 encoding can be calculated from the
    // rot value.
    imm8 = literal >> rot;

    // The validity of the literal can be checked by reversing the
    // calculation. It is much easier to decode the immediate than it is to
    // encode it!
    if (literal != (imm8 << rot)) {
        // The encoding is not valid, so report the failure. Calling code
        // should use some other method of loading the value (such as LDR).
        return false;
    }

    // The operand is valid, so encode it.
    // Note that the ARM encoding is actually described by a rotate to the
    // _right_, so rot must be negated here. Calculating a left shift (rather
    // than calculating a right rotation) simplifies the above code.
    *enc = ((-rot << 7) & 0xf00) | imm8;

    // Assert that the operand was properly encoded.
    NanoAssert(decOp2Imm(*enc) == literal);

    return true;
}

// Encode "rd = rn + imm" using an appropriate instruction sequence.
// Set stat to 1 to update the status flags. Otherwise, set it to 0 or omit it.
// (The declaration in NativeARM.h defines the default value of stat as 0.)
//
// It is not valid to call this function if:
//   (rd == IP) AND (rn == IP) AND !encOp2Imm(imm) AND !encOp2Imm(-imm)
// Where: if (encOp2Imm(imm)), imm can be encoded as an ARM operand 2 using the
// encOp2Imm method.
void
Assembler::asm_add_imm(Register rd, Register rn, int32_t imm, int stat /* =0 */)
{
    // Operand 2 encoding of the immediate.
    uint32_t    op2imm;

    NanoAssert(IsGpReg(rd));
    NanoAssert(IsGpReg(rn));
    NanoAssert((stat & 1) == stat);

    // Try to encode the value directly as an operand 2 immediate value, then
    // fall back to loading the value into a register.
    if (encOp2Imm(imm, &op2imm)) {
        ADDis(rd, rn, op2imm, stat);
    } else if (encOp2Imm(-imm, &op2imm)) {
        // We could not encode the value for ADD, so try to encode it for SUB.
        // Note that this is valid even if stat is set, _unless_ imm is 0, but
        // that case is caught above.
        NanoAssert(imm != 0);
        SUBis(rd, rn, op2imm, stat);
    } else {
        // We couldn't encode the value directly, so use an intermediate
        // register to encode the value. We will use IP to do this unless rn is
        // IP; in that case we can reuse rd. This allows every case other than
        // "ADD IP, IP, =#imm".
        Register    rm = (rn == IP) ? (rd) : (IP);
        NanoAssert(rn != rm);

        ADDs(rd, rn, rm, stat);
        asm_ld_imm(rm, imm);
    }
}

// Encode "rd = rn - imm" using an appropriate instruction sequence.
// Set stat to 1 to update the status flags. Otherwise, set it to 0 or omit it.
// (The declaration in NativeARM.h defines the default value of stat as 0.)
//
// It is not valid to call this function if:
//   (rd == IP) AND (rn == IP) AND !encOp2Imm(imm) AND !encOp2Imm(-imm)
// Where: if (encOp2Imm(imm)), imm can be encoded as an ARM operand 2 using the
// encOp2Imm method.
void
Assembler::asm_sub_imm(Register rd, Register rn, int32_t imm, int stat /* =0 */)
{
    // Operand 2 encoding of the immediate.
    uint32_t    op2imm;

    NanoAssert(IsGpReg(rd));
    NanoAssert(IsGpReg(rn));
    NanoAssert((stat & 1) == stat);

    // Try to encode the value directly as an operand 2 immediate value, then
    // fall back to loading the value into a register.
    if (encOp2Imm(imm, &op2imm)) {
        SUBis(rd, rn, op2imm, stat);
    } else if (encOp2Imm(-imm, &op2imm)) {
        // We could not encode the value for SUB, so try to encode it for ADD.
        // Note that this is valid even if stat is set, _unless_ imm is 0, but
        // that case is caught above.
        NanoAssert(imm != 0);
        ADDis(rd, rn, op2imm, stat);
    } else {
        // We couldn't encode the value directly, so use an intermediate
        // register to encode the value. We will use IP to do this unless rn is
        // IP; in that case we can reuse rd. This allows every case other than
        // "SUB IP, IP, =#imm".
        Register    rm = (rn == IP) ? (rd) : (IP);
        NanoAssert(rn != rm);

        SUBs(rd, rn, rm, stat);
        asm_ld_imm(rm, imm);
    }
}

// Encode "rd = rn & imm" using an appropriate instruction sequence.
// Set stat to 1 to update the status flags. Otherwise, set it to 0 or omit it.
// (The declaration in NativeARM.h defines the default value of stat as 0.)
//
// It is not valid to call this function if:
//   (rd == IP) AND (rn == IP) AND !encOp2Imm(imm) AND !encOp2Imm(~imm)
// Where: if (encOp2Imm(imm)), imm can be encoded as an ARM operand 2 using the
// encOp2Imm method.
void
Assembler::asm_and_imm(Register rd, Register rn, int32_t imm, int stat /* =0 */)
{
    // Operand 2 encoding of the immediate.
    uint32_t    op2imm;

    NanoAssert(IsGpReg(rd));
    NanoAssert(IsGpReg(rn));
    NanoAssert((stat & 1) == stat);

    // Try to encode the value directly as an operand 2 immediate value, then
    // fall back to loading the value into a register.
    if (encOp2Imm(imm, &op2imm)) {
        ANDis(rd, rn, op2imm, stat);
    } else if (encOp2Imm(~imm, &op2imm)) {
        // Use BIC with the inverted immediate.
        BICis(rd, rn, op2imm, stat);
    } else {
        // We couldn't encode the value directly, so use an intermediate
        // register to encode the value. We will use IP to do this unless rn is
        // IP; in that case we can reuse rd. This allows every case other than
        // "AND IP, IP, =#imm".
        Register    rm = (rn == IP) ? (rd) : (IP);
        NanoAssert(rn != rm);

        ANDs(rd, rn, rm, stat);
        asm_ld_imm(rm, imm);
    }
}

// Encode "rd = rn | imm" using an appropriate instruction sequence.
// Set stat to 1 to update the status flags. Otherwise, set it to 0 or omit it.
// (The declaration in NativeARM.h defines the default value of stat as 0.)
//
// It is not valid to call this function if:
//   (rd == IP) AND (rn == IP) AND !encOp2Imm(imm)
// Where: if (encOp2Imm(imm)), imm can be encoded as an ARM operand 2 using the
// encOp2Imm method.
void
Assembler::asm_orr_imm(Register rd, Register rn, int32_t imm, int stat /* =0 */)
{
    // Operand 2 encoding of the immediate.
    uint32_t    op2imm;

    NanoAssert(IsGpReg(rd));
    NanoAssert(IsGpReg(rn));
    NanoAssert((stat & 1) == stat);

    // Try to encode the value directly as an operand 2 immediate value, then
    // fall back to loading the value into a register.
    if (encOp2Imm(imm, &op2imm)) {
        ORRis(rd, rn, op2imm, stat);
    } else {
        // We couldn't encode the value directly, so use an intermediate
        // register to encode the value. We will use IP to do this unless rn is
        // IP; in that case we can reuse rd. This allows every case other than
        // "ORR IP, IP, =#imm".
        Register    rm = (rn == IP) ? (rd) : (IP);
        NanoAssert(rn != rm);

        ORRs(rd, rn, rm, stat);
        asm_ld_imm(rm, imm);
    }
}

// Encode "rd = rn ^ imm" using an appropriate instruction sequence.
// Set stat to 1 to update the status flags. Otherwise, set it to 0 or omit it.
// (The declaration in NativeARM.h defines the default value of stat as 0.)
//
// It is not valid to call this function if:
//   (rd == IP) AND (rn == IP) AND !encOp2Imm(imm)
// Where: if (encOp2Imm(imm)), imm can be encoded as an ARM operand 2 using the
// encOp2Imm method.
void
Assembler::asm_eor_imm(Register rd, Register rn, int32_t imm, int stat /* =0 */)
{
    // Operand 2 encoding of the immediate.
    uint32_t    op2imm;

    NanoAssert(IsGpReg(rd));
    NanoAssert(IsGpReg(rn));
    NanoAssert((stat & 1) == stat);

    // Try to encode the value directly as an operand 2 immediate value, then
    // fall back to loading the value into a register.
    if (encOp2Imm(imm, &op2imm)) {
        EORis(rd, rn, op2imm, stat);
    } else {
        // We couldn't encoder the value directly, so use an intermediate
        // register to encode the value. We will use IP to do this unless rn is
        // IP; in that case we can reuse rd. This allows every case other than
        // "EOR IP, IP, =#imm".
        Register    rm = (rn == IP) ? (rd) : (IP);
        NanoAssert(rn != rm);

        EORs(rd, rn, rm, stat);
        asm_ld_imm(rm, imm);
    }
}

// --------------------------------
// Assembler functions.
// --------------------------------

void
Assembler::nInit(AvmCore*)
{
#ifdef UNDER_CE
    blx_lr_bug = blx_lr_broken();
#else
    blx_lr_bug = 0;
#endif
}

void Assembler::nBeginAssembly()
{
}

NIns*
Assembler::genPrologue()
{
    /**
     * Prologue
     */

    // NJ_RESV_OFFSET is space at the top of the stack for us
    // to use for parameter passing (8 bytes at the moment)
    uint32_t stackNeeded = max_out_args + STACK_GRANULARITY * _activation.tos;
    uint32_t savingCount = NumSavedRegs + 2;
    uint32_t savingMask = SavedRegs | rmask(FP) | rmask(LR);

    // so for alignment purposes we've pushed  return addr, fp, and savingCount registers
    uint32_t stackPushed = STACK_GRANULARITY * savingCount;
    uint32_t aligned = alignUp(stackNeeded + stackPushed, NJ_ALIGN_STACK);
    int32_t amt = aligned - stackPushed;

    // Make room on stack for what we are doing
    if (amt)
        asm_sub_imm(SP, SP, amt);

    verbose_only( asm_output("## %p:",(void*)_nIns); )
    verbose_only( asm_output("## patch entry"); )
    NIns *patchEntry = _nIns;

    MOV(FP, SP);
    PUSH_mask(savingMask);
    return patchEntry;
}

void
Assembler::nFragExit(LInsp guard)
{
    (void)guard;
#if NJ_MERGE
    SideExit *  exit = guard->record()->exit;
    Fragment *  frag = exit->target;
    bool        target_is_known = frag && frag->fragEntry;

    if (target_is_known) {
        // The target exists so we can simply emit a branch to its location.
        JMP_far(frag->fragEntry);
    } else {
        // The target doesn't exit yet, so emit a jump to the epilogue. If the
        // target is created later on, the jump will be patched.

        GuardRecord *gr = guard->record();

        if (!_epilogue)
            _epilogue = genEpilogue();

        // Jump to the epilogue. This may get patched later, but JMP_far always
        // emits two instructions even when only one is required, so patching
        // will work correctly.
        JMP_far(_epilogue);

        asm_ld_imm(R0, int(gr));

        // Set the jmp pointer to the start of the sequence so that patched
        // branches can skip the LDi sequence.
        gr->jmp = _nIns;
    }

#ifdef NJ_VERBOSE
    if (config.show_stats) {
        // load R1 with Fragment *fromFrag, target fragment
        // will make use of this when calling fragenter().
        int fromfrag = int((Fragment*)_thisfrag);
        asm_ld_imm(argRegs[1], fromfrag);
    }
#endif

    // Pop the stack frame.
    MOV(SP, FP);
#endif // TM_MERGE
}

NIns*
Assembler::genEpilogue()
{
    // On v5 and above, this generates the following sequence:
    //   MOV SP,FP
    //   LDMFD SP!,{SavedRegs,FP,PC}
    // which will always handle interworking correctly.
    //
    // On v4T, LR is loaded instead of PC, and then a BX LR is executed.
    //
    // On v4 (and below), interworking is not required, and the v5+ code
    // sequence is used.
#if NJ_ARM_ARCH != NJ_ARM_V4T
    RegisterMask savingMask = SavedRegs | rmask(FP) | rmask(PC);
#else
    RegisterMask savingMask = SavedRegs | rmask(FP) | rmask(LR);
    BX(LR);
#endif
    POP_mask(savingMask);
    MOV(SP,FP);
    return _nIns;
}

/*
 * asm_arg will encode the specified argument according to the current ABI, and
 * will update r and stkd as appropriate so that the next argument can be
 * encoded.
 *
 * Linux has used ARM's EABI for some time. Windows CE uses the legacy ABI.
 *
 * Under EABI:
 * - doubles are 64-bit aligned both in registers and on the stack.
 *   If the next available argument register is R1, it is skipped
 *   and the double is placed in R2:R3.  If R0:R1 or R2:R3 are not
 *   available, the double is placed on the stack, 64-bit aligned.
 * - 32-bit arguments are placed in registers and 32-bit aligned
 *   on the stack.
 *
 * Under legacy ABI:
 * - doubles are placed in subsequent arg registers; if the next
 *   available register is r3, the low order word goes into r3
 *   and the high order goes on the stack.
 * - 32-bit arguments are placed in the next available arg register,
 * - both doubles and 32-bit arguments are placed on stack with 32-bit
 *   alignment.
 */
void 
Assembler::asm_arg(ArgSize sz, LInsp arg, Register& r, int& stkd)
{
    // The stack pointer must always be at least aligned to 4 bytes.
    NanoAssert((stkd & 3) == 0);

    if (sz == ARGSIZE_F) {
        // This task is fairly complex and so is delegated to asm_arg_64.
        asm_arg_64(arg, r, stkd);
    } else if (sz & ARGSIZE_MASK_INT) {
        // pre-assign registers R0-R3 for arguments (if they fit)
        if (r < R4) {
            asm_regarg(sz, arg, r);
            r = nextreg(r);
        } else {
            asm_stkarg(arg, stkd);
            stkd += 4;
        }
    } else {
        NanoAssert(sz == ARGSIZE_Q);
        // shouldn't have 64 bit int params on ARM
        NanoAssert(false);
    }
}

// Encode a 64-bit floating-point argument using the appropriate ABI.
// This function operates in the same way as asm_arg, except that it will only
// handle arguments where (ArgSize)sz == ARGSIZE_F.
void
Assembler::asm_arg_64(LInsp arg, Register& r, int& stkd)
{
    // The stack pointer must always be at least aligned to 4 bytes.
    NanoAssert((stkd & 3) == 0);
    // The only use for this function when we are using soft floating-point
    // is for LIR_qjoin.
    NanoAssert(IS_ARM_ARCH_VFP() || arg->isop(LIR_qjoin));

    Register    fp_reg = UnknownReg;

    if (IS_ARM_ARCH_VFP()) {
        fp_reg = findRegFor(arg, FpRegs);
        NanoAssert(fp_reg != UnknownReg);
    }

#ifdef NJ_ARM_EABI
    // EABI requires that 64-bit arguments are aligned on even-numbered
    // registers, as R0:R1 or R2:R3. If the register base is at an
    // odd-numbered register, advance it. Note that this will push r past
    // R3 if r is R3 to start with, and will force the argument to go on
    // the stack.
    if ((r == R1) || (r == R3)) {
        r = nextreg(r);
    }
#endif

    if (r < R3) {
        Register    ra = r;
        Register    rb = nextreg(r);
        r = nextreg(rb);
            
#ifdef NJ_ARM_EABI
        // EABI requires that 64-bit arguments are aligned on even-numbered
        // registers, as R0:R1 or R2:R3.
        NanoAssert( ((ra == R0) && (rb == R1)) || ((ra == R2) && (rb == R3)) );
#endif

        // Put the argument in ra and rb. If the argument is in a VFP register,
        // use FMRRD to move it to ra and rb. Otherwise, let asm_regarg deal
        // with the argument as if it were two 32-bit arguments.
        if (IS_ARM_ARCH_VFP()) {
            FMRRD(ra, rb, fp_reg);
        } else {
            asm_regarg(ARGSIZE_LO, arg->oprnd1(), ra);
            asm_regarg(ARGSIZE_LO, arg->oprnd2(), rb);
        }
        
#ifndef NJ_ARM_EABI
    } else if (r == R3) {
        // We only have one register left, but the legacy ABI requires that we
        // put 32 bits of the argument in the register (R3) and the remaining
        // 32 bits on the stack.
        Register    ra = r;
        r = nextreg(r);

        // This really just checks that nextreg() works properly, as we know
        // that r was previously R3.
        NanoAssert(r == R4);

        // We're splitting the argument between registers and the stack.  This
        // must be the first time that the stack is used, so stkd must be at 0.
        NanoAssert(stkd == 0);

        if (IS_ARM_ARCH_VFP()) {
            // TODO: We could optimize the this to store directly from
            // the VFP register to memory using "FMRRD ra, fp_reg[31:0]" and
            // "STR fp_reg[63:32], [SP, #stkd]".

            // Load from the floating-point register as usual, but use IP
            // as a swap register.
            STR(IP, SP, 0);
            stkd += 4;
            FMRRD(ra, IP, fp_reg);
        } else {
            // Without VFP, we can simply use asm_regarg and asm_stkarg to
            // encode the two 32-bit words as we don't need to load from a VFP
            // register.
            asm_regarg(ARGSIZE_LO, arg->oprnd1(), ra);
            asm_stkarg(arg->oprnd2(), 0);
            stkd += 4;
        }
#endif
    } else {
        // The argument won't fit in registers, so pass on to asm_stkarg.
#ifdef NJ_ARM_EABI
        // EABI requires that 64-bit arguments are 64-bit aligned.
        if ((stkd & 7) != 0) {
            // stkd will always be aligned to at least 4 bytes; this was
            // asserted on entry to this function.
            stkd += 4;
        }
#endif
        asm_stkarg(arg, stkd);
        stkd += 8;
    }
}

void 
Assembler::asm_regarg(ArgSize sz, LInsp p, Register r)
{
    NanoAssert(r != UnknownReg);
    if (sz & ARGSIZE_MASK_INT)
    {
        // arg goes in specific register
        if (p->isconst()) {
            asm_ld_imm(r, p->imm32());
        } else {
            Reservation* rA = getresv(p);
            if (rA) {
                if (rA->reg == UnknownReg) {
                    // load it into the arg reg
                    int d = findMemFor(p);
                    if (p->isop(LIR_alloc)) {
                        asm_add_imm(r, FP, d, 0);
                    } else {
                        LDR(r, FP, d);
                    }
                } else {
                    // it must be in a saved reg
                    MOV(r, rA->reg);
                }
            }
            else {
                // this is the last use, so fine to assign it
                // to the scratch reg, it's dead after this point.
                findSpecificRegFor(p, r);
            }
        }
    }
    else if (sz == ARGSIZE_Q) {
        // 64 bit integer argument - should never happen on ARM
        NanoAssert(false);
    }
    else
    {
        NanoAssert(sz == ARGSIZE_F);
        // fpu argument in register - should never happen since FPU
        // args are converted to two 32-bit ints on ARM
        NanoAssert(false);
    }
}

void
Assembler::asm_stkarg(LInsp arg, int stkd)
{
    Reservation* argRes = getresv(arg);
    bool isQuad = arg->isQuad();

    if (argRes && (argRes->reg != UnknownReg)) {
        // The argument resides somewhere in registers, so we simply need to
        // push it onto the stack.    
        if (!IS_ARM_ARCH_VFP() || !isQuad) {
            NanoAssert(IsGpReg(argRes->reg));
                    
            STR(argRes->reg, SP, stkd);
        } else {
            // According to the comments in asm_arg_64, LIR_qjoin
            // can have a 64-bit argument even if VFP is disabled. However,
            // asm_arg_64 will split the argument and issue two 32-bit
            // arguments to asm_stkarg so we can ignore that case here and
            // assert that we will never get 64-bit arguments unless VFP is
            // available.
            NanoAssert(IS_ARM_ARCH_VFP());
            NanoAssert(IsFpReg(argRes->reg));

#ifdef NJ_ARM_EABI
            // EABI requires that 64-bit arguments are 64-bit aligned.
            NanoAssert((stkd & 7) == 0);
#endif

            FSTD(argRes->reg, SP, stkd);
        }
    } else {
        // The argument does not reside in registers, so we need to get some
        // memory for it and then copy it onto the stack.
        int d = findMemFor(arg);
        if (!isQuad) {
            STR(IP, SP, stkd);
            if (arg->isop(LIR_alloc)) {
                asm_add_imm(IP, FP, d);
            } else {
                LDR(IP, FP, d);
            }
        } else {
#ifdef NJ_ARM_EABI
            // EABI requires that 64-bit arguments are 64-bit aligned.
            NanoAssert((stkd & 7) == 0);
#endif

            STR_preindex(IP, SP, stkd+4);
            LDR(IP, FP, d+4);
            STR_preindex(IP, SP, stkd);
            LDR(IP, FP, d);
        }
    }
}

void
Assembler::asm_call(LInsp ins)
{
    const CallInfo* call = ins->callInfo();
    bool needToLoadAddr = false;
    ArgSize sizes[MAXARGS];
    uint32_t argc = call->get_sizes(sizes);
    bool indirect = call->isIndirect();

    // If we aren't using VFP, assert that the LIR operation is an integer
    // function call.
    NanoAssert(IS_ARM_ARCH_VFP() || ins->isop(LIR_icall));

    // If we're using VFP, and the return type is a double, it'll come back in
    // R0/R1. We need to either place it in the result fp reg, or store it.
    // See comments in asm_prep_fcall() for more details as to why this is
    // necessary here for floating point calls, but not for integer calls.
    Reservation * callRes = getresv(ins);
    if (IS_ARM_ARCH_VFP() && callRes) {
        // Determine the size (and type) of the instruction result.
        ArgSize         rsize = (ArgSize)(call->_argtypes & ARGSIZE_MASK_ANY);

        // If the result size is a floating-point value, treat the result
        // specially, as described previously.
        if (rsize == ARGSIZE_F) {
            Register        rr = callRes->reg;

            NanoAssert(ins->opcode() == LIR_fcall);

            // We're about to write the result into a register (or a stack
            // slot). Because we emit code backwards, we must therefore free
            // it.
            //freeRsrcOf(ins, rr != UnknownReg);

            if (rr == UnknownReg) {
                int d = disp(callRes);
                NanoAssert(d != 0);
                freeRsrcOf(ins, false);

                // The result doesn't have a register allocated, so store the
                // result (in R0,R1) directly to its stack slot.
                STR(R0, FP, d+0);
                STR(R1, FP, d+4);
            } else {
                Register    rr = callRes->reg;
                NanoAssert(IsFpReg(rr));

                // Copy the result to the (VFP) result register.
                prepResultReg(ins, rmask(rr));
                FMDRR(rr, R0, R1);
            }
        }
    }

    if (!indirect) {
        verbose_only(if (_logc->lcbits & LC_Assembly)
            outputf("        %p:", _nIns);
        )
        // Direct call: on v5 and above (where the calling sequence doesn't
        // corrupt LR until the actual branch instruction), we can avoid an
        // interlock in the "long" branch sequence by manually loading the
        // target address into LR ourselves before setting up the parameters
        // in other registers.
#if NJ_ARM_ARCH >= NJ_ARM_V5
        needToLoadAddr = BL_noload((NIns*)call->_address, LR);
#else
        BL((NIns*)call->_address);
#endif
    } else {
        // Indirect call: we assign the address arg to LR since it's not
        // used for regular arguments, and is otherwise scratch since it's
        // clobberred by the call. On v4/v4T, where we have to manually do
        // the equivalent of a BLX, move LR into IP before corrupting LR
        // with the return address.
#if NJ_ARM_ARCH >= NJ_ARM_V5
        if (blx_lr_bug) {
            // workaround for msft device emulator bug (blx lr emulated as no-op)
            underrunProtect(8);
            BLX(IP);
            MOV(IP,LR);
        } else {
            BLX(LR);
        }
#else
        underrunProtect(8); // keep next instr (BX or MOV PC,IP) and MOV LR,PC in the same page
    #if NJ_ARM_ARCH == NJ_ARM_V4T
        BX(IP);
    #else
        MOV(PC,IP);
    #endif
        MOV(LR,PC);
        MOV(IP,LR);
#endif
        asm_regarg(ARGSIZE_LO, ins->arg(--argc), LR);
    }

    // Encode the arguments, starting at R0 and with an empty argument stack.
    Register    r = R0;
    int         stkd = 0;

    // Iterate through the argument list and encode each argument according to
    // the ABI.
    // Note that we loop through the arguments backwards as LIR specifies them
    // in reverse order.
    uint32_t    i = argc;
    while(i--) {
        asm_arg(sizes[i], ins->arg(i), r, stkd);
    }

    if (stkd > max_out_args) {
        max_out_args = stkd;
    }

    if (needToLoadAddr)
        LDi(LR, (int32_t)call->_address);
}

Register
Assembler::nRegisterAllocFromSet(RegisterMask set)
{
    NanoAssert(set != 0);

    // The CountLeadingZeroes function will use the CLZ instruction where
    // available. In other cases, it will fall back to a (slower) C
    // implementation.
    Register r = (Register)(31-CountLeadingZeroes(set));
    _allocator.free &= ~rmask(r);

    NanoAssert(IsGpReg(r) || IsFpReg(r));
    NanoAssert((rmask(r) & set) == rmask(r));

    return r;
}

void
{'fraction_non_alphanumeric': 0.11098754773854641, 'fraction_numerical': 0.016051857596464787, 'mean_word_length': 2.211107456655547, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 118, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 8, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3267846', 'n_tokens_mistral': 22778, 'n_tokens_neox': 21474, 'n_words': 8241}