Olaf Seibert 3a9d809b75 Optimize the previous version.
Fold the functionality of pull_up_reg() into evaluate_rec().
pull_up_reg() essentially returned a flag (EX_REG at the top, or not)
which can be represented with the outgoing flags word.
The check for recursion depth is made unneeded by doing the
"exceptional" case outside the recursion; luckily it is actually the
common case.
2021-11-15 21:35:37 +01:00

958 lines
27 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#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: <A+x>+y == A+<x+y> */
/* 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: <A-x>+y == A+<y-x> */
/* 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: <A+x>-y == A+<x-y> */
/* 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: <A-x>-y == A-<x+y> */
/* 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: <A*x>*y == A*<x*y> */
/* 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;
}