1987-03-09 19:15:41 +00:00
|
|
|
/*
|
|
|
|
* (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
|
|
|
|
* See the copyright notice in the ACK home directory, in the file "Copyright".
|
|
|
|
*/
|
1984-11-26 13:43:22 +00:00
|
|
|
/* C O N T R O L F L O W
|
|
|
|
*
|
|
|
|
* C F _ S U C C . C
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "../share/types.h"
|
|
|
|
#include "../share/def.h"
|
|
|
|
#include "../share/debug.h"
|
|
|
|
#include "../share/global.h"
|
|
|
|
#include "../share/lset.h"
|
|
|
|
#include "../share/cset.h"
|
|
|
|
#include "../../../h/em_spec.h"
|
|
|
|
#include "../../../h/em_pseu.h"
|
|
|
|
#include "../../../h/em_flag.h"
|
|
|
|
#include "../../../h/em_mnem.h"
|
|
|
|
#include "cf.h"
|
|
|
|
#include "../share/map.h"
|
|
|
|
|
|
|
|
extern char em_flag[];
|
|
|
|
|
|
|
|
|
|
|
|
STATIC succeeds(succ,pred)
|
|
|
|
bblock_p succ, pred;
|
|
|
|
{
|
|
|
|
assert(pred != (bblock_p) 0);
|
|
|
|
if (succ != (bblock_p) 0) {
|
|
|
|
Ladd(succ, &pred->b_succ);
|
|
|
|
Ladd(pred, &succ->b_pred);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define IS_RETURN(i) (i == op_ret || i == op_rtt)
|
|
|
|
#define IS_CASE_JUMP(i) (i == op_csa || i == op_csb)
|
|
|
|
#define IS_UNCOND_JUMP(i) (i <= sp_lmnem && (em_flag[i-sp_fmnem] & EM_FLO) == FLO_T)
|
|
|
|
#define IS_COND_JUMP(i) (i <= sp_lmnem && (em_flag[i-sp_fmnem] & EM_FLO) == FLO_C)
|
|
|
|
#define TARGET(lnp) (lbmap[INSTRLAB(lnp)])
|
|
|
|
#define ATARGET(arg) (lbmap[arg->a_a.a_instrlab])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
STATIC arg_p skip_const(arg)
|
|
|
|
arg_p arg;
|
|
|
|
{
|
|
|
|
assert(arg != (arg_p) 0);
|
|
|
|
switch(arg->a_type) {
|
|
|
|
case ARGOFF:
|
|
|
|
case ARGICN:
|
|
|
|
case ARGUCN:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
error("bad case descriptor");
|
|
|
|
}
|
|
|
|
return arg->a_next;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
STATIC arg_p use_label(arg,b)
|
|
|
|
arg_p arg;
|
|
|
|
bblock_p b;
|
|
|
|
{
|
|
|
|
if (arg->a_type == ARGINSTRLAB) {
|
|
|
|
/* arg is a non-null label */
|
|
|
|
succeeds(ATARGET(arg),b);
|
|
|
|
}
|
|
|
|
return arg->a_next;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
STATIC case_flow(instr,desc,b)
|
|
|
|
short instr;
|
|
|
|
line_p desc;
|
|
|
|
bblock_p b;
|
|
|
|
{
|
|
|
|
/* Analyse the case descriptor (given as a ROM pseudo instruction).
|
|
|
|
* Every instruction label appearing in the descriptor
|
|
|
|
* heads a basic block that is a successor of the block
|
|
|
|
* in which the case instruction appears (b).
|
|
|
|
*/
|
|
|
|
|
|
|
|
register arg_p arg;
|
|
|
|
|
|
|
|
assert(instr == op_csa || instr == op_csb);
|
|
|
|
assert(TYPE(desc) == OPLIST);
|
|
|
|
arg = ARG(desc);
|
|
|
|
arg = use_label(arg,b);
|
|
|
|
/* See if there is a default label. If so, then
|
|
|
|
* its block is a successor of b. Set arg to
|
|
|
|
* next argument.
|
|
|
|
*/
|
|
|
|
if (instr == op_csa) {
|
|
|
|
arg = skip_const(arg); /* skip lower bound */
|
|
|
|
arg = skip_const(arg); /* skip lower-upper bound */
|
|
|
|
while (arg != (arg_p) 0) {
|
|
|
|
/* All following arguments are case labels
|
|
|
|
* or zeroes.
|
|
|
|
*/
|
|
|
|
arg = use_label(arg,b);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* csb instruction */
|
|
|
|
arg = skip_const(arg); /* skip #entries */
|
|
|
|
while (arg != (arg_p) 0) {
|
|
|
|
/* All following arguments are alternatively
|
|
|
|
* an index and an instruction label (possibly 0).
|
|
|
|
*/
|
|
|
|
arg = skip_const(arg); /* skip index */
|
|
|
|
arg = use_label(arg,b);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
STATIC line_p case_descr(lnp)
|
|
|
|
line_p lnp;
|
|
|
|
{
|
|
|
|
/* lnp is the instruction just before a csa or csb,
|
|
|
|
* so it is the instruction that pushes the address
|
|
|
|
* of a case descriptor on the stack. Find that
|
|
|
|
* descriptor, i.e. a rom pseudo instruction.
|
|
|
|
* Note that this instruction will always be part
|
|
|
|
* of the procedure in which the csa/csb occurs.
|
|
|
|
*/
|
|
|
|
|
|
|
|
register line_p l;
|
|
|
|
dblock_p d;
|
|
|
|
obj_p obj;
|
|
|
|
dblock_id id;
|
|
|
|
|
|
|
|
if (lnp == (line_p) 0 || (INSTR(lnp)) != op_lae) {
|
|
|
|
error("cannot find 'lae descr' before csa/csb");
|
|
|
|
}
|
|
|
|
/* We'll first find the ROM and its dblock_id */
|
|
|
|
obj = OBJ(lnp);
|
|
|
|
if (obj->o_off != (offset) 0) {
|
|
|
|
error("bad 'lae descr' before csa/csb");
|
|
|
|
/* We require a descriptor to be an entire rom,
|
|
|
|
* not part of a rom.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
d = obj->o_dblock;
|
|
|
|
assert(d != (dblock_p) 0);
|
|
|
|
if (d->d_pseudo != DROM) {
|
|
|
|
error("case descriptor must be in rom");
|
|
|
|
}
|
|
|
|
id = d->d_id;
|
|
|
|
/* We'll use the dblock_id to find the defining occurrence
|
|
|
|
* of the rom in the EM text (i.e. a rom pseudo). As all
|
|
|
|
* pseudos appear at the beginning of a procedure, we only
|
|
|
|
* have to look in its first basic block.
|
|
|
|
*/
|
|
|
|
assert(curproc != (proc_p) 0);
|
|
|
|
assert(curproc->p_start != (bblock_p) 0);
|
|
|
|
l = curproc->p_start->b_start; /* first instruction of curproc */
|
|
|
|
while (l != (line_p) 0) {
|
|
|
|
if ((INSTR(l)) == ps_sym &&
|
|
|
|
SHORT(l) == id) {
|
|
|
|
/* found! */
|
|
|
|
assert((INSTR(l->l_next)) == ps_rom);
|
|
|
|
return l->l_next;
|
|
|
|
}
|
|
|
|
l = l->l_next;
|
|
|
|
}
|
|
|
|
error("cannot find rom pseudo for case descriptor");
|
|
|
|
/* NOTREACHED */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
STATIC last2_instrs(b,last_out,prev_out)
|
|
|
|
bblock_p b;
|
|
|
|
line_p *last_out,*prev_out;
|
|
|
|
{
|
|
|
|
/* Determine the last and one-but-last instruction
|
|
|
|
* of basic block b. An end-pseudo is not regarded
|
|
|
|
* as an instruction. If the block contains only 1
|
|
|
|
* instruction, prev_out is 0.
|
|
|
|
*/
|
|
|
|
|
|
|
|
register line_p l1,l2;
|
|
|
|
|
|
|
|
l2 = b->b_start; /* first instruction of b */
|
|
|
|
assert(l2 != (line_p) 0); /* block can not be empty */
|
|
|
|
if ((l1 = l2->l_next) == (line_p) 0 || INSTR(l1) == ps_end) {
|
|
|
|
*last_out = l2; /* single instruction */
|
|
|
|
*prev_out = (line_p) 0;
|
|
|
|
} else {
|
|
|
|
while(l1->l_next != (line_p) 0 && INSTR(l1->l_next) != ps_end) {
|
|
|
|
l2 = l1;
|
|
|
|
l1 = l1->l_next;
|
|
|
|
}
|
|
|
|
*last_out = l1;
|
|
|
|
*prev_out = l2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
control_flow(head)
|
|
|
|
bblock_p head;
|
|
|
|
{
|
|
|
|
/* compute the successor and predecessor relation
|
|
|
|
* for every basic block.
|
|
|
|
*/
|
|
|
|
|
|
|
|
register bblock_p b;
|
|
|
|
line_p lnp, prev;
|
|
|
|
short instr;
|
|
|
|
|
|
|
|
for (b = head; b != (bblock_p) 0; b = b->b_next) {
|
|
|
|
/* for every basic block, in textual order, do */
|
|
|
|
last2_instrs(b, &lnp, &prev);
|
|
|
|
/* find last and one-but-last instruction */
|
|
|
|
instr = INSTR(lnp);
|
|
|
|
/* The last instruction of the basic block
|
|
|
|
* determines the set of successors of the block.
|
|
|
|
*/
|
|
|
|
if (IS_CASE_JUMP(instr)) {
|
|
|
|
case_flow(instr,case_descr(prev),b);
|
|
|
|
/* If lnp is a csa or csb, then the instruction
|
|
|
|
* just before it (i.e. prev) must be the
|
|
|
|
* instruction that pushes the address of the
|
|
|
|
* case descriptor. This descriptor is found
|
|
|
|
* and analysed in order to build the successor
|
|
|
|
* and predecessor sets of b.
|
|
|
|
*/
|
|
|
|
} else {
|
|
|
|
if (!IS_RETURN(instr)) {
|
|
|
|
if (IS_UNCOND_JUMP(instr)) {
|
|
|
|
succeeds(TARGET(lnp),b);
|
|
|
|
} else {
|
|
|
|
if (IS_COND_JUMP(instr)) {
|
|
|
|
succeeds(TARGET(lnp),b);
|
|
|
|
succeeds(b->b_next, b);
|
|
|
|
/* Textually next block is
|
|
|
|
* a successor of b.
|
|
|
|
*/
|
|
|
|
} else {
|
|
|
|
/* normal instruction */
|
|
|
|
succeeds(b->b_next, b);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|