590 lines
12 KiB
C
590 lines
12 KiB
C
/* $Id$ */
|
|
/*
|
|
* (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
|
|
* See the copyright notice in the ACK home directory, in the file "Copyright".
|
|
*/
|
|
/* I N L I N E S U B S T I T U T I O N
|
|
*
|
|
* I L 3 _ C H A N G E . C
|
|
*/
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <em_mnem.h>
|
|
#include <em_pseu.h>
|
|
#include <em_spec.h>
|
|
#include <em_mes.h>
|
|
#include "../share/types.h"
|
|
#include "il.h"
|
|
#include "../share/debug.h"
|
|
#include "../share/alloc.h"
|
|
#include "../share/global.h"
|
|
#include "../share/def.h"
|
|
#include "../share/lset.h"
|
|
#include "../share/aux.h"
|
|
#include "../share/get.h"
|
|
#include "../share/put.h"
|
|
#include "il_aux.h"
|
|
#include "il3_change.h"
|
|
#include "il3_aux.h"
|
|
|
|
/* chg_callseq */
|
|
|
|
|
|
|
|
|
|
STATIC line_p par_expr(l,expr)
|
|
line_p l, expr;
|
|
{
|
|
/* Find the first line of the expression of which
|
|
* l is the last line; expr contains a pointer
|
|
* to a copy of that expression; effectively we
|
|
* just have to tally lines.
|
|
*/
|
|
|
|
line_p lnp;
|
|
|
|
for (lnp = expr->l_next; lnp != (line_p) 0; lnp = lnp->l_next) {
|
|
assert(l != (line_p) 0);
|
|
l = PREV(l);
|
|
}
|
|
return l;
|
|
}
|
|
|
|
|
|
|
|
STATIC rem_text(l1,l2)
|
|
line_p l1,l2;
|
|
{
|
|
/* Remove the lines from l1 to l2 (inclusive) */
|
|
|
|
line_p l, lstop;
|
|
l = PREV(l1);
|
|
lstop = l2->l_next;
|
|
while (l->l_next != lstop) {
|
|
rem_line(l->l_next);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
STATIC store_tmp(p,l,size)
|
|
proc_p p;
|
|
line_p l;
|
|
offset size;
|
|
{
|
|
/* Emit code to store a 'size'-byte value in a new
|
|
* temporary local variable in the stack frame of p.
|
|
* Put this code after line l.
|
|
*/
|
|
|
|
line_p lnp;
|
|
|
|
lnp = int_line(tmplocal(p,size)); /* line with operand temp. */
|
|
if (size == ws) {
|
|
lnp->l_instr = op_stl; /* STL temp. */
|
|
} else {
|
|
if (size == 2*ws) {
|
|
lnp->l_instr = op_sdl; /* SDL temp. */
|
|
} else {
|
|
/* emit 'LAL temp; STI size' */
|
|
lnp->l_instr = op_lal;
|
|
appnd_line(lnp,l);
|
|
l = lnp;
|
|
assert ((short) size == size);
|
|
lnp = newline(OPSHORT);
|
|
SHORT(lnp) = size;
|
|
lnp->l_instr = op_sti;
|
|
}
|
|
}
|
|
appnd_line(lnp,l);
|
|
}
|
|
|
|
|
|
|
|
STATIC chg_actuals(c,cal)
|
|
call_p c;
|
|
line_p cal;
|
|
{
|
|
/* Change the actual parameter expressions of the call. */
|
|
|
|
actual_p act;
|
|
line_p llast,lfirst,l;
|
|
|
|
llast = PREV(cal);
|
|
for (act = c->cl_actuals; act != (actual_p) 0; act = act->ac_next) {
|
|
lfirst = par_expr(llast,act->ac_exp);
|
|
/* the code from lfirst to llast is a parameter expression */
|
|
if (act->ac_inl) {
|
|
/* in line parameter; remove it */
|
|
l = llast;
|
|
llast = PREV(lfirst);
|
|
rem_text(lfirst,l);
|
|
} else {
|
|
store_tmp(curproc,llast,act->ac_size);
|
|
/* put a "STL tmp" -like instruction after the code */
|
|
llast = PREV(lfirst);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
STATIC rm_callpart(c,cal)
|
|
call_p c;
|
|
line_p cal;
|
|
{
|
|
/* Remove the call part, consisting of a CAL,
|
|
* an optional ASP and an optional LFR.
|
|
*/
|
|
|
|
line_p l;
|
|
|
|
l= PREV(cal);
|
|
rem_line(cal);
|
|
if (c->cl_proc->p_nrformals > 0) {
|
|
/* called procedure has parameters */
|
|
assert (INSTR(l->l_next) == op_asp);
|
|
rem_line(l->l_next);
|
|
}
|
|
if (INSTR(l->l_next) == op_lfr) {
|
|
rem_line(l->l_next);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
chg_callseq(c,cal,l_out)
|
|
call_p c;
|
|
line_p cal,*l_out;
|
|
{
|
|
/* Change the calling sequence. The actual parameter
|
|
* expressions are changed (in line parameters are
|
|
* removed, all other ones now store their result
|
|
* in a temporary local of the caller);
|
|
* the sequence "CAL ; ASP ; LFR" is removed.
|
|
*/
|
|
|
|
|
|
chg_actuals(c,cal);
|
|
*l_out = PREV(cal); /* last instr. of new parameter part */
|
|
rm_callpart(c,cal);
|
|
}
|
|
|
|
|
|
/* make_label */
|
|
|
|
line_p make_label(l,p)
|
|
line_p l;
|
|
proc_p p;
|
|
{
|
|
/* Make sure that the instruction after l
|
|
* contains an instruction label. If this is
|
|
* not already the case, create a new label.
|
|
*/
|
|
|
|
line_p lab;
|
|
|
|
if (l->l_next != (line_p) 0 && INSTR(l->l_next) == op_lab) {
|
|
return l->l_next;
|
|
}
|
|
lab = newline(OPINSTRLAB);
|
|
lab->l_instr = op_lab;
|
|
p->p_nrlabels++;
|
|
INSTRLAB(lab) = p->p_nrlabels;
|
|
appnd_line(lab,l);
|
|
return lab;
|
|
}
|
|
|
|
|
|
|
|
/* modify */
|
|
|
|
STATIC act_info(off,acts,ab_off,act_out,off_out)
|
|
offset off, ab_off, *off_out;
|
|
actual_p acts, *act_out;
|
|
{
|
|
/* Find the actual parameter that corresponds to
|
|
* the formal parameter with the given offset.
|
|
* Return it via act_out. If the actual is not
|
|
* an in-line actual, determine which temporary
|
|
* local is used for it; return the offset of that
|
|
* local via off_out.
|
|
*/
|
|
|
|
offset sum = 0, tmp = 0;
|
|
actual_p act;
|
|
|
|
for (act = acts; act != (actual_p) 0; act = act->ac_next) {
|
|
if (!act->ac_inl) {
|
|
tmp -= act->ac_size;
|
|
}
|
|
if (sum >= off) {
|
|
/* found */
|
|
*act_out = act;
|
|
if (!act->ac_inl) {
|
|
*off_out = tmp + sum - off + ab_off;
|
|
} else {
|
|
assert (sum == off);
|
|
}
|
|
return;
|
|
}
|
|
sum += act->ac_size;
|
|
}
|
|
assert(FALSE);
|
|
}
|
|
|
|
|
|
|
|
STATIC store_off(off,l)
|
|
offset off;
|
|
line_p l;
|
|
{
|
|
if (TYPE(l) == OPSHORT) {
|
|
assert ((short) off == off);
|
|
SHORT(l) = (short) off;
|
|
} else {
|
|
OFFSET(l) = off;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
STATIC inl_actual(l,expr)
|
|
line_p l, expr;
|
|
{
|
|
/* Expand an actual parameter in line.
|
|
* A LOL or LDL instruction is replaced
|
|
* by an expression.
|
|
* A SIL or LIL is replaced by the expression
|
|
* followed by a STI or LOI.
|
|
*/
|
|
|
|
line_p e, lnp, s;
|
|
short instr;
|
|
|
|
instr = INSTR(l);
|
|
assert(expr != (line_p) 0);
|
|
e = copy_expr(expr); /* make a copy of expr. */
|
|
if (instr == op_sil || instr == op_lil) {
|
|
s = int_line((offset) ws);
|
|
s->l_instr = (instr == op_sil ? op_sti : op_loi);
|
|
appnd_line(s,last_line(e));
|
|
} else {
|
|
assert(instr == op_lol || instr == op_ldl);
|
|
}
|
|
lnp = PREV(l);
|
|
rem_line(l);
|
|
app_list(e,lnp);
|
|
}
|
|
|
|
|
|
|
|
STATIC localref(l,c,ab_off,lb_off)
|
|
line_p l;
|
|
call_p c;
|
|
offset ab_off, lb_off;
|
|
{
|
|
/* Change a reference to a local variable or parameter
|
|
* of the called procedure.
|
|
*/
|
|
|
|
offset off, tmpoff;
|
|
actual_p act;
|
|
|
|
off = off_set(l);
|
|
if (off < 0) {
|
|
/* local variable, only the offset changes */
|
|
store_off(lb_off + off,l);
|
|
} else {
|
|
act_info(off,c->cl_actuals,ab_off,&act,&tmpoff); /* find actual */
|
|
if (act->ac_inl) {
|
|
/* inline actual parameter */
|
|
inl_actual(l,act->ac_exp);
|
|
} else {
|
|
/* parameter stored in temporary local */
|
|
store_off(tmpoff,l);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
STATIC chg_mes(l,c,ab_off,lb_off)
|
|
line_p l;
|
|
call_p c;
|
|
offset ab_off, lb_off;
|
|
{
|
|
/* The register messages of the called procedure
|
|
* must be changed. If the message applies to a
|
|
* local variable or to a parameter that is not
|
|
* expanded in line, the offset of the variable
|
|
* is changed; else the entire message is deleted.
|
|
*/
|
|
|
|
offset off, tmpoff;
|
|
actual_p act;
|
|
arg_p arg;
|
|
|
|
arg = ARG(l);
|
|
switch ((int) arg->a_a.a_offset) {
|
|
case ms_reg:
|
|
if ((arg = arg->a_next) != (arg_p) 0) {
|
|
/* "mes 3" without further argument is not changed */
|
|
off = arg->a_a.a_offset;
|
|
if (off < 0) {
|
|
/* local variable */
|
|
arg->a_a.a_offset += lb_off;
|
|
} else {
|
|
act_info(off,c->cl_actuals,ab_off,&act,&tmpoff);
|
|
if (act->ac_inl) {
|
|
/* in line actual */
|
|
rem_line(l);
|
|
} else {
|
|
arg->a_a.a_offset = tmpoff;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case ms_par:
|
|
rem_line(l);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
STATIC chg_ret(l,c,lab)
|
|
line_p l,lab;
|
|
call_p c;
|
|
{
|
|
/* Change the RET instruction appearing in the
|
|
* expanded text of a call. If the called procedure
|
|
* falls through, the RET is just deleted; else it
|
|
* is replaced by a branch.
|
|
*/
|
|
|
|
line_p lnp, bra;
|
|
|
|
lnp = PREV(l);
|
|
rem_line(l);
|
|
if (!FALLTHROUGH(c->cl_proc)) {
|
|
bra = newline(OPINSTRLAB);
|
|
bra->l_instr = op_bra;
|
|
INSTRLAB(bra) = INSTRLAB(lab);
|
|
appnd_line(bra,lnp);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
STATIC mod_instr(l,c,lab,ab_off,lb_off,lab_off)
|
|
line_p l,lab;
|
|
call_p c;
|
|
offset ab_off,lb_off;
|
|
int lab_off;
|
|
{
|
|
if (TYPE(l) == OPINSTRLAB) {
|
|
INSTRLAB(l) += lab_off;
|
|
} else {
|
|
switch(INSTR(l)) {
|
|
case op_stl:
|
|
case op_inl:
|
|
case op_del:
|
|
case op_zrl:
|
|
case op_sdl:
|
|
case op_lol:
|
|
case op_ldl:
|
|
case op_sil:
|
|
case op_lil:
|
|
case op_lal:
|
|
localref(l,c,ab_off,lb_off);
|
|
break;
|
|
case op_ret:
|
|
chg_ret(l,c,lab);
|
|
break;
|
|
case ps_pro:
|
|
case ps_end:
|
|
case ps_sym:
|
|
case ps_hol:
|
|
case ps_bss:
|
|
case ps_con:
|
|
case ps_rom:
|
|
rem_line(l);
|
|
break;
|
|
case ps_mes:
|
|
chg_mes(l,c,ab_off,lb_off);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
modify(text,c,lab,ab_off,lb_off,lab_off)
|
|
line_p text,lab;
|
|
call_p c;
|
|
offset ab_off,lb_off;
|
|
int lab_off;
|
|
{
|
|
/* Modify the EM text of the called procedure.
|
|
* References to locals and parameters are
|
|
* changed; RETs are either deleted or replaced
|
|
* by a BRA to the given label; PRO and END pseudos
|
|
* are removed; instruction labels are changed, in
|
|
* order to make them different from any label used
|
|
* by the caller; some messages need to be changed too.
|
|
* Note that the first line of the text is a dummy instruction.
|
|
*/
|
|
|
|
register line_p l;
|
|
line_p next;
|
|
|
|
for (l = text->l_next; l != (line_p) 0; l = next) {
|
|
next = l->l_next;
|
|
/* This is rather tricky. An instruction like
|
|
* LOL 2 may be replaced by a number of instructions
|
|
* (if the parameter is expanded in line). This inserted
|
|
* code, however, should not be modified!
|
|
*/
|
|
mod_instr(l,c,lab,ab_off,lb_off,lab_off);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
mod_actuals(nc,c,lab,ab_off,lb_off,lab_off)
|
|
call_p nc,c;
|
|
line_p lab;
|
|
offset ab_off,lb_off;
|
|
int lab_off;
|
|
{
|
|
actual_p act;
|
|
line_p l, next, dum;
|
|
|
|
dum = newline(OPNO);
|
|
PREV(dum) = (line_p) 0;
|
|
for (act = nc->cl_actuals; act != (actual_p) 0; act = act->ac_next) {
|
|
l = act->ac_exp;
|
|
assert(l != (line_p) 0);
|
|
/* Insert a dummy instruction before l */
|
|
dum->l_next = l;
|
|
PREV(l) = dum;
|
|
while(l != (line_p) 0) {
|
|
next = l->l_next;
|
|
mod_instr(l,c,lab,ab_off,lb_off,lab_off);
|
|
l = next;
|
|
}
|
|
act->ac_exp = dum->l_next;
|
|
PREV(dum->l_next) = (line_p) 0;
|
|
}
|
|
oldline(dum);
|
|
}
|
|
|
|
|
|
|
|
/* insert */
|
|
|
|
STATIC line_p first_nonpseudo(l)
|
|
line_p l;
|
|
{
|
|
/* Find the first non-pseudo instruction of
|
|
* a list of instructions.
|
|
*/
|
|
|
|
while (l != (line_p) 0 && INSTR(l) >= sp_fpseu &&
|
|
INSTR(l) <= ps_last) l = l->l_next;
|
|
return l;
|
|
}
|
|
|
|
|
|
|
|
insert(text,l,firstline)
|
|
line_p text,l,firstline;
|
|
{
|
|
/* Insert the modified EM text of the called
|
|
* routine in the calling routine. Pseudos are
|
|
* put after the pseudos of the caller; all
|
|
* normal instructions are put at the place
|
|
* where the CAL originally was.
|
|
*/
|
|
|
|
line_p l1,l2,lastpseu;
|
|
|
|
l1 = text->l_next;
|
|
oldline(text); /* remove dummy head instruction */
|
|
if (l1 == (line_p) 0) return; /* no text at all! */
|
|
l2 = first_nonpseudo(l1);
|
|
if (l2 == (line_p) 0) {
|
|
/* modified code consists only of pseudos */
|
|
app_list(l1,PREV(first_nonpseudo(firstline)));
|
|
} else {
|
|
if (l1 == l2) {
|
|
/* no pseudos */
|
|
app_list(l2,l);
|
|
} else {
|
|
lastpseu = PREV(first_nonpseudo(firstline));
|
|
PREV(l2)->l_next = (line_p) 0; /* cut link */
|
|
app_list(l2,l); /* insert normal instructions */
|
|
app_list(l1,lastpseu);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
liquidate(p,text)
|
|
proc_p p;
|
|
line_p text;
|
|
{
|
|
/* All calls to procedure p were expanded in line, so
|
|
* p is no longer needed. However, we must not throw away
|
|
* any data declarations appearing in p.
|
|
* The proctable entry of p is not removed, as we do not
|
|
* want to create holes in this table; however the PF_BODYSEEN
|
|
* flag is cleared, so p gets the same status as a procedure
|
|
* whose body is unmkown.
|
|
*/
|
|
|
|
line_p l, nextl, lastkept = (line_p) 0;
|
|
call_p c, nextc;
|
|
|
|
for (l = text; l != (line_p) 0; l = nextl) {
|
|
nextl = l->l_next;
|
|
switch(INSTR(l)) {
|
|
case ps_sym:
|
|
case ps_hol:
|
|
case ps_bss:
|
|
case ps_con:
|
|
case ps_rom:
|
|
lastkept = l;
|
|
break;
|
|
default:
|
|
rem_line(l);
|
|
}
|
|
}
|
|
if (lastkept != (line_p) 0) {
|
|
/* There were some data declarations in p,
|
|
* so we'll turn p into a data-unit; we'll
|
|
* have to append an end-pseudo for this
|
|
* purpose.
|
|
*/
|
|
lastkept->l_next = newline(OPNO);
|
|
lastkept->l_next->l_instr = (byte) ps_end;
|
|
}
|
|
/* There may be some calls in the body of p that
|
|
* ought to be expanded in line. As p is removed
|
|
* anyway, there is no use in really performing
|
|
* these substitutions, so the call-descriptors
|
|
* are just thrown away.
|
|
*/
|
|
|
|
for (c = p->P_CALS; c != (call_p) 0; c = nextc) {
|
|
nextc = c->cl_cdr;
|
|
rem_call(c);
|
|
}
|
|
/* change the proctable entry */
|
|
p->p_flags1 &= (byte) ~PF_BODYSEEN;
|
|
oldchange(p->p_change);
|
|
olduse(p->p_use);
|
|
}
|