#include <stdio.h>
#include <stdlib.h>

// peephole optimizations to the assembly code generated
// by schimper.scm. the input to this program is tightly coupled
// to the output of schimper.scm. if changes occur to schimper
// it could potentially affect whether peep.c works.

#define LINE_SIZE 2048
#define POOL_SIZE 1024

// arguments tokens
#define TOKEN_ARM 0
#define TOKEN_X86 16
#define TOKEN_RISCV 32
#define TOKEN_M68K 48
#define TOKEN_AARCH64 64
#define TOKEN_X32 80
#define TOKEN_X64 96
#define TOKEN_V6 112
#define TOKEN_V7 128

// generic simpvir mnemonic tokens
#define TOKEN_STACK_REF_N 144
#define TOKEN_STACK_SET_N 160
#define TOKEN_EXPR 176
#define TOKEN_PUSH_0 192
#define TOKEN_PUSH 208
#define TOKEN_STACK_SET 224
#define TOKEN_STACK_REF 240

// riscv tokens
#define TOKEN_LI_SX 256
#define TOKEN_SW 272
#define TOKEN_LW 288

// arm aarch64 tokens
#define TOKEN_STR 304
#define TOKEN_LDR 320
#define TOKEN_LSL 336
#define TOKEN_MOVK 352
#define TOKEN_MOV 368

// next available index = 384

int pool[POOL_SIZE]; // TODO check range

int line[LINE_SIZE];
int line2[LINE_SIZE];
int line3[LINE_SIZE];
int line4[LINE_SIZE];
int line5[LINE_SIZE];

int my_atoi(int line[], int end_idx) {
  int start_idx;
  int digit;
  int idx;
  int power;
  int int_val;
  // find start_idx
  start_idx = end_idx;
  digit = line[start_idx];
  while (digit >= '0' && digit <= '9') {
    start_idx = start_idx + 1;
    digit = line[start_idx];
  }
  start_idx = start_idx - 1;
  power = 1;
  int_val = 0;
  for (idx = start_idx; idx >= end_idx; idx--) {
    digit = line[idx];
    int_val = int_val + (digit - '0') * power;
    power = power * 10;
  }
  return int_val; 
}

int is_line(int line[]) {
  return line[0] != -1;
}

void get_line(int line[]) {
  int ch;
  int idx;
  idx = 0;
  ch = getchar();
  while (ch != -1 && ch != '\n') {
    if (idx < LINE_SIZE)
      line[idx] = ch;
    idx = idx + 1;
    ch = getchar();
  }

  if (ch == -1) // eof
    line[idx] = -1;
  else
  if (idx >= LINE_SIZE)
    line[LINE_SIZE - 1] = 0; // null
  else
    line[idx] = 0; // null

  return;
}

void print_line(int line[]) {
  int ch;
  int idx;
  idx = 0;
  ch = line[idx];
  while (ch != 0 && ch != -1) {
    putchar(ch);
    idx = idx + 1;
    ch = line[idx];
  }
  putchar('\n');
  return;
}

void copy_to(int line1[], int line2[]) {
  int idx;
  for (idx = 0; idx < LINE_SIZE; idx++)
    line2[idx] = line1[idx];
  return;
}

void set_pool_quarter(
  int offset,
  int c0, int c1, int c2, int c3) {
  pool[offset] = c0;
  pool[offset + 1] = c1;
  pool[offset + 2] = c2;
  pool[offset + 3] = c3;
  return;
}

void set_pool(
  int offset,
  int c00, int c01, int c02, int c03,
  int c04, int c05, int c06, int c07,
  int c08, int c09, int c10, int c11,
  int c12, int c13, int c14, int c15) {
  set_pool_quarter(offset,      c00, c01, c02, c03);
  set_pool_quarter(offset + 4,  c04, c05, c06, c07);
  set_pool_quarter(offset + 8,  c08, c09, c10, c11);
  set_pool_quarter(offset + 12, c12, c13, c14, c15);
  return;
}

int line_match(int line[], int offset1, int offset2, int offset3, int length) {
  int idx;
  idx = 0;
  while (idx < length) {
    if (line[offset1 + idx] != pool[offset2 + offset3 + idx])
      return 0; // not a match
    idx = idx + 1;
  }
  return idx + offset1; // match - return index after token
}

int line_skip_blanks(int line[], int idx) {
  int ch;
  ch = line[idx];
  while (ch == ' ') {
    idx = idx + 1;
    ch = line[idx];
  }
  return idx;
}

// arguments

int is_x86(int line[], int offset1) {
  return line_match(line, offset1, TOKEN_X86, 0, 3);
}

int is_arm(int line[], int offset1) {
  return line_match(line, offset1, TOKEN_ARM, 0, 3);
}

int is_riscv(int line[], int offset1) {
  return line_match(line, offset1, TOKEN_RISCV, 0, 5);
}

int is_32(int line[], int offset1) {
  return line_match(line, offset1, TOKEN_X32, 0, 2);
}

int is_m68k(int line[], int offset1) {
  return line_match(line, offset1, TOKEN_M68K, 0, 4);
}

int is_v6(int line[], int offset1) {
  return line_match(line, offset1, TOKEN_V6, 0, 2);
}

int is_v7(int line[], int offset1) {
  return line_match(line, offset1, TOKEN_V7, 0, 2);
}

// generic simpvir mnemonic routines

int adjust_for_double_char_comment(int line[]) {
  if (line[1] == '/')
    return 2; // double char comment
  else
    return 1; // single char comment
}

int is_stack_ref_n(int line[]) {
  int idx;
  idx = adjust_for_double_char_comment(line);
  return line_match(line, idx, TOKEN_STACK_REF_N, 1, 14);
}

int is_stack_set_n(int line[]) {
  int idx;
  idx = adjust_for_double_char_comment(line);
  return line_match(line, idx, TOKEN_STACK_SET_N, 1, 14);
}

int is_expr(int line[]) {
  int idx;
  idx = adjust_for_double_char_comment(line);
  return line_match(line, idx, TOKEN_EXPR, 1, 7);
}

int is_push_0(int line[]) {
  int idx;
  idx = adjust_for_double_char_comment(line);
  return line_match(line, idx, TOKEN_PUSH_0, 1, 9);
}

int is_push(int line[]) {
  int idx;
  idx = adjust_for_double_char_comment(line);
  return line_match(line, idx, TOKEN_PUSH, 1, 7);
}

int is_stack_set(int line[]) {
  int idx;
  idx = adjust_for_double_char_comment(line);
  return line_match(line, idx, TOKEN_STACK_SET, 1, 12);
}

int is_stack_ref(int line[]) {
  int idx;
  idx = adjust_for_double_char_comment(line);
  return line_match(line, idx, TOKEN_STACK_REF, 1, 12);
}

int is_mov_interior(int line[]) {
  int idx;
  idx = line_skip_blanks(line, 0);
  // match explicitly because string is longer than 16 chars
  return
    line[idx] == 'm' &&
    line[idx + 1] == 'o' &&
    line[idx + 2] == 'v' &&
    line[idx + 3] == ' ';
}

int is_stack_ref_n_match(int line1[], int line2[]) {
  int idx;
  int ch;
  if (!is_stack_ref_n(line1))
    return 0;
  idx = 15;
  ch = line1[idx];
  while (ch != 0) {
    if (ch != line2[idx])
      return 0;
    idx = idx + 1;
    ch = line1[idx];
  }
  return 1;
}

// riscv routines

int is_li_sx(int line[]) {
  return line_match(line, 0, TOKEN_LI_SX, 0, 15);
}

int get_sx(int line[], int idx) {
  return line[idx]; // sx register. return x
}

int is_sw(int line[]) {
  return line_match(line, 0, TOKEN_SW, 0, 11);
}

int is_lw(int line[]) {
  return line_match(line, 0, TOKEN_LW, 0, 11);
}

int get_sx_offset(int line[], int sx, int comment_char, int register_name) {
  int idx;
  int ch;

  if (line[0] == comment_char)
    return 0;

  idx = 0;
  ch = line[idx];
  while (ch != 0 && idx < LINE_SIZE) {
    idx = idx + 1;
    ch = line[idx];
  }
  idx = idx - 1;

  while (idx >= 2) {
    if (line[idx] == sx && // TODO need to handle double digit register number
        line[idx - 1] == register_name &&
        line[idx - 2] == ' ')
      return idx - 1;
    idx = idx - 1;
  } 
  return 0; // sx not found
}

void print_line_sx_substitute(int line[], int sx_offset, int method) {
  int idx;
  int ch;

  // substitute sx with zero

  for (idx = 0; idx < sx_offset; idx++)
    putchar(line[idx]);

  if (method == 0)
    printf("zero");
  else
    printf("w13");

  idx = sx_offset + 2;
  ch = line[idx];
  while (ch != 0 && idx < LINE_SIZE) {
    putchar(ch);
    idx = idx + 1;
    ch = line[idx];
  }

  putchar('\n');
  return;
}

void print_li(int line[]) {
  int idx;
  int ch;

  idx = 0;
  ch = line[idx];
  while (ch != ',') {
    putchar(line[idx]);
    idx = idx + 1;
    ch = line[idx];
  }
  printf(", ");

  return;
}

void print_double_push(int line[], int is_m68k_target) {
  int idx;
  if (is_m68k_target)
    idx = 17;
  else
    idx = 18;
  printf("%d", my_atoi(line, idx) << 2);
  if (!is_m68k_target)
    putchar('\n');
  return;
}

int sw_lw_match(int line1[], int line2[]) {
  int idx;
  int ch;
  if (!is_lw(line2))
    return 0;
  idx = 14;
  ch = line1[idx];
  while (ch != 0) {
    if (ch != line2[idx])
      return 0;
    idx = idx + 1;
    ch = line1[idx];
  }
  return 1;
}

// arm aarch64 routines

int is_str(int line[]) {
  return line_match(line, 0, TOKEN_STR, 0, 12);
}

int is_ldr(int line[]) {
  return line_match(line, 0, TOKEN_LDR, 0, 12);
}

int is_lsl(int line[]) {
  return line_match(line, 0, TOKEN_LSL, 0, 12);
}

int is_movk(int line[]) {
  return line_match(line, 0, TOKEN_MOVK, 0, 13);
}

int is_mov(int line[]) {
  return line_match(line, 0, TOKEN_MOV, 0, 12);
}

void print_double_lo(int line[]) {
  printf("%d", (my_atoi(line, 9) << 2) & 0xffff);
  return;
}

void print_double_hi(int line[]) {
  printf("%d", ((my_atoi(line, 9) << 2) >> 16) & 0xffff);
  return;
}

void print_register(int line[], int offset) {
  putchar(line[offset]);
  if (line[offset + 1] != ',') // double digit register number
    putchar(line[offset + 1]);
  return;
}

int str_ldr_match(int line1[], int line2[]) {
  int idx;
  int ch;
  if (!is_ldr(line2))
    return 0;
  idx = 15;
  ch = line1[idx];
  while (ch != 0) {
    if (ch != line2[idx])
      return 0;
    idx = idx + 1;
    ch = line1[idx];
  }
  return 1;
}

// m68k routines

void print_comma_and_after(int line[]) {
  int idx;
  int ch;

  idx = 0;
  ch = line[idx];
  while (ch != ',') {
    idx = idx + 1;
    ch = line[idx];
  }
  while (ch != 0) {
    putchar(ch);
    idx = idx + 1;
    ch = line[idx];
  }
  return;
}

int is_store_by_move(int line[]) {
  int idx;
  idx = line_skip_blanks(line, 0);
  // match explicitly because string is longer than 16 chars
  return
    line[idx] == 'm' &&
    line[idx + 1] == 'o' &&
    line[idx + 2] == 'v' &&
    line[idx + 3] == 'e' &&
    line[idx + 4] == '.' &&
    line[idx + 5] == 'l' &&
    line[idx + 6] == ' ' &&
    line[idx + 7] == ' ' &&
    line[idx + 8] == 'd'; // register operand here indicates a store move
}

// optimization routines

void optimize_nothing() {
  get_line(line);
  while (is_line(line)) {
    print_line(line);
    get_line(line);
  }
  return;
}

void optimize_x86() { // so far works for both 32 and 64 bit
  get_line(line);
  while (is_line(line)) {
    // checking for "(push 0)" and reserving a zero
    // register does not speed the code up any for 64bit x86
    if (is_stack_set_n(line)) {
      get_line(line2);
      if (is_mov_interior(line2)) {
        get_line(line3);
        if (is_expr(line3)) {
          get_line(line4);
          if (is_stack_ref_n_match(line4, line)) {
            // # (stack-set-n n)
            //   mov  reg, ...
            // # (expr ...
            // # (stack-ref-n n)
            //   mov  ..., reg // redundant instruction
            print_line(line);
            print_line(line2);
            print_line(line3);
            print_line(line4);
            get_line(line); // this line is an unnecessary load instruction
            get_line(line);
          }
          else {
            print_line(line);
            print_line(line2);
            print_line(line3);
            copy_to(line4, line);
          }
        }
        else {
          print_line(line);
          print_line(line2);
          copy_to(line3, line);
        }
      }
      else {
        print_line(line);
        copy_to(line2, line);
      }
    }
    else {
      print_line(line);
      get_line(line);
    }
  }
  return;
}

void optimize_riscv() {
  int sx;
  int sx_offset;

  get_line(line);
  while (is_line(line)) {
    if (is_push_0(line)) {
      get_line(line2);
      if (is_li_sx(line2)) {
        print_line(line); // # (push 0)
        sx = get_sx(line2, 15); // li instruction, dont print
        get_line(line2);
        sx_offset = get_sx_offset(line2, sx, '#', 's');
        while (sx_offset == 0) {
          // # (push 0)
          //   li sx, 0
          // ...
          // ... sx
          // =>
          // # (push 0)
          // ...
          // ... zero
          print_line(line2);
          get_line(line2);
          sx_offset = get_sx_offset(line2, sx, '#', 's');
        }
        print_line_sx_substitute(line2, sx_offset, 0);
        get_line(line);
      }
      else {
        print_line(line);
        copy_to(line2, line);
      }
    }
    else
    if (is_push(line)) {
      get_line(line2);
      get_line(line3);
      if (is_stack_set(line3) != 0 || is_stack_ref(line3) != 0) {
        // # (push n)
        //   li sx, n
        // # (stack-set) -or- (stack-ref)
        //   slli sx, sx, 2
        // =>
        // # (push n)
        // # (stack-set) -or- (stack-ref)
        //   li sx, n << 2
        get_line(line4);
        print_line(line);
        print_line(line3);
        print_li(line2); print_double_push(line2, 0);
        get_line(line);
      }
      else {
        print_line(line);
        print_line(line2);
        // line3 might be another push sequence
        copy_to(line3, line);
      }
    }
    else
    if (is_sw(line)) {
      get_line(line2);
      if (is_expr(line2)) {
        print_line(line);
        print_line(line2);
        get_line(line3);
        if (is_stack_ref_n(line3)) {
          print_line(line3);
          get_line(line4);
          if (sw_lw_match(line, line4)) {
            //   sw operands
            // # (expr ...
            // # (stack-ref-n n)
            //   lw operands
            get_line(line);
          }
          else
            copy_to(line4, line);
        }
        else
          copy_to(line3, line);
      }
      else {
        print_line(line);
        copy_to(line2, line);
      }
    }
    else {
      print_line(line);
      get_line(line);
    }
  }
  return;
}

void optimize_arm_aarch64() {
  int sx;
  int sx_offset;

  get_line(line);
  while (is_line(line)) {
    if (is_push_0(line)) {
      get_line(line2);
      if (is_mov(line2)) {
        print_line(line); // // (push 0)
        sx = get_sx(line2, 16); // mov instruction, dont print
        get_line(line2);
        sx_offset = get_sx_offset(line2, sx, '/', 'w');
        while (sx_offset == 0) {
          // // (push 0)
          //   mov wx, 0
          // ...
          // ... wx
          // =>
          // // (push 0)
          // ...
          // ... w13
          print_line(line2);
          get_line(line2);
          sx_offset = get_sx_offset(line2, sx, '/', 'w');
        }
        print_line_sx_substitute(line2, sx_offset, 1);
        get_line(line);
      }
      else {
        print_line(line);
        copy_to(line2, line);
      }
    }
    else
    if (is_push(line)) {
      get_line(line2); // mov
      get_line(line3); // movk
      if (is_movk(line3)) {
        get_line(line4); // // (stack-set) -or- (stack-ref)
        if (is_stack_set(line4) != 0 || is_stack_ref(line4) != 0) {
	  print_line(line); // // (push n)
          get_line(line5); // // lsl ...
	  if (is_lsl(line5)) {
            // // (push n)
            //   mov wx, lo
            //   movk wx, hi, lsl 16
            // // (stack-set) -or- (stack-ref)
            //   lsl sx, sx, 2
            // =>
            // // (push n)
            // // (stack-set) -or- (stack-ref)
            //   mov wx, lo(n << 2)
            //   movk wx, hi(n << 2)
	    printf("        mov   w");
	    print_register(line3, 15);
	    printf(", ");
	    print_double_lo(line);
	    putchar('\n');
	    printf("        movk  w");
	    print_register(line3, 15);
	    printf(", ");
	    print_double_hi(line);
	    printf(", lsl 16\n");
            print_line(line4); // // (stack-set) -or- (stack-ref)
            get_line(line);
	  }
	  else {
            print_line(line2);
            print_line(line3);
            print_line(line4);
            copy_to(line5, line);
	  }
	}
	else {
	  print_line(line); // // (push n)
          print_line(line2); // mov
          print_line(line3); // movk
          copy_to(line4, line);
	}
      }
      else {
        print_line(line);
        print_line(line2);
        // line3 might be another push sequence
        copy_to(line3, line);
      }
    }
    else
    if (is_str(line)) {
      get_line(line2);
      if (is_expr(line2)) {
        print_line(line);
        print_line(line2);
        get_line(line3);
        if (is_stack_ref_n(line3)) {
          print_line(line3);
          get_line(line4);
          if (str_ldr_match(line, line4)) {
            //   sdr operands
            // # (expr ...
            // # (stack-ref-n n)
            //   ldr operands
            get_line(line);
          }
          else
            copy_to(line4, line);
        }
        else
          copy_to(line3, line);
      }
      else {
        print_line(line);
        copy_to(line2, line);
      }
    }
    else {
      print_line(line);
      get_line(line);
    }
  }
  return;
}

void optimize_arm_v6() {
  get_line(line);
  while (is_line(line)) {
    if (is_str(line)) {
      get_line(line2);
      if (is_expr(line2)) {
        print_line(line);
        print_line(line2);
        get_line(line3);
        if (is_stack_ref_n(line3)) {
          print_line(line3);
          get_line(line4);
          if (str_ldr_match(line, line4)) {
            //   str operands
            // @ (expr ...
            // @ (stack-ref-n n)
            //   ldr operands
            get_line(line);
          }
          else
            copy_to(line4, line);
        }
        else
          copy_to(line3, line);
      }
      else {
        print_line(line);
        copy_to(line2, line);
      }
    }
    else {
      print_line(line);
      get_line(line);
    }
  }
  return;
}

void optimize_arm_v7() {
  get_line(line);
  while (is_line(line)) {
    if (is_push(line)) {
      get_line(line2); // mov
      get_line(line3); // movk
      if (is_movk(line3)) {
        get_line(line4); // // (stack-set) -or- (stack-ref)
        if (is_stack_set(line4) != 0 || is_stack_ref(line4) != 0) {
	  print_line(line); // // (push n)
          get_line(line5); // // lsl ...
	  if (is_lsl(line5)) {
            // // (push n)
            //   mov wx, lo
            //   movk wx, hi, lsl 16
            // // (stack-set) -or- (stack-ref)
            //   lsl sx, sx, 2
            // =>
            // // (push n)
            // // (stack-set) -or- (stack-ref)
            //   mov wx, lo(n << 2)
            //   movk wx, hi(n << 2)
	    printf("        mov   w");
	    print_register(line3, 15);
	    printf(", ");
	    print_double_lo(line);
	    putchar('\n');
	    printf("        movk  w");
	    print_register(line3, 15);
	    printf(", ");
	    print_double_hi(line);
	    printf(", lsl 16\n");
            print_line(line4); // // (stack-set) -or- (stack-ref)
            get_line(line);
	  }
	  else {
            print_line(line2);
            print_line(line3);
            print_line(line4);
            copy_to(line5, line);
	  }
	}
	else {
	  print_line(line); // // (push n)
          print_line(line2); // mov
          print_line(line3); // movk
          copy_to(line4, line);
	}
      }
      else {
        print_line(line);
        print_line(line2);
        // line3 might be another push sequence
        copy_to(line3, line);
      }
    }
    else
    if (is_str(line)) {
      get_line(line2);
      if (is_expr(line2)) {
        print_line(line);
        print_line(line2);
        get_line(line3);
        if (is_stack_ref_n(line3)) {
          print_line(line3);
          get_line(line4);
          if (str_ldr_match(line, line4)) {
            //   sdr operands
            // @ (expr ...
            // @ (stack-ref-n n)
            //   ldr operands
            get_line(line);
          }
          else
            copy_to(line4, line);
        }
        else
          copy_to(line3, line);
      }
      else {
        print_line(line);
        copy_to(line2, line);
      }
    }
    else {
      print_line(line);
      get_line(line);
    }
  }
  return;
}

void optimize_m68k() {
  get_line(line);
  while (is_line(line)) {
    if (is_push(line)) {
      get_line(line2);
      get_line(line3);
      if (is_stack_set(line3) != 0 || is_stack_ref(line3) != 0) {
        // # (push n)
        //   move.l #n, sx
        // # (stack-set) -or- (stack-ref)
        //   lsl.l #2, sx
        // =>
        // # (push n)
        // # (stack-set) -or- (stack-ref)
        //   move.l n << 2, sx
        get_line(line4);
        print_line(line);
        print_line(line3);
	printf("        move.l  #");
        print_double_push(line2, 1);
        print_comma_and_after(line2);
        putchar('\n');
        get_line(line);
      }
      else {
        print_line(line);
        print_line(line2);
        // line3 might be another push sequence
        copy_to(line3, line);
      }
    }
    else
    if (is_stack_set_n(line)) {
      get_line(line2);
      if (is_store_by_move(line2)) {
        get_line(line3);
        if (is_expr(line3)) {
          get_line(line4);
          if (is_stack_ref_n_match(line4, line)) {
            // # (stack-set-n n)
            //   move.l  dx, ...
            // # (expr ...
            // # (stack-ref-n n)
            //   move.l ..., dx // redundant instruction
            print_line(line);
            print_line(line2);
            print_line(line3);
            print_line(line4);
            get_line(line); // this line is an unnecessary load instruction
            get_line(line);
          }
          else {
            print_line(line);
            print_line(line2);
            print_line(line3);
            copy_to(line4, line);
          }
        }
        else {
          print_line(line);
          print_line(line2);
          copy_to(line3, line);
        }
      }
      else {
        print_line(line);
        copy_to(line2, line);
      }
    }
    else {
      print_line(line);
      get_line(line);
    }
  }

  return;
}

int main() {
  int idx;
  int idx2;

  // argument tokens
  set_pool(TOKEN_ARM,
    'a', 'r', 'm', ' ', ' ', ' ', ' ', ' ',
    ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ');
  set_pool(TOKEN_X86,
    'x', '8', '6', ' ', ' ', ' ', ' ', ' ',
    ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ');
  set_pool(TOKEN_RISCV,
    'r', 'i', 's', 'c', 'v', ' ', ' ', ' ',
    ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ');
  set_pool(TOKEN_M68K,
    'm', '6', '8', 'k', ' ', ' ', ' ', ' ',
    ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ');
  set_pool(TOKEN_AARCH64,
    'a', 'a', 'r', 'c', 'h', '6', '4', ' ',
    ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ');
  set_pool(TOKEN_X32,
    '3', '2', ' ', ' ', ' ', ' ', ' ', ' ',
    ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ');
  set_pool(TOKEN_X64,
    '6', '4', ' ', ' ', ' ', ' ', ' ', ' ',
    ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ');
  set_pool(TOKEN_V6,
    'v', '6', ' ', ' ', ' ', ' ', ' ', ' ',
    ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ');
  set_pool(TOKEN_V7,
    'v', '7', ' ', ' ', ' ', ' ', ' ', ' ',
    ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ');

  // generic simpvir mnemonic tokens
  set_pool(TOKEN_STACK_REF_N,
    '#', ' ', '(', 's', 't', 'a', 'c', 'k',
    '-', 'r', 'e', 'f', '-', 'n', ' ', ' ');
  set_pool(TOKEN_STACK_SET_N,
    '#', ' ', '(', 's', 't', 'a', 'c', 'k',
    '-', 's', 'e', 't', '-', 'n', ' ', ' ');
  set_pool(TOKEN_EXPR,
    '#', ' ', '(', 'e', 'x', 'p', 'r', ' ',
    ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ');
  set_pool(TOKEN_PUSH_0,
    '#', ' ', '(', 'p', 'u', 's', 'h', ' ',
    '0', ')', ' ', ' ', ' ', ' ', ' ', ' ');
  set_pool(TOKEN_PUSH,
    '#', ' ', '(', 'p', 'u', 's', 'h', ' ',
    ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ');
  set_pool(TOKEN_STACK_SET,
    '#', ' ', '(', 's', 't', 'a', 'c', 'k',
    '-', 's', 'e', 't', ')', ' ', ' ', ' ');
  set_pool(TOKEN_STACK_REF,
    '#', ' ', '(', 's', 't', 'a', 'c', 'k',
    '-', 'r', 'e', 'f', ')', ' ', ' ', ' ');

  // arm aarch64 tokens
  set_pool(TOKEN_STR,
    ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
    's', 't', 'r', ' ', ' ', ' ', ' ', ' ');
  set_pool(TOKEN_LDR,
    ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
    'l', 'd', 'r', ' ', ' ', ' ', ' ', ' ');
  set_pool(TOKEN_LSL,
    ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
    'l', 's', 'l', ' ', ' ', ' ', ' ', ' ');
  set_pool(TOKEN_MOVK,
    ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
    'm', 'o', 'v', 'k', ' ', ' ', ' ', ' ');
  set_pool(TOKEN_MOV,
    ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
    'm', 'o', 'v', ' ', ' ', ' ', ' ', ' ');

  // riscv tokens
  set_pool(TOKEN_LI_SX,
    ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
    'l', 'i', ' ', ' ', ' ', ' ', 's', ' ');
  set_pool(TOKEN_SW,
    ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
    's', 'w', ' ', ' ', ' ', ' ', ' ', ' ');
  set_pool(TOKEN_LW,
    ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
    'l', 'w', ' ', ' ', ' ', ' ', ' ', ' ');

  // get arguments then call the appropriate optimize routine

  get_line(line); // first line contains target and version args
//print_line(line);
  idx = line_skip_blanks(line, 0);
  idx = is_x86(line, idx);
  if (idx != 0) {
    idx = line_skip_blanks(line, idx);
    idx = is_32(line, idx);
    if (idx != 0)
      optimize_x86();
    else
      optimize_x86();
  }
  else {
    idx = line_skip_blanks(line, 0);
    idx = is_arm(line, idx);
    if (idx != 0) {
      idx = line_skip_blanks(line, idx);
      idx2 = idx;
      idx = is_v6(line, idx);
      if (idx != 0)
        optimize_arm_v6();
      else {
        idx = line_skip_blanks(line, idx2);
        idx = is_v7(line, idx);
        if (idx != 0)
          optimize_arm_v7();
	else
          optimize_arm_aarch64();
      }
    }
    else {
      idx = line_skip_blanks(line, 0);
      idx = is_riscv(line, idx);
      if (idx != 0)
        optimize_riscv();
      else {
        idx = line_skip_blanks(line, 0);
        idx = is_m68k(line, idx);
        if (idx != 0)
          optimize_m68k();
        else // bad arguments
          optimize_nothing();
      }
    }
  }

  return 0;
}
