Rev 6 | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*
* Copyright (c) 2002-2006 The TenDRA Project <http://www.tendra.org/>.
* 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.
* 3. Neither the name of The TenDRA Project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific, prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``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 THE COPYRIGHT HOLDERS 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.
*
* $Id$
*/
/*
Crown Copyright (c) 1997
This TenDRA(r) Computer Program is subject to Copyright
owned by the United Kingdom Secretary of State for Defence
acting through the Defence Evaluation and Research Agency
(DERA). It is made available to Recipients with a
royalty-free licence for its use, reproduction, transfer
to other parties and amendment for any purpose not excluding
product development provided that any such use et cetera
shall be deemed to be acceptance of the following conditions:-
(1) Its Recipients shall ensure that this Notice is
reproduced upon any copies or amended versions of it;
(2) Any amended version of it shall be clearly marked to
show both the nature of and the organisation responsible
for the relevant amendment or amendments;
(3) Its onward transfer from a recipient to another
party shall be deemed to be that party's acceptance of
these conditions;
(4) DERA gives no warranty or assurance as to its
quality or suitability for any purpose and DERA accepts
no liability whatsoever in relation to any use to which
it may be put.
*/
#include "config.h"
#include "c_types.h"
#include "exp_ops.h"
#include "hashid_ops.h"
#include "id_ops.h"
#include "member_ops.h"
#include "nspace_ops.h"
#include "error.h"
#include "catalog.h"
#include "basetype.h"
#include "dump.h"
#include "function.h"
#include "hash.h"
#include "label.h"
#include "namespace.h"
#include "statement.h"
#include "syntax.h"
/*
LABEL NAMESPACE
The labels in a function occupy a distinct namespace. This is given by
the following variable.
*/
NAMESPACE label_namespace = NULL_nspace;
/*
LABEL USAGE VALUES
The storage field of a label is used primarily to indicate whether that
label has been used or defined. However addition information is recorded
using the following values, namely, whether the label is jumped to in
an accessible portion of the program or reached by falling into it from
the previous statement, and whether or not it is an unnamed label.
*/
#define dspec_goto dspec_static
#define dspec_reached dspec_extern
#define dspec_fall_thru dspec_auto
#define dspec_anon dspec_register
#define dspec_solve dspec_mutable
#define dspec_scope dspec_inline
/*
CREATE A LABEL
This routine creates a label named nm with usage information info.
*/
static IDENTIFIER
make_label(HASHID nm, DECL_SPEC info, int op)
{
IDENTIFIER lab;
NAMESPACE ns = label_namespace;
MAKE_id_label(nm, info, ns, crt_loc, op, lab);
return (lab);
}
/*
BEGIN A LABELLED STATEMENT
This routine begins the construction of a statement labelled by the
label lab. If lab is the null identifier then a unique identifier
name is created. op gives the type of label being defined (for example
a break label, a case label or a normal identifier label). If the
label has already been defined then the null expression is returned.
Although from the syntax a label is associated with a single body
statement, the body of a labelled statement is actually all the
remaining statements in the block. That is:
{
stmt1 ;
...
label : body1 ;
body2 ;
....
}
is transformed into:
{
stmt1 ;
...
label : {
body1 ;
body2 ;
....
}
}
except that the introduced block does not establish a scope.
*/
EXP
begin_label_stmt(IDENTIFIER lab, int op)
{
EXP e;
EXP seq;
HASHID nm;
MEMBER mem;
DECL_SPEC def_info = (dspec_defn | dspec_scope);
/* Make up label name if necessary */
if (IS_NULL_id(lab)) {
nm = lookup_anon();
def_info |= dspec_anon;
if (op == lex_case || op == lex_default) {
/* Mark case and default labels */
def_info |= (dspec_used | dspec_goto);
}
} else {
nm = DEREF_hashid(id_name(lab));
}
/* Check for fall through */
if (!unreached_code) {
def_info |= dspec_fall_thru;
}
/* Check if label has already been defined */
mem = search_member(label_namespace, nm, 1);
lab = DEREF_id(member_id(mem));
if (!IS_NULL_id(lab)) {
DECL_SPEC info = DEREF_dspec(id_storage(lab));
if (info & dspec_defn) {
/* Already defined */
IDENTIFIER fn = crt_func_id;
PTR(LOCATION) loc = id_loc(lab);
report(crt_loc, ERR_stmt_label_redef(lab, fn, loc));
return (NULL_exp);
}
/* Already used */
info |= def_info;
COPY_dspec(id_storage(lab), info);
COPY_loc(id_loc(lab), crt_loc);
} else {
/* Not used or defined previously */
lab = make_label(nm, def_info, op);
COPY_id(member_id(mem), lab);
}
if (do_local && !IS_hashid_anon(nm)) {
dump_declare(lab, &crt_loc, 1);
}
/* Create a labelled statement */
seq = begin_compound_stmt(0);
MAKE_exp_label_stmt(type_void, lab, seq, e);
COPY_exp(exp_sequence_parent(seq), e);
COPY_exp(id_label_stmt(lab), e);
unreached_code = 0;
unreached_last = 0;
return (e);
}
/*
COMPLETE A LABELLED STATEMENT
This routine completes the construction of the labelled statement prev
using the statement body. It is also used to handle case and default
statements. If prev is the null expression, indicating any illegal
label of some kind, then body is returned. Otherwise body is added
to the compound statement which is labelled.
*/
EXP
end_label_stmt(EXP prev, EXP body)
{
EXP seq;
IDENTIFIER lab;
DECL_SPEC info;
if (IS_NULL_exp(prev)) {
return (body);
}
/* Mark end of label scope */
lab = DEREF_id(exp_label_stmt_label(prev));
info = DEREF_dspec(id_storage(lab));
info &= ~dspec_scope;
COPY_dspec(id_storage(lab), info);
/* Check for consecutive labels */
if (!IS_NULL_exp(body) && IS_exp_label_stmt(body)) {
/* Two consecutive labels */
IDENTIFIER blab = DEREF_id(exp_label_stmt_label(body));
blab = DEREF_id(id_alias(blab));
COPY_id(id_alias(lab), blab);
}
/* Assign to label body */
seq = DEREF_exp(exp_label_stmt_body(prev));
seq = add_compound_stmt(seq, body);
COPY_exp(exp_label_stmt_body(prev), seq);
return (prev);
}
/*
CONSTRUCT A JUMP TO A LABEL
This routine constructs a jump to the label lab (including break and
continue statements). join gives the smallest statement containing
both the label and the jump. This can only be filled in later for
named labels.
*/
EXP
make_jump_stmt(IDENTIFIER lab, EXP join)
{
DECL_SPEC info;
EXP e = DEREF_exp(id_label_gotos(lab));
/* Mark the label as used */
info = DEREF_dspec(id_storage(lab));
info |= (dspec_used | dspec_goto);
if (!unreached_code) {
info |= dspec_reached;
}
COPY_dspec(id_storage(lab), info);
/* Construct the jump statement */
if (IS_NULL_exp(join) && (info & dspec_scope)) {
join = DEREF_exp(id_label_stmt(lab));
}
MAKE_exp_goto_stmt(type_bottom, lab, join, e, e);
COPY_exp(id_label_gotos(lab), e);
unreached_code = 1;
unreached_last = 0;
return (e);
}
/*
CONSTRUCT A GOTO STATEMENT
This routine constructs a goto statement where the destination label
is given by lab. Note that it is possible to use a label before it
is defined.
*/
EXP
make_goto_stmt(IDENTIFIER lab)
{
/* Look up existing label */
EXP e;
HASHID nm;
MEMBER mem;
if (IS_NULL_id(lab)) {
nm = lookup_anon();
} else {
nm = DEREF_hashid(id_name(lab));
}
mem = search_member(label_namespace, nm, 1);
lab = DEREF_id(member_id(mem));
if (IS_NULL_id(lab)) {
/* Create new label */
DECL_SPEC info = (dspec_used | dspec_goto);
lab = make_label(nm, info, lex_identifier);
COPY_id(member_id(mem), lab);
} else {
DECL_SPEC info = DEREF_dspec(id_storage(lab));
if (info & dspec_defn) {
/* Backward jump */
info |= dspec_reserve;
COPY_dspec(id_storage(lab), info);
}
}
if (do_local && do_usage && !IS_hashid_anon(nm)) {
dump_use(lab, &crt_loc, 1);
}
e = make_jump_stmt(lab, NULL_exp);
return (e);
}
/*
POSTLUDE LABEL NAME
This value gives the name associated with all postlude labels. It is
assigned when the first postlude label is created.
*/
static HASHID postlude_name = NULL_hashid;
/*
CREATE A POSTLUDE LABEL
This routine creates a label name for a postlude expression, that is
to say an expression which will be called at the end of a function.
At present this is only used in functions which do not return a value.
A return statement within such a function is mapped onto a jump to
the postlude label.
*/
IDENTIFIER
postlude_label(void)
{
IDENTIFIER lab;
HASHID nm = postlude_name;
if (IS_NULL_hashid(nm)) {
/* Assign postlude label name */
nm = lookup_anon();
postlude_name = nm;
}
lab = DEREF_id(hashid_id(nm));
return (lab);
}
/*
FIND A POSTLUDE LABEL
This routine returns the postlude label associated with the current
function or the null identifier if the function has no postlude.
*/
IDENTIFIER
find_postlude_label(void)
{
HASHID nm = postlude_name;
if (!IS_NULL_hashid(nm)) {
MEMBER mem = search_member(label_namespace, nm, 0);
if (!IS_NULL_member(mem)) {
IDENTIFIER lab = DEREF_id(member_id(mem));
return (lab);
}
}
return (NULL_id);
}
/*
HAS A LABELLED STATEMENT BEEN REACHED?
This routine checks whether the label label has been reached using
an explicit goto, break or continue statement in a reached portion of
the program (when it returns 1) or by fall through from the previous
statement (when it returns 2).
*/
int
used_label(IDENTIFIER lab)
{
DECL_SPEC info = DEREF_dspec(id_storage(lab));
if (info & dspec_reached) {
return (1);
}
if (info & dspec_fall_thru) {
return (2);
}
return (0);
}
/*
CHECK ALL LABELS IN A FUNCTION
This routine scans through all the labels defined in the current
function searching for any which have been used but not defined.
It returns the number of named labels defined.
*/
unsigned
check_labels(void)
{
/* Scan through all labels */
unsigned no_labs = 0;
NAMESPACE ns = label_namespace;
MEMBER mem = DEREF_member(nspace_last(ns));
while (!IS_NULL_member(mem)) {
LOCATION loc;
IDENTIFIER lab = DEREF_id(member_id(mem));
if (!IS_NULL_id(lab)) {
/* Check label information */
DECL_SPEC info = DEREF_dspec(id_storage(lab));
IDENTIFIER flab = DEREF_id(id_alias(lab));
if (!EQ_id(lab, flab)) {
/* Deal with label aliases */
DECL_SPEC finfo = DEREF_dspec(id_storage(flab));
finfo |= (info & dspec_used);
COPY_dspec(id_storage(flab), finfo);
}
if (info & dspec_anon) {
/* Unnamed labels are ignored */
/* EMPTY */
} else if (info & dspec_defn) {
/* Defined labels */
HASHID nm = DEREF_hashid(id_name(lab));
if (!IS_hashid_anon(nm)) {
if (info & dspec_goto) {
/* Label used and defined */
/* EMPTY */
} else {
/* Label defined but not used */
IDENTIFIER fn = crt_func_id;
DEREF_loc(id_loc(lab), loc);
report(loc, ERR_stmt_label_unused(lab, fn));
}
if (info & (dspec_reached | dspec_fall_thru)) {
/* Label reached */
/* EMPTY */
} else {
/* Label unreached */
DEREF_loc(id_loc(lab), loc);
report(loc, ERR_stmt_stmt_unreach());
}
}
no_labs++;
} else {
/* Undefined labels */
HASHID nm = DEREF_hashid(id_name(lab));
if (!IS_hashid_anon(nm)) {
IDENTIFIER fn = crt_func_id;
DEREF_loc(id_loc(lab), loc);
report(loc, ERR_stmt_goto_undef(lab, fn));
}
}
}
/* Check next label */
mem = DEREF_member(member_next(mem));
}
return (no_labs);
}
/*
FIND THE VALUE OF A CASE LABEL
This routine determines the value associated with the case label lab.
*/
NAT
find_case_nat(IDENTIFIER lab)
{
EXP e = DEREF_exp(id_label_gotos(lab));
if (!IS_NULL_exp(e) && IS_exp_switch_stmt(e)) {
LIST(NAT) p;
LIST(IDENTIFIER) q;
p = DEREF_list(exp_switch_stmt_cases(e));
q = DEREF_list(exp_switch_stmt_case_labs(e));
while (!IS_NULL_list(q)) {
IDENTIFIER id = DEREF_id(HEAD_list(q));
if (EQ_id(id, lab)) {
NAT n = DEREF_nat(HEAD_list(p));
return (n);
}
p = TAIL_list(p);
q = TAIL_list(q);
}
}
return (NULL_nat);
}
/*
LISTS OF ALL SOLVE STATEMENTS AND TRY BLOCKS
The list all_solve_stmts keeps track of all the solve statements in
the current function. Similarly all_try_blocks keeps track of all the
try blocks.
*/
LIST(EXP) all_solve_stmts = NULL_list(EXP);
LIST(EXP) all_try_blocks = NULL_list(EXP);
/*
CHECK JUMPED OVER STATEMENTS
This routine checks whether a jump over the statement e to the label
lab causes the initialisation of a variable to be bypassed or control
to be transferred into an exception handler or another label body.
The variable force is used to indicate whether an error should be
reported. It is 2 for a jump into the statement, 1 for a jump from
one branch of a statement to another (for example, a handler to the
body of a try block), and 0 otherwise. The routine adds any variable
or label which is jumped over, whether initialised or not, to the
list ids.
*/
static LIST(IDENTIFIER)
jump_over_stmt(LIST(IDENTIFIER) ids, EXP e, IDENTIFIER lab, int force)
{
switch (TAG_exp(e)) {
case exp_decl_stmt_tag: {
/* Jump into declaration body */
IDENTIFIER id = DEREF_id(exp_decl_stmt_id(e));
DECL_SPEC ds = DEREF_dspec(id_storage(id));
if (ds & dspec_auto) {
if (force == 2) {
int init = 1;
EXP d = DEREF_exp(id_variable_init(id));
if (IS_NULL_exp(d) || IS_exp_null(d)) {
if (ds & dspec_reserve) {
/* Initialised in conditional */
/* EMPTY */
} else {
/* No initialiser */
init = 0;
}
}
if (init) {
/* Jump over initialiser */
ERROR err;
LOCATION loc;
int op = DEREF_int(id_label_op(lab));
if (op == lex_identifier) {
err = ERR_stmt_dcl_bypass_lab(lab, id);
} else if (op == lex_case) {
NAT n = find_case_nat(lab);
err = ERR_stmt_dcl_bypass_case(n, id);
} else {
err = ERR_stmt_dcl_bypass_default(id);
}
DEREF_loc(id_loc(id), loc);
report(loc, err);
}
}
CONS_id(id, ids, ids);
}
break;
}
case exp_if_stmt_tag: {
/* Jump into if statement */
IDENTIFIER lb = DEREF_id(exp_if_stmt_label(e));
if (!IS_NULL_id(lb)) {
CONS_id(lb, ids, ids);
}
break;
}
case exp_while_stmt_tag: {
/* Jump into while loop */
IDENTIFIER bk = DEREF_id(exp_while_stmt_break_lab(e));
IDENTIFIER cn = DEREF_id(exp_while_stmt_cont_lab(e));
IDENTIFIER lp = DEREF_id(exp_while_stmt_loop_lab(e));
CONS_id(bk, ids, ids);
CONS_id(cn, ids, ids);
CONS_id(lp, ids, ids);
break;
}
case exp_do_stmt_tag: {
/* Jump into do loop */
IDENTIFIER bk = DEREF_id(exp_do_stmt_break_lab(e));
IDENTIFIER cn = DEREF_id(exp_do_stmt_cont_lab(e));
IDENTIFIER lp = DEREF_id(exp_do_stmt_loop_lab(e));
CONS_id(bk, ids, ids);
CONS_id(cn, ids, ids);
CONS_id(lp, ids, ids);
break;
}
case exp_switch_stmt_tag: {
/* Jump into switch statement */
IDENTIFIER bk = DEREF_id(exp_switch_stmt_break_lab(e));
CONS_id(bk, ids, ids);
break;
}
case exp_solve_stmt_tag: {
/* Jump into solve statement */
LIST(IDENTIFIER) lbs;
LIST(IDENTIFIER) vars;
lbs = DEREF_list(exp_solve_stmt_labels(e));
while (!IS_NULL_list(lbs)) {
IDENTIFIER lb = DEREF_id(HEAD_list(lbs));
CONS_id(lb, ids, ids);
lbs = TAIL_list(lbs);
}
vars = DEREF_list(exp_solve_stmt_vars(e));
while (!IS_NULL_list(vars)) {
IDENTIFIER var = DEREF_id(HEAD_list(vars));
CONS_id(var, ids, ids);
vars = TAIL_list(vars);
}
break;
}
case exp_label_stmt_tag: {
/* Jump into labelled block */
IDENTIFIER lb = DEREF_id(exp_label_stmt_label(e));
CONS_id(lb, ids, ids);
break;
}
case exp_try_block_tag:
/* Jump into try block */
if (force != 0) {
LOCATION loc;
DEREF_loc(id_loc(lab), loc);
report(loc, ERR_except_jump_into());
}
break;
case exp_hash_if_tag:
/* Jump into target dependent '#if' */
if (force != 0) {
LOCATION loc;
DEREF_loc(id_loc(lab), loc);
report(loc, ERR_cpp_cond_if_jump_into());
}
break;
case exp_token_tag:
/* Jump into statement token */
if (force != 0) {
LOCATION loc;
DEREF_loc(id_loc(lab), loc);
report(loc, ERR_token_stmt_jump());
}
break;
}
return (ids);
}
/*
ADD AN IDENTIFIER TO A LIST
This routine adds the identifier id to the start of the list p if it
is not already a member.
*/
static LIST(IDENTIFIER)
add_id(IDENTIFIER id, LIST(IDENTIFIER) p)
{
LIST(IDENTIFIER) q = p;
while (!IS_NULL_list(q)) {
IDENTIFIER qid = DEREF_id(HEAD_list(q));
if (EQ_id(id, qid)) {
return (p);
}
q = TAIL_list(q);
}
CONS_id(id, p, p);
return (p);
}
/*
EXTEND A SOLVE STATEMENT
This routine extends the solve statement a by adding the label lab
and the variables ids.
*/
static void
extend_solve_stmt(EXP a, IDENTIFIER lab, LIST(IDENTIFIER) ids)
{
LIST(IDENTIFIER) vars = DEREF_list(exp_solve_stmt_vars(a));
LIST(IDENTIFIER) labels = DEREF_list(exp_solve_stmt_labels(a));
labels = add_id(lab, labels);
while (!IS_NULL_list(ids)) {
IDENTIFIER id = DEREF_id(HEAD_list(ids));
if (IS_id_label(id)) {
labels = add_id(id, labels);
} else {
vars = add_id(id, vars);
}
ids = TAIL_list(ids);
}
COPY_list(exp_solve_stmt_labels(a), labels);
COPY_list(exp_solve_stmt_vars(a), vars);
return;
}
/*
CHECK FOR UNSTRUCTURED JUMPS
The only instance where the mapping of a statement onto the
corresponding TDF construct is non-trivial is for unstructured
labels and gotos. This routine scans the body of the current
function, e, for such unstructured jumps and imposes some semblance
of order on them. The idea is to start with a labelled statement
and to gradually expand the statement outwards until it contains all
the goto statements for that label as substatements. If this is the
original labelled statement then no further action is required,
otherwise the enclosing statement is further expanded to the
enclosing block and the labelled statement is referenced from there.
Jumps which bypass the initialisation of a variable or transfer
control into an exception handler can also be detected during this
process.
*/
EXP
solve_labels(EXP e)
{
MEMBER mem = DEREF_member(nspace_last(label_namespace));
while (!IS_NULL_member(mem)) {
IDENTIFIER lab = DEREF_id(member_id(mem));
if (!IS_NULL_id(lab)) {
DECL_SPEC info = DEREF_dspec(id_storage(lab));
if (info & dspec_anon) {
/* Unnamed labels are ignored */
/* EMPTY */
} else if (info & dspec_defn) {
/* Only check defined labels */
int solve = 0;
EXP a = DEREF_exp(id_label_stmt(lab));
EXP p = DEREF_exp(id_label_gotos(lab));
LIST(IDENTIFIER) ids = NULL_list(IDENTIFIER);
for (;;) {
/* Scan for enclosing statement */
EXP q = p;
int ok = 1;
while (!IS_NULL_exp(q) && IS_exp_goto_stmt(q)) {
/* Check each goto statement */
EXP b = q;
PTR(EXP) pb = exp_goto_stmt_join(b);
if (IS_NULL_exp(DEREF_exp(pb))) {
/* Join statement not yet assigned */
for (;;) {
if (EQ_exp(a, b)) {
/* b is a sub-statement of a */
COPY_exp(pb, a);
break;
}
b = get_parent_stmt(b);
if (IS_NULL_exp(b)) {
/* b is not a sub-statement of a */
ok = 0;
break;
}
}
}
q = DEREF_exp(exp_goto_stmt_next(q));
}
/* Check whether a encloses all the jumps to lab */
if (ok) {
int force = 1;
while (!IS_exp_solve_stmt(a)) {
/* Scan to enclosing solve statement */
ids = jump_over_stmt(ids, a, lab, force);
a = get_parent_stmt(a);
if (IS_NULL_exp(a)) {
break;
}
force = 0;
}
break;
}
/* Some jump to lab is from outside a */
ids = jump_over_stmt(ids, a, lab, 2);
solve = 1;
/* Expand a to enclosing statement */
a = get_parent_stmt(a);
if (IS_NULL_exp(a)) {
/* Can happen with statement tokens */
a = e;
break;
}
}
/* Deal with unstructured labels */
if (solve) {
info |= dspec_solve;
COPY_dspec(id_storage(lab), info);
extend_solve_stmt(a, lab, ids);
}
DESTROY_list(ids, SIZE_id);
}
}
mem = DEREF_member(member_next(mem));
}
return (e);
}
/*
CHECK A JUMP TO A CASE OR DEFAULT STATEMENT
This routine checks whether the jump from the switch statement e to
the case or default label lab bypasses the initialisation of a variable
or jumps into an exception handler. The previous labelled statement
checked is passed in as prev. If e is a sub-statement of prev then
there is no need to check further. The routine returns the labelled
statement corresponding to lab.
*/
static EXP
solve_case(EXP e, IDENTIFIER lab, EXP prev)
{
DECL_SPEC info = DEREF_dspec(id_storage(lab));
if (info & dspec_defn) {
EXP b = DEREF_exp(id_label_stmt(lab));
EXP a = b;
LIST(IDENTIFIER) ids = NULL_list(IDENTIFIER);
while (!EQ_exp(a, e) && !EQ_exp(a, prev)) {
ids = jump_over_stmt(ids, a, lab, 2);
a = get_parent_stmt(a);
if (IS_NULL_exp(a)) {
break;
}
}
if (!IS_NULL_list(ids)) {
EXP s = DEREF_exp(exp_switch_stmt_body(e));
extend_solve_stmt(s, lab, ids);
DESTROY_list(ids, SIZE_id);
}
prev = b;
} else {
/* Case not defined */
EXP a, b;
LOCATION loc;
int uc = unreached_code;
IDENTIFIER flab = NULL_id;
int op = DEREF_int(id_label_op(lab));
DEREF_loc(id_loc(lab), loc);
if (op == lex_case) {
NAT n = find_case_nat(lab);
report(loc, ERR_stmt_switch_case_not(n));
flab = DEREF_id(exp_switch_stmt_default_lab(e));
} else {
report(loc, ERR_stmt_switch_default_not());
}
if (IS_NULL_id(flab)) {
flab = DEREF_id(exp_switch_stmt_break_lab(e));
}
a = begin_label_stmt(lab, op);
b = make_jump_stmt(flab, e);
IGNORE end_label_stmt(a, b);
unreached_code = uc;
}
return (prev);
}
/*
CHECK A SWITCH STATEMENT
This routine scans through the switch statement e for jumps which
bypass the initialisation of a variable.
*/
EXP
solve_switch(EXP e)
{
IDENTIFIER lab;
EXP prev = NULL_exp;
LIST(IDENTIFIER) cases;
cases = DEREF_list(exp_switch_stmt_case_labs(e));
while (!IS_NULL_list(cases)) {
/* Check each case statement */
lab = DEREF_id(HEAD_list(cases));
prev = solve_case(e, lab, prev);
cases = TAIL_list(cases);
}
lab = DEREF_id(exp_switch_stmt_default_lab(e));
if (!IS_NULL_id(lab)) {
/* Check any default statement */
IGNORE solve_case(e, lab, prev);
}
return (e);
}
/*
CONSTRUCT A LABEL FOR THE FOLLOWING STATEMENT
This routine turns the list of statements following the position p
in the block statement e into a labelled statement, returning the
label created. If p is the last statement in the block then the
null identifier is returned.
*/
static IDENTIFIER
follow_label(EXP e, LIST(EXP) p)
{
EXP a, b, c;
DECL_SPEC ds;
IDENTIFIER lab;
LIST(EXP) r;
LIST(EXP) q = TAIL_list(p);
if (IS_NULL_list(q)) {
return (NULL_id);
}
/* Examine following statement */
a = DEREF_exp(HEAD_list(q));
if (!IS_NULL_exp(a)) {
unsigned tag = TAG_exp(a);
if (tag == exp_location_tag) {
a = DEREF_exp(exp_location_arg(a));
if (!IS_NULL_exp(a)) {
tag = TAG_exp(a);
}
if (tag == exp_label_stmt_tag) {
/* Statement is already labelled */
lab = DEREF_id(exp_label_stmt_label(a));
return (lab);
}
}
}
/* Create new labelled statement */
b = begin_label_stmt(NULL_id, lex_end);
b = end_label_stmt(b, NULL_exp);
set_parent_stmt(b, e);
c = DEREF_exp(exp_label_stmt_body(b));
r = DEREF_list(exp_sequence_first(c));
IGNORE APPEND_list(r, q);
while (!IS_NULL_list(q)) {
a = DEREF_exp(HEAD_list(q));
set_parent_stmt(a, b);
q = TAIL_list(q);
}
COPY_list(PTR_TAIL_list(p), NULL_list(EXP));
CONS_exp(b, NULL_list(EXP), q);
IGNORE APPEND_list(p, q);
lab = DEREF_id(exp_label_stmt_label(b));
ds = DEREF_dspec(id_storage(lab));
ds |= (dspec_goto | dspec_used);
COPY_dspec(id_storage(lab), ds);
return (lab);
}
/*
FIND THE END OF A BRANCH OF A SOLVE STATEMENT
This routine finds the end of the branch of the solve statement e
given by the label lab. This is a label which gives any immediately
following code.
*/
static IDENTIFIER
end_solve_branch(IDENTIFIER lab, EXP e)
{
EXP a;
IDENTIFIER nlab;
int op = DEREF_int(id_label_op(lab));
switch (op) {
case lex_continue:
case lex_while:
case lex_for:
case lex_do:
/* Don't bother in these cases */
return (NULL_id);
}
a = DEREF_exp(id_label_stmt(lab));
if (IS_NULL_exp(a)) {
/* Ignore undefined labels */
return (NULL_id);
}
nlab = DEREF_id(exp_label_stmt_next(a));
if (IS_NULL_id(nlab)) {
/* Scan up to enclosing block */
EXP b = a;
EXP c = DEREF_exp(exp_label_stmt_parent(b));
while (!EQ_exp(c, e) && !IS_NULL_exp(c)) {
int again;
EXP d = c;
do {
again = 0;
switch (TAG_exp(d)) {
case exp_sequence_tag: {
/* Found enclosing block */
LIST(EXP) q;
q = DEREF_list(exp_sequence_first(d));
q = TAIL_list(q);
while (!IS_NULL_list(q)) {
EXP f = DEREF_exp(HEAD_list(q));
if (!IS_NULL_exp(f)) {
if (IS_exp_location(f)) {
/* Allow for location statements */
f = DEREF_exp(exp_location_arg(f));
}
if (EQ_exp(f, b)) {
/* Found labelled statement in block */
nlab = follow_label(d, q);
break;
}
}
q = TAIL_list(q);
}
break;
}
case exp_while_stmt_tag: {
/* Found enclosing while statement */
IDENTIFIER blab;
blab = DEREF_id(exp_while_stmt_break_lab(d));
if (!EQ_id(blab, lab)) {
nlab = DEREF_id(exp_while_stmt_cont_lab(d));
}
break;
}
case exp_do_stmt_tag: {
/* Found enclosing do statement */
IDENTIFIER blab;
blab = DEREF_id(exp_do_stmt_break_lab(d));
if (!EQ_id(blab, lab)) {
nlab = DEREF_id(exp_do_stmt_cont_lab(d));
}
break;
}
case exp_switch_stmt_tag:
/* Found enclosing switch statement */
nlab = DEREF_id(exp_switch_stmt_break_lab(d));
if (EQ_id(nlab, lab))nlab = NULL_id;
break;
case exp_decl_stmt_tag:
/* Found enclosing declaration */
d = DEREF_exp(exp_decl_stmt_body(d));
if (!EQ_exp(d, b))again = 1;
break;
case exp_label_stmt_tag:
/* Found enclosing label statement */
d = DEREF_exp(exp_label_stmt_body(d));
if (!EQ_exp(d, b))again = 1;
break;
}
} while (again);
if (!IS_NULL_id(nlab)) {
/* Label for next statement found */
nlab = DEREF_id(id_alias(nlab));
if (op == lex_break) {
/* Alias break labels */
COPY_id(id_alias(lab), nlab);
}
break;
}
b = c;
c = get_parent_stmt(b);
}
}
COPY_id(exp_label_stmt_next(a), nlab);
return (nlab);
}
/*
END ALL SOLVE STATEMENTS
This routine calls end_solve_branch for all the branches of all the
solve statements in the current function.
*/
void
end_solve_stmts(void)
{
LIST(EXP) p = all_solve_stmts;
if (!IS_NULL_list(p)) {
while (!IS_NULL_list(p)) {
int changed;
LIST(IDENTIFIER) q0;
EXP e = DEREF_exp(HEAD_list(p));
q0 = DEREF_list(exp_solve_stmt_labels(e));
do {
LIST(IDENTIFIER) q = q0;
changed = 0;
while (!IS_NULL_list(q)) {
IDENTIFIER lab = DEREF_id(HEAD_list(q));
IDENTIFIER nlab = end_solve_branch(lab, e);
if (!IS_NULL_id(nlab)) {
/* Add new label to list */
LIST(IDENTIFIER) q1 = add_id(nlab, q0);
if (!EQ_list(q1, q0)) {
q0 = q1;
changed = 1;
}
}
q = TAIL_list(q);
}
} while (changed);
COPY_list(exp_solve_stmt_labels(e), q0);
p = TAIL_list(p);
}
DESTROY_list(all_solve_stmts, SIZE_exp);
all_solve_stmts = NULL_list(EXP);
}
return;
}