/* * Copyright (C) 2003, 2004, 2005 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" #define C(exp) do { \ if (!(exp)) { \ goto failure; \ } \ } while (0) typedef struct { gpc_cell_t *cell; intptr_t height; } worklist_item_t; #include "gpc_worklist.h" static inline tsf_bool_t pop(worklist_t *worklist, gpc_cell_t **cell, intptr_t *height) { worklist_item_t *item=worklist_pop(worklist); if (item==NULL) { return tsf_false; } *cell=item->cell; *height=item->height; return tsf_true; } static inline tsf_bool_t push(worklist_t *worklist, gpc_cell_t *cell, intptr_t height) { worklist_item_t *item=worklist_push(worklist); if (item==NULL) { return tsf_false; } item->cell=cell; item->height=height; return tsf_true; } struct branch_action_closure { worklist_t *worklist; intptr_t height; }; static tsf_bool_t branch_action(gpc_cell_t *cell, void *arg) { struct branch_action_closure *closure=arg; return push(closure->worklist,(gpc_cell_t*)*cell,closure->height); } tsf_st_table *gpc_intable_get_stack_heights(gpc_intable_t *prog, intptr_t *max_height) { tsf_st_table *ret=tsf_st_init_ptrtable(); gpc_cell_t *cur; intptr_t height; worklist_t worklist; if (ret==NULL) { tsf_set_errno("Could not tsf_st_init_numtable()"); return NULL; } if (!worklist_init(&worklist)) { tsf_st_free_table(ret); return NULL; } C(push(&worklist, gpc_intable_get_stream(prog), gpc_intable_get_num_args(prog))); if (max_height!=NULL) { *max_height=0; } while (pop(&worklist,&cur,&height)) { intptr_t got_height; if (tsf_st_lookup(ret,(char*)cur,(void**)&got_height)) { if (got_height != height) { tsf_set_error(TSF_E_SEMANTIC, "The stack height is different on two " "different approaches to the same piece " "of code"); goto failure; } continue; /* we've seen it so we don't need to do anything * else. */ } if (tsf_st_add_direct(ret,(char*)cur,(void*)height)<0) { tsf_set_errno("Could not tsf_st_add_direct()"); goto failure; } if (max_height!=NULL && height>*max_height) { *max_height=height; } height+=gpc_instruction_stack_effects(*cur); { struct branch_action_closure closure; closure.worklist=&worklist; closure.height=height; C(gpc_instruction_for_all_branches(cur,branch_action,&closure)); } if (gpc_instruction_is_continuous(*cur)) { C(push(&worklist,cur+gpc_instruction_size(cur),height)); } } worklist_done(&worklist); return ret; failure: tsf_st_free_table(ret); worklist_done(&worklist); return NULL; }