//-----------------------------------------------------------------------------
//
// Copyright (C) 2015-2017 David Hill
//
// See COPYING for license information.
//
//-----------------------------------------------------------------------------
//
// Thread execution.
//
//-----------------------------------------------------------------------------
#ifdef __GNUC__
#ifndef __clang__
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
#endif
#include "Thread.hpp"

#include "Array.hpp"
#include "Code.hpp"
#include "Environment.hpp"
#include "Function.hpp"
#include "Jump.hpp"
#include "Module.hpp"
#include "Scope.hpp"
#include "Script.hpp"


//----------------------------------------------------------------------------|
// Macros                                                                     |
//

//
// ACSVM_DynamicGoto
//
// If nonzero, enables use of dynamic goto labels in core interpreter loop.
// Currently, only gcc syntax is supported.
//
#ifndef ACSVM_DynamicGoto
#if defined(__GNUC__)
#define ACSVM_DynamicGoto 1
#else
#define ACSVM_DynamicGoto 0
#endif
#endif

//
// BranchTo
//
#define BranchTo(target) \
   do \
   { \
      codePtr = &module->codeV[(target)]; \
      CountBranch(); \
   } \
   while(0)

//
// CountBranch
//
// Used to limit the number of branches to prevent infinite no-delay loops.
//
#define CountBranch() \
   if(branches && !--branches) \
   { \
      env->printKill(this, static_cast<Word>(KillType::BranchLimit), 0); \
      goto thread_stop; \
   } \
   else \
      ((void)0)

//
// DeclCase
//
#if ACSVM_DynamicGoto
#define DeclCase(name) case_Code##name
#else
#define DeclCase(name) case static_cast<Word>(Code::name)
#endif

//
// NextCase
//
#if ACSVM_DynamicGoto
#define NextCase() goto *cases[*codePtr++]
#else
#define NextCase() goto next_case
#endif

//
// Op_*
//

#define Op_AddU(lop) (dataStk.drop(), (lop) += dataStk[0])
#define Op_AndU(lop) (dataStk.drop(), (lop) &= dataStk[0])
#define Op_CmpI_GE(lop) (dataStk.drop(), OpFunc_CmpI_GE(lop, dataStk[0]))
#define Op_CmpI_GT(lop) (dataStk.drop(), OpFunc_CmpI_GT(lop, dataStk[0]))
#define Op_CmpI_LE(lop) (dataStk.drop(), OpFunc_CmpI_LE(lop, dataStk[0]))
#define Op_CmpI_LT(lop) (dataStk.drop(), OpFunc_CmpI_LT(lop, dataStk[0]))
#define Op_CmpU_EQ(lop) (dataStk.drop(), OpFunc_CmpU_EQ(lop, dataStk[0]))
#define Op_CmpU_NE(lop) (dataStk.drop(), OpFunc_CmpU_NE(lop, dataStk[0]))
#define Op_DecU(lop) (--(lop))
#define Op_DivI(lop) (dataStk.drop(), OpFunc_DivI(lop, dataStk[0]))
#define Op_DivX(lop) (dataStk.drop(), OpFunc_DivX(lop, dataStk[0]))
#define Op_Drop(lop) (dataStk.drop(), (lop) = dataStk[0])
#define Op_IncU(lop) (++(lop))
#define Op_LAnd(lop) (dataStk.drop(), OpFunc_LAnd(lop, dataStk[0]))
#define Op_LOrI(lop) (dataStk.drop(), OpFunc_LOrI(lop, dataStk[0]))
#define Op_ModI(lop) (dataStk.drop(), OpFunc_ModI(lop, dataStk[0]))
#define Op_MulU(lop) (dataStk.drop(), (lop) *= dataStk[0])
#define Op_MulX(lop) (dataStk.drop(), OpFunc_MulX(lop, dataStk[0]))
#define Op_OrIU(lop) (dataStk.drop(), (lop) |= dataStk[0])
#define Op_OrXU(lop) (dataStk.drop(), (lop) ^= dataStk[0])
#define Op_ShLU(lop) (dataStk.drop(), (lop) <<= dataStk[0] & 31)
#define Op_ShRI(lop) (dataStk.drop(), OpFunc_ShRI(lop, dataStk[0]))
#define Op_SubU(lop) (dataStk.drop(), (lop) -= dataStk[0])

//
// OpSet
//
#define OpSet(op) \
   DeclCase(op##_GblArr): \
      Op_##op(scopeGbl->arrV[*codePtr++][dataStk[1]]); dataStk.drop(); \
      NextCase(); \
   DeclCase(op##_GblReg): \
      Op_##op(scopeGbl->regV[*codePtr++]); \
      NextCase(); \
   DeclCase(op##_HubArr): \
      Op_##op(scopeHub->arrV[*codePtr++][dataStk[1]]); dataStk.drop(); \
      NextCase(); \
   DeclCase(op##_HubReg): \
      Op_##op(scopeHub->regV[*codePtr++]); \
      NextCase(); \
   DeclCase(op##_LocArr): \
      Op_##op(localArr[*codePtr++][dataStk[1]]); dataStk.drop(); \
      NextCase(); \
   DeclCase(op##_LocReg): \
      Op_##op(localReg[*codePtr++]); \
      NextCase(); \
   DeclCase(op##_ModArr): \
      Op_##op((*scopeMod->arrV[*codePtr++])[dataStk[1]]); dataStk.drop(); \
      NextCase(); \
   DeclCase(op##_ModReg): \
      Op_##op(*scopeMod->regV[*codePtr++]); \
      NextCase()


//----------------------------------------------------------------------------|
// Static Functions                                                           |
//

namespace ACSVM
{
   //
   // OpFunc_CmpI_GE
   //
   static inline void OpFunc_CmpI_GE(Word &lop, Word rop)
   {
      lop = static_cast<SWord>(lop) >= static_cast<SWord>(rop);
   }

   //
   // OpFunc_CmpI_GT
   //
   static inline void OpFunc_CmpI_GT(Word &lop, Word rop)
   {
      lop = static_cast<SWord>(lop) > static_cast<SWord>(rop);
   }

   //
   // OpFunc_CmpI_LE
   //
   static inline void OpFunc_CmpI_LE(Word &lop, Word rop)
   {
      lop = static_cast<SWord>(lop) <= static_cast<SWord>(rop);
   }

   //
   // OpFunc_CmpI_LT
   //
   static inline void OpFunc_CmpI_LT(Word &lop, Word rop)
   {
      lop = static_cast<SWord>(lop) < static_cast<SWord>(rop);
   }

   //
   // OpFunc_CmpU_EQ
   //
   static inline void OpFunc_CmpU_EQ(Word &lop, Word rop)
   {
      lop = lop == rop;
   }

   //
   // OpFunc_CmpU_NE
   //
   static inline void OpFunc_CmpU_NE(Word &lop, Word rop)
   {
      lop = lop != rop;
   }

   //
   // OpFunc_DivI
   //
   static inline void OpFunc_DivI(Word &lop, Word rop)
   {
      lop = rop ? static_cast<SWord>(lop) / static_cast<SWord>(rop) : 0;
   }

   //
   // OpFunc_DivX
   //
   static inline void OpFunc_DivX(Word &lop, Word rop)
   {
      if(rop)
         lop = (SDWord(SWord(lop)) << 16) / SWord(rop);
      else
         lop = 0;
   }

   //
   // OpFunc_LAnd
   //
   static inline void OpFunc_LAnd(Word &lop, Word rop)
   {
      lop = lop && rop;
   }

   //
   // OpFunc_LOrI
   //
   static inline void OpFunc_LOrI(Word &lop, Word rop)
   {
      lop = lop || rop;
   }

   //
   // OpFunc_ModI
   //
   static inline void OpFunc_ModI(Word &lop, Word rop)
   {
      lop = rop ? static_cast<SWord>(lop) % static_cast<SWord>(rop) : 0;
   }

   //
   // OpFunc_MulX
   //
   static inline void OpFunc_MulX(Word &lop, Word rop)
   {
      lop = DWord(SDWord(SWord(lop)) * SWord(rop)) >> 16;
   }

   //
   // OpFunc_ShRI
   //
   static inline void OpFunc_ShRI(Word &lop, Word rop)
   {
      // TODO: Implement this without relying on sign-extending shift.
      lop = static_cast<SWord>(lop) >> (rop & 31);
   }
}


//----------------------------------------------------------------------------|
// Extern Functions                                                           |
//

namespace ACSVM
{
   //
   // Thread::exec
   //
   void Thread::exec()
   {
      if(delay && --delay)
         return;

      auto branches = env->branchLimit;

   exec_intr:
      switch(state.state)
      {
      case ThreadState::Inactive: return;
      case ThreadState::Stopped:  goto thread_stop;
      case ThreadState::Paused:   return;

      case ThreadState::Running:
         if(delay)
            return;
         break;

      case ThreadState::WaitScrI:
         if(scopeMap->isScriptActive(scopeMap->findScript(state.data)))
            return;
         state = ThreadState::Running;
         break;

      case ThreadState::WaitScrS:
         if(scopeMap->isScriptActive(scopeMap->findScript(scopeMap->getString(state.data))))
            return;
         state = ThreadState::Running;
         break;

      case ThreadState::WaitTag:
         if(!module->env->checkTag(state.type, state.data))
            return;
         state = ThreadState::Running;
         break;
      }

      #if ACSVM_DynamicGoto
      static void const *const cases[] =
      {
         #define ACSVM_CodeList(name, ...) &&case_Code##name,
         #include "CodeList.hpp"
      };
      #endif

      #if ACSVM_DynamicGoto
      NextCase();
      #else
      next_case: switch(*codePtr++)
      #endif
      {
      DeclCase(Nop):
         NextCase();

      DeclCase(Kill):
         module->env->printKill(this, codePtr[0], codePtr[1]);
         goto thread_stop;

         //================================================
         // Binary operator codes.
         //

         OpSet(AddU);
         OpSet(AndU);
         OpSet(DivI);
         OpSet(ModI);
         OpSet(MulU);
         OpSet(OrIU);
         OpSet(OrXU);
         OpSet(ShLU);
         OpSet(ShRI);
         OpSet(SubU);

      DeclCase(AddU): Op_AddU(dataStk[1]); NextCase();
      DeclCase(AndU): Op_AndU(dataStk[1]); NextCase();
      DeclCase(CmpI_GE): Op_CmpI_GE(dataStk[1]); NextCase();
      DeclCase(CmpI_GT): Op_CmpI_GT(dataStk[1]); NextCase();
      DeclCase(CmpI_LE): Op_CmpI_LE(dataStk[1]); NextCase();
      DeclCase(CmpI_LT): Op_CmpI_LT(dataStk[1]); NextCase();
      DeclCase(CmpU_EQ): Op_CmpU_EQ(dataStk[1]); NextCase();
      DeclCase(CmpU_NE): Op_CmpU_NE(dataStk[1]); NextCase();
      DeclCase(DivI): Op_DivI(dataStk[1]); NextCase();
      DeclCase(DivX): Op_DivX(dataStk[1]); NextCase();
      DeclCase(LAnd): Op_LAnd(dataStk[1]); NextCase();
      DeclCase(LOrI): Op_LOrI(dataStk[1]); NextCase();
      DeclCase(ModI): Op_ModI(dataStk[1]); NextCase();
      DeclCase(MulU): Op_MulU(dataStk[1]); NextCase();
      DeclCase(MulX): Op_MulX(dataStk[1]); NextCase();
      DeclCase(OrIU): Op_OrIU(dataStk[1]); NextCase();
      DeclCase(OrXU): Op_OrXU(dataStk[1]); NextCase();
      DeclCase(ShLU): Op_ShLU(dataStk[1]); NextCase();
      DeclCase(ShRI): Op_ShRI(dataStk[1]); NextCase();
      DeclCase(SubU): Op_SubU(dataStk[1]); NextCase();

         //================================================
         // Call codes.
         //

      DeclCase(Call_Lit):
         {
            Function *func;

            func = *codePtr < module->functionV.size() ? module->functionV[*codePtr] : nullptr;
            ++codePtr;

         do_call:
            if(!func) {BranchTo(0); NextCase();}

            // Reserve stack space.
            callStk.reserve(CallStkSize);
            dataStk.reserve(DataStkSize);

            // Push call frame.
            callStk.push({codePtr, module, scopeMod, localArr.size(), localReg.size()});

            // Apply function data.
            codePtr      = &func->module->codeV[func->codeIdx];
            module       = func->module;
            scopeMod     = scopeMap->getModuleScope(module);
            localArr.alloc(func->locArrC);
            localReg.alloc(func->locRegC);

            // Read arguments.
            dataStk.drop(func->argC);
            memcpy(&localReg[0], &dataStk[0], func->argC * sizeof(Word));

            NextCase();

      DeclCase(Call_Stk):
            dataStk.drop();
            func = env->getFunction(dataStk[0]);
            goto do_call;
         }

      DeclCase(CallFunc):
         {
            Word argc = *codePtr++;
            Word func = *codePtr++;
            dataStk.drop(argc);
            if(env->callFunc(this, func, &dataStk[0], argc))
               goto exec_intr;
         }
         NextCase();

      DeclCase(CallFunc_Lit):
         {
            Word        argc = *codePtr++;
            Word        func = *codePtr++;
            Word const *argv =  codePtr;
            codePtr += argc;
            if(env->callFunc(this, func, argv, argc))
               goto exec_intr;
         }
         NextCase();

      DeclCase(CallSpec):
         {
            Word argc = *codePtr++;
            Word spec = *codePtr++;
            dataStk.drop(argc);
            env->callSpec(this, spec, &dataStk[0], argc);
         }
         NextCase();

      DeclCase(CallSpec_Lit):
         {
            Word        argc = *codePtr++;
            Word        spec = *codePtr++;
            Word const *argv =  codePtr;
            codePtr += argc;
            env->callSpec(this, spec, argv, argc);
         }
         NextCase();

      DeclCase(CallSpec_R1):
         {
            Word argc = *codePtr++;
            Word spec = *codePtr++;
            dataStk.drop(argc);
            dataStk.push(env->callSpec(this, spec, &dataStk[0], argc));
         }
         NextCase();

      DeclCase(Retn):
         // If no call frames left, terminate the thread.
         if(callStk.empty())
            goto thread_stop;

         // Apply call frame.
         codePtr     = callStk[1].codePtr;
         module      = callStk[1].module;
         scopeMod    = callStk[1].scopeMod;
         localArr.free(callStk[1].locArrC);
         localReg.free(callStk[1].locRegC);

         // Drop call frame.
         callStk.drop();

         NextCase();

         //================================================
         // Drop codes.
         //

         OpSet(Drop);

      DeclCase(Drop_Nul):
        dataStk.drop();
        NextCase();

      DeclCase(Drop_ScrRet):
        dataStk.drop();
        result = dataStk[0];
        NextCase();

         //================================================
         // Jump codes.
         //

      DeclCase(Jcnd_Lit):
        if(dataStk[1] == *codePtr++)
        {
           dataStk.drop();
           BranchTo(*codePtr);
        }
        else
           ++codePtr;
        NextCase();

      DeclCase(Jcnd_Nil):
         if(dataStk.drop(), dataStk[0])
            ++codePtr;
         else
            BranchTo(*codePtr);
         NextCase();

      DeclCase(Jcnd_Tab):
         if(auto jump = module->jumpMapV[*codePtr++].table.find(dataStk[1]))
         {
            dataStk.drop();
            BranchTo(*jump);
         }
         NextCase();

      DeclCase(Jcnd_Tru):
         if(dataStk.drop(), dataStk[0])
            BranchTo(*codePtr);
         else
            ++codePtr;
         NextCase();

      DeclCase(Jump_Lit):
        BranchTo(*codePtr);
        NextCase();

      DeclCase(Jump_Stk):
         dataStk.drop();
         BranchTo(dataStk[0] < module->jumpV.size() ? module->jumpV[dataStk[0]].codeIdx : 0);
         NextCase();

         //================================================
         // Push codes.
         //

      DeclCase(Pfun_Lit):
         if(*codePtr < module->functionV.size())
            dataStk.push(module->functionV[*codePtr]->idx);
         else
            dataStk.push(0);
         ++codePtr;
         NextCase();

      DeclCase(Pstr_Stk):
         if(dataStk[1] < module->stringV.size())
            dataStk[1] = ~module->stringV[dataStk[1]]->idx;
         NextCase();

      DeclCase(Push_GblArr): dataStk[1] = scopeGbl->arrV[*codePtr++].find(dataStk[1]); NextCase();
      DeclCase(Push_GblReg): dataStk.push(scopeGbl->regV[*codePtr++]); NextCase();
      DeclCase(Push_HubArr): dataStk[1] = scopeHub->arrV[*codePtr++].find(dataStk[1]); NextCase();
      DeclCase(Push_HubReg): dataStk.push(scopeHub->regV[*codePtr++]); NextCase();
      DeclCase(Push_Lit):    dataStk.push(*codePtr++); NextCase();
      DeclCase(Push_LitArr): for(auto i = *codePtr++; i--;) dataStk.push(*codePtr++); NextCase();
      DeclCase(Push_LocArr): dataStk[1] = localArr[*codePtr++].find(dataStk[1]); NextCase();
      DeclCase(Push_LocReg): dataStk.push(localReg[*codePtr++]); NextCase();
      DeclCase(Push_ModArr): dataStk[1] = scopeMod->arrV[*codePtr++]->find(dataStk[1]); NextCase();
      DeclCase(Push_ModReg): dataStk.push(*scopeMod->regV[*codePtr++]); NextCase();

      DeclCase(Push_StrArs):
         dataStk.drop();
         dataStk[1] = scopeMap->getString(dataStk[1])->get(dataStk[0]);
         NextCase();

         //================================================
         // Script control codes.
         //

      DeclCase(ScrDelay):
         dataStk.drop();
         delay = dataStk[0];
         goto exec_intr;

      DeclCase(ScrDelay_Lit):
         delay = *codePtr++;
         goto exec_intr;

      DeclCase(ScrHalt):
         state = ThreadState::Paused;
         goto exec_intr;

      DeclCase(ScrRestart):
         BranchTo(script->codeIdx);
         NextCase();

      DeclCase(ScrTerm):
         goto thread_stop;

      DeclCase(ScrWaitI):
         dataStk.drop();
         state = {ThreadState::WaitScrI, dataStk[0]};
         goto exec_intr;

      DeclCase(ScrWaitI_Lit):
         state = {ThreadState::WaitScrI, *codePtr++};
         goto exec_intr;

      DeclCase(ScrWaitS):
         dataStk.drop();
         state = {ThreadState::WaitScrS, dataStk[0]};
         goto exec_intr;

      DeclCase(ScrWaitS_Lit):
         state = {ThreadState::WaitScrS, *codePtr++};
         goto exec_intr;

         //================================================
         // Stack control codes.
         //

      DeclCase(Copy):
         {auto temp = dataStk[1]; dataStk.push(temp);}
         NextCase();

      DeclCase(Swap):
         std::swap(dataStk[2], dataStk[1]);
         NextCase();

         //================================================
         // Unary operator codes.
         //

         OpSet(DecU);
         OpSet(IncU);

      DeclCase(InvU):
         dataStk[1] = ~dataStk[1];
         NextCase();

      DeclCase(NegI):
         dataStk[1] = ~dataStk[1] + 1;
         NextCase();

      DeclCase(NotU):
         dataStk[1] = !dataStk[1];
         NextCase();
      }

   thread_stop:
      stop();
   }
}

// EOF

