blob: 5aa31d084dd1840d2786d257089f042d51b8bce3 [file] [log] [blame]
/*
* Copyright (C) 2003, 2004, 2005, 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.
*/
#include "gpc_internal.h"
#include "tsf_format.h"
struct addr_array {
gpc_cell_t **addrs;
uint32_t num;
};
static tsf_bool_t st_aa_append(tsf_st_table *table,
gpc_cell_t key,
gpc_cell_t *addr) {
struct addr_array *aa;
if (tsf_st_lookup(table,(char*)key,(void**)&aa)) {
gpc_cell_t **new_array=
realloc(aa->addrs,sizeof(gpc_cell_t*)*(aa->num+1));
if (new_array==NULL) {
tsf_set_errno("Could not realloc gpc_cell_t* array");
return tsf_false;
}
aa->addrs=new_array;
} else {
aa=malloc(sizeof(struct addr_array));
if (aa==NULL) {
tsf_set_errno("Could not malloc struct addr_array");
return tsf_false;
}
aa->addrs=malloc(sizeof(gpc_cell_t*));
if (aa->addrs==NULL) {
tsf_set_errno("Could not malloc gpc_cell_t*");
free(aa);
return tsf_false;
}
aa->num=0;
if (tsf_st_insert(table,(char*)key,aa)<0) {
tsf_set_errno("tsf_st_insert failed");
free(aa->addrs);
free(aa);
return tsf_false;
}
}
aa->addrs[aa->num++]=addr;
return tsf_true;
}
static int st_aa_free(void *key /* we ignored it anyway */,
struct addr_array *aa,
void *arg) {
free(aa->addrs);
free(aa);
return TSF_ST_CONTINUE;
}
static tsf_bool_t back_branch_action(gpc_cell_t *cell,
void *arg) {
tsf_st_table *back_labels=arg;
gpc_cell_t addr;
if (!tsf_st_lookup(back_labels,
(char*)*cell,
(void*)&addr)) {
tsf_set_error(TSF_E_BAD_JUMP,
"Backward reference to unknown label: " fuptr,
*cell);
return tsf_false;
}
*cell=addr;
return tsf_true;
}
static tsf_bool_t fore_branch_action(gpc_cell_t *cell,
void *arg) {
tsf_st_table *fore_labels=arg;
return st_aa_append(fore_labels,*cell,cell);
}
gpc_intable_t *gpc_intable_from_proto(gpc_proto_t *proto) {
gpc_intable_t *ret;
gpc_cell_t *write;
gpc_cell_t *read;
tsf_st_table *back_labels,*fore_labels;
ret=malloc(sizeof(gpc_intable_t));
if (ret==NULL) {
tsf_set_errno("Could not malloc gpc_intable_t");
return NULL;
}
ret->num_args=proto->num_args;
ret->targets=tsf_st_init_ptrtable();
if (ret->targets==NULL) {
tsf_set_errno("Could not tsf_st_init_ptrtable()");
free(ret);
return NULL;
}
ret->stream=malloc(sizeof(gpc_cell_t)*proto->size);
if (ret->stream==NULL) {
tsf_set_errno("Could not malloc stream for gpc_intable_t");
tsf_st_free_table(ret->targets);
free(ret);
return NULL;
}
back_labels=tsf_st_init_ptrtable();
if (back_labels==NULL) {
tsf_set_errno("Could not tsf_st_init_ptrtable()");
tsf_st_free_table(ret->targets);
free(ret->stream);
free(ret);
return NULL;
}
fore_labels=tsf_st_init_ptrtable();
if (fore_labels==NULL) {
tsf_set_errno("Could not tsf_st_init_ptrtable()");
tsf_st_free_table(ret->targets);
free(ret->stream);
free(ret);
tsf_st_free_table(back_labels);
return NULL;
}
write=ret->stream;
for (read=proto->stream;read<proto->stream+proto->size;) {
if (*read==GPC_I_LABEL) {
uint32_t i;
struct addr_array *aa;
gpc_cell_t *to_delete;
if (tsf_st_insert(back_labels,
(char*)read[1],
(void*)write)<0) {
tsf_set_errno("Could not tsf_st_insert");
goto failure;
}
to_delete = &read[1];
if (tsf_st_delete(fore_labels, (char**)to_delete, (void**)&aa)) {
for (i=0;i<aa->num;++i) {
*(aa->addrs[i])=(gpc_cell_t)write;
}
free(aa->addrs);
free(aa);
}
if (tsf_st_insert(ret->targets,
(char*)write,
NULL)<0) {
tsf_set_errno("Could not tsf_st_insert");
goto failure;
}
read+=2;
} else {
uint32_t num;
gpc_cell_t *pre_write=write;
num=gpc_instruction_size(read);
*write++=*read++;
while (--num>0) {
*write++=*read++;
}
if (!gpc_instruction_for_all_back_branches(
pre_write,back_branch_action,back_labels)) {
goto failure;
}
if (!gpc_instruction_for_all_forward_branches(
pre_write,fore_branch_action,fore_labels)) {
goto failure;
}
}
}
if (fore_labels->num_entries!=0) {
tsf_set_error(TSF_E_BAD_JUMP,
"There still exist unfulfilled label references");
goto failure;
}
ret->stack_heights=
gpc_intable_get_stack_heights(ret,&(ret->max_height));
if (ret->stack_heights==NULL) {
goto failure;
}
tsf_st_free_table(back_labels);
tsf_st_free_table(fore_labels);
ret->size=write-ret->stream;
return ret;
failure:
tsf_st_free_table(ret->targets);
free(ret->stream);
free(ret);
tsf_st_free_table(back_labels);
tsf_st_foreach(fore_labels,(tsf_st_func_t)st_aa_free,NULL);
tsf_st_free_table(fore_labels);
return NULL;
}
struct correct_branch_closure {
size_t offset;
tsf_st_table *targets;
};
static tsf_bool_t correct_branch(gpc_cell_t *cell,
void *arg) {
struct correct_branch_closure *closure=arg;
*cell=(gpc_cell_t)(((gpc_cell_t*)*cell)+closure->offset);
return tsf_st_insert(closure->targets,(char*)cell,NULL)>=0;
}
gpc_intable_t *gpc_intable_clone(gpc_intable_t *intable) {
gpc_intable_t *ret=malloc(sizeof(gpc_intable_t));
gpc_cell_t *cur;
struct correct_branch_closure closure;
if (ret==NULL) {
tsf_set_errno("Could not malloc gpc_intable_t");
return NULL;
}
ret->num_args=intable->num_args;
ret->size=intable->size;
ret->stream=malloc(sizeof(gpc_cell_t)*ret->size);
if (ret->stream==NULL) {
tsf_set_errno("Could not malloc array of gpc_cell_t");
free(ret);
return NULL;
}
memcpy(ret->stream,intable->stream,ret->size*sizeof(gpc_cell_t));
ret->targets=tsf_st_init_ptrtable();
if (ret->targets==NULL) {
tsf_set_errno("Could not tsf_st_init_ptrtable()");
free(ret->stream);
free(ret);
return NULL;
}
closure.offset=ret->stream-intable->stream;
closure.targets=ret->targets;
for (cur=ret->stream;
cur<ret->stream+ret->size;
cur+=gpc_instruction_size(cur)) {
if (!gpc_instruction_for_all_branches(cur,correct_branch,&closure)) {
tsf_set_errno("Could not tsf_st_insert");
tsf_st_free_table(ret->targets);
free(ret->stream);
free(ret);
return NULL;
}
}
ret->stack_heights=
gpc_intable_get_stack_heights(ret,&(ret->max_height));
if (ret->stack_heights==NULL) {
tsf_st_free_table(ret->targets);
free(ret->stream);
free(ret);
return NULL;
}
return ret;
}
void gpc_intable_destroy(gpc_intable_t *intable) {
tsf_st_free_table(intable->targets);
tsf_st_free_table(intable->stack_heights);
free(intable->stream);
free(intable);
}
static tsf_bool_t canonicalize_branch(gpc_cell_t *cell,
void *arg) {
gpc_cell_t *begin=arg;
*cell=((gpc_cell_t*)*cell)-begin;
return tsf_true;
}
tsf_bool_t gpc_intable_write(gpc_intable_t *intable,
tsf_writer_t writer,
void *arg) {
gpc_cell_t *cur;
for (cur=intable->stream;
cur<intable->stream+intable->size;
cur+=gpc_instruction_size(cur)) {
size_t size=sizeof(gpc_cell_t)
* gpc_instruction_size(cur);
gpc_cell_t *buffer=malloc(size);
tsf_bool_t res;
if (buffer==NULL) {
tsf_set_errno("Could not allocate buffer");
return tsf_false;
}
memcpy(buffer,cur,size);
gpc_instruction_for_all_branches(buffer,
canonicalize_branch,
intable->stream);
res=writer(arg,buffer,size);
free(buffer);
if (!res) {
return tsf_false;
}
}
return tsf_true;
}
intptr_t gpc_intable_get_height(gpc_intable_t *prog,
gpc_cell_t *address) {
intptr_t result;
if (!tsf_st_lookup(prog->stack_heights,
(char*)address,
(void*)&result)) {
return -1;
}
return result;
}
#ifdef HAVE_ADDRESS_OF_LABEL
tsf_bool_t gpc_intable_run_supported() {
return tsf_false;
}
uintptr_t gpc_intable_run(gpc_intable_t *prog,
void *region,
gpc_cell_t *args) {
tsf_set_error(TSF_E_NOT_SUPPORTED,
"Non-threaded interpretation support was not compiled into "
"the TSF package. This is because the threaded interpreter "
"was compiled instead. Please use the threaded "
"interpreter.");
return 0;
}
#else
tsf_bool_t gpc_intable_run_supported() {
return tsf_true;
}
#include "gpc_int_common.h"
#define INT_BEGIN(inst_name) \
case GPC_##inst_name: /* printf("%s\n",#inst_name); */ ++inst; {
#define INT_END() \
} goto int_loop;
#define INT_ADVANCE(amount) \
(inst+=(amount))
#define INT_GOTO(value) do { \
void *tmp=(void*)value; \
inst=tmp; \
goto int_loop; \
} while (0)
uintptr_t gpc_intable_run(gpc_intable_t *prog,
void *region,
gpc_cell_t *args) {
gpc_cell_t *inst;
uint8_t *buf=NULL,
*cur=NULL,
*end=NULL;
uint32_t size=0;
tsf_bool_t keep=tsf_false;
tsf_type_in_map_t *types=NULL;
tsf_type_out_map_t *out_map=NULL;
tsf_bool_t keep_region=tsf_false;
int8_t i8_tmp;
uint8_t ui8_tmp;
int16_t i16_tmp;
uint16_t ui16_tmp;
int32_t i32_tmp;
uint32_t ui32_tmp;
int64_t i64_tmp;
uint64_t ui64_tmp;
float f_tmp;
float d_tmp;
gpc_cell_t *stack;
gpc_cell_t *stack_top;
uint32_t i;
stack=alloca(sizeof(gpc_cell_t)*prog->max_height);
if (stack==NULL) {
tsf_set_errno("Could not allocate stack");
return 0;
}
stack_top=stack-1;
for (i=0;i<prog->num_args;++i) {
INT_PUSH();
INT_STACK(0)=args[i];
}
INT_GOTO(prog->stream);
int_loop:
switch (*inst) {
#include "gpc_interpreter.gen"
default:
tsf_abort("Bad instruction");
break;
}
bounds_error:
tsf_set_error(TSF_E_PARSE_ERROR,"Bounds check failure");
failure:
/* error! */
if (keep) {
free(buf);
}
if (keep_region) {
tsf_region_free(region);
}
if (out_map!=NULL) {
tsf_type_out_map_destroy(out_map);
}
return 0;
}
#endif