/*
 * JOOS is Copyright (C) 1997 Laurie Hendren & Michael I. Schwartzbach
 *
 * Reproduction of all or part of this software is permitted for
 * educational or research use on condition that this copyright notice is
 * included in any copy. This software comes with no warranty of any
 * kind. In no event will the authors be liable for any damages resulting from
 * use of this software.
 *
 * email: hendren@cs.mcgill.ca, mis@brics.dk
 */

#include <stdio.h>
#include <string.h>
#include "memory.h"
#include "optimize.h"

int is_nop(CODE *c)
{ if (c==NULL) return 0;
  return c->kind==nopCK;
}

int is_i2c(CODE *c)
{ if (c==NULL) return 0;
  return c->kind==i2cCK;
}

int is_new(CODE *c, char **arg)
{ if (c==NULL) return 0;
  if (c->kind == newCK) {
     (*arg) = c->val.newC;
     return 1;
  } else {
     return 0;
  }
}

int is_instanceof(CODE *c, char **arg)
{ if (c==NULL) return 0;
  if (c->kind == instanceofCK) {
     (*arg) = c->val.instanceofC;
     return 1;
  } else {
     return 0;
  }
}

int is_checkcast(CODE *c, char **arg)
{ if (c==NULL) return 0;
  if (c->kind == checkcastCK) {
     (*arg) = c->val.checkcastC;
     return 1;
  } else {
     return 0;
  }
}

int is_imul(CODE *c)
{ if (c==NULL) return 0;
  return c->kind==imulCK;
}

int is_ineg(CODE *c)
{ if (c==NULL) return 0;
  return c->kind==inegCK;
}

int is_irem(CODE *c)
{ if (c==NULL) return 0;
  return c->kind==iremCK;
}

int is_isub(CODE *c)
{ if (c==NULL) return 0;
  return c->kind==isubCK;
}

int is_idiv(CODE *c)
{ if (c==NULL) return 0;
  return c->kind==idivCK;
}

int is_iadd(CODE *c)
{ if (c==NULL) return 0;
  return c->kind==iaddCK;
}

int is_iinc(CODE *c, int *offset, int *amount)
{ if (c==NULL) return 0;
  if (c->kind == iincCK) {
     (*offset) = c->val.iincC.offset;
     (*amount) = c->val.iincC.amount;
     return 1;
  } else {
     return 0;
  }
}

int is_label(CODE *c, int *label)
{ if (c==NULL) return 0;
  if (c->kind == labelCK) {
     (*label) = c->val.labelC;
     return 1;
  } else {
     return 0;
  }
}

int is_goto(CODE *c, int *label)
{ if (c==NULL) return 0;
  if (c->kind == gotoCK) {
     (*label) = c->val.gotoC;
     return 1;
  } else {
     return 0;
  }
}

int is_ifeq(CODE *c, int *label)
{ if (c==NULL) return 0;
  if (c->kind == ifeqCK) {
     (*label) = c->val.ifeqC;
     return 1;
  } else {
     return 0;
  }
}

int is_ifne(CODE *c, int *label)
{ if (c==NULL) return 0;
  if (c->kind == ifneCK) {
     (*label) = c->val.ifneC;
     return 1;
  } else {
     return 0;
  }
}

int is_if_acmpeq(CODE *c, int *label)
{ if (c==NULL) return 0;
  if (c->kind == if_acmpeqCK) {
     (*label) = c->val.if_acmpeqC;
     return 1;
  } else {
     return 0;
  }
}

int is_if_acmpne(CODE *c, int *label)
{ if (c==NULL) return 0;
  if (c->kind == if_acmpneCK) {
     (*label) = c->val.if_acmpneC;
     return 1;
  } else {
     return 0;
  }
}

int is_ifnull(CODE *c, int *label)
{ if (c==NULL) return 0;
  if (c->kind == ifnullCK) {
     (*label) = c->val.ifnullC;
     return 1;
  } else {
     return 0;
  }
}

int is_ifnonnull(CODE *c, int *label)
{ if (c==NULL) return 0;
  if (c->kind == ifnonnullCK) {
     (*label) = c->val.ifnonnullC;
     return 1;
  } else {
     return 0;
  }
}

int is_if_icmpeq(CODE *c, int *label)
{ if (c==NULL) return 0;
  if (c->kind == if_icmpeqCK) {
     (*label) = c->val.if_icmpeqC;
     return 1;
  } else {
     return 0;
  }
}

int is_if_icmpgt(CODE *c, int *label)
{ if (c==NULL) return 0;
  if (c->kind == if_icmpgtCK) {
     (*label) = c->val.if_icmpgtC;
     return 1;
  } else {
     return 0;
  }
}

int is_if_icmplt(CODE *c, int *label)
{ if (c==NULL) return 0;
  if (c->kind == if_icmpltCK) {
     (*label) = c->val.if_icmpltC;
     return 1;
  } else {
     return 0;
  }
}

int is_if_icmple(CODE *c, int *label)
{ if (c==NULL) return 0;
  if (c->kind == if_icmpleCK) {
     (*label) = c->val.if_icmpleC;
     return 1;
  } else {
     return 0;
  }
}

int is_if_icmpge(CODE *c, int *label)
{ if (c==NULL) return 0;
  if (c->kind == if_icmpgeCK) {
     (*label) = c->val.if_icmpgeC;
     return 1;
  } else {
     return 0;
  }
}

int is_if_icmpne(CODE *c, int *label)
{ if (c==NULL) return 0;
  if (c->kind == if_icmpneCK) {
     (*label) = c->val.if_icmpneC;
     return 1;
  } else {
     return 0;
  }
}

int is_ireturn(CODE *c)
{ if (c==NULL) return 0;
  return c->kind==ireturnCK;
}

int is_areturn(CODE *c)
{ if (c==NULL) return 0;
  return c->kind==areturnCK;
}

int is_return(CODE *c)
{ if (c==NULL) return 0;
  return c->kind==returnCK;
}

int is_aload(CODE *c, int *arg)
{ if (c==NULL) return 0;
  if (c->kind == aloadCK) {
     (*arg) = c->val.aloadC;
     return 1;
  } else {
     return 0;
  }
}

int is_astore(CODE *c, int *arg)
{ if (c==NULL) return 0;
  if (c->kind == astoreCK) {
     (*arg) = c->val.astoreC;
     return 1;
  } else {
     return 0;
  }
}

int is_iload(CODE *c, int *arg)
{ if (c==NULL) return 0;
  if (c->kind == iloadCK) {
     (*arg) = c->val.iloadC;
     return 1;
  } else {
     return 0;
  }
}

int is_istore(CODE *c, int *arg)
{ if (c==NULL) return 0;
  if (c->kind == istoreCK) {
     (*arg) = c->val.istoreC;
     return 1;
  } else {
     return 0;
  }
}

int is_dup(CODE *c)
{ if (c==NULL) return 0;
  return c->kind==dupCK;
}

int is_pop(CODE *c)
{ if (c==NULL) return 0;
  return c->kind==popCK;
}

int is_swap(CODE *c)
{ if (c==NULL) return 0;
  return c->kind==swapCK;
}

int is_ldc_int(CODE *c, int *arg)
{ if (c==NULL) return 0;
  if (c->kind == ldc_intCK) {
     (*arg) = c->val.ldc_intC;
     return 1;
  } else {
     return 0;
  }
}

int is_ldc_string(CODE *c, char **arg)
{ if (c==NULL) return 0;
  if (c->kind == ldc_stringCK) {
     (*arg) = c->val.ldc_stringC;
     return 1;
  } else {
     return 0;
  }
}

int is_aconst_null(CODE *c)
{ if (c==NULL) return 0;
  return c->kind==aconst_nullCK;
}

int is_getfield(CODE *c, char **arg)
{ if (c==NULL) return 0;
  if (c->kind == getfieldCK) {
     (*arg) = c->val.getfieldC;
     return 1;
  } else {
     return 0;
  }
}

int is_putfield(CODE *c, char **arg)
{ if (c==NULL) return 0;
  if (c->kind == putfieldCK) {
     (*arg) = c->val.putfieldC;
     return 1;
  } else {
     return 0;
  }
}

int is_invokevirtual(CODE *c, char **arg)
{ if (c==NULL) return 0;
  if (c->kind == invokevirtualCK) {
     (*arg) = c->val.invokevirtualC;
     return 1;
  } else {
     return 0;
  }
}

int is_invokenonvirtual(CODE *c, char **arg)
{ if (c==NULL) return 0;
  if (c->kind == invokenonvirtualCK) {
     (*arg) = c->val.invokenonvirtualC;
     return 1;
  } else {
     return 0;
  }
}

int is_empty(CODE *c)
{ return c==NULL;
}

int is_simplepush(CODE *c)
{ if (c==NULL) return 0;
  return (c->kind==aloadCK) || (c->kind==iloadCK) || (c->kind==ldc_intCK) || 
         (c->kind==ldc_stringCK) || (c->kind==aconst_nullCK);
}

int uses_label(CODE *c, int *label)
{ if (c==NULL) return 0;
  return is_goto(c,label) || is_ifeq(c,label) || is_ifne(c,label) || 
         is_if_acmpeq(c,label) || is_if_acmpne(c,label) || 
         is_ifnull(c,label) || is_ifnonnull(c,label) ||
         is_if_icmpeq(c,label) || is_if_icmpgt(c,label) || 
         is_if_icmplt(c,label) || is_if_icmple(c,label) || 
         is_if_icmpge(c,label) || is_if_icmpne(c,label);
}

LABEL *currentlabels;

CODE *next(CODE *c)
{ if (c==NULL) return NULL;
  return c->next;
}

CODE *destination(int label)
{ return currentlabels[label].position;
}

int copylabel(int label)
{ currentlabels[label].sources++;
  return label;
}

void droplabel(int label)
{ currentlabels[label].sources--;
}

int deadlabel(int label)
{ return currentlabels[label].sources==0;
}

int uniquelabel(int label)
{ return currentlabels[label].sources==1;
}

int replace(CODE **c, int k, CODE *r)
{ CODE *p;
  int i;
  p = *c;
  for (i=0; i<k; i++) p=p->next;
  if (r==NULL) {
     *c = p;
  } else {
     *c = r;
     while (r->next!=NULL) r=r->next;
     r->next = p;
  }
  return 1;
}

int simplify_multiplication_right(CODE **c)
{ int x,k;
  if (is_iload(*c,&x) && 
      is_ldc_int(next(*c),&k) && 
      is_imul(next(next(*c)))) {
     if (k==0) return replace(c,3,makeCODEldc_int(0,NULL));
     else if (k==1) return replace(c,3,makeCODEiload(x,NULL));
     else if (k==2) return replace(c,3,makeCODEiload(x,
                                       makeCODEdup(
                                       makeCODEiadd(NULL))));
     return 0;
  }
  return 0;
}

int simplify_astore(CODE **c)
{ int x;
  if (is_dup(*c) &&
      is_astore(next(*c),&x) &&
      is_pop(next(next(*c)))) {
     return replace(c,3,makeCODEastore(x,NULL));
  }
  return 0;
}

int positive_increment(CODE **c)
{ int x,y,k;
  if (is_iload(*c,&x) &&
      is_ldc_int(next(*c),&k) &&
      is_iadd(next(next(*c))) &&
      is_istore(next(next(next(*c))),&y) &&
      x==y && 0<=k && k<=127) {
     return replace(c,4,makeCODEiinc(x,k,NULL));
  }
  return 0;
}

int simplify_goto_goto(CODE **c)
{ int l1,l2;
  if (is_goto(*c,&l1) && is_goto(next(destination(l1)),&l2) && l1>l2) {
     droplabel(l1);
     copylabel(l2);
     return replace(c,1,makeCODEgoto(l2,NULL));
  }
  return 0;
}

typedef int(*OPTI)(CODE **);

#define OPTS 4

OPTI optimization[OPTS] = {simplify_multiplication_right,
                           simplify_astore,
                           positive_increment,
                           simplify_goto_goto};

int optiCHANGE;

void optiCODEtraverse(CODE **c)
{ int i,change;
  change = 1;
  if (*c!=NULL) {
     while (change) {
       change = 0;
       for (i=0; i<OPTS; i++) {
           change = change | optimization[i](c);
       }
       optiCHANGE = optiCHANGE || change;
     }
     if (*c!=NULL) optiCODEtraverse(&((*c)->next));
  }
} 

void optiCODE(CODE **c)
{ optiCHANGE = 1;
  while (optiCHANGE) {
    optiCHANGE = 0;
    optiCODEtraverse(c);
  }
}

void optiPROGRAM(PROGRAM *p)
{ if (p!=NULL) {
     optiPROGRAM(p->next);
     optiCLASSFILE(p->classfile);
  }
}

void optiCLASSFILE(CLASSFILE *c)
{ if (c!=NULL) {
     optiCLASSFILE(c->next);
     optiCLASS(c->class);
  }
}

void optiCLASS(CLASS *c)
{ if (!c->external) {
     optiCONSTRUCTOR(c->constructors);
     optiMETHOD(c->methods);
  }
}

void optiCONSTRUCTOR(CONSTRUCTOR *c)
{ if (c!=NULL) {
     optiCONSTRUCTOR(c->next);
     currentlabels = c->labels;
     optiCODE(&c->opcodes);
  }
}

void optiMETHOD(METHOD *m)
{ if (m!=NULL) {
     optiMETHOD(m->next);
     currentlabels = m->labels;
     optiCODE(&m->opcodes);
  }
}
