/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
/* vi: set ts=2 sw=2 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef TYPEANALYZER_H_
#define TYPEANALYZER_H_

namespace halfmoon {
using avmplus::MethodFrame;

// typedefs for stub signatures
typedef int32_t  (*Stub_I_I)(MethodFrame*, int32_t);
typedef int32_t  (*Stub_I_U)(MethodFrame*, uint32_t);
typedef uint32_t (*Stub_U_I)(MethodFrame*, int32_t);
typedef double   (*Stub_D_D)(MethodFrame*, double);
typedef BoolKind (*Stub_D_B)(MethodFrame*, double);
typedef int32_t  (*Stub_D_I)(MethodFrame*, double);
typedef double   (*Stub_DD_D)(MethodFrame*, double, double);
typedef int32_t  (*Stub_II_I)(MethodFrame*, int32_t, int32_t);
typedef uint32_t (*Stub_II_U)(MethodFrame*, int32_t, int32_t);
typedef BoolKind (*Stub_II_B)(MethodFrame*, int32_t, int32_t);
typedef BoolKind (*Stub_BB_B)(MethodFrame*, BoolKind, BoolKind);
typedef BoolKind (*Stub_UU_B)(MethodFrame*, uint32_t, uint32_t);
typedef BoolKind (*Stub_DD_B)(MethodFrame*, double, double);

// Functions to invoke stubs for constant folding
const Type* callstub(Lattice*, const Type*, Stub_I_I);
const Type* callstub(Lattice* l, const Type* c, Stub_I_U stub);
const Type* callstub(Lattice* l, const Type* c, Stub_U_I stub);
const Type* callstub(Lattice* l, const Type* c, Stub_D_D stub);
const Type* callstub(Lattice* l, const Type* c, Stub_D_B stub);
const Type* callstub(Lattice* l, const Type* c1, const Type* c2, Stub_II_I stub);
const Type* callstub(Lattice* l, const Type* c1, const Type* c2, Stub_DD_D stub);
const Type* callstub(Lattice* l, const Type* c1, const Type* c2, Stub_II_U stub);
const Type* callstub(Lattice* l, const Type* c1, const Type* c2, Stub_II_B stub);
const Type* callstub(Lattice* l, const Type* c1, const Type* c2, Stub_UU_B stub);
const Type* callstub(Lattice* l, const Type* c1, const Type* c2, Stub_DD_B stub);
const Type* callstub(Lattice* l, const Type* c1, const Type* c2, Stub_BB_B stub);

/// TypeAnalyzer computes the type effect of each HR opcode.
/// TODO: we could use do_default() to set the return type for primitive
/// instructions from a table generated by templates.py, then override
/// functions when we can do something smarter.
///
class TypeAnalyzer: public KindAdapter<TypeAnalyzer, void> {
public:
  TypeAnalyzer(InstrGraph*);

  /// Evaluate and set the type of each def on instr.
  ///
  void computeTypes(Instr*);

public: // dispatch() adapter methods.
  void do_default(Instr*);
  void doTemplateInstr(Instr*);
  void do_start(StartInstr*) { } // has root defs only
  void do_template(StartInstr*) { } // has root defs only
  void do_catchblock(CatchBlockInstr*) { } // has root defs only
  void do_label(LabelInstr*);
  void do_arm(ArmInstr*);
  void do_const(ConstantExpr*) { } // polymorphic constant
  void do_abc_finddef(BinaryStmt*);
  void do_callmethod(CallStmt2*);
  void do_callinterface(CallStmt2*);
  void do_callstatic(CallStmt2*);
  void do_newinstance(UnaryExpr*);
  void do_toprimitive(UnaryStmt*);
  void do_string2atom(UnaryExpr* i) { doChangeModel(i, kModelAtom); }
  void do_int2atom(UnaryExpr* i) { doChangeModel(i, kModelAtom); }
  void do_uint2atom(UnaryExpr* i) { doChangeModel(i, kModelAtom); }
  void do_double2atom(UnaryExpr* i) { doChangeModel(i, kModelAtom); }
  void do_scriptobject2atom(UnaryExpr* i) { doChangeModel(i, kModelAtom); }
  void do_bool2atom(UnaryExpr* i) { doChangeModel(i, kModelAtom); }
  void do_ns2atom(UnaryExpr* i) { doChangeModel(i, kModelAtom); }
  void do_atom2scriptobject(UnaryExpr* i) { doChangeModel(i, kModelScriptObject); }
  void do_atom2ns(UnaryExpr* i) { doChangeModel(i, kModelNamespace); }
  void do_atom2bool(UnaryExpr* i) { doChangeModel(i, kModelInt); }
  void do_atom2string(UnaryExpr* i) { doChangeModel(i, kModelString); }
  void do_i2d(UnaryExpr* i) { doChangeModel(i, kModelDouble); }
  void do_u2d(UnaryExpr* i) { doChangeModel(i, kModelDouble); }
  void do_d2i(UnaryExpr* i) { doChangeModel(i, kModelInt); }
  void do_d2u(UnaryExpr* i) { doChangeModel(i, kModelInt); }
  void do_caststring(UnaryStmt* i) { doCoerceInstr(i, lattice_->string_type[kTypeNullable]); }
  void do_castobject(UnaryExpr* i) { doCoerceInstr(i, lattice_->object_type[kTypeNullable]); }
  void do_tonumber(UnaryStmt* i) { doCoerceInstr(i, lattice_->double_type); }
  void do_toint(UnaryStmt*);
  void do_touint(UnaryStmt*);
  void do_toboolean(UnaryExpr* i) { doCoerceInstr(i, lattice_->boolean_type); }
  void do_construct(CallStmt2*);
  void do_applytype(NaryStmt0*);
  void do_newclass(NaryStmt2*);
  void do_abc_callprop(CallStmt2*);
  void do_abc_constructprop(CallStmt2*);
  void do_toslot(BinaryExpr*);
  void do_slottype(BinaryExpr*);
  void do_coerce(BinaryStmt* i) { doCoerceInstr(i); }
  void do_cast(BinaryStmt* i) { doCoerceInstr(i); }
  void do_abc_findproperty(NaryStmt3* i) { doFindInstr(i); }
  void do_abc_findpropstrict(NaryStmt3* i) { doFindInstr(i); }
  void do_abc_add(BinaryStmt*);
  void do_abc_getprop(CallStmt2*);
  void do_abc_getpropx(CallStmt3*);
  void do_getpropertylate_u(BinaryStmt* i) { doGetpropertylate(i); }
  void do_getpropertylate_i(BinaryStmt* i) { doGetpropertylate(i); }
  void do_getpropertylate_d(BinaryStmt* i) { doGetpropertylate(i); }
  void do_getslot(CallStmt2*);
  void do_ckfilter(UnaryExpr*);
  void do_getouterscope(BinaryExpr*);
  void do_cknull(UnaryStmt*);
  void do_cknullobject(UnaryStmt* i) { do_cknull(i); }
  void do_newactivation(UnaryStmt*);
  void do_newcatch(UnaryStmt*);
  void do_loadenv(BinaryExpr*);
  void do_getlocal(GetlocalStmt* instr);
  void do_loadenv_atom(BinaryExpr* i) { do_loadenv(i); }
  void do_loadenv_interface(BinaryExpr*);
  void do_loadenv_string(BinaryExpr* i) { do_loadenv(i); }
  void do_loadenv_number(BinaryExpr* i) { do_loadenv(i); }
  void do_loadenv_boolean(BinaryExpr* i) { do_loadenv(i); }
  void do_loadenv_namespace(BinaryExpr* i) { do_loadenv(i); }
  void do_loadinitenv(UnaryExpr*);
  void do_loadsuperinitenv(UnaryExpr*);
  void do_loadenv_env(BinaryExpr*);

  // constant folding
  void do_addi(BinaryExpr* i)  { fold(i, Stubs::do_addi); }
  void do_subi(BinaryExpr* i)  { fold(i, Stubs::do_subi); }
  void do_muli(BinaryExpr* i)  { fold(i, Stubs::do_muli); }
  void do_ori(BinaryExpr* i)   { fold(i, Stubs::do_ori); }
  void do_andi(BinaryExpr* i)  { fold(i, Stubs::do_andi); }
  void do_xori(BinaryExpr* i)  { fold(i, Stubs::do_xori); }
  void do_lshi(BinaryExpr* i)  { fold(i, Stubs::do_lshi); }
  void do_rshi(BinaryExpr* i)  { fold(i, Stubs::do_rshi); }
  void do_rshui(BinaryExpr* i) { fold(i, Stubs::do_rshui); }
  void do_addd(BinaryExpr* i)  { fold(i, Stubs::do_addd); }
  void do_subd(BinaryExpr* i)  { fold(i, Stubs::do_subd); }
  void do_divd(BinaryExpr* i)  { fold(i, Stubs::do_divd); }
  void do_muld(BinaryExpr* i)  { fold(i, Stubs::do_muld); }
  void do_modulo(BinaryExpr* i){ fold(i, Stubs::do_modulo); }
  void do_negi(UnaryExpr* i)   { fold(i, Stubs::do_negi); }
  void do_noti(UnaryExpr* i)   { fold(i, Stubs::do_noti); }
  void do_negd(UnaryExpr* i)   { fold(i, Stubs::do_negd); }
  void do_eqi(BinaryExpr* i)   { fold(i, Stubs::do_eqi); }
  void do_lti(BinaryExpr* i)   { fold(i, Stubs::do_lti); }
  void do_lei(BinaryExpr* i)   { fold(i, Stubs::do_lei); }
  void do_gti(BinaryExpr* i)   { fold(i, Stubs::do_gti); }
  void do_gei(BinaryExpr* i)   { fold(i, Stubs::do_gei); }
  void do_eqd(BinaryExpr* i)   { fold(i, Stubs::do_eqd); }
  void do_ltd(BinaryExpr* i)   { fold(i, Stubs::do_ltd); }
  void do_led(BinaryExpr* i)   { fold(i, Stubs::do_led); }
  void do_gtd(BinaryExpr* i)   { fold(i, Stubs::do_gtd); }
  void do_ged(BinaryExpr* i)   { fold(i, Stubs::do_ged); }
  void do_equi(BinaryExpr* i)  { fold(i, Stubs::do_equi); }
  void do_ltui(BinaryExpr*);
  void do_leui(BinaryExpr* i)  { fold(i, Stubs::do_leui); }
  void do_gtui(BinaryExpr* i)  { fold(i, Stubs::do_gtui); }
  void do_geui(BinaryExpr* i)  { fold(i, Stubs::do_geui); }
  void do_eqb(BinaryExpr* i)   { fold(i, Stubs::do_eqb); }
  void do_u2i(UnaryExpr* i)    { fold(i, Stubs::do_u2i); }
  void do_i2u(UnaryExpr* i)    { fold(i, Stubs::do_i2u); }
  void do_d2b(UnaryExpr* i)    { fold(i, Stubs::do_d2b); }
  void do_doubletoint32(UnaryExpr* i) { fold(i, Stubs::do_doubletoint32); }

private:
  void doCoerceInstr(UnaryExpr*, const Type* to_type);
  void doCoerceInstr(UnaryStmt*, const Type* to_type);
  void doCoerceInstr(BinaryStmt*);
  void doFindInstr(NaryStmt3*);
  void doChangeModel(UnaryExpr*, ModelKind);
  void coerceOutputModels(Instr*);
  void doGetpropertylate(BinaryStmt*);

  template<typename STUB> void fold(BinaryExpr*, STUB);
  template<typename STUB> void fold(UnaryExpr*, STUB);

  template<int ARGMIN>
  inline void setStmtType(CallStmt<ARGMIN>* instr, const Type* t) {
    setType(instr->effect_out(), EFFECT);
    setType(instr->value_out(), t);
  }

  template<int ARGMIN>
  inline void setStmtType(NaryStmt<ARGMIN>* instr, const Type* t) {
    setType(instr->effect_out(), EFFECT);
    setType(instr->value_out(), t);
  }

  template<int USEC, int DEFC>
  inline void setStmtType(FixedArgStmt<USEC, DEFC>* instr, const Type* t) {
    const Type* effect_type = isBottom(t) ? (Type*)BOT : (Type*)EFFECT;
    setType(instr->effect_out(), effect_type);
    setType(instr->value_out(), t);
  }

private:
  InstrGraph* ir_;
  Lattice* lattice_;
};

} // namespace halfmoon
#endif // TYPEANALYZER_H_
