// dependance de tree-pass.H
#include <gcc-plugin.h>
// declare opt_pass
#include <tree-pass.h>

/* Enum to represent the collective operations */
#define DEFMPICOLLECTIVES(CODE, NAME) CODE,
enum mpi_collective_code
{
#include "mpi_collectives.def"
	LAST_AND_UNUSED_MPI_COLLECTIVE_CODE
};
#undef DEFMPICOLLECTIVES

/* Name of each MPI collective operations */
#define DEFMPICOLLECTIVES(CODE, NAME) NAME,
const char *const mpi_collective_name[] =
{
#include "mpi_collectives.def"
};
#undef DEFMPICOLLECTIVES

enum domination_status
{
	NOT_DOMINATED,
	DOMINATED,
	NOT_APPLICABLE
};

struct bb_data
{
	mpi_collective_code mpi_code;
	bitmap_head dom;
	bitmap_head post_dom;
	bitmap_head dom_front;
	bitmap_head post_dom_front;
	int collective_rank[LAST_AND_UNUSED_MPI_COLLECTIVE_CODE];
	bitmap_head seens;
	int mark1; // for graph parkour
	int mark2;
	domination_status dom_status;
};

struct edge_data
{
	// exclude adge that make loop
	// excluding edge tag with loop remove all loop from the graph
	// used to calculate collectives ranks
	bool loop;
};

class pass_mpi_collective : public opt_pass
{

public:
	// maximum rank for each collective in the current cfg.
	int collective_max_rank[LAST_AND_UNUSED_MPI_COLLECTIVE_CODE];

	//
	// mpi collective pass
	//
	// constructor & clone function
	pass_mpi_collective(gcc::context *ctxt);
	pass_mpi_collective *clone();
	// pass functions
	bool gate(function *fun);
	unsigned int execute(function *fun);
	// functions used to detect normal functions,
	// mpi functions & mpi collectives.
	bool is_func(gimple *stmt);
	bool __is_mpi_func(gimple *stmt);
	bool is_mpi_func(gimple *stmt);
	mpi_collective_code __is_mpi_collec(gimple *stmt);
	mpi_collective_code is_mpi_collec(gimple *stmt);
	// print basic block with mpi collective
	void print_tree(function *fun);
	// split blocks according to mpi collectives
	void split_blocks(function *fun);
	// label blocks according to mpi colelctives
	void label_collec(function *fun);

	//
	// mpi collective graph utils
	//
	// allocate, free & reset data structure that are used in graph parkour
	// in node and edge of the graph.
	void alloc_bb_aux(function *fun);
	void free_bb_aux(function *fun);
	void reset_bb_mark(function *fun);
	void alloc_edge_aux(function *fun);
	void free_edge_aux(function *fun);
	// loop detection algorithms
	void __mark_edge_naif(function *fun);
	void __mark_edge_naif(basic_block bb);
	void __mark_edge_test(function *fun);
	void mark_edge(function *fun);
	void mark_edge(basic_block bb);

	//
	// mpi collective dominance
	//
	// alloc & free dominance info in gcc
	void calc_dom_data();
	void free_dom_data();
	// label graph with dominance, post dominance, dominance frontier
	// and post dominance frontier
	void label_dom(function *fun);
	void label_post_dom(function *fun);
	void label_dom_front(function *fun);
	void label_post_dom_front(function *fun);
	// calculate post dominance of a set of nodes in a bitmap
	void __is_post_dom_bitmap(
	    function *fun, basic_block bb, bitmap nodes, bitmap pds);
	void get_post_dom_bitmap(function *fun, bitmap nodes, bitmap pds);
	// calculate post dominance frontier of a set of nodes in a bitmap
	void get_post_dom_frontier(function *fun, bitmap pds, bitmap pdf);

	//
	// mpi collective warnings
	//
	// functions that rank mpi collectives
	void rank_collective(function *fun);
	void __rank_collective(basic_block bb);
	void better_rank_collective(function *fun);
	int __better_rank_collective(
	    basic_block bb, mpi_collective_code mpi_code, int rank);
	// function that calculate agregate a mpi collective
	// with the same rank in a bitmap
	void get_mpi_coll_rank(
	    function *fun, int rank, int mpi_code, bitmap mpi_coll);
	// raise warnings when mpi collective are badly uses
	void raise_warning_mpi_rank(function *fun);
	void raise_warning_mpi_order(function *fun);
	basic_block get_bb_from_index(function *fun, int index);

	//
	// mpi collective graphviz
	//
	char *cfgviz_generate_filename(function *fun, const char *suffix);
	void cfgviz_internal_dump(function *fun, FILE *out);
	void cfgviz_dump(function *fun, const char *suffix);

};