// $Id: Xcons.java,v 1.15 2000/12/12 03:19:32 msato Exp $
// $RWC_Release: Omni-1.6 $
// $RWC_Copyright:
//  Omni Compiler Software Version 1.5-1.6
//  Copyright (C) 2002 PC Cluster Consortium
//  
//  This software is free software; you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License version
//  2.1 published by the Free Software Foundation.
//  
//  Omni Compiler Software Version 1.0-1.4
//  Copyright (C) 1999, 2000, 2001.
//   Tsukuba Research Center, Real World Computing Partnership, Japan.
//  
//  Please check the Copyright and License information in the files named
//  COPYRIGHT and LICENSE under the top  directory of the Omni Compiler
//  Software release kit.
//  
//  
//  $
package exc.object;

//
// class for static constructor
// 
public class Xcons {
  public static XobjString Symbol(int code,Xtype type,String value){
    return new XobjString(code,type,value);
  }
  public static XobjString Symbol(int code,String value){
    return new XobjString(code,value);
  }
  public static XobjString StringConstant(String value){
    return new XobjString(Xcode.STRING_CONSTANT,
			  BasicType.stringType, value);
  }
  public static XobjInt Int(int code,Xtype type,int value){
    return new XobjInt(code,type,value);
  }
  public static XobjInt Int(int code,int value){
    return new XobjInt(code,value);
  }
  public static XobjInt IntConstant(int value){
    return new XobjInt(Xcode.INT_CONSTANT,BasicType.intType,value);
  }
  public static XobjLong Long(int code,Xtype type,long l){
    return new XobjLong(code,type,l);
  }
  public static XobjLong Long(int code,Xtype type,int high,int low){
    return new XobjLong(code,type,high,low);
  }
  public static XobjLong Float(int code,Xtype type,double d){
    return new XobjLong(code,type,d);
  }

  public static XobjList List(){
    return new XobjList();
  }
  public static XobjList List(int code,Xtype type){
    return new XobjList(code,type);
  }
  public static XobjList List(int code,Xtype type,XobjArgs a){
    return new XobjList(code,type,a);
  }
  public static XobjList List(int code,Xtype type,Xobject a1){
    return new XobjList(code,type,a1);
  }
  public static XobjList List(int code,Xtype type,Xobject a1,Xobject a2){
    return new XobjList(code,type,a1,a2);
  }
  public static XobjList List(int code,Xtype type,Xobject a1,Xobject a2,
			      Xobject a3){
    return new XobjList(code,type,a1,a2,a3);
  }
  public static XobjList List(int code,Xtype type,Xobject a1,Xobject a2,
			      Xobject a3,Xobject a4){
    return new XobjList(code,type,a1,a2,a3,a4);
  }
  public static XobjList List(int code,Xtype type,Xobject a1,Xobject a2,
			      Xobject a3,Xobject a4,Xobject a5){
    return new XobjList(code,type,a1,a2,a3,a4,a5);
  }

  public static XobjList List(int code){
    return new XobjList(code);
  }
  public static XobjList List(int code,XobjArgs a){
    return new XobjList(code,a);
  }
  public static XobjList List(int code,Xobject a1){
    return new XobjList(code,a1);
  }
  public static XobjList List(int code,Xobject a1,Xobject a2){
    return new XobjList(code,a1,a2);
  }
  public static XobjList List(int code,Xobject a1,Xobject a2,Xobject a3){
    return new XobjList(code,a1,a2,a3);
  }
  public static XobjList List(int code,Xobject a1,Xobject a2,Xobject a3,Xobject a4){
    return new XobjList(code,a1,a2,a3,a4);
  }
  public static XobjList List(int code,Xobject a1,Xobject a2,Xobject a3,
			      Xobject a4,Xobject a5){
    return new XobjList(code,a1,a2,a3,a4,a5);
  }

  public static XobjList List(XobjArgs a){
    return new XobjList(Xcode.LIST,a);
  }
  public static XobjList List(Xobject a1){
    return new XobjList(Xcode.LIST,a1);
  }
  public static XobjList List(Xobject a1,Xobject a2){
    return new XobjList(Xcode.LIST,a1,a2);
  }
  public static XobjList List(Xobject a1,Xobject a2,Xobject a3){
    return new XobjList(Xcode.LIST,a1,a2,a3);
  }
  public static XobjList List(Xobject a1,Xobject a2,Xobject a3,Xobject a4){
    return new XobjList(Xcode.LIST,a1,a2,a3,a4);
  }
  public static XobjList List(Xobject a1,Xobject a2,Xobject a3,Xobject a4,
			      Xobject a5){
    return new XobjList(Xcode.LIST,a1,a2,a3,a4,a5);
  }

  public static XobjList emptyList(){
    return new XobjList(Xcode.LIST);
  }
  public static XobjList emptyIdList(){
    return new XobjList(Xcode.ID_LIST);
  }
  public static Ident Ident(String name,int stg_class,Xtype type, Xobject v){
    return new Ident(name,stg_class,type,v);
  }

  //
  // Constructor for expression
  // 	making constant folding (not implmented)
  //
  // if type == null, don't care data type!
  public static Xobject PointerRef(Xobject x){
    Xtype type = x.Type();
    if(type != null && type.isPointer()) type = type.getRef();
    switch(x.Opcode()){
    case Xcode.LVAR_ADDR:
      return Xcons.Symbol(Xcode.LVAR,type,x.getSym());
    case Xcode.PARAM_ADDR:
      return Xcons.Symbol(Xcode.PARAM_VAR,type,x.getSym());
    case Xcode.VAR_ADDR:
      return Xcons.Symbol(Xcode.VAR,type,x.getSym());

    case Xcode.FPARAM_VAR_ADDR:
      return Xcons.Symbol(Xcode.FPARAM_VAR,type,x.getSym());
    case Xcode.FCOMM_VAR_ADDR:
      return Xcons.List(Xcode.FCOMM_VAR,type,x.left(),x.right());
      
    case Xcode.FUNC_ADDR:
      fatal("PointerRef(): FUNC_ADDR");
      break;
    case Xcode.ADDR_OF:
      return x.operand();
    default:
      if(type == null || x.Type().isPointer())
	return Xcons.List(Xcode.POINTER_REF,type,x);
      else {
	fatal("PointerRef(): not Pointer");
      }
    }
    return null;
  }

  public static Xobject memberAddr(Xobject x,String member_name){
    Xtype type = x.Type();
    if(!type.isPointer()) fatal("memberAddr: not Pointer");
    type = type.getRef();
    if(!type.isStruct() && !type.isUnion())
      fatal("memberAddr: not struct/union");
    Xobject members = type.getMemberList(); // id_list
    for(XobjArgs a = members.getArgs(); a != null; a = a.nextArgs()){
      Ident mid = (Ident)a.getArg();
      if(member_name.equals(mid.getName())){
	type = mid.Type();
	return List(Xcode.MEMBER_ADDR,Xtype.Pointer(type),
		    x,Symbol(Xcode.IDENT,type,mid.getName()));
      }
    }
    fatal("memberAddr: member name is not found: "+member_name);
    return null;
  }

  public static Xobject memberAddr(Xobject x,Ident mid){
    Xtype type = x.Type();
    if(!type.isPointer()) fatal("memberAddr: not Pointer");
    type = type.getRef();
    if(!type.isStruct() && !type.isUnion())
      fatal("memberAddr: not struct/union");
    type = mid.Type();
    return List(Xcode.MEMBER_ADDR,Xtype.Pointer(type),
		x,Symbol(Xcode.IDENT,type,mid.getName()));
  }
    
  // don't reduce
  public static Xobject AddrOf(Xobject v){
    Xtype type = v.Type();
    if(type != null) type = Xtype.Pointer(type);
    return Xcons.List(Xcode.ADDR_OF,type,v);
  }

  public static Xobject AddrOfVar(Xobject x){
    switch(x.Opcode()){
    case Xcode.LVAR:
      return Xcons.Symbol(Xcode.LVAR_ADDR,
			  Xtype.Pointer(x.Type()),x.getSym());
    case Xcode.PARAM_VAR:
      return Xcons.Symbol(Xcode.PARAM_ADDR,
			  Xtype.Pointer(x.Type()),x.getSym());
    case Xcode.VAR:
      return Xcons.Symbol(Xcode.VAR_ADDR,
			  Xtype.Pointer(x.Type()),x.getSym());
    case Xcode.FPARAM_VAR:
      return Xcons.Symbol(Xcode.FPARAM_VAR_ADDR,
			  Xtype.Pointer(x.Type()),x.getSym());
    case Xcode.FCOMM_VAR:
      return Xcons.List(Xcode.FCOMM_VAR_ADDR,
			Xtype.Pointer(x.Type()),x.left(),x.right());

    default:
      fatal("AddrOfVar: not Variable");
      return null;
    }
  }

  public static Xobject Set(Xobject lv,Xobject rv){
    return Xcons.List(Xcode.ASSIGN_EXPR,lv.Type(),lv,rv);
  }

  public static Xobject Cast(Xtype t,Xobject v){
    return Xcons.List(Xcode.CAST_EXPR,t,v);
  }

  public static Xobject functionCall(Xobject f,Xobject args){
    Xtype t;
    if(f.Opcode() == Xcode.FUNC_ADDR) t = f.Type().getRef();
    else t = f.Type();
    if(!t.isFunction()) fatal("fuctionCall: not Function");
    return Xcons.List(Xcode.FUNCTION_CALL,t.getRef(),f,args);
  }

  public static Xobject binaryOp(int code,Xobject x,Xobject y){
    Xtype t,lt,rt;
    t = null;
    lt = x.Type();
    rt = y.Type();
    switch(code){
    case Xcode.PLUS_EXPR:
    case Xcode.MINUS_EXPR:
      if(y.isZeroConstant()) return x;
      if(code == Xcode.PLUS_EXPR && x.isZeroConstant()) return y;
      if(lt.isPointer() && rt.isIntegral()){
	t = lt; break;
      } 
      if(rt.isPointer() && lt.isIntegral()){
	t = rt; break;
      } 
      if(lt.isIntegral() && rt.isFloating()){
	t = lt; break;
      }
      if(rt.isIntegral() && lt.isFloating()){
	t = rt; break;
      }
      if(lt.isIntegral() && rt.isIntegral()){
        t = ConversionIntegral(lt,rt); break;
      }
      if(lt.isFloating() && rt.isFloating()){
	t = BasicType.Conversion(lt,rt); break;
      } 
      fatal("BinaryOp: bad type");
      
    case Xcode.MUL_EXPR:
      if(y.isZeroConstant()) return IntConstant(0); /* x*0 = 0 */
      if(x.isOneConstant()) return y;		    /* 1*y = y */
    case Xcode.DIV_EXPR:
      if(x.isZeroConstant()) return IntConstant(0); /* 0*y = 0, 0/y = 0 */
      if(y.isOneConstant()) return x;		/* x*1 = x, x/1 = x */

      if(lt.isIntegral() && rt.isFloating()){
	t = lt; break;
      }
      if(rt.isIntegral() && lt.isFloating()){
	t = rt; break;
      }
      if(lt.isIntegral() && rt.isIntegral()){
        t = ConversionIntegral(lt,rt); break;
      }
      if(lt.isFloating() && rt.isFloating()){
	t = BasicType.Conversion(lt,rt); break;
      } 
      fatal("BinaryOp: bad type");

    case Xcode.MOD_EXPR:
      if(y.isOneConstant()) return x;
      if(lt.isIntegral() && rt.isIntegral()){
	t = ConversionIntegral(lt,rt); break;
      }
      fatal("BinaryOp: bad type");
      
    case Xcode.BIT_OR_EXPR:
      if(y.isZeroConstant()) return x;
      if(x.isZeroConstant()) return y;
      if(lt.isIntegral() && rt.isIntegral()){
	t = ConversionIntegral(lt,rt); break;
      }
      fatal("BinaryOp: bad type");

    case Xcode.BIT_AND_EXPR:
      if(y.isZeroConstant() || x.isZeroConstant())
	return IntConstant(0);
      if(lt.isIntegral() && rt.isIntegral()){
	t = ConversionIntegral(lt,rt); break;
      }
      fatal("BinaryOp: bad type");

    case Xcode.BIT_XOR_EXPR:
      if(lt.isIntegral() && rt.isIntegral()){
	t = ConversionIntegral(lt,rt); break;
      }
      fatal("BinaryOp: bad type");

    case Xcode.LOG_GE_EXPR:
    case Xcode.LOG_GT_EXPR:
    case Xcode.LOG_LE_EXPR:
    case Xcode.LOG_LT_EXPR:
    case Xcode.LOG_EQ_EXPR:
    case Xcode.LOG_NEQ_EXPR:
      if(lt.isIntegral() && rt.isFloating()){
	t = BasicType.intType; break;
      }
      if(rt.isIntegral() && lt.isFloating()){
	t = BasicType.intType; break;
      }
      if(lt.isIntegral() && rt.isIntegral()){
	t = BasicType.intType; break;
      }
      if(lt.isFloating() && rt.isFloating()){
	t = BasicType.intType; break;
      } 
      fatal("BinaryOp: bad type");

    case Xcode.LOG_AND_EXPR:
    case Xcode.LOG_OR_EXPR:
      t = BasicType.intType; /* ok ? */
      break;

    case Xcode.LSHIFT_EXPR:
    case Xcode.RSHIFT_EXPR:
      if(lt.isIntegral() && rt.isIntegral()){
	t =lt; break;
      }
      fatal("BinaryOp: bad type");
    default:
      fatal("BinaryOp: bad code");
    }
    return Xcons.List(code,t,x,y);
  }

  public static Xobject asgOp(int code,Xobject x,Xobject y){
    Xtype t,lt,rt;
    t = null;
    lt = x.Type();
    rt = y.Type();
    switch(code){
    case Xcode.ASG_PLUS_EXPR:
    case Xcode.ASG_MINUS_EXPR:
      if(lt.isPointer() && rt.isIntegral()){
	t = lt; break;
      } 
      if(rt.isPointer() && lt.isIntegral()){
	t = rt; break;
      } 
    case Xcode.ASG_MUL_EXPR:
    case Xcode.ASG_DIV_EXPR:
      if(lt.isIntegral() && rt.isFloating()){
	t = lt; break;
      }
      if(rt.isIntegral() && lt.isFloating()){
	t = rt; break;
      }
      if(lt.isIntegral() && rt.isIntegral()){
	t = ConversionIntegral(lt,rt); break;
      }
      if(lt.isFloating() && rt.isFloating()){
	t = BasicType.Conversion(lt,rt); break;
      } 
      fatal("AgnOp: bad type");

    case Xcode.ASG_MOD_EXPR:
    case Xcode.ASG_BIT_OR_EXPR:
    case Xcode.ASG_BIT_AND_EXPR:
    case Xcode.ASG_BIT_XOR_EXPR:
      if(lt.isIntegral() && rt.isIntegral()){
	t = ConversionIntegral(lt,rt); break;
      }
      fatal("AsgOp: bad type");

    case Xcode.ASG_LSHIFT_EXPR:
    case Xcode.ASG_RSHIFT_EXPR:
      if(lt.isIntegral() && rt.isIntegral()){
	t =lt; break;
      }
      fatal("AsgOp: bad type");
    default:
      fatal("AsgOp: bad code");
    }
    return Xcons.List(code,t,x,y);
  }

  public static int unAsgOpcode(int code){
    switch(code){
    case Xcode.POST_INCR_EXPR:
    case Xcode.ASG_PLUS_EXPR:
      return Xcode.PLUS_EXPR;
    case Xcode.POST_DECR_EXPR:
    case Xcode.ASG_MINUS_EXPR:
      return Xcode.MINUS_EXPR;
    case Xcode.ASG_MUL_EXPR:
      return Xcode.MUL_EXPR;
    case Xcode.ASG_DIV_EXPR:
      return Xcode.DIV_EXPR;
    case Xcode.ASG_MOD_EXPR:
      return Xcode.MOD_EXPR;
    case Xcode.ASG_BIT_OR_EXPR:
      return Xcode.BIT_OR_EXPR;
    case Xcode.ASG_BIT_AND_EXPR:
      return Xcode.BIT_AND_EXPR;
    case Xcode.ASG_BIT_XOR_EXPR:
      return Xcode.BIT_XOR_EXPR;
    case Xcode.ASG_LSHIFT_EXPR:
      return Xcode.LSHIFT_EXPR;
    case Xcode.ASG_RSHIFT_EXPR:
      return Xcode.RSHIFT_EXPR;
    default:
      fatal("unAsgOpcode: bad code, "+code);
      return 0;
    }
  }

  public static Xobject unaryOp(int code,Xobject x){
    Xtype t = x.Type();
    switch(code){
    case Xcode.UNARY_MINUS_EXPR:
      /* if(x.isZeroConstant()) return IntConstant(0); */
      if(x.Opcode() == Xcode.INT_CONSTANT)
	return Int(Xcode.INT_CONSTANT,x.Type(),-x.getInt());
      if(t.isIntegral() || t.isFloating()) break;
      fatal("UnaryOp: bad type");
    case Xcode.LOG_NOT_EXPR:
      if(t.isIntegral() || t.isFloating()){
	t = BasicType.intType;
	break;
      }
      fatal("UnaryOp: bad type");
    case Xcode.BIT_NOT_EXPR:
      if(t.isIntegral()) break;
      fatal("UnaryOp: bad type");
    default:
      fatal("BinaryOp: bad code");
    }
    return Xcons.List(code,t,x);
  }

  static Xtype ConversionIntegral(Xtype lt, Xtype rt) {
    if(lt.isEnum() && rt.isEnum()) {
      return lt;
    } 
    if(lt.isEnum() && !rt.isEnum()) {
      if (lt.getSize() < rt.getSize()) {
	return rt;
      } else {
	return lt;
      }
    }
    if(!lt.isEnum() && rt.isEnum()) {
      if (rt.getSize() < lt.getSize()) {
	return rt;
      } else {
	return lt;
      }
    }
    return BasicType.Conversion(lt,rt);
  }

  /* constant folding. only simple & integer case is supported */
  public static Xobject Reduce(Xobject x){
    Xobject r,l;
    if(!x.Type().isIntegral()) return x;  // only for integer

    switch(x.Opcode()){
    case Xcode.UNARY_MINUS_EXPR:
      l = Reduce(x);
      if(l.isIntConstant())
	return Int(Xcode.INT_CONSTANT,x.Type(),-l.getInt());
      return l;
    case Xcode.PLUS_EXPR:
      l = Reduce(x.left());
      r = Reduce(x.right());
      if(l.isIntConstant() && r.isIntConstant())
	return Int(Xcode.INT_CONSTANT,x.Type(),l.getInt()+r.getInt());
      if(r.isZeroConstant()) return l; /* l+0 = l */
      if(l.isZeroConstant()) return r; /* 0+r=r */
      break;
    case Xcode.MINUS_EXPR:
      l = Reduce(x.left());
      r = Reduce(x.right());
      if(l.isIntConstant() && r.isIntConstant())
	return Int(Xcode.INT_CONSTANT,x.Type(),l.getInt()-r.getInt());
      if(r.isZeroConstant()) return l; /* l-0 = l */
      if(l.isZeroConstant()) 
	return Xcons.List(Xcode.UNARY_MINUS_EXPR,x.Type(),r); /* 0-r=-r */
      break;
    case Xcode.MUL_EXPR:
      l = Reduce(x.left());
      r = Reduce(x.right());
      if(l.isIntConstant() && r.isIntConstant())
	return Int(Xcode.INT_CONSTANT,x.Type(),l.getInt()*r.getInt());
      if(l.isZeroConstant() || r.isZeroConstant())
	return IntConstant(0);
      if(l.isOneConstant()) return r;
      if(r.isOneConstant()) return l;
      break;
    case Xcode.DIV_EXPR:
      l = Reduce(x.left());
      r = Reduce(x.right());
      if(r.isZeroConstant()) break;	// avoid error
      if(l.isIntConstant() && r.isIntConstant())
	return Int(Xcode.INT_CONSTANT,x.Type(),l.getInt()/r.getInt());
      if(l.isZeroConstant()) return IntConstant(0); /* 0/r = 0 */
      if(r.isOneConstant()) return l;		/* l/1 = l */
      break;

    default:
      return x;	// not reduce 
    }
    if(l != x.left() || r != x.right()) 
      return Xcons.List(x.Opcode(),x.Type(),l,r);
    else return x;
  }

  static void fatal(String msg){
    System.err.println("Fatal Xcons:"+msg);
    Thread.dumpStack();
    System.exit(1);
  }
}


