#include #include #include #include #include "extree.h" /* my own definitions */ #include "util.h" #include "assemble_globals.h" #include "assemble_aux.h" #include "listing.h" #include "object.h" #define DEBUG_REGEXPR 0 /* Diagnostic: print an expression tree. I used this in various places to help me diagnose parse problems, by putting in calls to print_tree when I didn't understand why something wasn't working. This is currently dead code, nothing calls it; but I don't want it to go away. Hopefully the compiler will realize when it's dead, and eliminate it. */ void print_tree( FILE *printfile, EX_TREE *tp, int depth) { SYMBOL *sym; if (printfile == NULL) { return; } if (tp == NULL) { fprintf(printfile, "(null)"); return; } switch (tp->type) { case EX_LIT: fprintf(printfile, "%o", tp->data.lit & 0177777); break; case EX_SYM: case EX_TEMP_SYM: sym = tp->data.symbol; fprintf(printfile, "%s{%s%o:%s}", tp->data.symbol->label, symflags(sym), sym->value, sym->section->label); break; case EX_UNDEFINED_SYM: fprintf(printfile, "%s{%o:undefined}", tp->data.symbol->label, tp->data.symbol->value); break; case EX_COM: fprintf(printfile, "^C<"); print_tree(printfile, tp->data.child.left, depth + 4); fprintf(printfile, ">"); break; case EX_NEG: fprintf(printfile, "-<"); print_tree(printfile, tp->data.child.left, depth + 4); fputc('>', printfile); break; case EX_REG: fprintf(printfile, "%%<"); print_tree(printfile, tp->data.child.left, depth + 4); fputc('>', printfile); break; case EX_ERR: fprintf(printfile, "{expression error}"); if (tp->data.child.left) { fputc('<', printfile); print_tree(printfile, tp->data.child.left, depth + 4); fputc('>', printfile); } break; case EX_ADD: fputc('<', printfile); print_tree(printfile, tp->data.child.left, depth + 4); fputc('+', printfile); print_tree(printfile, tp->data.child.right, depth + 4); fputc('>', printfile); break; case EX_SUB: fputc('<', printfile); print_tree(printfile, tp->data.child.left, depth + 4); fputc('-', printfile); print_tree(printfile, tp->data.child.right, depth + 4); fputc('>', printfile); break; case EX_MUL: fputc('<', printfile); print_tree(printfile, tp->data.child.left, depth + 4); fputc('*', printfile); print_tree(printfile, tp->data.child.right, depth + 4); fputc('>', printfile); break; case EX_DIV: fputc('<', printfile); print_tree(printfile, tp->data.child.left, depth + 4); fputc('/', printfile); print_tree(printfile, tp->data.child.right, depth + 4); fputc('>', printfile); break; case EX_AND: fputc('<', printfile); print_tree(printfile, tp->data.child.left, depth + 4); fputc('&', printfile); print_tree(printfile, tp->data.child.right, depth + 4); fputc('>', printfile); break; case EX_OR: fputc('<', printfile); print_tree(printfile, tp->data.child.left, depth + 4); fputc('!', printfile); print_tree(printfile, tp->data.child.right, depth + 4); fputc('>', printfile); break; case EX_LSH: fputc('<', printfile); print_tree(printfile, tp->data.child.left, depth + 4); fputc('_', printfile); print_tree(printfile, tp->data.child.right, depth + 4); fputc('>', printfile); break; default: fprintf(printfile, "(node %d)", tp->type); break; } if (depth == 0) fputc('\n', printfile); } /* num_subtrees tells you how many subtrees this EX_TREE has. */ int num_subtrees( EX_TREE *tp) { switch (tp->type) { case EX_UNDEFINED_SYM: case EX_TEMP_SYM: case EX_SYM: case EX_LIT: return 0; break; case EX_COM: case EX_NEG: case EX_REG: case EX_ERR: return 1; case EX_ADD: case EX_SUB: case EX_MUL: case EX_DIV: case EX_AND: case EX_OR: case EX_LSH: return 2; } return 0; } /* free_tree frees an expression tree. */ void free_tree( EX_TREE *tp) { if (!tp) return; switch (num_subtrees(tp)) { case 0: switch (tp->type) { case EX_UNDEFINED_SYM: case EX_TEMP_SYM: free(tp->data.symbol->label); free(tp->data.symbol); break; case EX_SYM: case EX_LIT: break; default: assert(0); break; } break; case 2: free_tree(tp->data.child.right); /* FALLTHROUGH */ case 1: free_tree(tp->data.child.left); break; } free(tp); } /* new_temp_sym allocates a new EX_TREE entry of type "TEMPORARY SYMBOL" (slight semantic difference from "UNDEFINED"). */ static EX_TREE *new_temp_sym( char *label, SECTION *section, unsigned value) { SYMBOL *sym; EX_TREE *tp; sym = memcheck(malloc(sizeof(SYMBOL))); sym->label = memcheck(strdup(label)); sym->flags = SYMBOLFLAG_DEFINITION; sym->stmtno = stmtno; sym->next = NULL; sym->section = section; sym->value = value; tp = new_ex_tree(EX_TEMP_SYM); tp->data.symbol = sym; return tp; } SYMBOL *dup_symbol( SYMBOL *sym) { SYMBOL *res; if (sym == NULL) { return NULL; } res = memcheck(malloc(sizeof(SYMBOL))); res->label = memcheck(strdup(sym->label)); res->flags = sym->flags; res->stmtno = sym->stmtno; res->next = NULL; res->section = sym->section; res->value = sym->value; return res; } EX_TREE *dup_tree( EX_TREE *tp) { EX_TREE *res = NULL; if (tp == NULL) { return NULL; } res = new_ex_tree(tp->type); res->cp = tp->cp; switch (num_subtrees(tp)) { case 0: switch (tp->type) { case EX_UNDEFINED_SYM: case EX_TEMP_SYM: res->data.symbol = dup_symbol(tp->data.symbol); break; /* The symbol reference in EX_SYM is not freed in free_tree() */ case EX_SYM: res->data.symbol = tp->data.symbol; break; case EX_LIT: res->data.lit = tp->data.lit; break; default: assert(0); break; } break; case 2: res->data.child.right = dup_tree(tp->data.child.right); /* FALLTHROUGH */ case 1: res->data.child.left = dup_tree(tp->data.child.left); break; } return res; } #define RELTYPE(tp) (((tp)->type == EX_SYM || (tp)->type == EX_TEMP_SYM) && \ (tp)->data.symbol->section->flags & PSECT_REL) /* evaluate "evaluates" an EX_TREE, ideally trying to produce a constant value, else a symbol plus an offset. Leaves the input tree untouched and creates a new tree as result. */ EX_TREE *evaluate_rec( EX_TREE *tp, int flags, int *outflags) { EX_TREE *res; char *cp = tp->cp; switch (tp->type) { case EX_SYM: { SYMBOL *sym = tp->data.symbol; /* Change some symbols to "undefined" */ if (flags & EVALUATE_DEFINEDNESS) { int is_undefined = 0; /* I'd prefer this behavior, but MACRO.SAV is a bit too primitive. */ #if 0 /* A temporary symbol defined later is "undefined." */ if (!(sym->flags & PERMANENT) && sym->stmtno > stmtno) is_undefined = 1; #endif /* A global symbol with no assignment is "undefined." */ /* Go figure. */ if (SYM_IS_IMPORTED(sym)) is_undefined = 1; /* A symbol marked as undefined is undefined */ if (sym->flags & SYMBOLFLAG_UNDEFINED) is_undefined = 1; if (is_undefined) { res = new_temp_sym(tp->data.symbol->label, tp->data.symbol->section, tp->data.symbol->value); res->type = EX_UNDEFINED_SYM; break; } } /* Turn defined absolute symbol to a literal */ if (!(sym->section->flags & PSECT_REL) && !SYM_IS_IMPORTED(sym)) { res = new_ex_lit(sym->value); if (sym->section->type == SECTION_REGISTER) { *outflags |= EVALUATE_OUT_IS_REGISTER; } break; } /* Make a temp copy of any reference to "." since it might change as complex relocatable expressions are written out */ if (strcmp(sym->label, ".") == 0) { res = new_temp_sym(".", sym->section, sym->value); break; } /* Copy other symbol reference verbatim. */ res = dup_tree(tp); break; } case EX_LIT: res = dup_tree(tp); break; case EX_TEMP_SYM: case EX_UNDEFINED_SYM: /* Copy temp and undefined symbols */ res = new_temp_sym(tp->data.symbol->label, tp->data.symbol->section, tp->data.symbol->value); res->type = tp->type; break; case EX_COM: /* Complement */ tp = evaluate_rec(tp->data.child.left, flags, outflags); if (tp->type == EX_LIT) { /* Complement the literal */ res = new_ex_lit(~tp->data.lit); free_tree(tp); } else { /* Copy verbatim. */ res = new_ex_una(EX_COM, tp); } break; case EX_NEG: tp = evaluate_rec(tp->data.child.left, flags, outflags); if (tp->type == EX_LIT) { /* negate literal */ res = new_ex_lit((unsigned) -(int) tp->data.lit); free_tree(tp); } else if (tp->type == EX_TEMP_SYM || (tp->type == EX_SYM && (tp->data.symbol->flags & SYMBOLFLAG_DEFINITION))) { /* Make a temp sym with the negative value of the given sym (this works for symbols within relocatable sections too) */ res = new_temp_sym("*NEG", tp->data.symbol->section, (unsigned) -(int) tp->data.symbol->value); res->cp = tp->cp; free_tree(tp); } else { /* Copy verbatim. */ res = new_ex_una(EX_NEG, tp); } break; case EX_REG: res = evaluate_rec(tp->data.child.left, flags, outflags); *outflags |= EVALUATE_OUT_IS_REGISTER; break; case EX_ERR: /* Copy */ res = dup_tree(tp); break; case EX_ADD: { EX_TREE *left, *right; left = evaluate_rec(tp->data.child.left, flags, outflags); right = evaluate_rec(tp->data.child.right, flags, outflags); /* Both literals? Sum them and return result. */ if (left->type == EX_LIT && right->type == EX_LIT) { res = new_ex_lit(left->data.lit + right->data.lit); free_tree(left); free_tree(right); break; } /* Commutative: A+x == x+A. Simplify by putting the literal on the right */ if (left->type == EX_LIT) { EX_TREE *temp = left; left = right; right = temp; } if (right->type == EX_LIT && /* Anything plus 0 == itself */ right->data.lit == 0) { res = left; free_tree(right); break; } /* Relative symbol plus lit is replaced with a temp sym holding the sum */ if (RELTYPE(left) && right->type == EX_LIT) { SYMBOL *sym = left->data.symbol; res = new_temp_sym("*ADD", sym->section, sym->value + right->data.lit); free_tree(left); free_tree(right); break; } /* Associative: +y == A+ */ /* and if x+y is constant, I can do that math. */ if (left->type == EX_ADD && right->type == EX_LIT) { EX_TREE *leftright = left->data.child.right; if (leftright->type == EX_LIT) { /* Do the shuffle */ res = left; leftright->data.lit += right->data.lit; free_tree(right); break; } } /* Associative: +y == A+ */ /* and if y-x is constant, I can do that math. */ if (left->type == EX_SUB && right->type == EX_LIT) { EX_TREE *leftright = left->data.child.right; if (leftright->type == EX_LIT) { /* Do the shuffle */ res = left; leftright->data.lit = right->data.lit - leftright->data.lit; free_tree(right); break; } } /* Anything else returns verbatim */ res = new_ex_bin(EX_ADD, left, right); } break; case EX_SUB: { EX_TREE *left, *right; left = evaluate_rec(tp->data.child.left, flags, outflags); right = evaluate_rec(tp->data.child.right, flags, outflags); /* Both literals? Subtract them and return a lit. */ if (left->type == EX_LIT && right->type == EX_LIT) { res = new_ex_lit(left->data.lit - right->data.lit); free_tree(left); free_tree(right); break; } if (right->type == EX_LIT && /* Symbol minus 0 == symbol */ right->data.lit == 0) { res = left; free_tree(right); break; } /* A relocatable minus an absolute - make a new temp sym to represent that. */ if (RELTYPE(left) && right->type == EX_LIT) { SYMBOL *sym = left->data.symbol; res = new_temp_sym("*SUB", sym->section, sym->value - right->data.lit); free_tree(left); free_tree(right); break; } if (RELTYPE(left) && RELTYPE(right) && left->data.symbol->section == right->data.symbol->section) { /* Two defined symbols in the same psect. Resolve their difference as a literal. */ res = new_ex_lit(left->data.symbol->value - right->data.symbol->value); free_tree(left); free_tree(right); break; } /* Associative: -y == A+ */ /* and if x-y is constant, I can do that math. */ if (left->type == EX_ADD && right->type == EX_LIT) { EX_TREE *leftright = left->data.child.right; if (leftright->type == EX_LIT) { /* Do the shuffle */ res = left; leftright->data.lit -= right->data.lit; free_tree(right); break; } } /* Associative: -y == A- */ /* and if x+y is constant, I can do that math. */ if (left->type == EX_SUB && right->type == EX_LIT) { EX_TREE *leftright = left->data.child.right; if (leftright->type == EX_LIT) { /* Do the shuffle */ res = left; leftright->data.lit += right->data.lit; free_tree(right); break; } } /* Anything else returns verbatim */ res = new_ex_bin(EX_SUB, left, right); } break; case EX_MUL: { EX_TREE *left, *right; left = evaluate_rec(tp->data.child.left, flags, outflags); right = evaluate_rec(tp->data.child.right, flags, outflags); /* Can only multiply if both are literals */ if (left->type == EX_LIT && right->type == EX_LIT) { res = new_ex_lit(left->data.lit * right->data.lit); free_tree(left); free_tree(right); break; } /* Commutative: A*x == x*A. Simplify by putting the literal on the right */ if (left->type == EX_LIT) { EX_TREE *temp = left; left = right; right = temp; } if (right->type == EX_LIT && /* Symbol times 1 == symbol */ right->data.lit == 1) { res = left; free_tree(right); break; } if (right->type == EX_LIT && /* Symbol times 0 == 0 */ right->data.lit == 0) { res = right; free_tree(left); break; } /* Associative: *y == A* */ /* If x*y is constant, I can do this math. */ /* Is this safe? I will potentially be doing it */ /* with greater accuracy than the target platform. */ /* Hmmm. */ if (left->type == EX_MUL && right->type == EX_LIT) { EX_TREE *leftright = left->data.child.right; if (leftright->type == EX_LIT) { /* Do the shuffle */ res = left; leftright->data.lit *= right->data.lit; free_tree(right); break; } } /* Anything else returns verbatim */ res = new_ex_bin(EX_MUL, left, right); } break; case EX_DIV: { EX_TREE *left, *right; left = evaluate_rec(tp->data.child.left, flags, outflags); right = evaluate_rec(tp->data.child.right, flags, outflags); /* Can only divide if both are literals */ if (left->type == EX_LIT && right->type == EX_LIT) { res = new_ex_lit(left->data.lit / right->data.lit); free_tree(left); free_tree(right); break; } if (right->type == EX_LIT && /* Symbol divided by 1 == symbol */ right->data.lit == 1) { res = left; free_tree(right); break; } /* Anything else returns verbatim */ res = new_ex_bin(EX_DIV, left, right); } break; case EX_AND: { EX_TREE *left, *right; left = evaluate_rec(tp->data.child.left, flags, outflags); right = evaluate_rec(tp->data.child.right, flags, outflags); /* Operate if both are literals */ if (left->type == EX_LIT && right->type == EX_LIT) { res = new_ex_lit(left->data.lit & right->data.lit); free_tree(left); free_tree(right); break; } /* Commutative: A&x == x&A. Simplify by putting the literal on the right */ if (left->type == EX_LIT) { EX_TREE *temp = left; left = right; right = temp; } if (right->type == EX_LIT && /* Symbol AND 0 == 0 */ right->data.lit == 0) { res = new_ex_lit(0); free_tree(left); free_tree(right); break; } if (right->type == EX_LIT && /* Symbol AND 0177777 == symbol */ right->data.lit == 0177777) { res = left; free_tree(right); break; } /* Anything else returns verbatim */ res = new_ex_bin(EX_AND, left, right); } break; case EX_OR: { EX_TREE *left, *right; left = evaluate_rec(tp->data.child.left, flags, outflags); right = evaluate_rec(tp->data.child.right, flags, outflags); /* Operate if both are literals */ if (left->type == EX_LIT && right->type == EX_LIT) { res = new_ex_lit(left->data.lit | right->data.lit); free_tree(left); free_tree(right); break; } /* Commutative: A!x == x!A. Simplify by putting the literal on the right */ if (left->type == EX_LIT) { EX_TREE *temp = left; left = right; right = temp; } if (right->type == EX_LIT && /* Symbol OR 0 == symbol */ right->data.lit == 0) { res = left; free_tree(right); break; } if (right->type == EX_LIT && /* Symbol OR 0177777 == 0177777 */ right->data.lit == 0177777) { res = new_ex_lit(0177777); free_tree(left); free_tree(right); break; } /* Anything else returns verbatim */ res = new_ex_bin(EX_OR, left, right); } break; case EX_LSH: { EX_TREE *left, *right; left = evaluate_rec(tp->data.child.left, flags, outflags); right = evaluate_rec(tp->data.child.right, flags, outflags); /* Operate if both are literals */ if (left->type == EX_LIT && right->type == EX_LIT) { int shift = right->data.lit; if (shift < 0) res = new_ex_lit(left->data.lit >> -shift); else res = new_ex_lit(left->data.lit << shift); free_tree(left); free_tree(right); break; } if (right->type == EX_LIT && /* Symbol shifted 0 == symbol */ right->data.lit == 0) { res = left; free_tree(right); break; } if (right->type == EX_LIT && /* Anything shifted 16 == 0 */ ((int)right->data.lit > 15 || (int)right->data.lit < -15)) { res = new_ex_lit(0); free_tree(left); free_tree(right); break; } if (right->type == EX_LIT) { /* Other shifts become * or / */ int shift = right->data.lit; if (shift > 0) res = new_ex_bin(EX_MUL, left, new_ex_lit(1 << shift)); else res = new_ex_bin(EX_DIV, left, new_ex_lit(1 << -shift)); free_tree(right); break; } /* Anything else returns verbatim */ res = new_ex_bin(EX_LSH, left, right); } break; default: fprintf(stderr, "evaluate_rec: Invalid tree: "); print_tree(stderr, tp, 0); return NULL; } res->cp = cp; return res; } EX_TREE *evaluate( EX_TREE *tp, int flags) { EX_TREE *res; int outflags = 0; #if DEBUG_REGEXPR fprintf(stderr, "evaluate: "); print_tree(stderr, tp, 0); #endif /* DEBUG_REGEXPR */ /* * Check the common simple case for register references: R0...PC. * Copy those directly, without going into the recursion. * Therefore, in the recursion it can be assumed that the expression * is more complex, and it is worth the overhead of converting them * to an EX_LIT plus the EVALUATE_OUT_IS_REGISTER flag * (and back again, up here). */ if (tp->type == EX_SYM && tp->data.symbol->section->type == SECTION_REGISTER && !SYM_IS_IMPORTED(tp->data.symbol)) { res = dup_tree(tp); } else { res = evaluate_rec(tp, flags, &outflags); if (outflags & EVALUATE_OUT_IS_REGISTER) { int regno = get_register(res); if (regno == NO_REG) { report(NULL, "Register expression out of range.\n"); #if DEBUG_REGEXPR print_tree(stderr, tp, 0); print_tree(stderr, res, 0); #endif /* DEBUG_REGEXPR */ /* TODO: maybe make this a EX_TEMP_SYM? */ res = ex_err(res, res->cp); } else { EX_TREE *newresult = new_ex_tree(EX_SYM); newresult->cp = res->cp; newresult->data.symbol = reg_sym[regno]; free_tree(res); res = newresult; } } } return res; } /* Allocate an EX_TREE */ EX_TREE *new_ex_tree( int type) { EX_TREE *tr = memcheck(calloc(1, sizeof(EX_TREE))); tr->type = type; return tr; } /* Create an EX_TREE representing a parse error */ EX_TREE *ex_err( EX_TREE *tp, char *cp) { EX_TREE *errtp; errtp = new_ex_tree(EX_ERR); errtp->cp = cp; errtp->data.child.left = tp; return errtp; } /* Create an EX_TREE representing a literal value */ EX_TREE *new_ex_lit( unsigned value) { EX_TREE *tp; tp = new_ex_tree(EX_LIT); tp->data.lit = value; return tp; } /* Create an EX_TREE representing a binary expression */ EX_TREE *new_ex_bin( int type, EX_TREE *left, EX_TREE *right) { EX_TREE *tp; tp = new_ex_tree(type); tp->data.child.left = left; tp->data.child.right = right; tp->cp = right->cp; return tp; } /* Create an EX_TREE representing a unary expression */ EX_TREE *new_ex_una( int type, EX_TREE *left) { EX_TREE *tp; tp = new_ex_tree(type); tp->data.child.left = left; tp->data.child.right = NULL; tp->cp = left->cp; return tp; }