/* * 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;readstream+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;inum;++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; curstream+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; curstream+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;inum_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