#ifndef BYTECODE_H__ #define BYTECODE_H__ 1 /*--------------------------------------------------------------------------- * Types and Macros used to describe bytecode * *--------------------------------------------------------------------------- * While topically part of exec.h, the actual bytecode types and macros * have been exported into this file to reduce the coupling on exec.h. * Most files only need to know about bytecode_t and bytecode_p, and not * about all the other stuff in exec.h . * * Bytecode * -------- * * As the name conveys, the LPC programs are compiled into a bytecode, * assuming 8-Bit-Bytes. Since we have more than 256 opcodes, the less * often used instructions are encoded in two-byte opcodes: a prefix * byte and an sub-opcode. The translation opcode -> prefix:sub-opcode * is defined in the instrs[] table using the .prefix and .opcode * fields. * * To achieve platform independance, the driver does not operate directly * with 'char's and 'char *'s, but instead with 'bytecode_t' and * 'bytecode_p's. Combined with some macros this allows the implementation * even on platforms with CHAR_BITs != 8. * TODO: This support is far from complete/comprehensive, and some values * TODO:: in the bytecode are stored in host-sizes and -layouts. * * The bytecode itself is divided into functions: the code for every * function is (except for absolute jumps) selfcontained and prepended * by a header holding the name of the function, and the number and types * of expected arguments. The advantage is that for a given function * the driver does not need to know if it is part of a program or a * lambda closure compiled at runtime: in both cases all necessary * information about the function is right where its code is. * * The maximum size of a program is limited by the biggest offset * that can be stored in the 'functions' array, currently 1 MByte. * A lot of internal offset counters are shorts even, though so far * this never caused a problem. *--------------------------------------------------------------------------- */ #include "driver.h" #include "typedefs.h" /* --- Byte code --- * * The program code is stored as byte code. The following definitions * and macros allow its implementation even on platforms with more than * 8 bits per character. * TODO: This portability is far from complete, and not used everywhere, * TODO:: not even in the compiler. * * bytecode_t: an integral type holding the numbers 0..255. * bytecode_p: an integral type addressing a bytecode. This need not * be a pointer. * * bytecode_t GET_CODE(bytecode_p p) * bytecode_t LOAD_CODE(bytecode_p p) * Return the bytecode from *p, the LOAD_ variant then increments p. * * void PUT_CODE(bytecode_p p, bytecode_t c) * void STORE_CODE(bytecode_p p, bytecode_t c) * void RSTORE_CODE(bytecode_p p, bytecode_t c) * Store the bytecode c in *p, the STORE_ variant then increments p. * The RSTORE_ variant pre-decrements p. * * char GET_INT8(p) , LOAD_INT8(p) * uchar GET_UINT8(p), LOAD_UINT8(p) * Return the 8-Bit (unsigned) int stored at
, the LOAD_ variants * then increment
.
*
* void PUT_INT8(p, char c), STORE_INT8(p, char c)
* void PUT_UINT8(p, uchar c), STORE_UINT8(p, uchar c)
* Store the 8-Bit (unsigned) int , the STORE_ variants
* then increment .
*
* void GET_SHORT ([unsigned] short d, bytecode_p p)
* void LOAD_SHORT([unsigned] short d, bytecode_p p)
* Load the (unsigned) short 'd' stored at , the LOAD_ variant
* then increments .
* TODO: Currently, all SHORTs must be 2 bytes.
*
* void PUT_SHORT (bytecode_p p, [unsigned] short d)
* void STORE_SHORT(bytecode_p p, [unsigned] short d)
* void RSTORE_SHORT(bytecode_p p, [unsigned] short d)
* Store the (unsigned) short , the STORE_ variant
* then increments . The RSTORE_ variant pre-decrements .
* TODO: Currently, all SHORTs must be 2 bytes.
*
* void GET_INT16 ([unsigned] int16 d, bytecode_p p)
* void LOAD_INT16([unsigned] int16 d, bytecode_p p)
* Load the (unsigned) int16 'd' stored at , the LOAD_ variant
* then increments .
*
* void GET_LONG ([unsigned] long d, bytecode_p p)
* void LOAD_LONG([unsigned] long d, bytecode_p p)
* Load the (unsigned) long 'd' stored at , the LOAD_ variant
* then increments .
* TODO: Currently, all LONGs must be 4 bytes.
*
* void PUT_LONG (bytecode_p p, [unsigned] long d)
* void STORE_LONG(bytecode_p p, [unsigned] long d)
* void RSTORE_LONG(bytecode_p p, [unsigned] long d)
* Store the (unsigned) long , the STORE_ variant
* then increments . The RSTORE_ variant pre-decrements .
* TODO: Currently, all LONGs must be 4 bytes.
*
* void LOAD_INT32([unsigned] int32 d, bytecode_p p)
* void GET_INT32([unsigned] int32 d, bytecode_p p)
* Load the (unsigned) in32 'd' stored at , the LOAD_ variant
* then increments .
*
* void STORE_INT32([unsigned] int32 d, bytecode_p p)
* void PUT_INT32([unsigned] int32 d, bytecode_p p)
* Store the (unsigned) int32 'd' at , the STORE_ variant
* then increments .
*/
#if CHAR_BIT == 8
typedef unsigned char bytecode_t;
typedef bytecode_t * bytecode_p;
#define GET_CODE(p) (*(p))
#define LOAD_CODE(p) (*(p)++)
#define PUT_CODE(p,c) (*(p) = (c))
#define STORE_CODE(p,c) (*(p)++ = (c))
#define RSTORE_CODE(p,c) (*--(p) = (c))
/* TODO: all these casts yield rvalues, so they shouldn't compile
* TODO:: (and on xlc on AIX some of them indeed don't).
*/
#define GET_UINT8(p) (*((unsigned char *)(p)))
#define GET_INT8(p) (*((signed char *)(p)))
#define LOAD_UINT8(p) (*((unsigned char *)(p)++))
#define LOAD_INT8(p) (*((signed char *)(p)++))
#define PUT_UINT8(p,c) (*((unsigned char *)(p)) = (c))
#define PUT_INT8(p,c) (*((signed char *)(p)) = (c))
#define STORE_UINT8(p,c) (*((unsigned char *)(p)++) = (c))
#define STORE_INT8(p,c) (*((signed char *)(p)++) = (c))
/* TODO: These generic mem-macros should go into a macros.h. See also
* TODO:: how mudos does it.
* Note: the lowlevel BYTE macros can't use MACRO(), since this is
* needed on the next abstraction level, and a macro can't be nested
* into itself.
*/
#define LOAD_2BYTE(d,p) ( ((char *)&(d))[0] = *(char *)(p)++, \
((char *)&(d))[1] = *(char *)(p)++)
#define GET_2BYTE(d,p) ( ((char *)&(d))[0] = ((char *)(p))[0], \
((char *)&(d))[1] = ((char *)(p))[1] )
#define STORE_2BYTE(p,d) do {\
unsigned char * _q, ** _qq; \
_q = (unsigned char *)(p); \
_qq = (unsigned char **)&(p); \
_q[0] = ((unsigned char *)&(d))[0]; \
_q[1] = ((unsigned char *)&(d))[1]; \
*_qq += 2; \
} while(0)
#define RSTORE_2BYTE(p,d) do {\
unsigned char * _q, ** _qq; \
_q = (unsigned char *)(p); \
_qq = (unsigned char **)&(p); \
_q[-2] = ((unsigned char *)&(d))[0]; \
_q[-1] = ((unsigned char *)&(d))[1]; \
*_qq -= 2; \
} while(0)
#define PUT_2BYTE(p,d) ( ((char *)(p))[0] = ((char *)&(d))[0], \
((char *)(p))[1] = ((char *)&(d))[1] )
#define LOAD_3BYTE(d,p) ( (d) = ((*(unsigned char *)(p)++) << 16) \
| ((*(unsigned char *)(p)++) << 8) \
| (*(unsigned char *)(p)++) )
#define GET_3BYTE(d,p) ( (d) = ((((unsigned char *)(p))[0]) << 16) \
| ((((unsigned char *)(p))[1]) << 8) \
| (((unsigned char *)(p))[2]) )
#define STORE_3BYTE(p,d) do {\
unsigned char * _q, ** _qq; \
unsigned long _d = (unsigned long)(d); \
_q = (unsigned char *)(p); \
_qq = (unsigned char **)&(p); \
_q[0] = (unsigned char) (_d >> 16) \
_q[1] = (unsigned char) (_d >> 8) \
_q[2] = (unsigned char) (_d) \
*_qq += 3; \
} while(0)
#define PUT_3BYTE(p,d) ( ((unsigned char *)(p))[0] = (unsigned char)((d) >> 16), \
((unsigned char *)(p))[1] = (unsigned char)((d) >> 8), \
((unsigned char *)(p))[2] = (unsigned char)(d) )
#define LOAD_4BYTE(d,p) ( ((char *)&(d))[0] = *(char *)(p)++, \
((char *)&(d))[1] = *(char *)(p)++, \
((char *)&(d))[2] = *(char *)(p)++, \
((char *)&(d))[3] = *(char *)(p)++ )
#define GET_4BYTE(d,p) ( ((char *)&(d))[0] = ((char *)(p))[0], \
((char *)&(d))[1] = ((char *)(p))[1], \
((char *)&(d))[2] = ((char *)(p))[2], \
((char *)&(d))[3] = ((char *)(p))[3] )
#define STORE_4BYTE(p,d) ( *(unsigned char *)(p)++ = ((char *)&(d))[0], \
*(unsigned char *)(p)++ = ((char *)&(d))[1], \
*(unsigned char *)(p)++ = ((char *)&(d))[2], \
*(unsigned char *)(p)++ = ((char *)&(d))[3] )
#define PUT_4BYTE(p,d) ( ((char *)(p))[0] = ((char *)&(d))[0], \
((char *)(p))[1] = ((char *)&(d))[1], \
((char *)(p))[2] = ((char *)&(d))[2], \
((char *)(p))[3] = ((char *)&(d))[3] )
#if SIZEOF_SHORT == 2
# define GET_SHORT(d,p) GET_2BYTE(d,p)
# define LOAD_SHORT(d,p) LOAD_2BYTE(d,p)
# define PUT_SHORT(p,d) MACRO(unsigned short _us = (unsigned short)d; \
PUT_2BYTE(p,_us);)
# define STORE_SHORT(p,d) MACRO(unsigned short _us = (unsigned short)d; \
STORE_2BYTE(p,_us);)
# define RSTORE_SHORT(p,d) MACRO(unsigned short _us = (unsigned short)d; \
RSTORE_2BYTE(p,_us);)
#else
# error "Unsupported size of short."
#endif /* SIZEOF_SHORT */
#if SIZEOF_LONG == 4
# define GET_LONG(d,p) GET_4BYTE(d,p)
# define LOAD_LONG(d,p) LOAD_4BYTE(d,p)
# define PUT_LONG(p,d) MACRO(unsigned long _us = (unsigned long)d; \
PUT_4BYTE(p,_us);)
# define STORE_LONG(p,d) MACRO(unsigned long _us = (unsigned long)d; \
STORE_4BYTE(p,_us);)
# define RSTORE_LONG(p,d) MACRO(unsigned long _us = (unsigned long)d; \
RSTORE_4BYTE(p,_us);)
#elif SIZEOF_LONG == 8 && SIZEOF_INT == 4
# define GET_LONG(d,p) MACRO(int _ui; GET_4BYTE(_ui,p); \
d = _ui;)
# define LOAD_LONG(d,p) MACRO(int _ui; LOAD_4BYTE(_ui,p); \
d = _ui;)
# define PUT_LONG(p,d) MACRO(unsigned int _ui = (unsigned int)d; \
PUT_4BYTE(p,_ui);)
# define STORE_LONG(p,d) MACRO(unsigned int _ui = (unsigned int)d; \
STORE_4BYTE(p,_ui);)
# define RSTORE_LONG(p,d) MACRO(unsigned int _ui = (unsigned int)d; \
RSTORE_4BYTE(p,_ui);)
#else
# error "Unsupported size of long."
#endif /* SIZEOF_LONG */
#define LOAD_INT16(d,p) LOAD_2BYTE(d,p)
#define GET_INT32(d,p) GET_4BYTE(d,p)
#define PUT_INT32(p,d) PUT_4BYTE(p,d)
#ifdef NO64BIT
# define LOAD_INT32(d,p) LOAD_4BYTE(d,p)
# define STORE_INT32(p,d) STORE_4BYTE(p,d)
#else
# define GET_LONG(d,p) GET_4BYTE(d,p)
# define LOAD_LONG(d,p) LOAD_4BYTE(d,p)
# define PUT_LONG(p,d) MACRO(unsigned long _us = (unsigned long)d; \
PUT_4BYTE(p,_us);)
# define STORE_LONG(p,d) MACRO(unsigned long _us = (unsigned long)d; \
STORE_4BYTE(p,_us);)
# define RSTORE_LONG(p,d) MACRO(unsigned long _us = (unsigned long)d; \
RSTORE_4BYTE(p,_us);)
/* TODO: The above assumes sizeof(long) == 4. */
# define LOAD_INT32(d,p) LOAD_2BYTE(d,p)
#endif
#endif /* CHAR_BIT */
#ifndef GET_CODE
# error "No bytecode type defined."
#endif
/***************************************************************************/
#endif /* BYTECODE_H__ */