/* core.c - 가상 머신의 모든 루틴이 들어 있는 곳.
   Copyright (C) 2006 Weongyo Jeong (weongyo@gmail.com)

This file is part of ROVM.

ROVM is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2, or (at your option) any later
version.

ROVM is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING.  If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.  */

#include "types.h"
#include "mpool.h"
#include "tktree.h"
#include "sha1.h"
#include "listen.h"
#include "rovm.h"

#include "thread.h"
#include "thread_mutex.h"
#include "thread_cond.h"

#include "mpm_worker_fdqueue.h"
#include "mpm_worker_pod.h"
#include "mpm_worker.h"

#include "connection.h"
#include "request.h"
#include "proc_rc.h"

#include "log.h"
#include "common.h"
#include "ticket.h"

#define ROVM_CODE_DEBUG(EXPN)           /* nothing */

#define GETPC(OFFSET)                   CORE_OP_IDX (ri, pc + 1 + (OFFSET))

#define PUSH(N)                         TICKET_SP (CORE_TICKET (ri)) += (N)
#define POP(N)                          TICKET_SP (CORE_TICKET (ri)) -= (N)
/** Stack 번호에 따라 - (마이너스) 가 붙는 것을 명심하기 바란다.  */
#define	STACK(N)                        (&TICKET_SP (CORE_TICKET (ri))[-(N)])
#define RSTACK                          STACK

/** 스택의 경계점을 검사하는 매크로이다.  MINNUM 은 스택상에 최소 존재해야 하는 
    채워진 slot 의 갯수를 의미하며 만약 그것보다 작다면 -1 을 반환해 버린다. */
#define _CB_(MINNUM)                                                    \
  if ((TICKET_STACK (CORE_TICKET (ri)) + (MINNUM - 1)) > TICKET_SP (CORE_TICKET (ri))) \
    {                                                                   \
      request_add_error (CORE_REQUEST (ri), ERRLOG_ERR, ERRLOG_MARK,    \
                         "Overflow of Stack Boundary");                 \
      return -1;                                                        \
    }

#define _CHAR_(NODE)                    SET_STACK_TYPE (NODE, STACK_TYPE_CHAR)
#define _INT_(NODE)                     SET_STACK_TYPE (NODE, STACK_TYPE_INT)

#define	ADD_INT(DST, SRC1, SRC2)                                        \
  _CB_ (2);                                                             \
  STACK_V_INT (DST) = STACK_V_INT (SRC1) + STACK_V_INT (SRC2);          \
  _INT_ (DST)

#define MOVE_NUM_CHAR_CONST(DST, SRC)           \
  _CHAR_ (DST);                                 \
  STACK_V_CHAR (DST) = (SRC)

typedef char            rovm_int8_t;

#define CORE_REQUEST(NODE)              ((NODE)->r)
#define CORE_TICKET(NODE)               ((NODE)->tk)
#define CORE_OP(NODE)                   ((NODE)->op)
#define CORE_OP_IDX(NODE, IDX)          ((NODE)->op[(IDX)])
#define CODE_OPLEN(NODE)                ((NODE)->oplen)
#define CORE_NPC(NODE)                  ((NODE)->npc)
#define CORE_LOW(NODE)                  ((NODE)->low)
#define CORE_HIGH(NODE)                 ((NODE)->high)
#define CORE_IDX(NODE)                  ((NODE)->idx)

/**
   가상 머신을 호출하는 것의 정보를 보관하기 위해 사용하는 구조체.  이 구조체가 생성되게 된
   이유는 가상 머신의 각각의 opcode 를 호출할 때 전달되는 argument 수를 최소로 줄이기 위해서
   이다.
 */
struct rovmcore_info
{
  /** 사용자 request 에 대한 구조체.  */
  request_rec *r;
  /** 현재 가상 머신이 실행되는 ticket 기반.  */
  struct ticket *tk;

  /** 현재 처리 중인 OPCODE.  */
  rc_opcode_t *op;
  /** 현재 처리 중인 OPCODE 의 길이.  */
  rc_size_t oplen;

  /** 다음 PC (Program Counter).  */
  uint32_t volatile npc;

  /** 가상 머신상에서 계산시 자유롭게 사용하는 변수.  대부분 값의 lower 파트를 다루는데,
      사용된다.  */
  int low;
  /** 가상 머신상에서 계산시 자유롭게 사용하는 변수.  대부분 값의 higher 파트를 다루는데,
      사용된다.  */
  int high;
  /** 가상 머신상에서 계산시 자유롭게 사용하는 변수.  Index 번호를 다룰 때 사용된다.  */
  int idx;  
};

enum opcode
  {
#define DEF_OPCODE(ENUM, NAME, NUMBER, OPLEN, OPFUNC)   ENUM ,
#include "opcode.def"
#undef DEF_OPCODE
    MAX_OPCODE
  };

/**
   각 Opcode 가 가지는 길이에 대한 배열.  Opcode 순서대로 나열되어 있으며,
   virtual machine 에서 현재 PC (Program Counter) 의 다음 PC 를
   계산할 때 사용된다.  
*/
const int opcode_len[MAX_OPCODE] = 
  {
#define DEF_OPCODE(ENUM, NAME, NUMBER, OPLEN, OPFUNC)   OPLEN ,
#include "opcode.def"
#undef DEF_OPCODE
  };

const char *opcode_name[MAX_OPCODE] = 
  {
#define DEF_OPCODE(ENUM, NAME, NUMBER, OPLEN, OPFUNC)   NAME ,
#include "opcode.def"
#undef DEF_OPCODE
  };

/**
   OPCODEFUNC_* 함수들이 공통적으로 가지게 되는 인자들을 정의한 매크로이다.
   이 값이 수정되면 전체 함수 모양이 수정되게 된다.
 */
#define OPCODEFUNC_ARGUMENT     struct rovmcore_info *ri, unsigned int pc
/**
   OPCODEFUNC_* 함수들의 리턴 타입을 정의하는 매크로이다.
 */
#define OPCODEFUNC_RET_T        int

/**
   OPCODEFUNC_* 함수들의 prototype 정의
 */
#define DEF_OPCODE(ENUM, NAME, NUMBER, OPLEN, OPFUNC)   OPCODEFUNC_RET_T OPFUNC (OPCODEFUNC_ARGUMENT);
#include "opcode.def"
#undef DEF_OPCODE

/**
   OPCODEFUNC_* 함수 포인터 배열
 */
OPCODEFUNC_RET_T (*opcode_func[MAX_OPCODE]) (OPCODEFUNC_ARGUMENT) = 
{
#define DEF_OPCODE(ENUM, NAME, NUMBER, OPLEN, OPFUNC)   OPFUNC ,
#include "opcode.def"
#undef DEF_OPCODE  
};

/** [0 (0x0)] OPCODE `NOP' 을 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_nop (OPCODEFUNC_ARGUMENT)
{
  /* nothing to do.  */
  return 0;
}

/** [16 (0x10)] OPCODE `CPUSH' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_cpush (OPCODEFUNC_ARGUMENT)
{
  CORE_LOW (ri) = (rovm_int8_t) GETPC (0);

  ROVM_CODE_DEBUG (("cpush\t%d", CORE_LOW (ri)));
  PUSH (1);
  MOVE_NUM_CHAR_CONST (STACK (0), CORE_LOW (ri));

  return 0;
}

/** [90 (0x60)] OPCODE `IADD' 를 처리한다.  */
OPCODEFUNC_RET_T
opcodefunc_iadd (OPCODEFUNC_ARGUMENT)
{
  ROVM_CODE_DEBUG (("iadd"));

  ADD_INT (STACK (1), RSTACK (1), RSTACK (0));
  POP (1);

  return 0;
}

/**
   OPCODE 중 아직 만들어지지 않았거나, 나중에 사용할 목적으로 미리 예약해 놓은
   값이 호출되었을 때, 사용되는 callback 함수이다.  이 함수는 호출되어서는 
   않된다.
 */
OPCODEFUNC_RET_T
opcodefunc_reserved (OPCODEFUNC_ARGUMENT)
{
  /* 여기에 도달했다는 것은 opcode 값이 예약된 opcode 값을 가지고 있다는 것을
     뜻한다.  이것은 잘못된 수행 과정이다.  */

  request_add_error (CORE_REQUEST (ri), ERRLOG_ERR, ERRLOG_MARK,
                     "Invalid Opcode");

  return -1;
}

/**
   가상 머신 상에서 실제 opcode 들을 실행시키는 함수.
 */
rc_status_t
rovmcore (ri)
     struct rovmcore_info *ri;
{
  /* ??? 현재 구조로는 아래의 PC 변수를 register 로 등록한다고 하더라도 실질적인
         속도 개선에 별로 영향을 줄 것 같지 않은 느낌이 든다.  저의 의견이 맞나요?  */
  register unsigned int pc;

  while (1)
    {
      pc = CORE_NPC (ri);
      
      CORE_NPC (ri) = pc + opcode_len[(CORE_OP_IDX (ri, pc))];

      if (opcode_func[(CORE_OP_IDX (ri, pc))] (ri, pc))
        return -1;

      if (CORE_NPC (ri) >= CODE_OPLEN (ri))
        break;
    }

  return 0;
}

/**
   ROVMCORE_INFO 구조체를 새롭게 할당하여 반환한다.

   @param r     Request 구조체
   @param tk    사용자가 사용하고자 하는 Ticket 구조체
   @return      새롭게 할당된 ROVMCORE_INFO 구조체를 반환한다.
 */
struct rovmcore_info *
init_rovmcore_info (r, tk, op, oplen)
     request_rec *r;
     struct ticket *tk;
     rc_opcode_t *op;
     rc_size_t oplen;
{
  struct rovmcore_info *ri;

  ri = (struct rovmcore_info *) mp_alloc (REQUEST_POOL (r), sizeof (struct rovmcore_info));
  CORE_REQUEST (ri) = r;
  CORE_TICKET (ri) = tk;
  CORE_NPC (ri) = 0;

  CORE_OP (ri) = op;
  CODE_OPLEN (ri) = oplen;

  return ri;
}

/**
   사용을 완료한 ROVMCORE_INFO 구조체를 완료한다.

   @param ri    마무리하길 원하는 ROVMCORE_INFO 구조체.
 */
int
finish_rovmcore_info (ri)
     struct rovmcore_info *ri;
{
  /* nothing.  */

  return 0;
}

/**
   Request `OPCODE' 를 처리하기 위한 메인 VM 함수 중 하나이다.  원격에서 전송된
   길이가 OPLEN 이고 OP 인 opcode 를 처리하게 된다.

   @param r     Request 구조체
   @param tk    사용자가 사용하고자 하는 Ticket 구조체
   @param op    사용자가 보낸 opcode
   @param oplen 사용자가 보낸 opcode 의 총 길이
   @return      만약 성공적으로 VM 이 실행되었다면 0 을 반환하고 그렇지 않다면
                -1 을 반환하게 된다.
 */
int
rovmcore_main (r, tk, op, oplen)
     request_rec *r;
     struct ticket *tk;
     rc_opcode_t *op;
     rc_size_t oplen;
{
  int rv;
  struct rovmcore_info *ri;

  ri = init_rovmcore_info (r, tk, op, oplen);
  if (!ri)
    return -1;

  rv = rovmcore (ri);

  finish_rovmcore_info (ri);

  return rv;
}
