summaryrefslogtreecommitdiff
path: root/src/icont/lglob.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/icont/lglob.c')
-rw-r--r--src/icont/lglob.c356
1 files changed, 356 insertions, 0 deletions
diff --git a/src/icont/lglob.c b/src/icont/lglob.c
new file mode 100644
index 0000000..6583b8a
--- /dev/null
+++ b/src/icont/lglob.c
@@ -0,0 +1,356 @@
+/*
+ * lglob.c -- routines for processing globals.
+ */
+
+#include "link.h"
+#include "tglobals.h"
+#include "tproto.h"
+#include "opcode.h"
+#include "../h/version.h"
+
+/*
+ * Prototypes.
+ */
+
+static void scanfile (char *filename);
+static void reference (struct gentry *gp);
+
+int nrecords = 0; /* number of records in program */
+
+/*
+ * readglob reads the global information from infile (.u2) and merges it with
+ * the global table and record table.
+ */
+void readglob()
+ {
+ register word id;
+ register int n, op;
+ int k;
+ int implicit;
+ char *name;
+ struct gentry *gp;
+ extern char *progname;
+
+ if (getopc(&name) != Op_Version)
+ quitf("ucode file %s has no version identification",inname);
+ id = getid(); /* get version number of ucode */
+ newline();
+ if (strcmp(&lsspace[id],UVersion)) {
+ fprintf(stderr,"version mismatch in ucode file %s\n",inname);
+ fprintf(stderr,"\tucode version: %s\n",&lsspace[id]);
+ fprintf(stderr,"\texpected version: %s\n",UVersion);
+ exit(EXIT_FAILURE);
+ }
+ while ((op = getopc(&name)) != EOF) {
+ switch (op) {
+ case Op_Record: /* a record declaration */
+ id = getid(); /* record name */
+ n = getdec(); /* number of fields */
+ newline();
+ gp = glocate(id);
+ /*
+ * It's ok if the name isn't already in use or if the
+ * name is just used in a "global" declaration. Otherwise,
+ * it is an inconsistent redeclaration.
+ */
+ if (gp == NULL || (gp->g_flag & ~F_Global) == 0) {
+ gp = putglobal(id, F_Record, n, ++nrecords);
+ while (n--) { /* loop reading field numbers and names */
+ k = getdec();
+ putfield(getid(), gp, k);
+ newline();
+ }
+ }
+ else {
+ lfatal(&lsspace[id], "inconsistent redeclaration");
+ while (n--)
+ newline();
+ }
+ break;
+
+ case Op_Impl: /* undeclared identifiers should be noted */
+ if (getopc(&name) == Op_Local)
+ implicit = 0;
+ else
+ implicit = F_ImpError;
+ break;
+
+ case Op_Trace: /* turn on tracing */
+ trace = -1;
+ break;
+
+ case Op_Global: /* global variable declarations */
+ n = getdec(); /* number of global declarations */
+ newline();
+ while (n--) { /* process each declaration */
+ getdec(); /* throw away sequence number */
+ k = getoct(); /* get flags */
+ if (k & F_Proc)
+ k |= implicit;
+ id = getid(); /* get variable name */
+ gp = glocate(id);
+ /*
+ * Check for conflicting declarations and install the
+ * variable.
+ */
+ if (gp != NULL && (k & F_Proc) && gp->g_flag != F_Global)
+ lfatal(&lsspace[id], "inconsistent redeclaration");
+ else if (gp == NULL || (k & F_Proc))
+ putglobal(id, k, getdec(), 0);
+ newline();
+ }
+ break;
+
+ case Op_Invocable: /* "invocable" declaration */
+ id = getid(); /* get name */
+ if (lsspace[id] == '0')
+ strinv = 1; /* name of "0" means "invocable all" */
+ else
+ addinvk(&lsspace[id], 2);
+ newline();
+ break;
+
+ case Op_Link: /* link the named file */
+ name = &lsspace[getrest()]; /* get the name and */
+ alsolink(name); /* put it on the list of files to link */
+ newline();
+ break;
+
+ default:
+ quitf("ill-formed global file %s",inname);
+ }
+ }
+ }
+
+/*
+ * scanrefs - scan .u1 files for references and mark unreferenced globals.
+ *
+ * Called only if -fs is *not* specified (or implied by "invocable all").
+ */
+void scanrefs()
+ {
+ int i, n;
+ char *t, *old;
+ struct fentry *fp, **fpp;
+ struct gentry *gp, **gpp;
+ struct rentry *rp;
+ struct lfile *lf, *lfls;
+ struct ientry *ip, *ipnext;
+ struct invkl *inv;
+
+ /*
+ * Loop through .u1 files and accumulate reference lists.
+ */
+ lfls = llfiles;
+ while ((lf = getlfile(&lfls)) != 0)
+ scanfile(lf->lf_name);
+ lstatics = 0; /* discard accumulated statics */
+
+ /*
+ * Mark every global as unreferenced.
+ */
+ for (gp = lgfirst; gp != NULL; gp = gp->g_next)
+ gp->g_flag |= F_Unref;
+
+ /*
+ * Clear the F_Unref flag for referenced globals, starting with main()
+ * and marking references within procedures recursively.
+ */
+ reference(lgfirst);
+
+ /*
+ * Reference (recursively) every global declared to be "invocable".
+ */
+ for (inv = invkls; inv != NULL; inv = inv->iv_link)
+ if ((gp = glocate(instid(inv->iv_name))) != NULL)
+ reference(gp);
+
+ /*
+ * Rebuild the global list to include only referenced globals,
+ * and renumber them. Also renumber all record constructors.
+ * Free all reference lists.
+ */
+ n = 0;
+ nrecords = 0;
+ gpp = &lgfirst;
+ while ((gp = *gpp) != NULL) {
+ if (gp->g_refs != NULL) {
+ free((char *)gp->g_refs); /* free the reference list */
+ gp->g_refs = NULL;
+ }
+ if (gp->g_flag & F_Unref) {
+ /*
+ * Global is not referenced anywhere.
+ */
+ gp->g_index = gp->g_procid = -1; /* flag as unused */
+ if (verbose >= 3) {
+ if (gp->g_flag & F_Proc)
+ t = "procedure";
+ else if (gp->g_flag & F_Record)
+ t = "record ";
+ else
+ t = "global ";
+ if (!(gp->g_flag & F_Builtin))
+ fprintf(stderr, " discarding %s %s\n", t, &lsspace[gp->g_name]);
+ }
+ *gpp = gp->g_next;
+ }
+ else {
+ /*
+ * The global is used. Assign it new serial number(s).
+ */
+ gp->g_index = n++;
+ if (gp->g_flag & F_Record)
+ gp->g_procid = ++nrecords;
+ gpp = &gp->g_next;
+ }
+ }
+
+ /*
+ * Rebuild the field list to include only referenced fields,
+ * and renumber them.
+ */
+ n = 0;
+ fpp = &lffirst;
+ while ((fp = *fpp) != NULL) {
+ for (rp = fp->f_rlist; rp != NULL; rp = rp->r_link)
+ if (rp->r_gp->g_procid > 0) /* if record was referenced */
+ break;
+ if (rp == NULL) {
+ /*
+ * The field was used only in unreferenced record constructors.
+ */
+ fp->f_fid = 0;
+ *fpp = fp->f_nextentry;
+ }
+ else {
+ /*
+ * The field was referenced. Give it the next number.
+ */
+ fp->f_fid = ++n;
+ fpp = &fp->f_nextentry;
+ }
+ }
+
+ /*
+ * Create a new, empty string space, saving a pointer to the old one.
+ * Clear the old identifier hash table.
+ */
+ old = lsspace;
+ lsspace = (char *)tcalloc(stsize, 1);
+ lsfree = 0;
+ for (i = 0; i < ihsize; i++) {
+ for (ip = lihash[i]; ip != NULL; ip = ipnext) {
+ ipnext = ip->i_blink;
+ free((char *)ip);
+ }
+ lihash[i] = NULL;
+ }
+
+ /*
+ * Reinstall the global identifiers that are actually referenced.
+ * This changes the hashing, so clear and rebuild the hash table.
+ */
+ for (i = 0; i < ghsize; i++)
+ lghash[i] = NULL;
+ for (gp = lgfirst; gp != NULL; gp = gp->g_next) {
+ gp->g_name = instid(&old[gp->g_name]);
+ i = ghasher(gp->g_name);
+ gp->g_blink = lghash[i];
+ lghash[i] = gp;
+ }
+
+ /*
+ * Reinstall the referenced record fields in similar fashion.
+ */
+ for (i = 0; i < fhsize; i++)
+ lfhash[i] = NULL;
+ for (fp = lffirst; fp != NULL; fp = fp->f_nextentry) {
+ fp->f_name = instid(&old[fp->f_name]);
+ i = fhasher(fp->f_name);
+ fp->f_blink = lfhash[i];
+ lfhash[i] = fp;
+ }
+
+ /*
+ * Free the old string space.
+ */
+ free((char *)old);
+ }
+
+/*
+ * scanfile -- scan one file for references.
+ */
+static void scanfile(filename)
+char *filename;
+ {
+ int i, k, f, op, nrefs, flags;
+ word id, procid;
+ char *name;
+ struct gentry *gp, **rp;
+
+ makename(inname, SourceDir, filename, U1Suffix);
+ infile = fopen(inname, "r");
+ if (infile == NULL)
+ quitf("cannot open %s", inname);
+
+ while ((op = getopc(&name)) != EOF) {
+ switch (op) {
+ case Op_Proc:
+ procid = getid();
+ newline();
+ gp = glocate(procid);
+ locinit();
+ nrefs = 0;
+ break;
+ case Op_Local:
+ k = getdec();
+ flags = getoct();
+ id = getid();
+ putlocal(k, id, flags, 0, procid);
+ lltable[k].l_flag |= F_Unref;
+ break;
+ case Op_Var:
+ k = getdec();
+ newline();
+ f = lltable[k].l_flag;
+ if ((f & F_Global) && (f & F_Unref)) {
+ lltable[k].l_flag = f & ~F_Unref;
+ nrefs++;
+ }
+ break;
+ case Op_End:
+ newline();
+ if (nrefs > 0) {
+ rp = (struct gentry **)tcalloc(nrefs + 1, sizeof(*rp));
+ gp->g_refs = rp;
+ for (i = 0; i <= nlocal; i++)
+ if ((lltable[i].l_flag & (F_Unref + F_Global)) == F_Global)
+ *rp++ = lltable[i].l_val.global;
+ *rp = NULL;
+ }
+ break;
+ default:
+ newline();
+ break;
+ }
+ }
+
+ fclose(infile);
+ }
+
+/*
+ *
+ */
+static void reference(gp)
+struct gentry *gp;
+ {
+ struct gentry **rp;
+
+ if (gp->g_flag & F_Unref) {
+ gp->g_flag &= ~F_Unref;
+ if ((rp = gp->g_refs) != NULL)
+ while ((gp = *rp++) != 0)
+ reference(gp);
+ }
+ }