/* 1615, Wed 21 Feb 01 (PST) SRL_EMIT.C: PME code emitter for SRL compier Copyright (C) 1998-2002 by Nevil Brownlee, CAIDA | University of Auckland */ /* * $Log: srl_emit.c,v $ * Revision 1.1.1.2.2.10 2002/02/23 01:57:42 nevil * Moving srl examples to examples/ directory. Modified examples/Makefile.in * * Revision 1.1.1.2.2.6 2001/02/22 00:28:42 nevil * Emit an o0 line after && nodes in expressions; this stops the optimiser * from tryng to treat them as part of a following || group. * Update copyright notice. * * Revision 1.1.1.2.2.5 2000/08/08 19:45:00 nevil * 44b8 release * * Revision 1.1.1.2.2.3 2000/06/06 03:38:33 nevil * Combine NEW_ATR with TCP_ATR, various bug fixes * * Revision 1.1.1.2 1999/10/03 21:06:35 nevil * *** empty log message *** * * Revision 1.1.1.1.2.4 1999/09/22 05:34:11 nevil * Implement command-line defines * - Initialise scanner in init_symbol_table() * - Add get_command_define() to scanner. This dummies up a define * statement then calls push_include to invoke it * - Call get_command_define from main when we see a -D option * * Revision 1.1.1.1.2.3 1999/01/27 04:26:17 nevil * Minor corrections to fix compiler warnings * * Revision 1.1.1.1.2.2 1999/01/08 01:38:42 nevil * Distribution file for 4.3b7 * * Revision 1.1.1.1.2.1 1998/12/16 02:59:10 nevil * Make compiler distinguish between 'save attrib' and 'save attrib = 0' * These both used to produce a rule which saved the whole attrib value! * * Revision 1.1.1.1 1998/11/16 03:57:33 nevil * Import of NeTraMet 4.3b3 * * Revision 1.1.1.1 1998/11/16 03:22:03 nevil * Import of release 4.3b3 * * Revision 1.1.1.1.2.2 1998/11/12 23:43:55 nevil * Changed emit_imperative to force PushPkt action when a save statement * uses a distribution-valued attribute. * * Revision 1.1.1.1.2.1 1998/11/11 23:14:48 nevil * Only include malloc.h if we HAVE_MALLOC_H * * Revision 1.1.1.1 1998/10/28 20:31:33 nevil * Import of NeTraMet 4.3b1 * * Revision 1.1.2.1 1998/10/22 21:40:39 nevil * Moved srl from src/manager to its own subdirectory * * Revision 1.1.3.2.2.1 1998/10/20 03:04:24 nevil * Nicolai: Fixed bug with fclose(command) being called even when * command is NULL -- which caused srl to crash on certain compiles * * Revision 1.1.3.2 1998/10/18 23:44:14 nevil * Added Nicolai's patches, some 'tidying up' of the source * * Revision 1.1.3.1 1998/10/13 02:48:28 nevil * Import of Nicolai's 4.2.2 * * Revision 1.1.1.1 1998/08/24 12:09:29 nguba * NetraMet 4.2 Original Distribution */ #if HAVE_CONFIG_H #include #endif #define PRINT_INTERMEDIATE 0 #define OPT_EXPR_TRACE 0 #define OPT_LABEL_TRACE 0 #define RET_ACTION_TRACE 0 #define OR_OPTIMISE_TRACE 0 #include #include #include #include #if HAVE_MALLOC_H # include #endif #include #include #include #include #include "rtfm_atr.h" #include "srl.h" int lbl_lookup(char *id) /* Return 0 if id was not in id[] */ { /* Otherwise returns 1, lbl_index */ int id_len, j; unsigned int hash; struct lbl_info *lip, *ip; hash = id_len = strlen(id); for (j = 0; j != id_len; ++j) hash = (hash << 1) + id[j]; lbl_index = hash & LBLTHASHMASK; ip = &lbl_table[lbl_index]; lip = NULL; if (ip->id[0] != 0) { /* At least one entry for this hash */ if (strcmp(ip->id,id) == 0) return 1; else { for (;;) { lip = ip; if (lip->next == 0) break; /* End of chain */ ip = &lbl_table[lbl_index = lip->next]; if (strcmp(ip->id,id) == 0) return 1; } } } /* Not there, put it in */ if (lip != NULL) { /* Add to hash chain */ if (lbl_top == LBLTSIZE) { err_msg(ET_ERR, "More than %d labels",LBLTSIZE); } else { /* Add new identifier */ lbl_index = lbl_top++; lip->next = lbl_index; ip = &lbl_table[lbl_index]; } } memset(ip, 0, sizeof(struct lbl_info)); strncpy(ip->id, id, id_len); ip->id[id_len] = '\0'; return 0; /* Not already in lbl_table, now added */ } void dump_label_table(void) { int j; struct lbl_info *lp; if (1) { printf("Label table ---------------------\n"); for (j = 0; j != lbl_top; ++j) { lp = &lbl_table[j]; if (lp->id[0] != 0 && lp->type == TOK_LABEL) { printf("%3d, %5d: %s prev=%d, ngo=%d, in_expr=%d, go_ix=%d\n", j, lp->next, lp->id, lp->prev, lp->ngotos, lp->in_expr, lp->goto_ix); } } } } void add_label(char *id, int type, int subtype) { struct lbl_info *lp; lbl_lookup(id); lp = &lbl_table[lbl_index]; lp->type = type; lp->subtype = subtype; } void init_label_table() { int j; lbl_top = LBLTHASHBASE; for (j = 0; j != LBLTHASHBASE; ++j) lbl_table[j].id[0] = '\0'; add_label("succeed", TOK_PMEACT, RA_SUCCEED); /* 0 PME actions */ add_label("ignore", TOK_PMEACT, RA_IGNORE); /* 1 */ add_label("nomatch", TOK_PMEACT, RA_NOMATCH); /* 2 */ add_label("count", TOK_PMEACT, RA_COUNT); /* 3 */ add_label("countpkt", TOK_PMEACT, RA_COUNTPKT); /* 4 */ add_label("return", TOK_PMEACT, RA_RETURN); /* 5 */ add_label("gosub", TOK_PMEACT, RA_GOSUB); /* 6 */ add_label("gosubact", TOK_PMEACT, RA_GOSUBACT); /* 7 */ add_label("assign", TOK_PMEACT, RA_ASSIGN); /* 8 */ add_label("assignact", TOK_PMEACT, RA_ASSIGNACT); /* 9 */ add_label("goto", TOK_PMEACT, RA_GOTO); /* 10 */ add_label("gotoact", TOK_PMEACT, RA_GOTOACT); /* 11 */ add_label("pushto", TOK_PMEACT, RA_PUSHTO); /* 12 */ add_label("pushtoact", TOK_PMEACT, RA_PUSHTOACT); /* 13 */ add_label("pushpktto", TOK_PMEACT, RA_PUSHPKTTO); /* 14 */ add_label("pushpkttoact", TOK_PMEACT, RA_PUSHPKTTOACT); /* 15 */ add_label("popto", TOK_PMEACT, RA_POPTO); /* 16 */ add_label("poptoact", TOK_PMEACT, RA_POPTOACT); /* 17 */ add_label("next", TOK_LABEL, RA_NEXT); /* Special targets */ lbl_table[lbl_index].declared = 1; add_label("0", TOK_LABEL, 0); lbl_table[lbl_index].declared = 1; } static char *actions[] = { "", /* 0 = RA_SUCCEED (never used) */ "ignore", /* 1 = RA_IGNORE */ "nomatch", /* 2 = RA_NOMATCH */ "count", /* 3 = RA_COUNT */ "countpkt", /* 4 = RA_COUNTPKT */ "return", /* 5 = RA_RETURN */ "gosub", /* 6 = RA_GOSUB */ "gosubact", /* 7 = RA_GOSUBACT */ "assign", /* 8 = RA_ASSIGN */ "assignact", /* 9 = RA_ASSIGNACT */ "goto", /* 10 = RA_GOTO */ "gotoact", /* 11 = RA_GOTOACT */ "pushto", /* 12 = RA_PUSHTO */ "pushtoact", /* 13 = RA_PUSHTOACT */ "pushpktto", /* 14 = RA_PUSHPKTTO */ "pushpkttoact", /* 15 = RA_PUSHPKTTOACT */ "popto", /* 16 = RA_POPTO */ "poptoact", /* 17 = RA_POPTOACT */ }; void emit_rule(FILE *f, char *body, int which, char *target); #define NULL_TEST "0 & 0 = 0" #define ASM_NULL_TEST "null & 0 = 0" static FILE *code; static FILE *inter[MXIFGRP+2]; /* 1 for outer block, 1 for end marker */ static FILE *command; /* For NeMaC commands */ static if_grp_ix; static char fn_prefix[FNAME_LN+1]; static int /* Label numbers for .. */ s_ln, /* Success within an expression */ f_ln; /* Failure within an expression */ int intermediate_open(char *prefix) { /* Open intermediate file(s), initialise emitters */ char fn[FNAME_LN+1]; int j; for (j = 0; j != MXIFGRP+2; ++j) inter[j] = NULL; strcpy(fn_prefix, prefix); s_ln = f_ln = 0; sprintf(fn, "%s.sri.0", prefix); if ((inter[if_grp_ix = 0] = fopen(fn, "w")) == NULL) return 0; command = NULL; init_label_table(); return 1; } void intermediate_close(void) { int j; emit_rule(inter[0], /* Prevent fall-through into rest of rules */ NULL_TEST, RA_NOMATCH, "0"); for (j = 0; inter[j] != NULL; ++j) fclose(inter[j]); if (command != NULL) fclose(command); if (verbose) dump_label_table(); } void emit_comment(char *fmt, ...) { char output[INPUT_LN+20], *op; va_list ap; va_start(ap, fmt); vsprintf(output, fmt, ap); for (op = output; *op != '\0'; ++op) ; if (op[-1] != '\n') strcpy(op, "\n"); fprintf(inter[if_grp_ix], "c%s", output); } void emit_opt_level(int level) { if (level >= 0) fprintf(inter[if_grp_ix], "o%d\n", level); else { /* Mark break between expressions to be optimised */ fprintf(inter[if_grp_ix], "o*\n"); } } void emit_NeMaC_command(char *fmt, ...) { char output[INPUT_LN+20]; va_list ap; if (command == NULL) { sprintf(output, "%s.cmd", fn_prefix); if ((command = fopen(output, "w")) == NULL) return; } va_start(ap, fmt); vsprintf(output, fmt, ap); fprintf(command, "c%s", output); } static int prev_label = 0; /* Last emit_() call was emit_label() */ static struct lbl_info *lp; /* Last label declared */ static int in_expression = 0; static char rule_kind = 'i'; /* 'e' in expression, 'i' for imperative, 'r' for return rules */ static int first_rule = 1; void actual_emit_label(FILE *f, char *lbl) { int r; r = lbl_lookup(lbl); lp = &lbl_table[lbl_index]; if (r && /* Found in LT */ lp->type == TOK_LABEL && lp->declared != 0) err_msg(ET_ERR, "Label %s multiply declared", lbl); else /* New label */ lp->type = TOK_LABEL; if (prev_label) { /* Build chain of labels to same statement */ lp->prev = prev_label; } ++lp->declared; fprintf(f, "%c%s:\n", rule_kind, lbl); prev_label = lbl_index; #if OPT_LABEL_TRACE printf("+++ actual_emit(): lbl=%s, if_grp_ix=%d\n", lbl, if_grp_ix); #endif } void emit_label(char *lbl) { actual_emit_label(inter[if_grp_ix], lbl); } void emit_goto(char *target) { int r; struct lbl_info *tp; r = lbl_lookup(target); tp = &lbl_table[lbl_index]; if (!r) /* Wasn't in LT */ tp->type = TOK_LABEL; ++tp->ngotos; if (!asmint) fprintf(inter[if_grp_ix], "%c %s: %d, %s;\n", 'i', NULL_TEST, RA_GOTO, target); else fprintf(inter[if_grp_ix], "%c %s: %s, %s;\n", 'i', ASM_NULL_TEST, actions[RA_GOTO], target); } int param_attrib(struct symbol *sp) { return FTV1-1 + sp->d.arg.reg; } void emit_rule(FILE *f, char *body, int which, char *target) { int r, null_body, goto_ix; struct lbl_info *tp; if (first_rule) { if (rule_kind == 'i') { /* First rule is an imperative */ if (!asmint) fprintf(f, "i %s: %d, next;\n", NULL_TEST, RA_GOTOACT); else fprintf(f, "i %s: %s, next;\n", ASM_NULL_TEST, actions[RA_GOTOACT]); } first_rule = 0; } r = lbl_lookup(target); tp = &lbl_table[lbl_index]; if (!r) /* Wasn't in LT */ tp->type = TOK_LABEL; ++tp->ngotos; null_body = strcmp(body,NULL_TEST) == 0; if (null_body && asmint) body = ASM_NULL_TEST; if (!asmint) fprintf(f, "%c %s: %d, %s;\n", rule_kind, body, which, target); else fprintf(f, "%c %s: %s, %s;\n", rule_kind, body, actions[which], target); if (which == RA_GOTO && null_body && tp->subtype != RA_NEXT) goto_ix = lbl_index; else goto_ix = 0; if (prev_label) { /* Undo label chain */ for (;;) { lp->in_expr = in_expression; lp->goto_ix = goto_ix; #if OPT_LABEL_TRACE printf("+++ emit_rule(): if_grp_ix=%d, lp->id=%s, lp->goto_ix=%d\n", if_grp_ix, lp->id, lp->goto_ix); #endif if (lp->prev == 0) break; lp = &lbl_table[lp->prev]; } } prev_label = 0; } unsigned char *sprintoperand(unsigned char *s, struct pt_node *n) { unsigned char b[50], c[50], *cp; if (!asmint) sprintf(b, "%d & ", n->d.operand.attrib); else sprintf(b, "%s & ", attribs[attr_ix[n->d.operand.attrib]].name); s = strmov(s, b); s = sprintvalue(s, n->d.operand.mask); s = strmov(s, (unsigned char *)" = "); s = sprintvalue(s, n->d.operand.value); *s = '\0'; return s; } static struct pt_node **na; static int na_ix; static int attr_order[LASTATTRIB+1]; static int a_nbr; static int or_grp_cmp(const void *ap, const void *bp) { struct pt_node *a, *b; int r; a = *(struct pt_node **)ap; b = *(struct pt_node **)bp; if (attr_order[a->d.operand.attrib] == attr_order[b->d.operand.attrib]) { if (a->d.operand.mask_nbr == b->d.operand.mask_nbr) { if (a->d.operand.tag_bits == b->d.operand.tag_bits) { r = memcmp(a->d.operand.mask, b->d.operand.mask, sizeof(a->d.operand.mask)); if (r == 0) { return memcmp(a->d.operand.value, b->d.operand.value, sizeof(a->d.operand.value)); } else return r; /* Increasing mask width */ } else if (a->d.operand.tag_bits < b->d.operand.tag_bits) return 1; /* Decreasing tag bits */ else return -1; } else if (a->d.operand.mask_nbr < b->d.operand.mask_nbr) return 1; /* Decreasing mask number */ else return -1; } else if (attr_order[a->d.operand.attrib] > attr_order[b->d.operand.attrib]) return 1; /* Increasing attribute order */ else return -1; } void find_or_nodes(struct pt_node *n) { if (n->type == NT_OPERAND) { na[na_ix++] = n; if (attr_order[n->d.operand.attrib] == 0) attr_order[n->d.operand.attrib] = ++a_nbr; n->d.operand.mask_nbr = n->d.operand.tag_bits = 0; } else if (n->type == NT_BINOP) { find_or_nodes(n->left); find_or_nodes(n->right); } } int is_subset(struct pt_node *a, struct pt_node *b) { /* Returns 1 if *b is containd within *a */ int k, r; if (a->d.operand.attrib != b->d.operand.attrib) return 0; /* Different attributes */ for (k = 0; k != sizeof(a->d.operand.attrib); ++k) { if ((b->d.operand.value[k] & a->d.operand.mask[k]) != (a->d.operand.value[k] & a->d.operand.mask[k])) return 0; /* Different values within *a width */ } r = memcmp(b->d.operand.mask, a->d.operand.mask, sizeof(a->d.operand.mask)); return r > 0; /* *b has greater width than *a */ } void emit_node(struct pt_node *n, int dmy_pushes, char *succeed); void optimise_or_group(int *g_sz) { struct pt_node *a, *b; unsigned char avm[50], bvm[50]; int grpsz, m, duplicates, j, r, again, k, cycle; unsigned int tag_bit, last_tag_bit; grpsz = *g_sz; if (grpsz == 1) check_mask(na[0]); else { qsort(na, grpsz,sizeof(struct pt_node *), or_grp_cmp); tag_bit = #if OR_OPTIMISE_TRACE 0x20; #else (unsigned int)(1 << (sizeof(unsigned int)*8 - 1)); #endif b = na[grpsz-1]; b->d.operand.mask_nbr = m = 1; b->d.operand.tag_bits = tag_bit; /* Init: set tag for all operands */ duplicates = 0; for (j = grpsz-2; j >= 0; --j) { a = na[j]; r = memcmp(a->d.operand.mask, b->d.operand.mask, sizeof(a->d.operand.mask)); if (r == 0) { if (memcmp(a->d.operand.value, na[j+1]->d.operand.value, sizeof(a->d.operand.value)) == 0) { sprintvalmsk(avm, a); err_msg(ET_WARN, "%s is duplicated in OR group", avm); a->d.operand.mask_nbr = 0; /* Sort to top */ ++duplicates; } else a->d.operand.mask_nbr = m; } else { b = a; ++m; a->d.operand.mask_nbr = m; } a->d.operand.tag_bits = tag_bit; } if (duplicates != 0) { qsort(na, grpsz,sizeof(struct pt_node *), or_grp_cmp); grpsz -= duplicates; } } if (grpsz > 1) { for (cycle = 1; ; ++cycle) { last_tag_bit = tag_bit; tag_bit >>= 1; #if OR_OPTIMISE_TRACE printf("=== Optimise OR: tag_bit=%X\n", tag_bit); #endif again = 0; for (j = 0; j != grpsz-1; ++j) { a = na[j]; for (k = j+1; k != grpsz; ++k) { b = na[k]; if ((b->d.operand.tag_bits & last_tag_bit) == 0) break; if (is_subset(a, b)) { sprintvalmsk(avm, a); sprintvalmsk(bvm, b); #if OR_OPTIMISE_TRACE printf("--- %s (%lX.%lX) contains %s (%lX,%lX)\n", avm, a->d.operand.mask_nbr, a->d.operand.tag_bits, bvm, b->d.operand.mask_nbr, b->d.operand.tag_bits); #else if (cycle == 1) err_msg(ET_WARN, "%s contains %s", avm,bvm); #endif b->d.operand.mask_nbr = a->d.operand.mask_nbr; b->d.operand.tag_bits |= tag_bit; again = 1; } } } if (again == 0) break; if (tag_bit == 1) { err_msg(ET_ERR, "Attempt to optimise OR group failed"); break; } qsort(na, grpsz,sizeof(struct pt_node *), or_grp_cmp); } } *g_sz = grpsz; } void emit_or_group(struct pt_node *n, char *succeed) { int grpsz, j; grpsz = n->grpsz; na = (struct pt_node **)malloc(sizeof(struct pt_node *)*grpsz); for (j = 0; j <= LASTATTRIB; ++j) attr_order[j] = 0; a_nbr = na_ix = 0; find_or_nodes(n); if (optimise_level >= 2) optimise_or_group(&grpsz); for (j = 0; j != grpsz; ++j) emit_node(na[j], 0, succeed); free(na); } static int if_save; static char if_target[IDENT_LN+1]; static int if_act; void emit_node(struct pt_node *n, int dmy_pushes, char *succeed) { char sbuf[IDENT_LN+1], fbuf[IDENT_LN+1]; unsigned char cline[100], *cp, b[50]; int act, final, j, mxn, mxl, mxr; if (n == NULL) return; if (n->type == NT_OPERAND) { cp = sprintoperand(cline, n); if (strcmp(succeed, if_target) == 0) act = if_act; else act = if_save ? RA_PUSHTO : RA_GOTO; emit_rule(inter[if_grp_ix], (char *)cline, act, succeed); } else if (n->type == NT_BINOP) { if (n->left == NULL || n->right == NULL) return; /* Empty operands left by syntax errors */ mxl = n->left->maxdepth; mxr = n->right->maxdepth; mxn = n->maxdepth; if (n->d.binop.operator == SC_LOR || n->d.binop.operator == SC_SAMEOR) { if (n->grpsz != 0) emit_or_group(n, succeed); else { emit_node(n->left, mxn-mxl, succeed); emit_node(n->right, mxn-mxr, succeed); } } else if (n->d.binop.operator == SC_LAND) { sprintf(fbuf, "f%d", ++f_ln); sprintf(sbuf, "s%d", ++s_ln); if (if_save) { for (j = dmy_pushes; j != 0; --j) emit_rule(inter[if_grp_ix], NULL_TEST, RA_PUSHTO, "next"); } emit_node(n->left, 0, sbuf); emit_rule(inter[if_grp_ix], NULL_TEST, RA_GOTO, fbuf); emit_label(sbuf); emit_node(n->right, 0, succeed); if (if_save) { for (j = mxl; j != 0; --j) emit_rule(inter[if_grp_ix], NULL_TEST, RA_POPTO, "next"); } emit_label(fbuf); if (if_save) { for (j = dmy_pushes; j != 0; --j) emit_rule(inter[if_grp_ix], NULL_TEST, RA_POPTO, "next"); } emit_opt_level(0); /* Mark break between expressions to be optimised */ } } } void emit_IF_level(int incr) { char fn[FNAME_LN+1]; if (prev_label) { /* Undo label chain */ for (;;) { lp->in_expr = in_expression; lp->goto_ix = 0; #if OPT_LABEL_TRACE printf("+++ emit_rule(IF_level): if_grp_ix=%d, lp->id=%s\n", if_grp_ix, lp->id); #endif if (lp->prev == 0) break; lp = &lbl_table[lp->prev]; } } prev_label = 0; if_grp_ix += incr; if (if_grp_ix < 0 || if_grp_ix > MXIFGRP) { err_msg(ET_ERR, "Compiler error, IF level under/overflow"); exit(66); } if (inter[if_grp_ix] == NULL) { sprintf(fn, "%s.sri.%d", fn_prefix, if_grp_ix); if ((inter[if_grp_ix] = fopen(fn, "w")) == NULL) err_msg(ET_ERR, "Couldn't open intermediate file %s", fn); } if (asmint > 1) fprintf(inter[if_grp_ix], "c# -------- if_grp_ix = %d ---------\n", if_grp_ix); } void emit_expression(struct pt_node *op, int which, char *succeed, char *next_if) { if (op == NULL) return; switch(which) { case RW_EXIT: if_save = 0; if_act = RA_GOTO; strcpy(if_target, succeed); break; case RW_SAVE: if_save = 1; if_act = RA_PUSHTO; strcpy(if_target, succeed == NULL ? "?s?s?" : succeed); break; case RW_IGNORE: if_save = 0; if_act = RA_IGNORE; strcpy(if_target, "0"); break; case RW_NOMATCH: if_save = 0; if_act = RA_NOMATCH; strcpy(if_target, "0"); break; case RW_RETURN: if_save = 1; if_act = RA_RETURN; strcpy(if_target, succeed == NULL ? "?r?r?" : succeed); break; } in_expression = 1; rule_kind = 'e'; emit_node(op, 0, if_target); emit_rule(inter[if_grp_ix], NULL_TEST, RA_GOTO, next_if); in_expression = 0; rule_kind = 'i'; } void emit_imperative(struct pt_node *op, int which, char *target) { char body[100]; static char zero_v[RULE_ADDR_LEN] = {0,0,0,0,0,0}; switch(which) { case RW_EXIT: emit_rule(inter[if_grp_ix], NULL_TEST, RA_GOTO, target); break; case RW_COUNT: emit_rule(inter[if_grp_ix], NULL_TEST, RA_COUNT, "0"); break; case RW_IGNORE: emit_rule(inter[if_grp_ix], NULL_TEST, RA_IGNORE, "0"); break; case RW_NOMATCH: emit_rule(inter[if_grp_ix], NULL_TEST, RA_NOMATCH, "0"); break; case RW_SAVE: case RW_SAVE_V: sprintoperand((unsigned char *)body, op); emit_rule(inter[if_grp_ix], body, /* memcmp(op->d.operand.value, zero_v, RULE_ADDR_LEN) == 0 */ which == RW_SAVE || DISTRIB_ATTRIB(op->d.operand.attrib) ? RA_PUSHPKTTO : RA_PUSHTO, target == NULL ? "next" : target); break; case RW_STORE: sprintoperand((unsigned char *)body, op); emit_rule(inter[if_grp_ix], body, RA_PUSHTO, target == NULL ? "next" : target); break; case RW_RETURN: emit_rule(inter[if_grp_ix], NULL_TEST, RA_RETURN, target == NULL ? "$$$" : target); break; } } void emit_return_code(struct pt_node *op, int which, char *target) { rule_kind = 'r'; emit_imperative(op, which, target); rule_kind = 'i'; } void emit_subr_call(int call_stx) { struct symbol *call_sp, *sp; int j; char b[50]; struct lbl_info *tp; call_sp = &st[call_stx]; for (j = 0; j != call_sp->d.sub.n_params; ++j) { sp = &st[call_stx+2 + j]; if (!asmint) sprintf(b, "%d & 0 = &d", FTV1-1 + sp->d.arg.reg, sp->d.arg.attrib); else sprintf(b, "%s & 0 = %s", attribs[attr_ix[FTV1-1 + sp->d.arg.reg]].name, attribs[attr_ix[sp->d.arg.attrib]].name); emit_rule(inter[if_grp_ix], b, RA_ASSIGNACT, "next"); } j = lbl_lookup(call_sp->name); /* Make sure subroutine is in label table */ tp = &lbl_table[lbl_index]; if (!j) { /* Wasn't in LT */ tp->type = TOK_LABEL; /* Its really a subroutine name! */ } tp->stx = call_stx; emit_rule(inter[if_grp_ix], NULL_TEST, RA_GOSUB, call_sp->name); } int next_inter_line(char *buf, char **bp) { char fn[FNAME_LN+1]; if (inter[if_grp_ix] == NULL) return 0; for (;;) { if (fgets(buf, INPUT_LN, inter[if_grp_ix]) == NULL) { /* EOF */ fclose(inter[if_grp_ix]); if (inter[++if_grp_ix] == NULL) return 0; /* No more intermediate files */ else { sprintf(fn, "%s.sri.%d", fn_prefix, if_grp_ix); if ((inter[if_grp_ix] = fopen(fn, "r")) == NULL) { err_msg(ET_ERR, "Couldn't open intermediate file %s", fn); return 0; } continue; } } if (buf[0] == 'c') { fprintf(code, "%s", &buf[1]); continue; } #if PRINT_INTERMEDIATE else printf("icode: %s", buf); #endif break; } *bp = &buf[1]; return 1; } void delete_intermediate_files(void) { char fn[FNAME_LN+1]; for (if_grp_ix = 0; inter[if_grp_ix] != NULL; ++if_grp_ix) { sprintf(fn, "%s.sri.%d", fn_prefix, if_grp_ix); if (unlink(fn) != 0) { err_msg(ET_ERR, "Couldn't delete intermediate file %s", fn); return; } } if (command != NULL) { sprintf(fn, "%s.cmd", fn_prefix); if (unlink(fn) != 0) err_msg(ET_ERR, "Couldn't delete NeMaC command file %s", fn); } } static int lookahead; static char labuf[INPUT_LN], *lbp; static char la_copy[INPUT_LN], *lcp, *la_bodyp, *la_actionp, *la_targetp; static int la_null_body, la_act; int p2_lookahead(void) { struct lbl_info *la_tp; unsigned long n; la_act = 0; if (!next_inter_line(labuf, &lbp)) return 0; else { strcpy(la_copy, labuf); lcp = &la_copy[1]; if (labuf[1] != ' ') { /* It's a label */ for (; *lcp != ':'; ++lcp) ; *lcp++ = '\0'; } else { /* Not a label */ for (; *lcp == ' '; ++lcp) ; /* Break next line into parts */ la_bodyp = lcp; for (; *lcp != ':'; ++lcp) ; *lcp++ = '\0'; for (; *lcp == ' '; ++lcp) ; la_actionp = lcp; for (; *lcp != ','; ++lcp) ; *lcp++ = '\0'; for (; *lcp == ' '; ++lcp) ; la_targetp = lcp; for (; *lcp != ';'; ++lcp) ; *lcp++ = '\0'; if (asmint) { la_null_body = strcmp(la_bodyp,ASM_NULL_TEST) == 0; lbl_lookup(la_actionp); la_tp = &lbl_table[lbl_index]; la_act = la_tp->subtype; } else { la_null_body = strcmp(la_bodyp,NULL_TEST) == 0; gnbr(&n, la_actionp); la_act = n; } if (lbl_lookup(la_targetp)) { la_tp = &lbl_table[lbl_index]; if (la_tp->subtype != RA_NEXT) { switch (la_act) { case RA_GOTO: case RA_PUSHTO: if (!la_tp->in_expr) ++la_act; break; case RA_GOTOACT: case RA_PUSHTOACT: if (la_tp->in_expr) --la_act; break; } } } } } return lookahead = 1; } int p2_nextline(void) { if (lookahead) { strcpy(inbuf, labuf); ibp = &inbuf[1]; lookahead = 0; return 1; } else return next_inter_line(inbuf, &ibp); } char *getdecimal(int *v, char *s) { int n = 0; while (isdigit(*s)) { n = n*10 + *s-'0'; ++s; } *v = n; return s; } void read_node(struct pt_node *a, char *bodyp) { char *bp, attrib[IDENT_LN+1], *ap; int v, k; for (bp = bodyp; *bp == ' '; ++bp) ; if (!isdigit(*bp)) { for (ap = attrib; *bp != ' '; ) *ap++ = *bp++; *ap = '\0'; lookup(attrib); a->d.operand.attrib = id_table[id_index].subtype; } else { bp = getdecimal(&v, bp); a->d.operand.attrib = v; } bp += 3; /* Skip over blank & blank */ for (k = 0; ; ++k) { bp = getdecimal(&v, bp); a->d.operand.mask[k] = v; if (*bp != '.') break; ++bp; } bp += 3; /* Skip over blank = blank */ for (k = 0; ; ++k) { bp = getdecimal(&v, bp); a->d.operand.value[k] = v; if (*bp != '.') break; ++bp; } } static int n_e_lines, enl_head, enl_tail; void process_en_list(struct pt_node *en_list) { int grpsz, j; struct pt_node *en; char body[200]; #if OPT_EXPR_TRACE printf("~~~ process_en_list(): n_e_lines=%d, enl_head=%d\n", n_e_lines, enl_head); #endif grpsz = n_e_lines; na = (struct pt_node **)malloc(sizeof(struct pt_node *)*grpsz); for (j = 0; j <= LASTATTRIB; ++j) attr_order[j] = 0; a_nbr = na_ix = 0; en = en_list; for (na_ix = 0; na_ix != grpsz; ++na_ix) { na[na_ix] = en; if (attr_order[en->d.operand.attrib] == 0) attr_order[en->d.operand.attrib] = ++a_nbr; en = en->left; } optimise_or_group(&grpsz); for (j = 0; j != grpsz; ++j) { en = na[j]; sprintoperand((unsigned char *)body, en); if (!asmint) fprintf(code, " %s: %d, %s;\n", body, en->d.operand.action, en->d.operand.target); else fprintf(code, " %s: %s, %s;\n", body, actions[en->d.operand.action], en->d.operand.target); free_node(en); } free(na); while (enl_head != 0) { fprintf(code, "%s:\n", lbl_table[enl_head].id); enl_head = lbl_table[enl_head].prev; } n_e_lines = enl_head = enl_tail = 0; } void emit_pass2(char *codefn) { char fn[FNAME_LN+1], n_buf[IDENT_LN+1]; char *bodyp, *actionp, *targetp; struct lbl_info *plp, *tp; struct symbol *sp; int null_body, act, target_in_expr, target_is_next, n_ret_actions, goto_ix, discard, opt_lev; unsigned long n; struct pt_node *en_head, *en_tail, *en; sprintf(fn, "%s.sri.0", fn_prefix); if ((inter[if_grp_ix = 0] = fopen(fn, "r")) == NULL) { err_msg(ET_ERR, "Couldn't open intermediate file %s", fn); return; } if (*codefn == '\0') sprintf(fn, "%s.rules", fn_prefix); else strcpy(fn, codefn); if ((code = fopen(fn, "w")) == NULL) { err_msg(ET_ERR, "Couldn't open output file %s", fn); return; } lookahead = n_ret_actions = n_e_lines = enl_head = enl_tail = 0; en_head = en_tail = NULL; for (;;) { if (!p2_nextline()) break; /* EOF */ if (inbuf[0] == 'o') { /* Set optimise level */ opt_lev = optimise_level; if (inbuf[1] == '*') { if (n_e_lines != 0) process_en_list(en_head); } else { getdecimal(&optimise_level, ibp); if (optimise_level != opt_lev && n_e_lines != 0) process_en_list(en_head); } continue; } if (*ibp != ' ') { /* Label */ for (; *ibp != ':'; ++ibp) ; *ibp = '\0'; if (!lbl_lookup(&inbuf[1])) /* Wasn't in LT */ err_msg(ET_ERR, "Label %s missing from pass 2 input ???", &inbuf[1]); if (lbl_table[lbl_index].ngotos != 0) { *ibp = ':'; if (n_e_lines != 0) { if (enl_head == 0) enl_head = lbl_index; else lbl_table[enl_tail].prev = lbl_index; lbl_table[enl_tail = lbl_index].prev = 0; } else fprintf(code, "%s", &inbuf[1]); } prev_label = lbl_index; } else { for (; *ibp == ' '; ++ibp) ; /* Break code line into parts */ bodyp = ibp; for (; *ibp != ':'; ++ibp) ; *ibp++ = '\0'; for (; *ibp == ' '; ++ibp) ; actionp = ibp; for (; *ibp != ','; ++ibp) ; *ibp++ = '\0'; for (; *ibp == ' '; ++ibp) ; targetp = ibp; for (; *ibp != ';'; ++ibp) ; *ibp++ = '\0'; if (asmint) { null_body = strcmp(bodyp,ASM_NULL_TEST) == 0; lbl_lookup(actionp); tp = &lbl_table[lbl_index]; act = tp->subtype; } else { null_body = strcmp(bodyp,NULL_TEST) == 0; gnbr(&n, actionp); act = n; } if (!lbl_lookup(targetp)) /* Wasn't in LT */ err_msg(ET_ERR, "Label %s missing from pass 2 input ???", targetp); tp = &lbl_table[lbl_index]; p2_lookahead(); if (tp->subtype == RA_NEXT) { target_is_next = 1; if (lookahead) { if (labuf[1] == ' ') target_in_expr = labuf[0] == 'e'; else { /* Next line is a label */ if (!lbl_lookup(&la_copy[1])) { /* Wasn't in LT */ err_msg(ET_ERR, "Label %s missing from pass 2 input !!!", &la_copy[1]); target_in_expr = 0; } else target_in_expr = lbl_table[lbl_index].in_expr; } } else target_in_expr = 0; } else { target_is_next = 0; target_in_expr = tp->in_expr; } switch (act) { case RA_IGNORE: case RA_NOMATCH: case RA_COUNT: case RA_COUNTPKT: break; case RA_RETURN: if (!isdigit(tp->id[0])) { lookup(tp->id); sp = &st[id_table[id_index].stx]; sprintf(n_buf, "%u", sp->d.sub.n_returns+1); targetp = n_buf; } break; case RA_GOSUB: case RA_GOSUBACT: lbl_lookup(tp->id); sp = &st[lbl_table[lbl_index].stx]; n_ret_actions = sp->d.sub.n_returns; if (act == RA_GOSUB && !target_in_expr) ++act; else if (act == RA_GOSUBACT && target_in_expr) --act; break; case RA_ASSIGN: case RA_GOTO: case RA_PUSHTO: case RA_PUSHPKTTO: case RA_POPTO: if (!target_in_expr) ++act; break; case RA_ASSIGNACT: case RA_GOTOACT: case RA_PUSHTOACT: case RA_PUSHPKTTOACT: case RA_POPTOACT: if (target_in_expr) --act; break; } if (prev_label) { if (act == RA_GOTOACT && null_body && tp->subtype != RA_NEXT) { goto_ix = lbl_index; /* LT index of target (?) */ plp = &lbl_table[prev_label]; if (plp->goto_ix == 0) { plp->goto_ix = goto_ix; #if OPT_LABEL_TRACE printf("+++ ==0 (%s, plp->0, goto=%d) %s: %s, %s\n", plp->id, goto_ix, bodyp, actions[act], targetp); #endif } else if (plp->goto_ix != goto_ix) { err_msg(ET_ERR, "Compiler error, inconsistent label %s", plp->id); #if OPT_LABEL_TRACE printf("+++ (%s, plp->goto=%d, goto=%d) %s: %s, %s\n", plp->id, plp->goto_ix, goto_ix, bodyp, actions[act], targetp); #endif } } } prev_label = 0; #if RET_ACTION_TRACE if (n_ret_actions > 0) { printf("\n@@@@@ nra=%d, %c %s: %s, %s;\n", n_ret_actions, inbuf[0], bodyp, actions[act], targetp); printf(" labuf: %s", labuf); } #endif if (inbuf[0] == 'r') { --n_ret_actions; #if RET_ACTION_TRACE if (n_ret_actions != 0) printf("===== nra=%d\n", n_ret_actions); #endif if (lookahead && labuf[0] != 'r' && /* 'Return;' action reached */ n_ret_actions >= 0) { /* Missing ret actions */ #if RET_ACTION_TRACE printf("+++++ %d return actions\n", n_ret_actions+1); #endif for (; n_ret_actions >= 0; --n_ret_actions) { if (!asmint) fprintf(code, " %s: %d, %s;\n", bodyp, act, targetp); else fprintf(code, " %s: %s, %s;\n", bodyp, actions[act], targetp); } } } discard = 0; if (optimise_level >= 1) { if (act == RA_GOTO && null_body && tp->subtype != RA_NEXT) { if (tp->goto_ix != 0) { /* Target is a null-body goto */ /* e null & 0 = 0: goto, lbl-l; */ /* ... */ /* elbl-1: */ /* e null & 0 = 0: goto, lbl-2; */ --tp->ngotos; while (tp->goto_ix != 0) { /* Find end of chain */ tp = &lbl_table[tp->goto_ix]; } ++tp->ngotos; targetp = tp->id; /* => Goto last label in chain */ } else if (lookahead && *lbp != ' ') { /* Label following */ /* e null & 0 = 0: goto, lbl; */ /* elbl: */ /* e ... */ for (; *lbp != ':'; ++lbp) ; *lbp = '\0'; if (strcmp(&labuf[1], targetp) == 0 && tp->in_expr && inbuf[0] != 'r') { --tp->ngotos; *lbp = ':'; continue; /* => Delete redundant goto, lbl */ } *lbp = ':'; } } if (target_is_next && lookahead && *lbp == ' ') { if (la_null_body && (act == RA_PUSHTOACT || act == RA_PUSHPKTTOACT || act == RA_GOTOACT) && la_act == RA_GOTOACT ) { /* i operand: pushto, next; # store */ /* i null & 0 = 0: goto, lbl; */ targetp = la_targetp; discard = 1; /* => Delete redundant goto */ if (!lbl_lookup(targetp)) err_msg(ET_ERR, /* Wasn't in LT */ "Opt label %s missing from pass 2 input ???", targetp); tp = &lbl_table[lbl_index]; if (tp->goto_ix != 0) { /* Target is null-body goto */ --tp->ngotos; while (tp->goto_ix != 0) { /* Find end of chain */ tp = &lbl_table[tp->goto_ix]; } ++tp->ngotos; targetp = tp->id; /* => Goto last label in chain */ } } } if (null_body && (act == RA_IGNORE || act == RA_NOMATCH || act == RA_RETURN || act == RA_COUNT || act == RA_COUNTPKT) ) { if (lookahead && labuf[0] != 'r' && *lbp == ' ') { if (la_act == RA_GOTO || la_act == RA_GOTOACT || la_act == RA_RETURN || la_act == RA_NOMATCH) /* ? null & 0 = 0: ignore/countpkt, n */ /* ? null & 0 = 0: goto/gotoact */ discard = 1; /* => Delete redundant goto */ else { err_msg(ET_WARN, "Program contains unreachable statement"); if (asmint > 1) { printf(" %s: %s, %s;\n", bodyp, actions[act], targetp); printf(" %s: %s, %s;\n", la_bodyp, actions[la_act], la_targetp); } } } } } if (discard) lookahead = 0; #if OPT_EXPR_TRACE if (n_e_lines > 0 && n_e_lines < 10) printf("~~~ n_e_lines=%d, inbuf= %s: %s, %s;\n", n_e_lines, bodyp, actions[act], targetp); #endif if (optimise_level >= 3) { if (inbuf[0] == 'e' && memcmp(bodyp, "null", 4) != 0 && *bodyp != '0') { en = alloc_node(); read_node(en, bodyp); en->d.operand.action = act; strcpy(en->d.operand.target, targetp); if (n_e_lines != 0 && en->d.operand.attrib != en_tail->d.operand.attrib) process_en_list(en_head); if (n_e_lines == 0) en_head = en; else en_tail->left = en; en_tail = en; ++n_e_lines; } else { if (n_e_lines != 0) process_en_list(en_head); if (!asmint) fprintf(code, " %s: %d, %s;\n", bodyp, act, targetp); else fprintf(code, " %s: %s, %s;\n", bodyp, actions[act], targetp); } } else { if (!asmint) fprintf(code, " %s: %d, %s;\n", bodyp, act, targetp); else fprintf(code, " %s: %s, %s;\n", bodyp, actions[act], targetp); } } } if (n_e_lines != 0) process_en_list(en_head); if (command != NULL) { /* Copy NeMaC commands to tail of code file */ sprintf(fn, "%s.cmd", fn_prefix); if ((command = fopen(fn, "r")) != NULL) { for (;;) { if (fgets(inbuf, INPUT_LN, command) == NULL) break; /* EOF */ fprintf(code, &inbuf[1]); } } fclose(command); } fclose(code); /* Intermediate files closed by next_inter_line() */ if (asmint < 2) delete_intermediate_files(); }