ack/util/ego/il/il2_aux.c
George Koehler 9037d137f5 Add prototypes, void in util/ego/share
This uncovers a problem in il/il_aux.c: it passes 3 arguments to
getlines(), but the function expects 4 arguments.  I add FALSE as the
4th argument.  TRUE would fill in the list of mesregs.  IL uses
mesregs during phase 1, but this call to getlines() is in phase 2.
TRUE would leak memory unless I added a call to Ldeleteset(mesregs).
So I pass FALSE.

Functions passed to go() now have a `void *` parameter because
no_action() now takes a `void *`.
2017-11-15 17:19:56 -05:00

733 lines
15 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 2 _ A U X . C
*/
#include <stdlib.h>
#include <stdio.h>
#include <em_spec.h>
#include <em_mnem.h>
#include "../share/types.h"
#include "il.h"
#include "../share/debug.h"
#include "../share/alloc.h"
#include "../share/global.h"
#include "../share/lset.h"
#include "il_aux.h"
#include "il2_aux.h"
#include "../share/get.h"
#include "../share/aux.h"
#define USE_INDIR(p) (p->p_use->u_flags & UF_INDIR)
#define OFTEN_USED(f) ((f->f_flags&FF_OFTENUSED) == FF_OFTENUSED)
#define CHANGE_EXT(p) (Cnrelems(p->p_change->c_ext) > 0)
#define NOT_INLINE(a) (a->ac_inl = FALSE)
#define INLINE(a) (a->ac_inl = TRUE)
#define CHANGED(p) p->p_flags2 |= PF_CHANGED
#define IS_CHANGED(p) (p->p_flags2 & PF_CHANGED)
STATIC bool match_pars(fm,act)
formal_p fm;
actual_p act;
{
/* Check if every actual parameter has the same
* size as its corresponding formal. If not, the
* actual parameters should not be expanded in line.
*/
while (act != (actual_p) 0) {
if (fm == (formal_p) 0 || tsize(fm->f_type) != act->ac_size) {
return FALSE;
}
act = act->ac_next;
fm = fm->f_next;
}
return (fm == (formal_p) 0 ? TRUE : FALSE);
}
STATIC bool change_act(p,act)
proc_p p;
actual_p act;
{
/* See if a call to p migth change any of the
* operands of the actual parameter expression.
* If the parameter is to be expanded in line,
* we must be sure its value does not depend
* on the point in the program where it is
* evaluated.
*/
line_p l;
for (l = act->ac_exp; l != (line_p) 0; l = l->l_next) {
switch(INSTR(l)) {
case op_lil:
case op_lof:
case op_loi:
case op_los:
case op_ldf:
return TRUE;
/* assume worst case */
case op_lol:
case op_ldl:
if (CHANGE_INDIR(p)) {
return TRUE;
}
break;
case op_loe:
case op_lde:
if (CHANGE_INDIR(p) || CHANGE_EXT(p)) {
return TRUE;
}
break;
}
}
return FALSE;
}
STATIC bool is_simple(expr)
line_p expr;
{
/* See if expr is something simple, i.e. a constant or
* a variable. So the expression must consist of
* only one instruction.
*/
if (expr->l_next == (line_p) 0) {
switch(INSTR(expr)) {
case op_loc:
case op_ldc:
case op_lol:
case op_ldl:
case op_loe:
case op_lde:
return TRUE;
}
}
return FALSE;
}
STATIC bool too_expensive(fm,act)
formal_p fm;
actual_p act;
{
/* If the formal parameter is used often and the
* actual parameter is not something simple
* (i.e. an expression, not a constant or variable)
* it may be too expensive too expand the parameter
* in line.
*/
return (OFTEN_USED(fm) && !is_simple(act->ac_exp));
}
bool anal_params(c)
call_p c;
{
/* Determine which of the actual parameters of a
* call may be expanded in line.
*/
proc_p p;
actual_p act;
formal_p form;
int inlpars = 0;
p = c->cl_proc; /* the called procedure */
if (!match_pars(p->P_FORMALS, c->cl_actuals)) return FALSE;
if (!INLINE_PARS(p)) {
for (act = c->cl_actuals; act != (actual_p) 0;
act = act->ac_next) {
NOT_INLINE(act);
}
return TRUE; /* "# of inline pars." field in cl_flags remains 0 */
}
for (act = c->cl_actuals, form = p->P_FORMALS; act != (actual_p) 0;
act = act->ac_next, form = form->f_next) {
if (form->f_flags & FF_BAD ||
change_act(p,act) || too_expensive(form,act)) {
NOT_INLINE(act);
} else {
INLINE(act);
inlpars++;
}
}
if (inlpars > 15) inlpars = 15; /* We've only got 4 bits! */
c->cl_flags |= inlpars; /* number of inline parameters */
return TRUE;
}
STATIC short space_saved(c)
call_p c;
{
/* When a call gets expanded in line, the total size of the
* code usually gets incremented, because we have to
* duplicate the text of the called routine. However, we save
* ourselves a CAL instruction and possibly anASP instruction
* (if the called procedure has parameters). Moreover, if we
* can put some parameters in line, we don't have to push
* their results on the stack before doing the call, so we
* save some code here too. The routine estimates the amount of
* code saved, expressed in number of EM instructions.
*/
return (1 + (c->cl_flags & CLF_INLPARS) + (c->cl_proc->p_nrformals>0));
}
STATIC short param_score(c)
call_p c;
{
/* If a call has an inline parameter that is a constant,
* chances are high that other optimization techniques
* can do further optimizations, especially if the constant
* happens to be "0". So the call gets extra points for this.
*/
register actual_p act;
line_p l;
short score = 0;
for (act = c->cl_actuals; act != (actual_p) 0; act = act->ac_next) {
if (act->ac_inl) {
l = act->ac_exp;
if (l->l_next == (line_p) 0 &&
(INSTR(l) == op_loc || INSTR(l) == op_ldc)) {
score += (off_set(l) == (offset) 0 ? 2 : 1);
/* 0's count for two! */
}
}
}
return score;
}
assign_ratio(c)
call_p c;
{
/* This routine is one of the most important ones
* of the inline substitution phase. It assigns a number
* (a 'ratio') to a call, indicating how desirable
* it is to expand the call in line.
* Currently, a very simplified straightforward heuristic
* is used.
*/
short ll, loopfact, ratio;
ll = c->cl_proc->P_SIZE - space_saved(c);
if (ll <= 0) ll = 1;
ratio = 1000 / ll;
if (ratio == 0) ratio = 1;
/* Add points if the called procedure falls through
* it's end (no BRA needed) or has formal parameters
* (ASP can be deleted).
*/
if (c->cl_proc->p_flags2 & PF_FALLTHROUGH) {
ratio += 10;
}
if (c->cl_proc->p_nrformals > 0) {
ratio += 10;
}
if (c->cl_caller->p_localbytes == 0) {
ratio -= 10;
}
ratio += (10 *param_score(c));
/* Extra points for constants as parameters */
if (ratio <= 0) ratio = 1;
ll = c->cl_looplevel+1;
if (ll == 1 && !IS_CALLED_IN_LOOP(c->cl_caller)) ll = 0;
/* If the call is not in a loop and the called proc. is never called
* in a loop, ll is set to 0.
*/
loopfact = (ll > 3 ? 10 : ll*ll);
ratio *= loopfact;
if (c->cl_flags & CLF_FIRM) {
ratio = 2*ratio;
}
c->cl_ratio = ratio;
}
call_p abstract(c)
call_p c;
{
/* Abstract information from the call that is essential
* for choosing the calls that will be expanded.
* Put the information is an 'abstracted call'.
*/
call_p a;
a = newcall();
a->cl_caller = c->cl_caller;
a->cl_id = c->cl_id;
a->cl_proc = c->cl_proc;
a->cl_looplevel = c->cl_looplevel;
a->cl_ratio = c->cl_ratio;
a->cl_flags = c->cl_flags;
return a;
}
STATIC adjust_counts(callee,ccf)
proc_p callee;
FILE *ccf;
{
/* A call to callee is expanded in line;
* the text of callee is not removed, so
* every proc called by callee gets its
* P_NRCALLED field incremented.
*/
calcnt_p cc, head;
head = getcc(ccf,callee); /* get calcnt info of called proc */
for (cc = head; cc != (calcnt_p) 0; cc = cc->cc_next) {
cc->cc_proc->P_NRCALLED += cc->cc_count;
}
remcc(head); /* remove calcnt info */
}
STATIC bool is_dispensable(callee,ccf)
proc_p callee;
FILE *ccf;
{
/* A call to callee is expanded in line.
* Decrement its P_NRCALLED field and see if
* it can now be removed because it is no
* longer called. Procedures that ever have
* their address taken (via LPI) will never
* be removed, as they might be called indirectly.
*/
if ((--callee->P_NRCALLED) == 0 &&
(complete_program || (callee->p_flags1 & PF_EXTERNAL) == 0) &&
(callee->p_flags1 & PF_LPI) == 0) {
DISPENSABLE(callee);
OUTVERBOSE("dispensable: procedure %d can be removed",callee->p_id,0);
#ifdef VERBOSE
Spremoved++;
#endif
return TRUE;
} else {
adjust_counts(callee,ccf);
return FALSE;
}
}
STATIC call_p nested_calls(a)
call_p a;
{
/* Get a list of all calls that will appear in the
* EM text if the call 'a' is expanded in line.
* These are the calls in the P_CALS list of the
* called procedure.
*/
call_p c, cp, head, *cpp;
head = (call_p) 0;
cpp = &head;
for (c = a->cl_proc->P_CALS; c != (call_p) 0; c = c->cl_cdr) {
cp = abstract(c);
cp->cl_looplevel += a->cl_looplevel;
cp->cl_flags = (byte) 0;
if (a->cl_flags & CLF_FIRM) {
cp->cl_flags |= CLF_FIRM;
}
assign_ratio(cp);
*cpp = cp;
cpp = &cp->cl_cdr;
}
return head;
}
STATIC call_p find_origin(c)
call_p c;
{
/* c is a nested call. Find the original call.
* This origional must be in the P_CALS list
* of the calling procedure.
*/
register call_p x;
for (x = c->cl_caller->P_CALS; x != (call_p) 0; x = x->cl_cdr) {
if (x->cl_id == c->cl_id) return x;
}
assert(FALSE);
/* NOTREACHED */
}
STATIC selected(a)
call_p a;
{
/* The call a is selected for in line expansion.
* Mark the call as being selected and get the
* calls nested in it; these will be candidates
* too now.
*/
SELECTED(a);
EVER_EXPANDED(find_origin(a));
a->cl_car = nested_calls(a);
}
STATIC compare(x,best,space)
call_p x, *best;
long space;
{
/* See if x is better than the current best choice */
if (x != (call_p) 0 && !IS_CHANGED(x->cl_proc) &&
x->cl_proc->P_SIZE - space_saved(x) <= space) {
if ((*best == (call_p) 0 && x->cl_ratio != 0) ||
(*best != (call_p) 0 && x->cl_ratio > (*best)->cl_ratio )) {
*best = x;
}
}
}
STATIC call_p best_one(list,space)
call_p list;
long space;
{
/* Find the best candidate of the list
* that has not already been selected. The
* candidate must fit in the given space.
* We look in the cdr as well as in the car
* direction.
*/
call_p best = (call_p) 0;
call_p c;
for (c = list; c != (call_p) 0; c = c->cl_cdr) {
if (IS_SELECTED(c)) {
compare(best_one(c->cl_car,space),&best,space);
} else {
compare(c,&best,space);
}
}
return best;
}
STATIC singles(cals)
call_p cals;
{
/* If a procedure is only called once, this call
* will be expanded in line, because it costs
* no extra space.
*/
call_p c;
for (c = cals; c != (call_p) 0; c = c->cl_cdr) {
if (IS_SELECTED(c)) {
singles(c->cl_car);
} else {
if (c->cl_proc->P_NRCALLED == 1 &&
!IS_CHANGED(c->cl_proc) &&
(complete_program ||
(c->cl_proc->p_flags1 & PF_EXTERNAL) == 0) &&
(c->cl_proc->p_flags1 & PF_LPI) == 0) {
c->cl_proc->P_NRCALLED = 0;
SELECTED(c);
EVER_EXPANDED(find_origin(c));
DISPENSABLE(c->cl_proc);
CHANGED(c->cl_caller);
OUTVERBOSE("singles: procedure %d can be removed",
c->cl_proc->p_id, 0);
#ifdef VERBOSE
Spremoved++;
#endif
}
}
}
}
STATIC single_calls(proclist)
proc_p proclist;
{
proc_p p;
for (p = proclist; p != (proc_p) 0; p = p->p_next) {
if (!BIG_CALLER(p) && !IS_DISPENSABLE(p)) {
/* Calls appearing in a large procedure or in
* a procedure that was already eliminated
* are not considered.
*/
singles(p->P_CALS);
}
}
}
select_calls(proclist,ccf,space)
proc_p proclist;
FILE *ccf;
long space ;
{
/* Select all calls that are to be expanded in line. */
proc_p p,chp;
call_p best, x;
for (;;) {
best = (call_p) 0;
chp = (proc_p) 0; /* the changed procedure */
for (p = proclist; p != (proc_p) 0; p = p->p_next) {
if (!BIG_CALLER(p) && !IS_DISPENSABLE(p)) {
/* Calls appearing in a large procedure or in
* a procedure that was already eliminated
* are not considered.
*/
x = best_one(p->P_CALS,space);
compare(x,&best,space);
if (x == best) chp = p;
}
}
if (best == (call_p) 0) break;
if (!is_dispensable(best->cl_proc,ccf)) {
space -= (best->cl_proc->P_SIZE - space_saved(best));
}
else space += space_saved(best);
#ifdef VERBOSE
if (verbose_flag) fprintf(stderr, "space left: %ld\n", space);
#endif
selected(best);
CHANGED(chp);
}
single_calls(proclist);
#ifdef VERBOSE
Sstat(proclist,space);
#endif
}
STATIC nonnested_calls(cfile)
FILE *cfile;
{
register call_p c,a;
while((c = getcall(cfile)) != (call_p) 0) {
/* find the call in the call list of the caller */
for (a = c->cl_caller->P_CALS;
a != (call_p) 0 && c->cl_id != a->cl_id; a = a->cl_cdr);
assert(a != (call_p) 0 && a->cl_proc == c->cl_proc);
if (IS_EVER_EXPANDED(a)) {
a->cl_actuals = c->cl_actuals;
c->cl_actuals = (actual_p) 0;
}
rem_call(c);
}
}
STATIC copy_pars(src,dest)
call_p src, dest;
{
/* Copy the actual parameters of src to dest. */
actual_p as,ad, *app;
app = &dest->cl_actuals;
for (as = src->cl_actuals; as != (actual_p) 0; as = as->ac_next) {
ad = newactual();
ad->ac_exp = copy_expr(as->ac_exp);
ad->ac_size = as->ac_size;
ad->ac_inl = as->ac_inl;
*app = ad;
app = &ad->ac_next;
}
}
STATIC nest_pars(cals)
call_p cals;
{
/* Recursive auxiliary procedure of add_actuals. */
call_p c,org;
for (c = cals; c != (call_p) 0; c = c->cl_cdr) {
if (IS_SELECTED(c)) {
org = find_origin(c);
copy_pars(org,c);
nest_pars(c->cl_car);
}
}
}
add_actuals(proclist,cfile)
proc_p proclist;
FILE *cfile;
{
/* Fetch the actual parameters of all selected calls.
* For all non-nested calls (i.e. those calls that
* appeared originally in the EM text), we get the
* parameters from the cal-file.
* For nested calls (i.e. calls
* that are a result of in line substitution) we
* get the parameters from the original call.
*/
proc_p p;
call_p a;
nonnested_calls(cfile);
for (p = proclist; p != (proc_p) 0; p = p->p_next) {
for (a = p->P_CALS; a != (call_p) 0; a = a->cl_cdr) {
nest_pars(a->cl_car);
}
}
}
STATIC clean(cals)
call_p *cals;
{
call_p c,next,*cpp;
/* Recursive auxiliary routine of cleancals */
cpp = cals;
for (c = *cpp; c != (call_p) 0; c = next) {
next = c->cl_cdr;
if (IS_SELECTED(c)) {
clean(&c->cl_car);
cpp = &c->cl_cdr;
} else {
assert(c->cl_car == (call_p) 0);
oldcall(c);
*cpp = next;
}
}
}
cleancals(proclist)
proc_p proclist;
{
/* Remove all calls in the P_CALS list of p
* that were not selected for in line expansion.
*/
register proc_p p;
for (p = proclist; p != (proc_p) 0; p = p->p_next) {
clean(&p->P_CALS);
}
}
append_abstract(a,p)
call_p a;
proc_p p;
{
/* Append an abstract of a call-descriptor to
* the call-list of procedure p.
*/
call_p c;
if (p->P_CALS == (call_p) 0) {
p->P_CALS = a;
} else {
for (c = p->P_CALS; c->cl_cdr != (call_p) 0; c = c->cl_cdr);
c->cl_cdr = a;
}
}
#ifdef VERBOSE
/* At the end, we traverse the entire call-list, to see why the
* remaining calls were not expanded inline.
*/
Sstatist(list,space)
call_p list;
long space;
{
call_p c;
for (c = list; c != (call_p) 0; c = c->cl_cdr) {
if (IS_SELECTED(c)) {
Sstatist(c->cl_car,space);
} else {
if (IS_CHANGED(c->cl_proc)) Schangedcallee++;
else if (BIG_PROC(c->cl_proc)) Sbigcallee++;
else if (c->cl_proc->P_SIZE > space) Sspace++;
else if (c->cl_ratio == 0) Szeroratio++;
else assert(FALSE);
}
}
}
Sstat(proclist,space)
proc_p proclist;
long space;
{
proc_p p;
for (p = proclist; p != (proc_p) 0; p = p->p_next) {
if (BIG_CALLER(p)) Sbig_caller++;
else if (IS_DISPENSABLE(p)) Sdispensable++;
else Sstatist(p->P_CALS,space);
}
}
#endif