blob: 7dd5629b12284f95ea6b69340865becc12b58a30 [file] [log] [blame]
/*
* 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;
}