//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "Backend.h"
#include "PrologEncoderMD.h"

unsigned __int8 PrologEncoderMD::GetRequiredNodeCountForAlloca(size_t size)
{
    Assert(size);
    Assert(size % 8 == 0);

    if (size <= 0x80)
        return 1;
    else if (size <= 0x7FF8)
        return 2;
    else
        return 3;
}

unsigned __int8 PrologEncoderMD::GetOp(IR::Instr *instr)
{
    switch (instr->m_opcode)
    {
    case Js::OpCode::PUSH:
        Assert(instr->GetSrc1()->IsRegOpnd());
        return UWOP_PUSH_NONVOL;

    case Js::OpCode::MOVAPD:
    case Js::OpCode::MOVAPS:
        Assert(instr->GetSrc1()->IsRegOpnd() && REGNUM_ISXMMXREG(instr->GetSrc1()->AsRegOpnd()->GetReg()));
        return UWOP_SAVE_XMM128;

    case Js::OpCode::MOV:
        // MOV rbp, rsp (or)
        // MOV rax, imm
        Assert(instr->GetDst() && instr->GetSrc1());
        return UWOP_IGNORE;

    case Js::OpCode::SUB:
        Assert(instr->GetSrc1() && instr->GetSrc2());
        Assert(instr->GetSrc1()->AsRegOpnd()->GetReg() == RegRSP &&
               instr->GetSrc2()->IsIntConstOpnd());
        if (instr->GetSrc2()->AsIntConstOpnd()->GetValue() <= 128)
            return UWOP_ALLOC_SMALL;
        else
            return UWOP_ALLOC_LARGE;

    case Js::OpCode::CALL:
        // TODO: call target could also be a stack overflow check.
        // Assert(instr->GetSrc1()->AsHelperCallOpnd()->m_fnHelper == IR::HelperCRT_chkstk);
        Assert(instr->GetSrc1()->IsRegOpnd());
        return UWOP_IGNORE;

    default:
        AssertMsg(false, "Unsupported opcode in prolog.");
    }

    return UWOP_IGNORE;
}

unsigned __int8 PrologEncoderMD::GetNonVolRegToSave(IR::Instr *instr)
{
    Assert(instr->m_opcode == Js::OpCode::PUSH);
    return (instr->GetSrc1()->AsRegOpnd()->GetReg() - 1) & 0xFF;
}

unsigned __int8 PrologEncoderMD::GetXmmRegToSave(IR::Instr *instr, unsigned __int16 *scaledOffset)
{
    Assert(scaledOffset);
    Assert(instr->m_opcode == Js::OpCode::MOVAPD || instr->m_opcode == Js::OpCode::MOVAPS);
    Assert(instr->GetDst() && instr->GetDst()->IsIndirOpnd());

    unsigned __int8 reg = ((instr->GetSrc1()->AsRegOpnd()->GetReg() - FIRST_XMM_REG) & 0xFF);
    unsigned __int32 offsetFromInstr = instr->GetDst()->AsIndirOpnd()->GetOffset();

    // The offset in the instruction is relative to the stack pointer before the saved reg size and stack args size were
    // subtracted, but the offset in the unwind info needs to be relative to the final stack pointer value
    // (see LowererMDArch::LowerEntryInstr), so adjust for that
    Assert(instr->GetDst()->AsIndirOpnd()->GetBaseOpnd()->GetReg() == LowererMDArch::GetRegStackPointer());
    Func *const topFunc = instr->m_func->GetTopFunc();
    offsetFromInstr += topFunc->GetArgsSize() + topFunc->GetSavedRegSize();

    // Can only encode nonnegative 16-byte-aligned offsets in the unwind info
    Assert(static_cast<int32>(offsetFromInstr) >= 0);
    Assert(::Math::Align(offsetFromInstr, static_cast<unsigned __int32>(MachDouble * 2)) == offsetFromInstr);

    // Stored offset is scaled by 16
    offsetFromInstr /= MachDouble * 2;

    // We currently don't allow a total stack size greater than 1 MB. If we start allowing that, we will need to handle offsets
    // greater than (1 MB - 16) in the unwind info as well, for which a FAR version of the op-code is necessary.
    Assert(IS_UINT16(offsetFromInstr));

    *scaledOffset = TO_UINT16(offsetFromInstr);

    return reg;
}

size_t PrologEncoderMD::GetAllocaSize(IR::Instr *instr)
{
    Assert(instr->m_opcode == Js::OpCode::SUB);
    Assert(instr->GetSrc1() && instr->GetSrc2());
    Assert(instr->GetSrc1()->AsRegOpnd()->GetReg() == RegRSP &&
           instr->GetSrc2()->IsIntConstOpnd());
    Assert(instr->GetSrc2()->AsIntConstOpnd()->GetValue() % 8 == 0);

    return instr->GetSrc2()->AsIntConstOpnd()->GetValue();
}

unsigned __int8 PrologEncoderMD::GetFPReg()
{
    return RegRBP - 1;
}
