//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft Corporation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------

#pragma once

#ifdef ASMJS_PLAT
namespace Js
{
    class AsmJsEncoder
    {
        struct EncoderRelocLabel;
        struct EncoderReloc
        {
            static void New( EncoderRelocLabel* label, BYTE* _patchAddr, BYTE* _pc, ArenaAllocator* allocator );
            BYTE* patchAddr;
            BYTE* pc;
            EncoderReloc* next;
        };
        struct EncoderRelocLabel
        {
            EncoderRelocLabel() :labelSeen( false ), relocList( nullptr ){}
            EncoderRelocLabel(BYTE* _pc) :labelSeen( true ), pc(_pc), relocList( nullptr ){}
            bool labelSeen : 1;
            BYTE* pc;
            EncoderReloc* relocList;
        };
        // the key is the bytecode address
        typedef JsUtil::BaseDictionary<int, EncoderRelocLabel, ArenaAllocator> RelocLabelMap;
        const byte* ip;
        ByteCodeReader mReader;
        uint32 mEncodeBufferSize;
        BYTE* mEncodeBuffer;
        BYTE* mPc;
        PageAllocator* mPageAllocator;
        InProcCodeGenAllocators* mForegroundAllocators;
        FunctionBody* mFunctionBody;
        RelocLabelMap* mRelocLabelMap;
        ArenaAllocator* mLocalAlloc;
        // Byte offset of first int and double
        int mIntOffset, mDoubleOffset, mFloatOffset;
        int mSimdOffset;
        // architecture dependant data to build templatized JIT
        void* mTemplateData;
    public:
        void* Encode( FunctionBody* functionBody );
        void* GetTemplateData() { return mTemplateData; }
        inline PageAllocator* GetPageAllocator() const{return mPageAllocator;}
        inline void SetPageAllocator( PageAllocator* val ){mPageAllocator = val;}
        inline InProcCodeGenAllocators* GetCodeGenAllocator() const{return mForegroundAllocators;}
        inline void SetCodeGenAllocator( InProcCodeGenAllocators* val ){mForegroundAllocators = val;}
        FunctionBody* GetFunctionBody() { return mFunctionBody; }

    private:
        void ApplyRelocs();
        void AddReloc( const int labelOffset, BYTE* patchAddr );
        uint32 GetEncodeBufferSize(FunctionBody* functionBody);
        AsmJsFunctionInfo* GetAsmJsFunctionInfo(){ return mFunctionBody->GetAsmJsFunctionInfo(); }
        AsmJsFunctionInfo* GetAsmJsFunctionInfoWithLock() { return mFunctionBody->GetAsmJsFunctionInfoWithLock(); }
        bool ReadOp();
        template<LayoutSize T> void ReadOpTemplate( OpCodeAsmJs op );

        template<typename T> int GetOffset() const;

        template<typename T> int CalculateOffset(int stackLocation) { return stackLocation*sizeof(T)+GetOffset<T>(); }


        void OP_Label( const unaligned OpLayoutEmpty* playout );
        template <class T> void OP_LdUndef( const unaligned T* playout );
        template <class T> void OP_Br( const unaligned T* playout );
        template <class T> void OP_BrEq( const unaligned T* playout );
        template <class T> void OP_BrEqConst( const unaligned T* playout );
        template <class T> void OP_BrTrue( const unaligned T* playout );
        template <class T> void OP_Empty( const unaligned T* playout );
        template <class T> void Op_LdSlot_Db( const unaligned T* playout );
        template <class T> void Op_LdSlot_Int(const unaligned T* playout);
        template <class T> void Op_LdSlot_Flt(const unaligned T* playout);
        template <class T> void Op_StSlot_Db( const unaligned T* playout );
        template <class T> void Op_StSlot_Int(const unaligned T* playout);
        template <class T> void Op_StSlot_Flt(const unaligned T* playout);
        template <class T> void Op_LdConst_Int(const unaligned T* playout);

        template <class T> void Op_LdArr     ( const unaligned T* playout );
        template <class T> void Op_LdArrConst( const unaligned T* playout );
        template <class T> void Op_StArr     ( const unaligned T* playout );
        template <class T> void Op_StArrConst( const unaligned T* playout );

        template <class T> void OP_SetReturnInt( const unaligned T* playout );
        template <class T> void OP_SetReturnDouble(const unaligned T* playout);
        template <class T> void OP_SetReturnFloat(const unaligned T* playout);
        template <class T> void OP_SetFroundInt(const unaligned T* playout);
        template <class T> void OP_SetFroundDb(const unaligned T* playout);
        template <class T> void OP_SetFroundFlt(const unaligned T* playout);
        template <class T> void Op_Float_To_Int(const unaligned T* playout);
        template <class T> void Op_Float_To_Db(const unaligned T* playout);
        template <class T> void Op_UInt_To_Db(const unaligned T* playout);
        template <class T> void Op_Int_To_Db( const unaligned T* playout );
        template <class T> void Op_Db_To_Int( const unaligned T* playout );
        template <class T> void Op_LdAddr_Db( const unaligned T* playout );
        template <class T> void OP_LdSlot( const unaligned T* playout );

        template <class T> void OP_StartCall( const unaligned T* playout );
        template <class T> void OP_Call( const unaligned T* playout );
        template <class T> void OP_ArgOut_Db( const unaligned T* playout );
        template <class T> void OP_ArgOut_Int(const unaligned T* playout);
        template <class T> void OP_Conv_VTD( const unaligned T* playout );
        template <class T> void OP_Conv_VTI( const unaligned T* playout );
        template <class T> void OP_Conv_VTF( const unaligned T* playout );

        template <class T> void OP_I_StartCall( const unaligned T* playout );
        template <class T> void OP_I_Call( const unaligned T* playout );
        template <class T> void OP_I_ArgOut_Db( const unaligned T* playout );
        template <class T> void OP_I_ArgOut_Int(const unaligned T* playout);
        template <class T> void OP_I_ArgOut_Flt(const unaligned T* playout);
        template <class T> void OP_I_Conv_VTD( const unaligned T* playout );
        template <class T> void OP_I_Conv_VTI( const unaligned T* playout );
        template <class T> void OP_I_Conv_VTF( const unaligned T* playout );

        template <class T> void OP_AsmJsLoopBody(const unaligned T* playout);

        template <class T> void OP_Simd128_LdF4(const unaligned T* playout);
        template <class T> void OP_Simd128_LdI4(const unaligned T* playout);
        template <class T> void OP_Simd128_LdD2(const unaligned T* playout);

        template <class T> void OP_Simd128_LdSlotF4(const unaligned T* playout);
        template <class T> void OP_Simd128_LdSlotI4(const unaligned T* playout);
        template <class T> void OP_Simd128_LdSlotD2(const unaligned T* playout);
        template <class T> void OP_Simd128_StSlotF4(const unaligned T* playout);
        template <class T> void OP_Simd128_StSlotI4(const unaligned T* playout);
        template <class T> void OP_Simd128_StSlotD2(const unaligned T* playout);

        template <class T> void OP_Simd128_FloatsToF4(const unaligned T* playout);
        template <class T> void OP_Simd128_IntsToI4(const unaligned T* playout);
        template <class T> void OP_Simd128_DoublesToD2(const unaligned T* playout);

        template <class T> void OP_Simd128_ReturnF4(const unaligned T* playout);
        template <class T> void OP_Simd128_ReturnI4(const unaligned T* playout);
        template <class T> void OP_Simd128_ReturnD2(const unaligned T* playout);

        template <class T> void OP_Simd128_SplatF4(const unaligned T* playout);
        template <class T> void OP_Simd128_SplatI4(const unaligned T* playout);
        template <class T> void OP_Simd128_SplatD2(const unaligned T* playout);

        template <class T> void OP_Simd128_FromFloat64x2F4(const unaligned T* playout);
        template <class T> void OP_Simd128_FromInt32x4F4(const unaligned T* playout);
        template <class T> void OP_Simd128_FromFloat32x4I4(const unaligned T* playout);
        template <class T> void OP_Simd128_FromFloat64x2I4(const unaligned T* playout);
        template <class T> void OP_Simd128_FromFloat32x4D2(const unaligned T* playout);
        template <class T> void OP_Simd128_FromInt32x4D2(const unaligned T* playout);

        template <class T> void OP_Simd128_FromFloat32x4BitsD2(const unaligned T* playout);
        template <class T> void OP_Simd128_FromInt32x4BitsD2(const unaligned T* playout);
        template <class T> void OP_Simd128_FromFloat32x4BitsI4(const unaligned T* playout);
        template <class T> void OP_Simd128_FromFloat64x2BitsI4(const unaligned T* playout);
        template <class T> void OP_Simd128_FromFloat64x2BitsF4(const unaligned T* playout);
        template <class T> void OP_Simd128_FromInt32x4BitsF4(const unaligned T* playout);

        template <class T> void OP_Simd128_AbsF4(const unaligned T* playout);
        template <class T> void OP_Simd128_AbsD2(const unaligned T* playout);
        template <class T> void OP_Simd128_NegF4(const unaligned T* playout);
        template <class T> void OP_Simd128_NegI4(const unaligned T* playout);
        template <class T> void OP_Simd128_NegD2(const unaligned T* playout);
        template <class T> void OP_Simd128_RcpF4(const unaligned T* playout);
        template <class T> void OP_Simd128_RcpD2(const unaligned T* playout);
        template <class T> void OP_Simd128_RcpSqrtF4(const unaligned T* playout);
        template <class T> void OP_Simd128_RcpSqrtD2(const unaligned T* playout);
        template <class T> void OP_Simd128_SqrtF4(const unaligned T* playout);
        template <class T> void OP_Simd128_SqrtD2(const unaligned T* playout);

        template <class T> void OP_Simd128_NotF4(const unaligned T* playout);
        template <class T> void OP_Simd128_NotI4(const unaligned T* playout);
        template <class T> void OP_Simd128_AddF4(const unaligned T* playout);
        template <class T> void OP_Simd128_AddI4(const unaligned T* playout);
        template <class T> void OP_Simd128_AddD2(const unaligned T* playout);
        template <class T> void OP_Simd128_SubF4(const unaligned T* playout);
        template <class T> void OP_Simd128_SubI4(const unaligned T* playout);
        template <class T> void OP_Simd128_SubD2(const unaligned T* playout);
        template <class T> void OP_Simd128_MulF4(const unaligned T* playout);
        template <class T> void OP_Simd128_MulI4(const unaligned T* playout);
        template <class T> void OP_Simd128_MulD2(const unaligned T* playout);
        template <class T> void OP_Simd128_DivF4(const unaligned T* playout);
        template <class T> void OP_Simd128_DivD2(const unaligned T* playout);

        template <class T> void OP_Simd128_MinF4(const unaligned T* playout);
        template <class T> void OP_Simd128_MinD2(const unaligned T* playout);
        template <class T> void OP_Simd128_MaxF4(const unaligned T* playout);
        template <class T> void OP_Simd128_MaxD2(const unaligned T* playout);

        template <class T> void OP_Simd128_LtF4(const unaligned T* playout);
        template <class T> void OP_Simd128_LtI4(const unaligned T* playout);
        template <class T> void OP_Simd128_LtD2(const unaligned T* playout);
        template <class T> void OP_Simd128_GtF4(const unaligned T* playout);
        template <class T> void OP_Simd128_GtI4(const unaligned T* playout);
        template <class T> void OP_Simd128_GtD2(const unaligned T* playout);
        template <class T> void OP_Simd128_LtEqF4(const unaligned T* playout);
        template <class T> void OP_Simd128_LtEqD2(const unaligned T* playout);
        template <class T> void OP_Simd128_GtEqF4(const unaligned T* playout);
        template <class T> void OP_Simd128_GtEqD2(const unaligned T* playout);
        template <class T> void OP_Simd128_EqF4(const unaligned T* playout);
        template <class T> void OP_Simd128_EqI4(const unaligned T* playout);
        template <class T> void OP_Simd128_EqD2(const unaligned T* playout);
        template <class T> void OP_Simd128_NeqF4(const unaligned T* playout);
        template <class T> void OP_Simd128_NeqD2(const unaligned T* playout);

        template <class T> void OP_Simd128_AndF4(const unaligned T* playout);
        template <class T> void OP_Simd128_AndI4(const unaligned T* playout);
        template <class T> void OP_Simd128_OrF4(const unaligned T* playout);
        template <class T> void OP_Simd128_OrI4(const unaligned T* playout);
        template <class T> void OP_Simd128_XorF4(const unaligned T* playout);
        template <class T> void OP_Simd128_XorI4(const unaligned T* playout);

        template <class T> void OP_Simd128_SelectF4(const unaligned T* playout);
        template <class T> void OP_Simd128_SelectI4(const unaligned T* playout);
        template <class T> void OP_Simd128_SelectD2(const unaligned T* playout);

        template <class T> void OP_Simd128_ExtractLaneI4(const unaligned T* playout);
        template <class T> void OP_Simd128_ExtractLaneF4(const unaligned T* playout);
        template <class T> void OP_Simd128_ReplaceLaneI4(const unaligned T* playout);
        template <class T> void OP_Simd128_ReplaceLaneF4(const unaligned T* playout);

        template <class T> void OP_Simd128_I_ArgOutF4(const unaligned T* playout);
        template <class T> void OP_Simd128_I_ArgOutI4(const unaligned T* playout);
        template <class T> void OP_Simd128_I_ArgOutD2(const unaligned T* playout);

        template <class T> void OP_Simd128_I_Conv_VTF4(const unaligned T* playout);
        template <class T> void OP_Simd128_I_Conv_VTI4(const unaligned T* playout);
        template <class T> void OP_Simd128_I_Conv_VTD2(const unaligned T* playout);
    };
}

#endif
