Initial revision

This commit is contained in:
ceriel
1986-11-24 20:58:35 +00:00
parent 374d4aac17
commit 0e38359528
11 changed files with 1356 additions and 0 deletions

41
mach/proto/top/Makefile Normal file
View File

@@ -0,0 +1,41 @@
EM=../../..
PREFLAGS=-I.
PFLAGS=
CFLAGS=$(PREFLAGS) $(PFLAGS) -O
LDFLAGS=-i $(PFLAGS)
LINTOPTS=-hbxac
CDIR=$(EM)/mach/proto/top
CFILES=$(CDIR)/top.c $(CDIR)/queue.c gen.c
OFILES=top.o queue.o gen.o
all: gen.c
make top
top: $(OFILES)
$(CC) $(LDFLAGS) $(OFILES) $(LIBS) -o top
top.o: $(CDIR)/top.c
$(CC) -c $(CFLAGS) $(CDIR)/top.c
queue.o: $(CDIR)/queue.c
$(CC) -c $(CFLAGS) $(CDIR)/queue.c
install: all
$(EM)/mach/install top
cmp: all
-$(EM)/mach/compare top
gen.c: table
$(EM)/lib/topgen table
lint: $(CFILES)
lint $(LINTOPTS) $(PREFLAGS) $(CFILES)
clean:
rm -f *.o gen.c gen.h top
top.o: gen.h
top.o: $(CDIR)/top.h
top.o: $(CDIR)/queue.h
queue.o: $(CDIR)/queue.h

72
mach/proto/top/queue.c Normal file
View File

@@ -0,0 +1,72 @@
#include "top.h"
#include "queue.h"
empty_queue(q)
queue q;
{
q->head = q->tail = (instr_p) 0;
q->qlen = 0;
}
int empty(q)
queue q;
{
return q->qlen == 0;
}
remove_head(q)
queue q;
{
if ( (q->head = q->head->fw) == (instr_p) 0) {
q->tail = (instr_p) 0;
} else {
q->head->bw = (instr_p) 0;
}
q->qlen--;
}
add(q,instr)
register queue q;
register instr_p instr;
{
if (q->qlen++ == 0) {
q->head = q->tail = instr;
instr->bw = (instr_p) 0;
} else {
q->tail->fw = instr;
instr->bw = q->tail;
q->tail = instr;
}
instr->fw = (instr_p) 0;
}
insert(q,instr)
queue q;
instr_p instr;
{
if (q->qlen++ == 0) {
q->head = q->tail = instr;
instr->fw = (instr_p) 0;
} else {
q->head->bw = instr;
instr->fw = q->head;
q->head = instr;
}
instr->bw = (instr_p) 0;
}
join_queues(q1,q2)
queue q1,q2;
{
if (q1->qlen > 0) {
q2->qlen += q1->qlen;
q1->tail->fw = q2->head;
if (q2->qlen > 0) {
q2->head->bw = q1->tail;
} else {
q2->tail = q1->tail;
}
q2->head = q1->head;
empty_queue(q1);
}
}

12
mach/proto/top/queue.h Normal file
View File

@@ -0,0 +1,12 @@
typedef struct item *item_p;
typedef struct queue_t *queue;
struct queue_t {
instr_p head;
instr_p tail;
int qlen;
};
#define qhead(q) (q)->head
#define qlength(q) (q)->qlen
#define next(x) (x)->fw

651
mach/proto/top/top.c Normal file
View File

@@ -0,0 +1,651 @@
#include <stdio.h>
#include "gen.h"
#include "top.h"
#include "queue.h"
/* STANDARD MACHINE-INDEPENT C CODE *************/
extern char *lstrip();
extern instr_p newinstr();
extern instr_p read_instr();
extern instr_p gen_instr();
extern char *eval_templ();
extern instr_p malloc();
struct variable var[NRVARS+1];
struct variable ANY; /* ANY symbol matching any instruction */
char *REST; /* Opcode of first instruction not matched by current pattern */
#include "gen.c"
/* Macros for efficiency: */
#define is_white(c) ( (c) == ' ' || (c) == '\t')
/* Skip white space in the unprocessed part of instruction 'ip' */
#define skip_white(ip) while (is_white(*(ip)->rest_line)) (ip)->rest_line++
main()
{
optimize();
exit(0);
}
/* Optimize the standard input.
* The optimizer uses a moving window. The instructions in the current
* window are matched against a set of patterns generated from the
* machine description table. If the match fails, the first instruction of
* the window is moved to a back-up queue and a new instruction is
* read from the input and appended to the end of the window.
* If a matching pattern is found (and its operands etc. are ok),
* the instructions at the head of the window are replaced by new
* instructions, as indicated in the descriptor table; a new window
* is created, consisting of the back-up instructions and the new
* instructions and the rest of the old window. So effectively the
* window moves a bit to the left after every hit. Hence sequences of
* optimizations like:
* movl r0,x ; cmpl $0,x -> movl r0,x ; tstl x -> movl r0,x
* are captured without having a separate pattern for "movl ; cmpl".
*
* Whenever the backup queue exceeds some threshold, its first instruction
* is written to the output and is removed.
*/
optimize()
{
struct queue_t windowq, backupq;
queue window, backup;
instr_p ip;
bool change;
window = &windowq;
backup = &backupq;
empty_queue(window);
empty_queue(backup);
fill_window(window,MIN_WINDOW_SIZE);
while (!empty(window)) {
if (try_hashentry(hashtab[hash(window)],window) ||
try_hashentry(hashany,window)) {
join_queues(backup,window);
} else {
ip = qhead(window);
remove_head(window);
add(backup,ip);
if (qlength(backup) > MIN_WINDOW_SIZE) {
write_first(backup);
}
}
fill_window(window,MIN_WINDOW_SIZE);
}
while (!empty(backup)) write_first(backup);
}
bool try_hashentry(list,window)
int *list;
queue window;
{
int *pp;
patdescr_p p;
for (pp = list; *pp != -1; pp++) {
p = &patterns[*pp];
if (check_pattern(p,window) &&
check_operands(p,window) &&
check_constraint(p-patterns)) {
xform(p,window);
return TRUE;
}
}
return FALSE;
}
/* TEMP. */
/* int hash(w)
queue w;
{
instr_p ip;
ip = qhead(w);
return ip->opc[0] % 31;
}
*/
int hash(w)
queue w;
{
register char *p;
register sum,i;
instr_p ip;
ip = qhead(w);
/* for (sum=0,p=ip->opc;*p;p++)
sum += *p;
*/
for (sum=i=0,p=ip->opc;*p;i += 3)
sum += (*p++)<<(i&07);
return(sum%127);
}
/* Fill the working window until it contains at least 'len' items.
* When end-of-file is encountered it may contain fewer items.
*/
fill_window(w,len)
queue w;
{
instr_p ip;
while(qlength(w) < len) {
if ((ip = read_instr()) == NIL) break;
ip->rest_line = ip->line;
set_opcode(ip);
add(w,ip);
}
}
write_first(w)
queue w;
{
instr_p ip;
write_instr(ip = qhead(w));
remove_head(w);
oldinstr(ip);
}
/* Try to recognize the opcode part of an instruction */
set_opcode(ip)
instr_p ip;
{
register char *p,*q;
char *qlim;
if (ip->state == JUNK) return;
skip_white(ip);
p = ip->rest_line;
if (*p == LABEL_STARTER) {
strcpy(ip->opc,"labdef");
ip->state = ONLY_OPC;
return;
}
q = ip->opc;
qlim = q + MAX_OPC_LEN;
while(*p != OPC_TERMINATOR && *p != '\n') {
if (q == qlim) {
ip->state = JUNK;
return;
}
*q++ = *p++;
}
*q = '\0';
ip->rest_line = p;
ip->state = (well_shaped(ip->opc) ? ONLY_OPC : JUNK);
}
/* Check if pattern 'p' matches the current input */
bool check_pattern(p,w)
patdescr_p p;
queue w;
{
register idescr_p id_p;
idescr_p idlim;
register instr_p ip;
ip = qhead(w);
ANY.vstate = UNINSTANTIATED;
idlim = &p->pat[p->patlen];
for (id_p = p->pat; id_p < idlim; id_p++) {
if (ip == NIL || ip->state == JUNK) return FALSE;
if (id_p->opcode == (char *) 0) {
unify(ip->opc,&ANY);
} else {
if (strcmp(ip->opc,id_p->opcode) != 0) return FALSE;
}
ip = next(ip);
}
REST = ip->opc;
return TRUE;
}
bool check_operands(p,w)
patdescr_p p;
queue w;
{
instr_p ip;
idescr_p id_p;
int n;
/* fprintf(stderr,"try pattern %d\n",p-patterns); */
clear_vars();
for (id_p = p->pat, ip = qhead(w); id_p < &p->pat[p->patlen];
id_p++, ip = next(ip)) {
assert(ip != NIL);
if (ip->state == JUNK ||
(ip->state == ONLY_OPC && !split_operands(ip))) {
return FALSE;
}
for (n = 0; n < MAXOP; n++) {
if (!opmatch(&id_p->templates[n],ip->op[n])) {
return FALSE;
}
}
}
/* fprintf(stderr,"yes\n"); */
return TRUE;
}
/* Reset all variables to uninstantiated */
clear_vars()
{
register v;
for (v = 1; v <= NRVARS; v++) var[v].vstate = UNINSTANTIATED;
}
/* See if operand 's' matches the operand described by template 't'.
* As a side effect, any uninstantiated variables used in the
* template may become instantiated. For such a variable we also
* check if it satisfies the constraint imposed on it in the
* mode-definitions part of the table.
*/
bool opmatch(t,s)
templ_p t;
char *s;
{
char *l, buf[MAXOPLEN];
bool was_instantiated;
int vno;
vno = t->varno;
if (vno == -1 || s[0] == '\0' ) {
return (vno == -1 && s[0] == '\0');
}
was_instantiated = (var[vno].vstate == INSTANTIATED);
strcpy(buf,s);
if ( (l=lstrip(buf,t->lctxt)) != NULLSTRING && rstrip(l,t->rctxt)) {
return vno == 0 || (unify(l,&var[vno]) &&
(was_instantiated || tok_chk(vno)));
}
return FALSE;
}
/* Try to recognize the operands of an instruction */
bool split_operands(ip)
instr_p ip;
{
int i;
bool res;
if (strcmp(ip->opc,"labdef") ==0) {
labeldef(ip);
} else {
for (i = 0; operand(ip,i) && op_separator(ip); i++);
}
res = remainder_empty(ip);
ip->state = (res ? DONE : JUNK);
return res;
}
labeldef(ip)
instr_p ip;
{
char *p;
int oplen;
p = ip->rest_line;
while( *p != LABEL_TERMINATOR) p++;
oplen = p - ip->rest_line;
if (oplen == 0 || oplen > MAXOPLEN) return;
strncpy(ip->op[0],ip->rest_line,oplen);
ip->op[0][oplen] = '\0';
ip->rest_line = ++p;
return;
}
/* Try to recognize the next operand of instruction 'ip' */
bool operand(ip,n)
register instr_p ip;
{
register char *p;
int oplen;
skip_white(ip);
p = ip->rest_line;
while( *p != OP_SEPARATOR && *p != '\n') p++;
oplen = p - ip->rest_line;
if (oplen == 0 || oplen > MAXOPLEN) return FALSE;
strncpy(ip->op[n],ip->rest_line,oplen);
ip->op[n][oplen] = '\0';
ip->rest_line = p;
return TRUE;
}
/* See if the unprocessed part of instruction 'ip' is empty
* (or contains only white space).
*/
bool remainder_empty(ip)
instr_p ip;
{
skip_white(ip);
return *ip->rest_line == '\n';
}
/* Try to match 'ctxt' at the beginning of string 'str'. If this
* succeeds then return a pointer to the rest (unmatched part) of 'str'.
*/
char *lstrip(str,ctxt)
register char *str, *ctxt;
{
assert(ctxt != NULLSTRING);
while (*str != '\0' && *str == *ctxt) {
str++;
ctxt++;
}
return (*ctxt == '\0' ? str : NULLSTRING);
}
/* Try to match 'ctxt' at the end of 'str'. If this succeeds then
* replace truncate 'str'.
*/
bool rstrip(str,ctxt)
char *str,*ctxt;
{
register char *s, *c;
for (s = str; *s != '\0'; s++);
for (c = ctxt; *c != '\0'; c++);
while (c >= ctxt) {
if (s < str || *s != *c--) return FALSE;
*s-- = '\0';
}
return TRUE;
}
/* Try to unify variable 'v' with string 'str'. If the variable
* was instantiated the unification only succeeds if the variable
* and the string match; else the unification succeeds and the
* variable becomes instantiated to the string.
*/
bool unify(str,v)
char *str;
struct variable *v;
{
if (v->vstate == UNINSTANTIATED) {
v->vstate = INSTANTIATED;
strcpy(v->value,str);
return TRUE;
} else {
return strcmp(v->value,str) == 0;
}
}
/* Transform the working window according to pattern 'p' */
xform(p,w)
patdescr_p p;
queue w;
{
register instr_p ip;
int i;
for (i = 0; i < p->patlen; i++) {
ip = qhead(w);
remove_head(w);
oldinstr(ip);
}
replacement(p,w);
}
/* Generate the replacement for pattern 'p' and insert it
* at the front of the working window.
* Note that we generate instructions in reverser order.
*/
replacement(p,w)
patdescr_p p;
queue w;
{
idescr_p id_p;
for (id_p = &p->repl[p->replen-1]; id_p >= p->repl; id_p--) {
insert(w,gen_instr(id_p));
}
}
/* Generate an instruction described by 'id_p'.
* We build a 'line' for the new instruction and call set_opcode()
* to re-recognize its opcode. Hence generated instructions are treated
* in exactly the same way as normal instructions that are just read in.
*/
instr_p gen_instr(id_p)
idescr_p id_p;
{
char *opc;
instr_p ip;
templ_p t;
register char *s;
bool islabdef;
int n;
static char tmp[] = "x";
ip = newinstr();
s = ip->line;
opc = id_p->opcode;
if (opc == (char *) 0) opc = ANY.value;
if (strcmp(opc,"labdef") == 0) {
islabdef = TRUE;
s[0] = '\0';
} else {
strcpy(s,opc);
tmp[0] = OPC_TERMINATOR;
strcat(s,tmp);
islabdef = FALSE;
}
for (n = 0; n < MAXOP;) {
t = &id_p->templates[n++];
if (t->varno == -1) break;
strcat(s,t->lctxt);
if (t->varno != 0) strcat(s,var[t->varno].value);
strcat(s,t->rctxt);
if (n<MAXOP && id_p->templates[n].varno!=-1) {
tmp[0] = OP_SEPARATOR;
strcat(s,tmp);
}
}
if (islabdef) {
tmp[0] = LABEL_TERMINATOR;
strcat(s,tmp);
}
strcat(s,"\n");
ip->rest_line = ip->line;
set_opcode(ip);
return ip;
}
/* Reading and writing instructions.
* An instruction is assumed to be on one line. The line
* is copied to the 'line' field of an instruction struct,
* terminated by a \n and \0. If the line is too long (>MAXLINELEN),
* it is split into pieces of length MAXLINELEN and the state of
* each such struct is set to JUNK (so it will not be optimized).
*/
static bool junk_state = FALSE; /* TRUE while processing a very long line */
instr_p read_instr()
{
register instr_p ip;
register int c;
register char *p;
char *plim;
ip = newinstr();
plim = &ip->line[MAXLINELEN];
if (( c = getchar()) == EOF) return NIL;
for (p = ip->line; p < plim;) {
*p++ = c;
if (c == '\n') {
*p = '\0';
if (junk_state) ip->state = JUNK;
junk_state = FALSE;
return ip;
}
c = getchar();
}
ungetc(c,stdin);
*p = '\0';
junk_state = ip->state = JUNK;
return ip;
}
write_instr(ip)
instr_p ip;
{
register char *p;
for (p = ip->line; *p != '\0'; p++) {
putchar(*p);
}
}
/* Core allocation.
* As all instruction struct have the same size we can re-use every struct.
* We maintain a pool of free instruction structs.
*/
static instr_p instr_pool;
int nr_mallocs = 0; /* for statistics */
instr_p newinstr()
{
register instr_p ip;
int i;
if (instr_pool == NIL) {
instr_pool = malloc(sizeof(struct instruction));
nr_mallocs++;
}
assert(instr_pool != NIL);
ip = instr_pool;
instr_pool = instr_pool->fw;
ip->fw = ip->bw = NIL;
ip->rest_line = NULLSTRING;
ip->line[0] = ip->opc[0] = '\0';
ip->state = ONLY_OPC;
for (i = 0; i < MAXOP; i++) ip->op[i][0] = '\0';
return ip;
}
oldinstr(ip)
instr_p ip;
{
ip->fw = instr_pool;
instr_pool = ip;
}
/* Debugging stuff */
badassertion(file,line)
char *file;
unsigned line;
{
fprintf(stderr,"assertion failed file %s, line %u\n",file,line);
error("assertion");
}
/* VARARGS1 */
error(s,a)
char *s,*a;
{
fprintf(stderr,s,a);
fprintf(stderr,"\n");
_cleanup();
abort();
exit(-1);
}
/* Low level routines */
bool op_separator(ip)
instr_p ip;
{
skip_white(ip);
if (*(ip->rest_line) == OP_SEPARATOR) {
ip->rest_line++;
return TRUE;
} else {
return FALSE;
}
}
bool well_shaped(opc)
char *opc;
{
return is_letter(opc[0]);
}
bool is_letter(c)
{
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
/* is_white(c) : turned into a macro, see beginning of file */

88
mach/proto/top/top.h Normal file
View File

@@ -0,0 +1,88 @@
/* Tunable constants; may be overruled by machine descriptor table */
#ifndef OP_SEPARATOR
#define OP_SEPARATOR ','
#endif
#ifndef LABEL_TERMINATOR
#define LABEL_TERMINATOR ':'
#endif
#ifndef LABEL_STARTER
#define LABEL_STARTER 'I'
#endif
#ifndef OPC_TERMINATOR
#define OPC_TERMINATOR ' '
#endif
#ifndef MAX_OPC_LEN
#define MAX_OPC_LEN 10
#endif
#ifndef MAXOPLEN
#define MAXOPLEN 25
#endif
#ifndef MAXOP
#define MAXOP 2
#endif
#ifndef MAXLINELEN
#define MAXLINELEN 100
#endif
#ifndef MAXVARLEN
#define MAXVARLEN 25
#endif
typedef struct instruction *instr_p;
typedef struct pattern_descr *patdescr_p;
typedef struct instr_descr *idescr_p;
typedef struct template *templ_p;
struct instruction {
instr_p fw;
instr_p bw;
char line[MAXLINELEN+1];
char *rest_line;
char opc[MAX_OPC_LEN+1];
char op[MAXOP][MAXOPLEN+1];
short state;
};
/* state: */
#define ONLY_OPC 0
#define JUNK 1
#define DONE 2
struct variable {
int vstate;
char value[MAXVARLEN];
};
/* vstate: */
#define UNINSTANTIATED 0
#define INSTANTIATED 1
struct pattern_descr {
int patlen;
idescr_p pat;
int replen;
idescr_p repl;
};
struct template {
char *lctxt;
int varno;
char *rctxt;
};
struct instr_descr {
char *opcode;
struct template templates[MAXOP];
};
typedef int bool;
#define TRUE 1
#define FALSE 0
#define NIL (instr_p) 0
#define NULLSTRING (char *) 0
#define assert(x) if(!(x)) badassertion(__FILE__,__LINE__)