mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-05-08 17:00:47 +00:00
Revert "Remove the dead block removal code."
This reverts commit 83ead0842f
.
Note: does not compile.
It turns out basic dead block removal is needed for the "control reaches
end of non-void function" warning to work correctly.
This commit is contained in:
parent
65561fc219
commit
ab6a3fefd9
1 changed files with 166 additions and 2 deletions
|
@ -473,7 +473,7 @@ statement_get_targetlist (statement_t *s)
|
||||||
}
|
}
|
||||||
return target_list;
|
return target_list;
|
||||||
}
|
}
|
||||||
#if 0
|
|
||||||
static void
|
static void
|
||||||
invert_conditional (statement_t *s)
|
invert_conditional (statement_t *s)
|
||||||
{
|
{
|
||||||
|
@ -490,7 +490,7 @@ invert_conditional (statement_t *s)
|
||||||
else if (!strcmp (s->opcode, "<IFA>"))
|
else if (!strcmp (s->opcode, "<IFA>"))
|
||||||
s->opcode = "<IFBE>";
|
s->opcode = "<IFBE>";
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
typedef sblock_t *(*statement_f) (sblock_t *, expr_t *);
|
typedef sblock_t *(*statement_f) (sblock_t *, expr_t *);
|
||||||
typedef sblock_t *(*expr_f) (sblock_t *, expr_t *, operand_t **);
|
typedef sblock_t *(*expr_f) (sblock_t *, expr_t *, operand_t **);
|
||||||
|
|
||||||
|
@ -1433,6 +1433,165 @@ thread_jumps (sblock_t *blocks)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
join_blocks (sblock_t *sblock, sblock_t *dest)
|
||||||
|
{
|
||||||
|
statement_t **s;
|
||||||
|
statement_t *g;
|
||||||
|
expr_t *label;
|
||||||
|
|
||||||
|
for (s = &sblock->statements; (*s) != (statement_t *) sblock->tail;
|
||||||
|
s = &(*s)->next)
|
||||||
|
;
|
||||||
|
debug (0, "joining blocks %p %p", sblock, dest);
|
||||||
|
unuse_label ((*s)->opa->o.label);
|
||||||
|
free_statement (*s);
|
||||||
|
sblock->tail = s;
|
||||||
|
// append dest's statements to sblock
|
||||||
|
*sblock->tail = dest->statements;
|
||||||
|
sblock->tail = dest->tail;
|
||||||
|
// clear dest's statement list
|
||||||
|
dest->tail = &dest->statements;
|
||||||
|
dest->statements = 0;
|
||||||
|
// put a goto statement into dest incase the code in sblock flows into dest
|
||||||
|
// the goto will jump to dest's old
|
||||||
|
label = new_label_expr ();
|
||||||
|
label->e.label.dest = dest->next;
|
||||||
|
label->e.label.next = dest->next->labels;
|
||||||
|
label->e.label.used++;
|
||||||
|
dest->next->labels = &label->e.label;
|
||||||
|
g = new_statement (st_flow, "<GOTO>", 0);
|
||||||
|
g->opa = new_operand (op_label);
|
||||||
|
g->opa->o.label = &label->e.label;
|
||||||
|
sblock_add_statement (dest, g);
|
||||||
|
// stich dest back into the block list immediately after sblock
|
||||||
|
dest->next = sblock->next;
|
||||||
|
sblock->next = dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
merge_blocks (sblock_t *blocks)
|
||||||
|
{
|
||||||
|
sblock_t *sblock;
|
||||||
|
int did_something = 0;
|
||||||
|
|
||||||
|
if (!blocks)
|
||||||
|
return 0;
|
||||||
|
for (sblock = blocks; sblock; sblock = sblock->next) {
|
||||||
|
statement_t *s;
|
||||||
|
sblock_t *dest;
|
||||||
|
sblock_t *sb;
|
||||||
|
|
||||||
|
if (!sblock->statements)
|
||||||
|
continue;
|
||||||
|
s = (statement_t *) sblock->tail;
|
||||||
|
if (!statement_is_goto (s))
|
||||||
|
continue;
|
||||||
|
dest = s->opa->o.label->dest;
|
||||||
|
// The destination block must not be the current block
|
||||||
|
if (dest == sblock) {
|
||||||
|
warning (0, "infinite loop detected");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// the destiniation block must have only one label and one user for
|
||||||
|
// that label (ie, no other branch statement jumps to the block).
|
||||||
|
// also, don't try to move a marker end-block
|
||||||
|
if (dest->labels->next || dest->labels->used > 1 || !dest->statements)
|
||||||
|
continue;
|
||||||
|
// the destination block must be otherwise unreachable (preceeded by
|
||||||
|
// an unconditional jump (goto or return))
|
||||||
|
if (dest == blocks)
|
||||||
|
continue;
|
||||||
|
for (sb = blocks; sb; sb = sb->next)
|
||||||
|
if (sb->next == dest)
|
||||||
|
break;
|
||||||
|
if (!sb) // dest is
|
||||||
|
internal_error (0, "dangling label");
|
||||||
|
if (!sb->statements)
|
||||||
|
continue;
|
||||||
|
s = (statement_t *) sb->tail;
|
||||||
|
if (!statement_is_goto (s) && !statement_is_return (s))
|
||||||
|
continue;
|
||||||
|
// desination block is reachable only via goto of the current block
|
||||||
|
if (!dest->next)
|
||||||
|
dest->next = new_sblock ();
|
||||||
|
sb->next = dest->next; // pull dest out of the chain
|
||||||
|
join_blocks (sblock, dest);
|
||||||
|
did_something = 1;
|
||||||
|
}
|
||||||
|
return did_something;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
remove_dead_blocks (sblock_t *blocks)
|
||||||
|
{
|
||||||
|
sblock_t *sblock;
|
||||||
|
int did_something;
|
||||||
|
int pass = 0;
|
||||||
|
|
||||||
|
if (!blocks)
|
||||||
|
return;
|
||||||
|
|
||||||
|
do {
|
||||||
|
debug (0, "dead block pass %d", pass++);
|
||||||
|
did_something = 0;
|
||||||
|
blocks->reachable = 1;
|
||||||
|
for (sblock = blocks; sblock->next; sblock = sblock->next) {
|
||||||
|
sblock_t *sb = sblock->next;
|
||||||
|
statement_t *s;
|
||||||
|
|
||||||
|
if (sb->labels) {
|
||||||
|
sb->reachable = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!sblock->statements) {
|
||||||
|
sb->reachable = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
s = (statement_t *) sblock->tail;
|
||||||
|
if (statement_is_cond (s)
|
||||||
|
&& sb->statements && statement_is_goto (sb->statements)
|
||||||
|
&& s->opb->o.label->dest == sb->next) {
|
||||||
|
debug (0, "merging if/goto %p %p", sblock, sb);
|
||||||
|
unuse_label (s->opb->o.label);
|
||||||
|
s->opb->o.label = sb->statements->opa->o.label;
|
||||||
|
s->opb->o.label->used++;
|
||||||
|
invert_conditional (s);
|
||||||
|
sb->reachable = 0;
|
||||||
|
for (sb = sb->next; sb; sb = sb->next)
|
||||||
|
sb->reachable = 1;
|
||||||
|
break;
|
||||||
|
} else if (!statement_is_goto (s) && !statement_is_return (s)) {
|
||||||
|
sb->reachable = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
sb->reachable = 0;
|
||||||
|
}
|
||||||
|
for (sblock = blocks; sblock; sblock = sblock->next) {
|
||||||
|
while (sblock->next && !sblock->next->reachable) {
|
||||||
|
sblock_t *sb = sblock->next;
|
||||||
|
statement_t *s;
|
||||||
|
ex_label_t *label = 0;
|
||||||
|
|
||||||
|
debug (0, "removing dead block %p", sb);
|
||||||
|
|
||||||
|
if (sb->statements) {
|
||||||
|
s = (statement_t *) sb->tail;
|
||||||
|
if (statement_is_goto (s))
|
||||||
|
label = s->opa->o.label;
|
||||||
|
else if (statement_is_cond (s))
|
||||||
|
label = s->opb->o.label;
|
||||||
|
}
|
||||||
|
unuse_label (label);
|
||||||
|
did_something = 1;
|
||||||
|
|
||||||
|
sblock->next = sb->next;
|
||||||
|
free_sblock (sb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (did_something);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
check_final_block (sblock_t *sblock)
|
check_final_block (sblock_t *sblock)
|
||||||
{
|
{
|
||||||
|
@ -1487,6 +1646,11 @@ make_statements (expr_t *e)
|
||||||
thread_jumps (sblock);
|
thread_jumps (sblock);
|
||||||
if (options.block_dot.thread)
|
if (options.block_dot.thread)
|
||||||
dump_dot ("thread", sblock, dump_dot_sblock);
|
dump_dot ("thread", sblock, dump_dot_sblock);
|
||||||
|
do {
|
||||||
|
remove_dead_blocks (sblock);
|
||||||
|
} while (merge_blocks (sblock));
|
||||||
|
if (options.block_dot.dead)
|
||||||
|
dump_dot ("dead", sblock, dump_dot_sblock);
|
||||||
check_final_block (sblock);
|
check_final_block (sblock);
|
||||||
if (options.block_dot.final)
|
if (options.block_dot.final)
|
||||||
dump_dot ("final", sblock, dump_dot_sblock);
|
dump_dot ("final", sblock, dump_dot_sblock);
|
||||||
|
|
Loading…
Reference in a new issue