#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
#include "rtl.h"
#include "tm_p.h"
#include "hard-reg-set.h"
#include "basic-block.h"
#include "output.h"
#include "flags.h"
#include "function.h"
#include "expr.h"
#include "ggc.h"
#include "langhooks.h"
#include "diagnostic.h"
#include "tree-flow.h"
#include "timevar.h"
#include "tree-dump.h"
#include "tree-pass.h"
#include "toplev.h"
#include "except.h"
#include "cfgloop.h"
#include "cfglayout.h"
#include "tree-ssa-propagate.h"
#include "value-prof.h"
#include "pointer-set.h"
#include "tree-inline.h"


/* global declarations */
int num_assignment, total_assignment, num_copy_stmt, num_const_stmt, num_arith_stmt, num_global_ref;
void initialize_stats(void);
void process_statement(tree stmt);
void printstatistics(void);
static unsigned int gccwk09_main(void);
void compute_local_consts (void);
void compute_global_consts (void);

/* -----------------------------------------------------------------------
 Function to initialize the variables used for collecting the information.
 -------------------------------------------------------------------------*/
void 
initialize_stats(void)
{
  num_assignment = 0;
  total_assignment = 0;
  num_copy_stmt = 0;
  num_const_stmt = 0;
  num_arith_stmt = 0;
  num_global_ref = 0;
}

/* --------------------------------------------
    Function to print the collected statistics. 
   --------------------------------------------*/
void 
printstatistics(void)
{
  if(dump_file)
  {
     fprintf(dump_file,"\nNumber of assignment statements:: %d\n",num_assignment);
     fprintf(dump_file,"\nTotal Number of assignment statements (including temp vars): %d\n",total_assignment);
     fprintf(dump_file,"\nNumber of copy statements:: %d\n",num_copy_stmt);
     fprintf(dump_file,"\nNumber of const statements:: %d\n",num_const_stmt);
     fprintf(dump_file,"\nNumber of arithmetic statements:: %d\n",num_arith_stmt);
     fprintf(dump_file,"\nNumber of references to global vars: %d\n",num_global_ref);
  } 
}

/* ----------------------------------
   function to process each statement. 
   ----------------------------------*/
void 
process_statement(tree stmt)
{
  tree lval,rval;
		switch (TREE_CODE(stmt))
		{
			case GIMPLE_MODIFY_STMT:		
				/* extract the lval and rval trees from the given statement.
				   If the statement is of the form a = b + c, 
				   lval = a, which is the tree indexed at 0
				   rval = b + c, which is the tree indexed at 1. */			 
				lval=GIMPLE_STMT_OPERAND(stmt,0);
				rval=GIMPLE_STMT_OPERAND(stmt,1);

				/* For counting number of assignment statements, we have to check if lval is variable 
                                   declaration, and if it is, then ensure that its not an artificial variable. */
				if (TREE_CODE(lval) == VAR_DECL) 
				{
					if (!DECL_ARTIFICIAL(lval))
						num_assignment++;
					/* the total assignments including temporary variables */
					total_assignment++;
				}
			
				/* For counting the number of copy statements of the form a = b, we have to make 
				   sure that both lhs and rhs is a variable, and neither of them is aritifical. */
				if (TREE_CODE(lval) == VAR_DECL && TREE_CODE(rval) == VAR_DECL)
				{
					if (!DECL_ARTIFICIAL(lval) && !DECL_ARTIFICIAL(rval))
						num_copy_stmt++;
				}
		
				/* For counting the number of arithmetic statements, both unary and binary.
				   'tcc_unary' is the class for unary arithmetic expressions and 'tcc_binary'
                                   is the class for binary arithmetic expressions */
				if (TREE_CODE_CLASS (TREE_CODE(rval)) == tcc_unary ||
				    TREE_CODE_CLASS (TREE_CODE(rval)) == tcc_binary)		
						num_arith_stmt++;
				
				/* For counting the number of global references in our gimple cfg, we use the 
                                   function 'is_global_var ()' which returns true if the variable is global. */ 
          			if (TREE_CODE(lval) == VAR_DECL && is_global_var (lval))
					num_global_ref++;
			        if (TREE_CODE(rval) == VAR_DECL && is_global_var (rval))
					num_global_ref++;	
				else
				{
					/* check if rhs is of class tcc_binary */
					 if (TREE_CODE_CLASS (TREE_CODE(rval)) == tcc_binary)
					 {
						tree lhs = TREE_OPERAND (rval, 0);
						tree rhs = TREE_OPERAND (rval, 1);
						if (TREE_CODE(lhs) == VAR_DECL && is_global_var(lhs))
							num_global_ref++;
						if (TREE_CODE(rhs) == VAR_DECL && is_global_var(rhs))
							num_global_ref++;
				  	 }
				}
				break;
			default :
				break;	
		}    		
}

/* ---------------------------------------------
   The main driver function to perform analysis.
   ---------------------------------------------*/
static unsigned int 
gccwk09_main(void)
{
   struct cgraph_node *node;
   basic_block bb;
   block_stmt_iterator bsi;
   
   initialize_stats();  
   compute_global_consts (); 
   for (node = cgraph_nodes; node; node=node->next)
   {
	if (node->analyzed && cgraph_is_master_clone (node))
	{
		push_cfun (DECL_STRUCT_FUNCTION (node->decl));
		compute_local_consts();
   		FOR_EACH_BB (bb)
		{			
			for (bsi=bsi_start(bb); !bsi_end_p(bsi)  ; bsi_next(&bsi) )
		 	 {
				tree stmt = bsi_stmt(bsi);	
				process_statement(stmt); 
			 }
		}
		/* restoring the context by popping cfun. */
		pop_cfun ();
	}
   }        
   printstatistics();  
   return 0;
}

/* ---------------------------------------------------------------------------------
   compute the number of local const variables by traversing the unexpanded_var_list
   for each cfun (current function). unexpanded_var_list is a data structure that
   maintains a linked list of all the variable declarations in the given function. 
   ---------------------------------------------------------------------------------*/
void
compute_local_consts (void)
{
	tree list = cfun->unexpanded_var_list;
        while (list)
	{
		tree vars = TREE_VALUE (list);
		if (TREE_READONLY (vars))
			num_const_stmt++;
		list = TREE_CHAIN(list);
	}
}

/* -----------------------------------------------------------------------------
   compute the number of global const variables by traversing the varpool_nodes.
   varpool_nodes is a data structure that stores a linked list of all the global 
   declarations in a program. 
   -----------------------------------------------------------------------------*/
void
compute_global_consts (void)
{
	struct varpool_node *node;
	for (node = varpool_nodes; node; node = node->next)
	{
		if (TREE_READONLY (node->decl))
			num_const_stmt++;
	}
}

/* ------------------------------------------
   Return true if we should execute our pass.
   ------------------------------------------*/

struct gimple_opt_pass pass_gccwk09 =
{ 
   {
   GIMPLE_PASS,
   "gccwk09",                /* name */
   NULL,             /* gate */
   gccwk09_main,             /* execute */
   NULL,		     /* sub */
   NULL,		     /* next */
   0,			     /* static pass number */
   0,			     /* tv_id */
   0,			     /* properties required */
   0,			     /* properties provided */
   0,			     /* properties destroyed */
   0,			     /* todo_flags start */
   0			     /* todo_flags end */
   }
};

