/* 
    At the entry to each basic block, we look at all the significant
    registers.  If any register is the same every time we enter this
    block, we note the fact, and use it later in the code generation
    phase.  For the moment I'm hoping simply to optimise away the 'P'
    and 'I' registers, but in the long term, this partial evaluation
    may improve code efficiency in other areas too.

    The only exported procedure here is 'note_invariants()' and it can
    be replaced with an empty procedure if data gathering is not
    required.  However this *ought* be be so cheap (being executed
    only on entry to basic blocks, not on every instruction) that
    it should be left in the final code to ensure total coverage during
    exhaustive play-testing.
 */
#define merge_context(R) \
  if (context_##R[addr] == 0xFEEDBEEFUL) { \
    context_##R[addr] = R; \
  } else if (context_##R[addr] != (unsigned long)R) { \
    context_##R[addr] = 0xDEADBEEFUL; \
  }

unsigned long context_register_A[0x2000];   /* A-Register (accumulator) */
unsigned long context_register_B[0x2000];   /* B-Register (accumulator) */
unsigned long context_register_I[0x2000];   /* I-Register (last access RAM location) */
unsigned long context_register_J[0x2000];   /* J-Register (target address for JMP opcodes) */
unsigned long context_register_P[0x2000];   /* Page-Register (4 bits, shifts to high short nibble for code, hight byte nibble for ram) */

#include <stdlib.h>
void dump_merged_contexts(void)
{
  int i;
  FILE *context_file = fopen("tailgunr.opt", "w");
  if (context_file == NULL) return;
  for (i = 0; i < 0x2000; i++) {
    /* Might as well minimise the size of the file. */
    if (
        context_register_A[i] == 0xFEEDBEEFUL &&
        context_register_B[i] == 0xFEEDBEEFUL &&
        context_register_I[i] == 0xFEEDBEEFUL &&
        context_register_J[i] == 0xFEEDBEEFUL &&
        context_register_P[i] == 0xFEEDBEEFUL
       ) continue;
    if (
        context_register_A[i] == 0xDEADBEEFUL &&
        context_register_B[i] == 0xDEADBEEFUL &&
        context_register_I[i] == 0xDEADBEEFUL &&
        context_register_J[i] == 0xDEADBEEFUL &&
        context_register_P[i] == 0xDEADBEEFUL
       ) continue;
    fprintf(context_file, "L%04x ", i);
    fprintf(context_file, "A %x ", context_register_A[i]);
    fprintf(context_file, "B %x ", context_register_B[i]);
    fprintf(context_file, "I %x ", context_register_I[i]);
    fprintf(context_file, "J %x ", context_register_J[i]);
    fprintf(context_file, "P %x\n", context_register_P[i]);
  }
  fflush(context_file);
  fclose(context_file);
}

void note_invariants(int addr)
{
static int init = 0;
  if (init == 0) {
    FILE *context_file = fopen("tailgunr.opt", "r");
    int i, rc;
    init = 1;
    /* Initialise everything to 'unknown' */
    for (i = 0; i < 0x2000; i++) {
      context_register_A[i] = 0xFEEDBEEFUL;
      context_register_B[i] = 0xFEEDBEEFUL;
      context_register_I[i] = 0xFEEDBEEFUL;
      context_register_J[i] = 0xFEEDBEEFUL;
      context_register_P[i] = 0xFEEDBEEFUL;
    }
    if (context_file != NULL) {
      /* All executions are merged over the long term */
      for (;;) {
        /* Read in results of previous executions */
        rc = fscanf(context_file, "L%04x ", &i);
        if (rc == 0) break;
        fscanf(context_file, "A %04x ", &context_register_A[i]);
        fscanf(context_file, "B %04x ", &context_register_B[i]);
        fscanf(context_file, "I %04x ", &context_register_I[i]);
        fscanf(context_file, "J %04x ", &context_register_J[i]);
        fscanf(context_file, "P %04x\n", &context_register_P[i]);
      }
      fclose(context_file);
      if (debugf) fprintf(debugf, "OPTIMISE: pulled in %d previous records\n");
    }
    if (atexit(dump_merged_contexts) != 0) {
      if (debugf) fprintf(debugf, "Could not install atexit() handler\n");
    }
  }
  merge_context(register_A);
  merge_context(register_B);
  merge_context(register_I);
  merge_context(register_J);
  merge_context(register_P);
}

