/*

  $Id$

  Copyright 1989-2016 MINES ParisTech

  This file is part of PIPS.

  PIPS is free software: you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  any later version.

  PIPS is distributed in the hope that it will be useful, but WITHOUT ANY
  WARRANTY; without even the implied warranty of MERCHANTABILITY or
  FITNESS FOR A PARTICULAR PURPOSE.

  See the GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with PIPS.  If not, see <http://www.gnu.org/licenses/>.

*/
#ifdef HAVE_CONFIG_H
    #include "pips_config.h"
#endif
/**
 * The spaghettifier is used in context of PHRASE project while
 * creating "Finite State Machine"-like code portions in order to synthetise
 * them in reconfigurables units.
 *
 * This phase transforms structured code portions (eg. loops) in
 * unstructured statements.
 * 
 * To add flexibility, the behavior of \texttt{spaghettifier} is
 * controlled by the properties 
 * - DESTRUCTURE_TESTS
 * - DESTRUCTURE_LOOPS
 * - DESTRUCTURE_WHILELOOPS
 * - DESTRUCTURE_FORLOOPS
 * to allow more or less destruction power !
 *
 * spaghettify          > MODULE.code
 *       < PROGRAM.entities
 *       < MODULE.code
 *
 */

#include <stdio.h>
#include <ctype.h>

#include "genC.h"
#include "linear.h"
#include "ri.h"
#include "effects.h"

#include "resources.h"

#include "misc.h"
#include "ri-util.h"
#include "prettyprint.h"
#include "effects-util.h"
#include "pipsdbm.h"

#include "text-util.h"

#include "dg.h"
#include "properties.h"

#include "control.h"
#include "callgraph.h"
#include "spaghettify.h"
#include "phrase_tools.h"

/* 
 * This function is recursively called during spaghettization.
 * It takes the statement stat as parameter and return a new 
 * spaghettized statement (or the same if nothing has been done).
 * Spaguettization is done:
 * - on Tests (if property DESTRUCTURE_TESTS set to true)
 * - on Loops (if property DESTRUCTURE_LOOPS set to true)
 * - on WhileLoops (if property DESTRUCTURE_WHILELOOPS set to true)
 * - on ForLoops (if property DESTRUCTURE_FORLOOPS set to true)
 */
statement spaghettify_statement (statement stat, const char* module_name)
{
  // Defaut behaviour is to return parameter statement stat
  statement returned_statement = stat;
  instruction i = statement_instruction(stat);

  pips_debug(2,"\nSPAGHETTIFY: Module statement: =====================================\n");
  ifdebug(2) {
    print_statement(stat);
  }
  pips_debug(2,"domain number = %"PRIdPTR"\n", statement_domain_number(stat));
  pips_debug(2,"entity = UNDEFINED\n");
  pips_debug(2,"statement number = %"PRIdPTR"\n", statement_number(stat));
  pips_debug(2,"statement ordering = %"PRIdPTR"\n", statement_ordering(stat));
  if (statement_with_empty_comment_p(stat)) {
    pips_debug(2,"statement comments = EMPTY\n");
  }
  else {
    pips_debug(2,"statement comments = %s\n", statement_comments(stat));
  }
  pips_debug(2,"statement instruction = %s\n", statement_type_as_string(stat));
  switch (instruction_tag(i)) {
  case is_instruction_test: 
    {
      pips_debug(2, "TEST\n");   
    if (get_bool_property("DESTRUCTURE_TESTS")) {
      returned_statement = spaghettify_test (stat, module_name);
    }
      break;
  }
  case is_instruction_sequence: 
    {
      sequence seq = instruction_sequence(i);
      pips_debug(2, "SEQUENCE\n");   
      MAP(STATEMENT, current_stat,
      {
	statement new_stat = spaghettify_statement(current_stat, module_name);
	if (new_stat != NULL) {
	  gen_list_patch (sequence_statements(seq), current_stat, new_stat);
	}
      }, sequence_statements(seq));
      break;
    }
  case is_instruction_loop: {
    pips_debug(2, "LOOP\n");   
    if (get_bool_property("DESTRUCTURE_LOOPS")) {
      returned_statement = spaghettify_loop (stat, module_name);
    }
    break;
  }
  case is_instruction_whileloop: {
    pips_debug(2, "WHILELOOP\n");   
    if (get_bool_property("DESTRUCTURE_WHILELOOPS")) {
      returned_statement = spaghettify_whileloop (stat, module_name);
    }
    break;
  }
  case is_instruction_forloop: {
    pips_debug(2, "FORLOOP\n");   
    if (get_bool_property("DESTRUCTURE_FORLOOPS")) {
      returned_statement = spaghettify_forloop (stat, module_name);
    }
    break;
  }
  case is_instruction_call: {
    pips_debug(2, "CALL\n");   
    break;
  }
  case is_instruction_unstructured: {
    pips_debug(2, "UNSTRUCTURED\n");  
    break;
  }
  case is_instruction_goto: {
    pips_debug(2, "GOTO\n");   
    break;
  }
  default:
    pips_debug(2, "UNDEFINED\n");   
    break;
  }

  return returned_statement;
}

/*********************************************************
 * Phase main
 *********************************************************/

bool spaghettify(const char* module_name)
{
   /* get the resources */
  statement stat = (statement) db_get_memory_resource(DBR_CODE, 
						      module_name, 
						      true);

  set_current_module_statement(stat);
  set_current_module_entity(local_name_to_top_level_entity(module_name));
  
  debug_on("SPAGUETTIFY_DEBUG_LEVEL");

  /* Now do the job */  
  stat = spaghettify_statement(stat,module_name);

  pips_assert("Statement is consistent after SPAGUETTIFY", 
	       statement_consistent_p(stat));
  
  /* Reorder the module, because new statements have been added */  
  module_reorder(stat);
  DB_PUT_MEMORY_RESOURCE(DBR_CODE, module_name, stat);
  DB_PUT_MEMORY_RESOURCE(DBR_CALLEES, module_name, 
			 compute_callees(stat));
  
  /* update/release resources */
  reset_current_module_statement();
  reset_current_module_entity();
  
  debug_off();
  
  return true;
}
