ack/util/ego/ic/ic.c
George Koehler 17bc9cdef7 More void, fewer clang warnings in util/ego
Most warnings are for functions implicitly returning int.  Change most
of these functions to return void.  (Traditional K&R C had no void
type, but C89 has it.)

Add prototypes to most function declarations in headers.  This is
easy, because ego declares most of its extern functions, and the
comments listed most parameters.  There were a few outdated or missing
declarations, and a few .c files that failed to include an .h with the
declarations.

Add prototypes to a few function definitions in .c files.  Most
functions still have traditional K&R definitions.  Most STATIC
functions still don't have prototypes, because they have no earlier
declaration where I would have added the prototype.

Change some prototypes in util/ego/share/alloc.h.  Functions newmap()
and oldmap() handle an array of pointers to something; change the
array's type from `short **` to `void **`.  Callers use casts to go
between `void **` and the correct type, like `line_p *`.  Function
oldtable() takes a `short *`, not a `short **`; I added the wrong type
in 5bbbaf4.

Make a few other changes to silence warnings.  There are a few places
where clang wants extra parentheses in the code.

Edit util/ego/ra/build.lua to add the missing dependency on ra*.h; I
needed this to prevent crashes from ra.
2019-11-01 15:27:16 -04:00

592 lines
14 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 T E R M E D I A T E C O D E
*
* I C . C
*/
#include <stdlib.h>
#include <stdio.h>
#include <em_spec.h>
#include <em_pseu.h>
#include <em_flag.h>
#include <em_mes.h>
#include "../share/types.h"
#include "../share/debug.h"
#include "../share/def.h"
#include "../share/map.h"
#include "ic.h"
#include "ic_lookup.h"
#include "ic_aux.h"
#include "ic_io.h"
#include "ic_lib.h"
#include "../share/alloc.h"
#include "../share/global.h"
#include "../share/files.h"
#include "../share/put.h"
#include "../share/utils.h"
/* Global variables */
dblock_p db;
dblock_p hol0_db; /* dblock for ABS block */
char* curhol; /* name of hol block in current scope */
dblock_p ldblock; /* last dblock */
proc_p lproc; /* last proc */
short tabval; /* used by table1, table2 and table3 */
offset tabval2;
char string[IDL + 1];
line_p firstline; /* first line of current procedure */
line_p lastline; /* last line read */
int labelcount; /* # labels in current procedure */
short fragm_type = DUNKNOWN; /* fragm. type: DCON, DROM or DUNKNOWN */
short fragm_nr = 0; /* fragment number */
obj_id lastoid = 0;
proc_id lastpid = 0;
dblock_id lastdid = 0;
lab_id lastlid = 0;
offset mespar = UNKNOWN_SIZE;
/* argumument of ps_par message of current procedure */
STATIC void process_lines(FILE *);
STATIC int readline(short *, line_p *);
STATIC line_p readoperand(short);
STATIC line_p inpseudo(short);
int main(argc, argv) int argc;
char* argv[];
{
/* The input files must be legal EM Compact
* Assembly Language files, as produced by the EM Peephole
* Optimizer.
* Their file names are passed as arguments.
* The output consists of the files:
* - lfile: the EM code in Intermediate Code format
* - dfile: the data block table file
* - pfile: the proc table file
* - pdump: the names of all procedures
* - ddump: the names of all data blocks
*/
/* The input file names */
const char* pdump_out = argv[1];
const char* ddump_out = argv[2];
/* The output file names */
const char* pname_out = argv[5];
const char* dname_out = argv[6];
const char* lname_out = argv[7];
FILE* lfile = openfile(lname_out, "w");
FILE* pdump = openfile(pdump_out, "w");
FILE* ddump = openfile(ddump_out, "w");
FILE* dfile;
FILE* pfile;
hol0_db = block_of_lab((char*)0);
while (next_file(argc-8, argv+8) != NULL)
{
/* Read all EM input files, process the code
* and concatenate all output.
*/
curhol = (char*)0;
process_lines(lfile);
dump_procnames(prochash, NPROCHASH, pdump);
dump_dblocknames(symhash, NSYMHASH, ddump);
/* Save the names of all procedures that were
* first come accross in this file.
*/
cleanprocs(prochash, NPROCHASH, PF_EXTERNAL);
cleandblocks(symhash, NSYMHASH, DF_EXTERNAL);
/* Make all procedure names that were internal
* in this input file invisible.
*/
}
fclose(lfile);
fclose(pdump);
fclose(ddump);
/* remove the remainder of the hashing tables */
cleanprocs(prochash, NPROCHASH, 0);
cleandblocks(symhash, NSYMHASH, 0);
/* Now write the datablock table and the proctable */
dfile = openfile(dname_out, "w");
putdtable(fdblock, dfile);
pfile = openfile(pname_out, "w");
putptable(fproc, pfile, FALSE);
exit(0);
}
/* Value returned by readline */
#define NORMAL 0
#define WITH_OPERAND 1
#define EOFILE 2
#define PRO_INSTR 3
#define END_INSTR 4
#define DELETED_INSTR 5
STATIC void add_end()
{
/* Add an end-pseudo to the current instruction list */
lastline->l_next = newline(OPNO);
lastline = lastline->l_next;
lastline->l_instr = ps_end;
}
STATIC void process_lines(fout)
FILE* fout;
{
line_p lnp;
short instr;
bool eof;
/* Read and process the code contained in the current file,
* on a per procedure basis.
* On the fly, fragments are formed. Recall that two
* successive CON pseudos are allocated consecutively
* in a single fragment, unless these CON pseudos are
* separated in the assembly language program by one
* of: ROM, BSS, HOL and END (and of course EndOfFile).
* The same is true for ROM pseudos.
* We keep track of a fragment type (DROM after a ROM
* pseudo, DCON after a CON and DUNKNOWN after a HOL,
* BSS, END or EndOfFile) and a fragment number (which
* is incremented every time we enter a new fragment).
* Every data block is assigned such a number
* when we come accross its defining occurrence.
*/
eof = FALSE;
firstline = (line_p)0;
lastline = (line_p)0;
while (!eof)
{
linecount++; /* for error messages */
switch (readline(&instr, &lnp))
{
/* read one line, see what kind it is */
case WITH_OPERAND:
/* instruction with operand, e.g. LOL 10 */
lnp = readoperand(instr);
lnp->l_instr = instr;
/* Fall through! */
case NORMAL:
VL(lnp);
if (lastline != (line_p)0)
{
lastline->l_next = lnp;
}
lastline = lnp;
break;
case EOFILE:
eof = TRUE;
fragm_type = DUNKNOWN;
if (firstline != (line_p)0)
{
add_end();
putlines(firstline, fout);
firstline = (line_p)0;
}
break;
case PRO_INSTR:
VL(lnp);
labelcount = 0;
if (firstline != lnp)
{
/* If PRO is not the first
* instruction:
*/
add_end();
putlines(firstline, fout);
firstline = lnp;
}
lastline = lnp;
break;
case END_INSTR:
curproc->p_nrformals = mespar;
mespar = UNKNOWN_SIZE;
assert(lastline != (line_p)0);
lastline->l_next = lnp;
putlines(firstline, fout);
/* write and delete code */
firstline = (line_p)0;
lastline = (line_p)0;
cleaninstrlabs();
/* scope of instruction labels ends here,
* so forget about them.
*/
fragm_type = DUNKNOWN;
break;
case DELETED_INSTR:
/* EXP, INA etc. are deleted */
break;
default:
error("illegal readline");
}
}
}
STATIC int readline(short *instr_out, line_p *lnp_out)
{
register line_p lnp;
short n;
/* Read one line. If it is a normal EM instruction without
* operand, we can allocate a line struct for it here.
* If so, return a pointer to it via lnp_out, else just
* return the instruction code via instr_out.
*/
VA((short*)instr_out);
VA((short*)lnp_out);
switch (table1())
{
/* table1 sets string, tabval or tabval2 and
* returns an indication of what was read.
*/
case ATEOF:
return EOFILE;
case INST:
*instr_out = tabval; /* instruction code */
return WITH_OPERAND;
case DLBX:
/* data label defining occurrence, precedes
* a data block.
*/
db = block_of_lab(string);
/* global variable, used by inpseudo */
lnp = newline(OPSHORT);
SHORT(lnp) = (short)db->d_id;
lnp->l_instr = ps_sym;
*lnp_out = lnp;
if (firstline == (line_p)0)
{
firstline = lnp;
/* only a pseudo (e.g. PRO) or data label
* can be the first instruction.
*/
}
return NORMAL;
case ILBX:
/* instruction label defining occurrence */
labelcount++;
lnp = newline(OPINSTRLAB);
lnp->l_instr = op_lab;
INSTRLAB(lnp) = instr_lab(tabval);
*lnp_out = lnp;
return NORMAL;
case PSEU:
n = tabval;
lnp = inpseudo(n); /* read a pseudo */
if (n == ps_hol)
n = ps_bss;
if (lnp == (line_p)0)
return DELETED_INSTR;
*lnp_out = lnp;
lnp->l_instr = n;
if (firstline == (line_p)0)
{
firstline = lnp;
/* only a pseudo (e.g. PRO) or data label
* can be the first instruction.
*/
}
if (n == ps_end)
return END_INSTR;
if (n == ps_pro)
return PRO_INSTR;
return NORMAL;
}
/* NOTREACHED */
}
STATIC line_p readoperand(short instr)
{
/* Read the operand of the given instruction.
* Create a line struct and return a pointer to it.
*/
register line_p lnp;
short flag;
VI(instr);
flag = em_flag[instr - sp_fmnem] & EM_PAR;
if (flag == PAR_NO)
{
return (newline(OPNO));
}
switch (table2())
{
case sp_cend:
return (newline(OPNO));
case CSTX1:
/* constant */
/* If the instruction has the address
* of an external variable as argument,
* the constant must be regarded as an
* offset in the current hol block,
* so an object must be created.
* Similarly, the instruction may have
* an instruction label as argument.
*/
switch (flag)
{
case PAR_G:
lnp = newline(OPOBJECT);
OBJ(lnp) = object(curhol, (offset)tabval,
opr_size(instr));
break;
case PAR_B:
lnp = newline(OPINSTRLAB);
INSTRLAB(lnp) = instr_lab(tabval);
break;
default:
lnp = newline(OPSHORT);
SHORT(lnp) = tabval;
break;
}
break;
#ifdef LONGOFF
case CSTX2:
/* double constant */
if (flag == PAR_G)
{
lnp = newline(OPOBJECT);
OBJ(lnp) = object(curhol, tabval2,
opr_size(instr));
break;
}
lnp = newline(OPOFFSET);
OFFSET(lnp) = tabval2;
break;
#endif
case ILBX:
/* applied occurrence instruction label */
lnp = newline(OPINSTRLAB);
INSTRLAB(lnp) = instr_lab(tabval);
break;
case DLBX:
/* applied occurrence data label */
lnp = newline(OPOBJECT);
OBJ(lnp) = object(string, (offset)0,
opr_size(instr));
break;
case VALX1:
lnp = newline(OPOBJECT);
OBJ(lnp) = object(string, (offset)tabval,
opr_size(instr));
break;
#ifdef LONGOFF
case VALX2:
lnp = newline(OPOBJECT);
OBJ(lnp) = object(string, tabval2,
opr_size(instr));
break;
#endif
case sp_pnam:
lnp = newline(OPPROC);
PROC(lnp) = proclookup(string, OCCURRING);
VP(PROC(lnp));
break;
default:
assert(FALSE);
}
return lnp;
}
static char* hol_label()
{
static int holno;
line_p lnp;
extern char* lastname;
/* Create a label for a hol pseudo, so that it can be converted
* into a bss. The label is appended to the list of instructions.
*/
sprintf(string, "_HH%d", ++holno);
symlookup(string, OCCURRING); /* to make it exa */
db = block_of_lab(string);
lnp = newline(OPSHORT);
SHORT(lnp) = (short)db->d_id;
lnp->l_instr = ps_sym;
if (firstline == (line_p)0)
{
firstline = lnp;
}
if (lastline != (line_p)0)
{
lastline->l_next = lnp;
}
lastline = lnp;
return lastname;
}
STATIC line_p inpseudo(short n)
{
int m;
line_p lnp;
byte pseu;
short nlast;
/* Read the (remainder of) a pseudo instruction, the instruction
* code of which is n. The END pseudo may be deleted (return 0).
* The pseudos INA, EXA, INP and EXP (visibility pseudos) must
* also be deleted, although the effects they have on the
* visibility of global names and procedure names must first
* be recorded in the datablock or procedure table.
*/
switch (n)
{
case ps_hol:
/* hol pseudos are carefully converted into bss
* pseudos, so that the IL phase will not be
* bothered by this. Also, references to the ABS
* block will still work when passed through EGO.
*/
if (lastline != (line_p)0 && is_datalabel(lastline))
{
extern char* lastname;
curhol = lastname;
}
else
{
curhol = hol_label();
}
n = ps_bss;
/* fall through */
case ps_bss:
case ps_rom:
case ps_con:
if (lastline == (line_p)0 || !is_datalabel(lastline))
{
assert(lastline != (line_p)0);
nlast = INSTR(lastline);
if (n == nlast && (n == ps_rom || n == ps_con))
{
/* Two successive roms/cons are
* combined into one data block
* if the second is not preceded by
* a data label.
*/
lnp = arglist(0);
pseu = (byte)(n == ps_rom ? DROM : DCON);
combine(db, lastline, lnp, pseu);
oldline(lnp);
return (line_p)0;
}
else
{
error("datablock without label");
}
}
VD(db);
m = (n == ps_bss ? 3 : 0);
lnp = arglist(m);
/* Read the arguments, 3 for hol or bss and a list
* of undetermined length for rom and con.
*/
dblockdef(db, n, lnp);
/* Fill in d_pseudo, d_size and d_values fields of db */
if (fragm_type != db->d_pseudo)
{
/* Keep track of fragment numbers,
* enter a new fragment.
*/
fragm_nr++;
switch (db->d_pseudo)
{
case DCON:
case DROM:
fragm_type = db->d_pseudo;
break;
default:
fragm_type = DUNKNOWN;
break;
}
}
db->d_fragmnr = fragm_nr;
return lnp;
case ps_ina:
getsym(DEFINING);
/* Read and lookup a symbol. As this must be
* the first occurrence of the symbol and we
* say it's a defining occurrence, getsym will
* automatically make it internal (according to
* the EM visibility rules).
* The result (a dblock pointer) is voided.
*/
return (line_p)0;
case ps_inp:
getproc(DEFINING); /* same idea */
return (line_p)0;
case ps_exa:
getsym(OCCURRING);
return (line_p)0;
case ps_exp:
getproc(OCCURRING);
return (line_p)0;
case ps_pro:
curproc = getproc(DEFINING);
/* This is a real defining occurrence of a proc */
curproc->p_localbytes = get_off();
curproc->p_flags1 |= PF_BODYSEEN;
/* Record the fact that we came accross
* the body of this procedure.
*/
lnp = newline(OPPROC);
PROC(lnp) = curproc;
lnp->l_instr = (byte)ps_pro;
return lnp;
case ps_end:
curproc->p_nrlabels = labelcount;
lnp = newline(OPNO);
get_off();
/* Void # localbytes, which we already know
* from the PRO instruction.
*/
return lnp;
case ps_mes:
lnp = arglist(0);
switch ((int)aoff(ARG(lnp), 0))
{
case ms_err:
error("ms_err encountered");
case ms_opt:
error("ms_opt encountered");
case ms_emx:
ws = aoff(ARG(lnp), 1);
ps = aoff(ARG(lnp), 2);
break;
case ms_ext:
/* this message was already processed
* by the lib package
*/
case ms_src:
/* Don't bother about linecounts */
oldline(lnp);
return (line_p)0;
case ms_par:
mespar = aoff(ARG(lnp), 1);
/* #bytes of parameters of current proc */
break;
}
return lnp;
default:
assert(FALSE);
}
/* NOTREACHED */
}