* Copyright (C) 2003, 2004, 2005, 2014, 2015 Filip Pizlo. All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY FILIP PIZLO ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL FILIP PIZLO OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef FP_GPC_H
#define FP_GPC_H
#include "tsf.h"
#include <stdio.h>
* as gpc_spec.in. */
enum gpc_cell {
GPC_I_NOP,
* it contains a label 'number' as an operand. these numbers
* do not have to be unique. whenever a label is used, it will be
* translated into a jump to the nearest label instruction that has
* that label as its operand, by searching either in the forward or
* backward direction, depending on whether or not it is a forward
* or backward jump. */
GPC_I_LABEL,
* copy macros and then add conversion to them. the operands are
* as follows:
* COPY_xTOyz_INCabc <offset>
* The effect is that the pointer at the top of the stack has
* offset added to it; in the case of INCDST, this serves as the
* source, while for INCSRC it serves as the destination. */
GPC_I_COPY_HTONC_INCDST,
GPC_I_COPY_HTONS_INCDST,
GPC_I_COPY_HTONL_INCDST,
GPC_I_COPY_HTONLL_INCDST,
GPC_I_COPY_HTONF_INCDST,
GPC_I_COPY_HTOND_INCDST,
GPC_I_COPY_HTONCHOICE_TO_UI8_INCDST,
* -> if we do not change size or reduce size, we can ignore the
* size.
* -> if we expand, then the signedness of the original determines
* whether or not we do sign expansion.
* -> if expanding floats to ints, the signedness of the target
* matters (though I'm not sure why - this is only a conservative
* estimate based on looking at the things that gcc does for
* float to int conversion).
*/
GPC_I_COPY_NTOHC_TO_BIT_INCSRC,
GPC_I_COPY_NTOHC_INCSRC,
GPC_I_COPY_NTOHC_TO_S_Z_INCSRC,
GPC_I_COPY_NTOHC_TO_S_E_INCSRC,
GPC_I_COPY_NTOHC_TO_L_Z_INCSRC,
GPC_I_COPY_NTOHC_TO_L_E_INCSRC,
GPC_I_COPY_NTOHC_TO_LL_Z_INCSRC,
GPC_I_COPY_NTOHC_TO_LL_E_INCSRC,
GPC_I_COPY_NTOHC_TO_F_Z_INCSRC,
GPC_I_COPY_NTOHC_TO_F_E_INCSRC,
GPC_I_COPY_NTOHC_TO_D_Z_INCSRC,
GPC_I_COPY_NTOHC_TO_D_E_INCSRC,
GPC_I_COPY_NTOHS_TO_BIT_INCSRC,
GPC_I_COPY_NTOHS_TO_C_INCSRC,
GPC_I_COPY_NTOHS_INCSRC,
GPC_I_COPY_NTOHS_TO_L_Z_INCSRC,
GPC_I_COPY_NTOHS_TO_L_E_INCSRC,
GPC_I_COPY_NTOHS_TO_LL_Z_INCSRC,
GPC_I_COPY_NTOHS_TO_LL_E_INCSRC,
GPC_I_COPY_NTOHS_TO_F_Z_INCSRC,
GPC_I_COPY_NTOHS_TO_F_E_INCSRC,
GPC_I_COPY_NTOHS_TO_D_Z_INCSRC,
GPC_I_COPY_NTOHS_TO_D_E_INCSRC,
GPC_I_COPY_NTOHL_TO_BIT_INCSRC,
GPC_I_COPY_NTOHL_TO_C_INCSRC,
GPC_I_COPY_NTOHL_TO_S_INCSRC,
GPC_I_COPY_NTOHL_INCSRC,
GPC_I_COPY_NTOHL_TO_LL_Z_INCSRC,
GPC_I_COPY_NTOHL_TO_LL_E_INCSRC,
GPC_I_COPY_NTOHL_TO_F_Z_INCSRC,
GPC_I_COPY_NTOHL_TO_F_E_INCSRC,
GPC_I_COPY_NTOHL_TO_D_Z_INCSRC,
GPC_I_COPY_NTOHL_TO_D_E_INCSRC,
GPC_I_COPY_NTOHLL_TO_BIT_INCSRC,
GPC_I_COPY_NTOHLL_TO_C_INCSRC,
GPC_I_COPY_NTOHLL_TO_S_INCSRC,
GPC_I_COPY_NTOHLL_TO_L_INCSRC,
GPC_I_COPY_NTOHLL_INCSRC,
GPC_I_COPY_NTOHLL_TO_F_Z_INCSRC,
GPC_I_COPY_NTOHLL_TO_F_E_INCSRC,
GPC_I_COPY_NTOHLL_TO_D_Z_INCSRC,
GPC_I_COPY_NTOHLL_TO_D_E_INCSRC,
GPC_I_COPY_NTOHF_TO_BIT_INCSRC,
GPC_I_COPY_NTOHF_TO_C_Z_INCSRC,
GPC_I_COPY_NTOHF_TO_C_E_INCSRC,
GPC_I_COPY_NTOHF_TO_S_Z_INCSRC,
GPC_I_COPY_NTOHF_TO_S_E_INCSRC,
GPC_I_COPY_NTOHF_TO_L_Z_INCSRC,
GPC_I_COPY_NTOHF_TO_L_E_INCSRC,
GPC_I_COPY_NTOHF_TO_LL_Z_INCSRC,
GPC_I_COPY_NTOHF_TO_LL_E_INCSRC,
GPC_I_COPY_NTOHF_INCSRC,
GPC_I_COPY_NTOHF_TO_D_INCSRC,
GPC_I_COPY_NTOHD_TO_BIT_INCSRC,
GPC_I_COPY_NTOHD_TO_C_Z_INCSRC,
GPC_I_COPY_NTOHD_TO_C_E_INCSRC,
GPC_I_COPY_NTOHD_TO_S_Z_INCSRC,
GPC_I_COPY_NTOHD_TO_S_E_INCSRC,
GPC_I_COPY_NTOHD_TO_L_Z_INCSRC,
GPC_I_COPY_NTOHD_TO_L_E_INCSRC,
GPC_I_COPY_NTOHD_TO_LL_Z_INCSRC,
GPC_I_COPY_NTOHD_TO_LL_E_INCSRC,
GPC_I_COPY_NTOHD_TO_F_INCSRC,
GPC_I_COPY_NTOHD_INCSRC,
* offsets; these are designed for primitives. the data is copied
* from the given offset from the topmost pointer to the given offset
* from the second-to-topmost pointer. call these guys like so:
* GPC_I_COPY_x_TO_y <dest offset> <source offset> */
GPC_I_COPY_BIT,
from bits */
GPC_I_COPY_BIT_TO_S,
GPC_I_COPY_BIT_TO_L,
GPC_I_COPY_BIT_TO_LL,
GPC_I_COPY_BIT_TO_F,
GPC_I_COPY_BIT_TO_D,
GPC_I_COPY_C,
GPC_I_COPY_C_TO_S_Z,
GPC_I_COPY_C_TO_S_E,
GPC_I_COPY_C_TO_L_Z,
GPC_I_COPY_C_TO_L_E,
GPC_I_COPY_C_TO_LL_Z,
GPC_I_COPY_C_TO_LL_E,
GPC_I_COPY_C_TO_F_Z,
GPC_I_COPY_C_TO_F_E,
GPC_I_COPY_C_TO_D_Z,
GPC_I_COPY_C_TO_D_E,
GPC_I_COPY_S_TO_BIT,
GPC_I_COPY_S_TO_C,
GPC_I_COPY_S,
GPC_I_COPY_S_TO_L_Z,
GPC_I_COPY_S_TO_L_E,
GPC_I_COPY_S_TO_LL_Z,
GPC_I_COPY_S_TO_LL_E,
GPC_I_COPY_S_TO_F_Z,
GPC_I_COPY_S_TO_F_E,
GPC_I_COPY_S_TO_D_Z,
GPC_I_COPY_S_TO_D_E,
GPC_I_COPY_L_TO_BIT,
GPC_I_COPY_L_TO_C,
GPC_I_COPY_L_TO_S,
GPC_I_COPY_L,
GPC_I_COPY_L_TO_LL_Z,
GPC_I_COPY_L_TO_LL_E,
GPC_I_COPY_L_TO_F_Z,
GPC_I_COPY_L_TO_F_E,
GPC_I_COPY_L_TO_D_Z,
GPC_I_COPY_L_TO_D_E,
GPC_I_COPY_LL_TO_BIT,
GPC_I_COPY_LL_TO_C,
GPC_I_COPY_LL_TO_S,
GPC_I_COPY_LL_TO_L,
GPC_I_COPY_LL,
GPC_I_COPY_LL_TO_F_Z,
GPC_I_COPY_LL_TO_F_E,
GPC_I_COPY_LL_TO_D_Z,
GPC_I_COPY_LL_TO_D_E,
GPC_I_COPY_F_TO_BIT,
GPC_I_COPY_F_TO_C_Z,
GPC_I_COPY_F_TO_C_E,
GPC_I_COPY_F_TO_S_Z,
GPC_I_COPY_F_TO_S_E,
GPC_I_COPY_F_TO_L_Z,
GPC_I_COPY_F_TO_L_E,
GPC_I_COPY_F_TO_LL_Z,
GPC_I_COPY_F_TO_LL_E,
GPC_I_COPY_F,
GPC_I_COPY_F_TO_D,
GPC_I_COPY_D_TO_BIT,
GPC_I_COPY_D_TO_C_Z,
GPC_I_COPY_D_TO_C_E,
GPC_I_COPY_D_TO_S_Z,
GPC_I_COPY_D_TO_S_E,
GPC_I_COPY_D_TO_L_Z,
GPC_I_COPY_D_TO_L_E,
GPC_I_COPY_D_TO_LL_Z,
GPC_I_COPY_D_TO_LL_E,
GPC_I_COPY_D_TO_F,
GPC_I_COPY_D,
* result of cells possibly being 32-bit instead of 64-bit. */
* shove it into the requested offset from the ptr
* on top of the stack. use it like so:
* GPC_I_SET_XX <dest offset> <constant> */
GPC_I_SET_C,
GPC_I_SET_S,
GPC_I_SET_L,
* shove it into the requested offset from the ptr that is
* second from top of the stack. use it like so:
* GPC_I_SET_2ND_XX <dest offset> <constant> */
GPC_I_SET_2ND_C,
GPC_I_SET_2ND_S,
GPC_I_SET_2ND_L,
* current buffer pointer register. */
* at the given offset from the topmost pointer. call this like so:
* GPC_I_BITVECTOR_READ <offset> */
GPC_I_BITVECTOR_READ,
* a bitvector */
GPC_I_BITVECTOR_READ_TO_C,
GPC_I_BITVECTOR_READ_TO_S,
GPC_I_BITVECTOR_READ_TO_L,
GPC_I_BITVECTOR_READ_TO_LL,
GPC_I_BITVECTOR_READ_TO_F,
GPC_I_BITVECTOR_READ_TO_D,
* a bitvector */
GPC_I_BITVECTOR_READ_FROM_C,
GPC_I_BITVECTOR_READ_FROM_S,
GPC_I_BITVECTOR_READ_FROM_L,
GPC_I_BITVECTOR_READ_FROM_LL,
GPC_I_BITVECTOR_READ_FROM_F,
GPC_I_BITVECTOR_READ_FROM_D,
GPC_I_BITVECTOR_READ_FROM_TSF_INTEGER_ARRAY,
GPC_I_BITVECTOR_READ_FROM_TSF_LONG_ARRAY,
* the given offset from the topmost pointer. call this like so:
* GPC_I_BITVECTOR_WRITE <offset> */
GPC_I_BITVECTOR_WRITE,
* given offset from the topmost pointer. the length is multiplied
* by the given multiplier to obtain the size of the array in bytes.
* call this like so:
* GPC_I_BYTE_ARRAY_READ <offset> <multiplier> */
GPC_I_BYTE_ARRAY_READ,
* given offset from the topmost pointer. the length is multiplied
* by the given multiplier to obtain the size of the array in bytes.
* call this like so:
* GPC_I_BYTE_ARRAY_WRITE <offset> <multiplier> */
GPC_I_BYTE_ARRAY_WRITE,
* comment above the declaration of TSF_TK_INTEGER in tsf.h. */
GPC_I_TSF_INTEGER_SKIP,
GPC_I_TSF_UNSIGNED_SKIP,
GPC_I_TSF_LONG_SKIP,
GPC_I_TSF_INTEGER_READ_TO_BIT,
GPC_I_TSF_INTEGER_READ_TO_C,
GPC_I_TSF_INTEGER_READ_TO_S,
GPC_I_TSF_INTEGER_READ,
GPC_I_TSF_INTEGER_READ_TO_LL,
GPC_I_TSF_INTEGER_READ_TO_F,
GPC_I_TSF_INTEGER_READ_TO_D,
GPC_I_TSF_UNSIGNED_READ_SUB1,
GPC_I_TSF_LONG_READ_TO_BIT,
GPC_I_TSF_LONG_READ_TO_C,
GPC_I_TSF_LONG_READ_TO_S,
GPC_I_TSF_LONG_READ_TO_L,
GPC_I_TSF_LONG_READ,
GPC_I_TSF_LONG_READ_TO_F,
GPC_I_TSF_LONG_READ_TO_D,
GPC_I_TSF_INTEGER_SIZE,
GPC_I_TSF_UNSIGNED_PLUS1_SIZE,
GPC_I_TSF_LONG_SIZE,
GPC_I_TSF_INTEGER_WRITE,
GPC_I_TSF_UNSIGNED_PLUS1_WRITE,
GPC_I_TSF_LONG_WRITE,
* offset from the topmost pointer. */
GPC_I_STRING_SETEMPTY,
* topmost pointer. */
GPC_I_STRING_READ,
GPC_I_STRING_SKIP,
* from the topmost pointer. */
GPC_I_STRING_SIZE,
* topmost pointer. */
GPC_I_STRING_WRITE,
* destination offset points to the destination char*, while the
* topmost pointer plus source offset points to the source char*.
* call this like so:
* GPC_I_STRING_COPY <dest offset> <src offset> */
GPC_I_STRING_COPY,
* is at the given offset frm the topmost pointer. */
GPC_I_ANY_SETEMPTY,
* topmost pointer. */
GPC_I_ANY_READ,
GPC_I_ANY_SKIP,
* offset frm the topmost pointer. */
GPC_I_ANY_SIZE,
* topmost pointer. */
GPC_I_ANY_WRITE,
* offset points to the destination tsf_buffer_t*, while the topmost
* pointer plus source offset points to the source tsf_buffer_t*. call
* this like so:
* GPC_I_ANY_COPY <dest offset> <src offset> */
GPC_I_ANY_COPY,
* destination is second to topmost, while the source is topmost.
* the operands are: offset from destination, offset from source, and
* the size. */
GPC_I_MEMCPY,
* destination offset points to the destination tsf_native_array_t,
* while the topmost pointer plus source offset points to the source
* tsf_native_array_t. the other operand is the multiplier that
* specifies the number of bytes for each array element. call this
* like so:
* GPC_I_COPY_ARRAY <dest offset> <src offset> <multiplier> */
GPC_I_COPY_ARRAY,
* destination offset points to the destination tsf_native_bitvector_t,
* while the topmost pointer plus source offset points to the source
* tsf_native_bitvector_t. call this like so:
* GPC_I_COPY_ARRAY <dest offset> <src offset> */
GPC_I_COPY_BITVECTOR,
GPC_I_COPY_BITVECTOR_FROM_C,
GPC_I_COPY_BITVECTOR_FROM_S,
GPC_I_COPY_BITVECTOR_FROM_L,
GPC_I_COPY_BITVECTOR_FROM_LL,
GPC_I_COPY_BITVECTOR_FROM_F,
GPC_I_COPY_BITVECTOR_FROM_D,
GPC_I_COPY_BITVECTOR_TO_C,
GPC_I_COPY_BITVECTOR_TO_S,
GPC_I_COPY_BITVECTOR_TO_L,
GPC_I_COPY_BITVECTOR_TO_LL,
GPC_I_COPY_BITVECTOR_TO_F,
GPC_I_COPY_BITVECTOR_TO_D,
* 255, it is instead converted to UINT32_MAX. */
GPC_I_READC_TO_CHOICE,
GPC_I_READL,
* topmost pointer to the 32-bit array length. relevant
* bounds checks are done. */
GPC_I_ARRAY_LEN_WRITE_FIELD,
* topmost pointer to the 32-bit array length. relevant
* bounds checks are done. */
GPC_I_ARRAY_LEN_READ_FIELD,
* the stack. relevant bounds checks are done. */
GPC_I_ARRAY_LEN_READ_LOCAL,
GPC_I_ARRAY_LEN_SKIP,
* the topmost pointer is non-zero, set set specified bit in
* the current buffer location. otherwise reset it. the
* buffer pointer is not advanced. you call this thing as
* follows:
* GPC_I_BIT_MASK_WRITE <bit index> <offset> */
GPC_I_BIT_MASK_WRITE,
* without advancing the pointer. if set, then set the 8-bit
* value pointed to by the specified offset from the topmost
* pointer to 1; otherwise, set it to 0. this instruction is
* used as follows:
* GPC_I_BIT_MASK_READ <bit index> <offset> */
GPC_I_BIT_MASK_READ,
GPC_I_BIT_MASK_READ_TO_S,
GPC_I_BIT_MASK_READ_TO_L,
GPC_I_BIT_MASK_READ_TO_LL,
GPC_I_BIT_MASK_READ_TO_F,
GPC_I_BIT_MASK_READ_TO_D,
* is 'normalized', or converted to either a 1 or 0 prior to
* being placed into the buffer. the only argument is the
* offset. */
GPC_I_BIT_WRITE,
* 'normalized', or converted to either a 1 or 0 prior to being
* placed into the user's data structure. the only argument is
* the offset. */
GPC_I_BIT_READ,
GPC_I_BIT_READ_TO_S,
GPC_I_BIT_READ_TO_L,
GPC_I_BIT_READ_TO_LL,
GPC_I_BIT_READ_TO_F,
GPC_I_BIT_READ_TO_D,
GPC_I_SKIP,
* an offset from the topmost pointer. the array length is found
* at that offset. call this like so:
* GPC_I_ARRAY_BC_AND_SKIP_FIELD <offset> <multiplier> */
GPC_I_ARRAY_BC_AND_SKIP_FIELD,
* relevant bounds checks, and skips the array. all it needs to
* know is the number of bytes required by each array element.
* call this like so:
* GPC_I_ARRAY_SKIP <constant> */
GPC_I_ARRAY_SKIP,
* pointer where the number of bits can be found. */
GPC_I_BITVECTOR_BC_AND_SKIP_FIELD,
* relevant bounds checks, and skips the bitvector. this
* instruction takes no operands. */
GPC_I_BITVECTOR_SKIP,
GPC_I_INC_SIZE,
* pointer, and increment the size register by the number of
* bytes that this array length would occupy in the stream. */
GPC_I_INC_SIZE_ARRAY_LEN,
* second operand is the size in bytes of each element and the
* first operand is an offset to a tsf_native_array_t structure. */
GPC_I_INC_SIZE_ARRAY,
* as specified by the 32-bit value at the given offset from the
* topmost pointer. */
GPC_I_INC_SIZE_BITVECTOR,
* the interpreter with an error. operand is the number of
* bytes we expect to be able to see. */
GPC_I_BC,
* of the interpreter with an error. operands are a size
* constant and the offset from the topmost pointer to the
* length of the array (32-bit). if constant*array_len bytes
* are not available, the bounds check fails. call this like so:
* GPC_I_ARRAY_BC_FIELD <offset> <multiplier> */
GPC_I_ARRAY_BC_FIELD,
* of the interpreter with an error. operand is a size
* constant. the array length is the topmost stack element. if
* constant*array_len bytes are not available, the bounds check
* fails. */
GPC_I_ARRAY_BC_LOCAL,
* of the interpreter with an error. the operand is the offset
* from the topmost pointer to a 32-bit number of bits. if
* (num_bits+7)>>3 bytes are not available, the bounds check fails.
*/
GPC_I_BITVECTOR_BC_FIELD,
* of the interpreter with an error. the number of bits is the
* topmost element on the stack. if (num_bits+7)>>3 bytes are not
* available, the bounds check fails.
*/
GPC_I_BITVECTOR_BC_LOCAL,
* interpreter failure or if it is not returned. if allocation
* fails, the interpreter fails. the allocation size is determined
* by the size register. performing this allocation automatically sets
* the end register so that bounds checks can be performed. */
GPC_I_MALLOC_BUF,
* pointer, the end pointer, the keep bit, and the types pointer are
* all set. however, size is not set (so you cannot do a MALLOC_BUF
* after this call). */
GPC_I_POP_BUF,
GPC_I_DESTROY_BUFFER,
GPC_I_MAKE_OUT_MAP,
* region alloc and replace the second-to-top-most pointer with the result.
* if the region pointer was NULL, then it is replaced with the result as
* well, and the region is put into keep mode. the only operand is the
* size. */
GPC_I_ALLOC_ROOT_MAYBE_2ND,
* leads to termination of the interpreter. the operand is
* the size. */
GPC_I_ALLOC,
* leads to termination of the interpreter. the operands are
* the element size and the offset from the topmost pointer to
* the tsf_native_array_t. tsf_native_array_t::len should be set
* already, and is used to determine how many bytes to alloc.
* tsf_native_array_t::data will be set to the same thing that
* gets pushed -- that is, the newly allocated array. call this like
* so:
* GPC_I_ALLOC_ARRAY <offset> <multiplier> */
GPC_I_ALLOC_ARRAY,
* leads to termination of the interpreter. the operands are
* the element size and the offset from the second to topmost pointer to
* the tsf_native_array_t. tsf_native_array_t::len should be set
* already, and is used to determine how many bytes to alloc.
* tsf_native_array_t::data will be set to the same thing that
* gets pushed -- that is, the newly allocated array. call this like
* so:
* GPC_I_ALLOC_ARRAY <offset> <multiplier> */
GPC_I_ALLOC_ARRAY_2ND,
* offset from the topmost pointer. call it like so:
* GPC_I_CALL <offset> <function pointer> */
GPC_I_CALL,
* void(*)(void*). the argument is computed as an offset from the
* topmost pointer. call it like so:
* GPC_I_ADD_CBACK <offset> <function pointer> */
GPC_I_ADD_CBACK,
GPC_I_FREE_IMMEDIATE,
GPC_I_FREE,
* topmost pointer. */
GPC_I_STORE_PTR,
* topmost pointer. */
GPC_I_STORE_PTR_2ND,
* topmost pointer. */
GPC_I_STORE_VAL,
* offset from current top-most pointer where this pointer
* can be found. */
GPC_I_PUSH_PTR,
* offset from current second-to-top-most pointer where this pointer
* can be found. */
GPC_I_PUSH_PTR_2ND,
* offset from current third-to-top-most pointer where this pointer
* can be found. */
GPC_I_PUSH_PTR_3RD,
* offset from current top-most pointer where this
* value can be found. */
GPC_I_PUSH_VAL,
GPC_I_DUP_PTR_2ND,
GPC_I_POP,
GPC_I_TWO_POP,
GPC_I_THREE_POP,
* 32-bit value stored at an offset from the second to
* topmost pointer multiplied by the supplied constant to the topmost
* pointer. finally, push the saved pointer onto the stack.
* this weird instruction looks like this:
* GPC_I_MULADD_PTR <offset> <constant>
* What the heck does this instruction really do? It sets you up
* for a pointer loop! */
GPC_I_REPUSH_MULADD_PTR,
* 32-bit value stored at an offset from the third to
* topmost pointer multiplied by the supplied constant to the topmost
* pointer. finally, push the saved pointer onto the stack.
* this weird instruction looks like this:
* GPC_I_MULADD_PTR <offset> <constant> */
GPC_I_REPUSH_MULADD_PTR_2ND,
* pointer. if the comparison fails, jump to the given label.
* this is a forward jump. */
GPC_I_COMPFAILJUMP,
* to topmost pointer. if the comparison fails, jump to the given
* label. this is a forward jump. */
GPC_I_COMPFAILJUMP_2ND,
* (less-than) to the next-to-topmost pointer, and if the
* comparison is true, jump to the given label. note that
* this is a backward branch, so the label is looked for
* backwards, not forwards. here's how to call it:
* GPC_I_ADDCOMPJUMP <constant> <label> */
GPC_I_ADDCOMPJUMP,
* add the source constant to the topmost pointer, compare the
* second-to-topmost pointer to the third-to-topmost pointer, and
* if the comparison is true, jump to the given label. note that
* this is a backward branch, so the label is looked for backwards,
* not forwards. here's how to call it:
* GPC_I_TWO_ADDCOMPJUMP <dest const> <src const> <label> */
GPC_I_TWO_ADDCOMPJUMP,
* the given label. */
GPC_I_ZEROJUMP,
* jump backward to the given label. */
GPC_I_DECCOMPJUMP,
* if the topmost element is UINT32_MAX, nothing happens. if the
* topmost element is less than the supplied constant, nothing happens.
* otherwise, it is an error, and the interpreter fails out. */
GPC_I_CHECKCHOICE,
* and use it as an index into the given table, and then place
* the looked up value into the location pointed to by the topmost
* pointer plus the offset. here's the way you call this beast:
* GPC_I_TABLESET_LOCAL_TO_FIELD <offset> <num table entries>
* <entry 0> <entry 1> ...
* If the value at the top of the stack is beyond the bounds of
* this table, then it is an error and the interpreter is
* terminated. If the value is UINT32_MAX, then the location is
* set to UINT32_MAX. */
GPC_I_TABLESET_LOCAL_TO_FIELD,
* topmost pointer and use it as an index into the given table, and
* then place the looked up value into the location pointed to by
* the destination offset plus the second to topmost pointer.
* call this like so:
* GPC_I_TABLESET_FIELD_TO_FIELD <dest offset> <source offset>
* <num table entries> <entry 0> ...
* If the value at the given offset from the topmost pointer is
* beyond the bounds of this table, then it is an error and the
* interpreter is terminated. If the value is UINT32_MAX, then the
* location is set to UINT32_MAX. */
GPC_I_TABLESET_FIELD_TO_FIELD,
* pointer and use it as an index into the given table, and then jump
* to the location labeled by the looked up value. You call this
* beast like so:
* GPC_I_TABLEJUMP_FIELD <offset> <num table entries> <entry 0> ...
* If the value at the given offset from the topmost pointer is
* beyond the bounds of this table, then it is an error and the
* interpreter is terminated. If the value is UINT32_MAX, then no
* jump occurs. */
GPC_I_TABLEJUMP_FIELD,
* use it as an index into the given table, and then jump to the
* location labeled by the looked up value. You call this
* beast like so:
* GPC_I_TABLEJUMP <num table entries> <entry 0> ...
* If the value at the topmost pointer is beyond the bounds of
* this table, then it is an error and the interpreter is
* terminated. If the value is UINT32_MAX, then no jump occurs. */
GPC_I_TABLEJUMP_LOCAL,
GPC_I_JUMP,
GPC_I_RETURN_ONE,
GPC_I_RETURN_TOP_WITH_REGION,
* below the top one. initializes it with the resulting data buffer and sets
* the types to the type_in_man empty singleton. */
GPC_I_RETURN_ONE_INIT_BUF_WITH_EMPTY_TYPES,
* below the top one. initializes it with the resulting data buffer and the
* inverse of the current running out_map. */
GPC_I_RETURN_ONE_INIT_BUF_WITH_TYPES_FROM_OUT_MAP,
* it is always at the end of this enumeration. */
GPC_I_LAST
};
typedef uintptr_t gpc_cell_t;
struct gpc_cell_list {
gpc_cell_t *cell;
struct gpc_cell_list *next;
};
typedef struct gpc_cell_list gpc_cell_list_t;
struct gpc_proto {
uint32_t num_args;
gpc_cell_t *stream;
uint32_t size;
* the number of cells that have code in them. */
};
struct gpc_intable {
uint32_t num_args;
gpc_cell_t *stream;
uint32_t size;
* the number of cells that have code in them. */
tsf_st_table *targets;
tsf_st_table *stack_heights;
intptr_t max_height;
};
typedef struct gpc_intable gpc_intable_t;
struct gpc_threaded {
uint32_t num_args;
gpc_cell_t *stream;
uint32_t size;
* the number of cells that have code in them. */
intptr_t max_height;
};
typedef struct gpc_threaded gpc_threaded_t;
typedef uintptr_t (*gpc_program_runner_t)(void *payload,
void *region,
gpc_cell_t *args);
typedef void (*gpc_program_destroyer_t)(void *payload);
* of the execution engines. */
struct gpc_program {
void *payload;
gpc_program_runner_t runner;
gpc_program_destroyer_t destroyer;
};
* occupies. note that some instructions are variable length. for
* these instructions, this macro returns UINT32_MAX.
* FIXME: this macro is wrong. the nh copy opcodes take one
* operand, not two. */
uint32_t gpc_instruction_static_size(gpc_cell_t inst);
* if 0 is returned, that means that the stack height is unaffected. if
* a positive number is returned, then the stack will grow by that amount.
* if a negative number is returned, then the stack will shrink by that
* amount. */
int32_t gpc_instruction_stack_effects(gpc_cell_t inst);
const char *gpc_instruction_to_string(gpc_cell_t inst);
* occupies. note that some instructions are variable length. for
* this reason, this macro requires that you pass in a pointer to
* an instruction in the actual stream, so that the instruction
* stream can be properly queried. */
uint32_t gpc_instruction_size(gpc_cell_t *instptr);
typedef tsf_bool_t (*gpc_target_cback_t)(gpc_cell_t *cell,void *arg);
* the given instruction. */
tsf_bool_t gpc_instruction_for_all_back_branches(gpc_cell_t *inst,
gpc_target_cback_t cback,
void *arg);
* the given instruction. */
tsf_bool_t gpc_instruction_for_all_forward_branches(gpc_cell_t *inst,
gpc_target_cback_t cback,
void *arg);
* the given instruction. */
tsf_bool_t gpc_instruction_for_all_branches(gpc_cell_t *inst,
gpc_target_cback_t cback,
void *arg);
* return. */
tsf_bool_t gpc_instruction_is_return(gpc_cell_t inst);
* to continue to the instruction directly after it. returns tsf_false
* for instructions that do unconditional jumps and that terminate the
* program, or for anything else that will never lead to control flow
* continuing to the next instruction. */
tsf_bool_t gpc_instruction_is_continuous(gpc_cell_t inst);
* process. it is then your responsibility to drop in the proper
* opcodes and operands. */
gpc_proto_t *gpc_proto_create(uint32_t num_args);
void gpc_proto_destroy(gpc_proto_t *proto);
#define gpc_proto_get_num_args(proto) \
((proto)->num_args)
tsf_bool_t gpc_proto_reserve(gpc_proto_t *proto,
uint32_t num_slots);
* number of operands. */
tsf_bool_t gpc_proto_append(gpc_proto_t *proto,
gpc_cell_t inst,
...);
tsf_bool_t gpc_proto_append_tableset_local_to_field(gpc_proto_t *proto,
gpc_cell_t offset,
gpc_cell_t num,
const gpc_cell_t *args);
tsf_bool_t gpc_proto_append_tableset_field_to_field(gpc_proto_t *proto,
gpc_cell_t dest_offset,
gpc_cell_t src_offset,
gpc_cell_t num,
const gpc_cell_t *args);
tsf_bool_t gpc_proto_append_tablejump_local(gpc_proto_t *proto,
gpc_cell_t num,
const gpc_cell_t *args);
tsf_bool_t gpc_proto_append_tablejump_field(gpc_proto_t *proto,
gpc_cell_t offset,
gpc_cell_t num,
const gpc_cell_t *args);
void gpc_proto_print(gpc_proto_t *proto,
FILE *out);
void gpc_code_gen_debug(const char *type,
gpc_proto_t *proto);
gpc_intable_t *gpc_intable_from_proto(gpc_proto_t *proto);
gpc_intable_t *gpc_intable_clone(gpc_intable_t *intable);
void gpc_intable_destroy(gpc_intable_t *intable);
#define gpc_intable_get_num_args(intable) \
((intable)->num_args)
#define gpc_intable_get_stream(intable) \
((intable)->stream)
#define gpc_intable_get_stream_end(intable) \
((intable)->stream+(intable)->size)
#define gpc_intable_is_target(intable,address) \
(tsf_st_lookup((intable)->targets,(char*)address,NULL))
#define gpc_intable_is_live(intable,address) \
(tsf_st_lookup((intable)->stack_heights,(char*)address,NULL))
-1 if the code is not live. note that the error code is not set in
either case. */
intptr_t gpc_intable_get_height(gpc_intable_t *prog,
gpc_cell_t *address);
tsf_bool_t gpc_intable_run_supported();
uintptr_t gpc_intable_run(gpc_intable_t *prog,
void *region,
gpc_cell_t *args);
tsf_bool_t gpc_intable_write(gpc_intable_t *prog,
tsf_writer_t writer,
void *arg);
* currently unused and so hence are excluded from the Makefile and from
* the distribution. They are, however, included in CVS, lest they ever
* become useful. */
* points in the given program.
*
* FIXME: this really should not be a separate analysis. it should be just
* an extra return from gpc_intable_get_predecessors(). */
gpc_cell_t **gpc_intable_find_all_returns(gpc_intable_t *prog);
* to a linked list of its predecessors. the linked list is typed as a
* gpc_cell_list_t. */
tsf_st_table *gpc_intable_get_predecessors(gpc_intable_t *prog);
* gpc_intable_get_predecessors() function. */
void gpc_intable_destroy_predecessors(tsf_st_table *preds);
* bytes that you are guaranteed to have left in the buffer, assuming
* that the program does not overrun the buffer in the end. */
tsf_st_table *gpc_intable_get_min_remaining(gpc_intable_t *prog);
* conversion. */
tsf_bool_t gpc_intable_can_do_int_to_float_conversion(gpc_intable_t *prog);
* a pointer to the buffer pointer register. */
tsf_bool_t gpc_intable_can_call_buffer_mutator_functions(gpc_intable_t *prog);
known as types). definitions of in_map don't con't - so this will
return tsf_false if the intable defines but does not use the
in_map. */
tsf_bool_t gpc_intable_can_use_in_map(gpc_intable_t *prog);
definitions of out_map don't con't - so this will return tsf_false
if the intable defines but does not use the out_map. */
tsf_bool_t gpc_intable_can_use_out_map(gpc_intable_t *prog);
tsf_bool_t gpc_threaded_supported();
* was compiled with a compiler that does not support the address-of-
* label extension. */
gpc_threaded_t *gpc_threaded_from_intable(gpc_intable_t *intable);
void gpc_threaded_destroy(gpc_threaded_t *threaded);
#define gpc_threaded_get_num_args(threaded) \
((threaded)->num_args)
uintptr_t gpc_threaded_run(gpc_threaded_t *prog,
void *region,
gpc_cell_t *args);
* TSF_EN_DEFAULT, then the default engine is used (based on the
* TSF_ENGINE_CONFIG environment variable; if it is not set, then
* the default is GPINT). if you pass in TSF_EN_NOT_SUPPORTED, then
* NULL is returned without the error being set (this allows for error
* propagation). */
gpc_program_t *gpc_program_from_proto(gpc_proto_t *proto);
gpc_program_t *gpc_program_from_intable(gpc_intable_t *intable);
void gpc_program_destroy(gpc_program_t *prog);
uintptr_t gpc_program_run(gpc_program_t *prog,
void *region,
gpc_cell_t *args);
#endif