https://hal.archives-ouvertes.fr/hal-03445821
Raw File
ddg.c
/*
   Copyright Universite de Versailles Saint-Quentin en Yvelines 2009
   AUTHORS: Sebastien Briais, Sid Touati

   This file is part of GDD.
   
   GDD is free software: you can redistribute it and/or modify it
   under the terms of the GNU Lesser General Public License as
   published by the Free Software Foundation, either version 3 of the
   License, or (at your option) any later version.
   
   GDD 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
   Lesser General Public License for more details.
   
   You should have received a copy of the GNU Lesser General Public
   License along with GDD.  If not, see
   <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>

#include <SCEDA/hashset.h>
#include <SCEDA/list.h>
#include <SCEDA/graph_dag.h>
#include <SCEDA/graph_path.h>
#include <SCEDA/graph_negcycle.h>
#include <SCEDA/graph_mrc.h>

#include "ddg.h"
#include "util.h"
#include "opcode.h"
#include "arch.h"

static GDD_DepKind depkind_of_string(const char *kind) {
  if(strcmp(kind, "flowdep_reg") == 0) {
    return GDD_FLOWDEP_REG;
  } else if(strcmp(kind,"antidep_reg") == 0) {
    return GDD_ANTIDEP_REG;
  } else if(strcmp(kind,"outputdep_reg") == 0) {
    return GDD_OUTPUTDEP_REG;
  } else if(strcmp(kind,"inputdep_reg") == 0) {
    return GDD_INPUTDEP_REG;
  } else if(strcmp(kind,"flowdep_mem") == 0) {
    return GDD_FLOWDEP_MEM;
  } else if(strcmp(kind,"antidep_mem") == 0) {
    return GDD_ANTIDEP_MEM;
  } else if(strcmp(kind,"outputdep_mem") == 0) {
    return GDD_OUTPUTDEP_MEM;
  } else if(strcmp(kind,"inputdep_mem") == 0) {
    return GDD_INPUTDEP_MEM;
  } else if(strcmp(kind,"spilldep_mem") == 0) {
    return GDD_SPILLDEP_MEM;
  } else if(strcmp(kind,"other_mem") == 0) {
    return GDD_OTHERDEP_MEM;
  } else if(strcmp(kind,"killerdep") == 0) {
    return GDD_KILLERDEP;
  } else if(strcmp(kind,"reusedep") == 0) {
    return GDD_REUSEDEP;
  } else if(strcmp(kind,"serial") == 0) {
    return GDD_SERIALDEP;
  } else if(strcmp(kind,"virtual") == 0) {
    return GDD_VIRTUALDEP;
  } else {
    return GDD_UNKNOWNDEP;
  }
}

static int GDD_depkind_needs_regtype(GDD_DepKind kind) {
  switch(kind) {
  case GDD_FLOWDEP_REG:
  case GDD_ANTIDEP_REG:
  case GDD_OUTPUTDEP_REG:
  case GDD_INPUTDEP_REG:
  case GDD_REUSEDEP:
  case GDD_KILLERDEP: {
    return TRUE;
    break;
  }
  default: {
    return FALSE;
    break;
  }
  }
}

/** Create a new GDD edge label.
    
    @param[in] regtype = considered type or NULL if not applicable
    @param[in] dep = dependency kind (converted to GDD_DepKind)
    @param[in] latency = latency 
    @param[in] distance = interloop distance

    @return the GDD edge label */
GDD_EdgeLabel *GDD_edge_label_create(const char *regtype, const char *dep, int latency, int distance) {
  GDD_EdgeLabel *edge = (GDD_EdgeLabel *)safe_malloc(sizeof(GDD_EdgeLabel));
  edge->dep = depkind_of_string(dep);
  if(regtype != NULL) {
    edge->regtype = safe_strdup(regtype);
  } else {
    if(GDD_depkind_needs_regtype(edge->dep)) {
      fprintf(stderr,"error: this kind of dependency requires a type\n");
      exit(1);
    } else {
      edge->regtype = NULL;
    }
  }
  edge->latency = latency;
  edge->distance = distance;
  return edge;
}

/** Delete a GDD edge label.

    @param[in] edge = GDD edge label to delete */
void GDD_edge_label_delete(GDD_EdgeLabel *edge) {
  if(edge->regtype != NULL) {
    free(edge->regtype);
  }
  free(edge);
}

GDD_NodeLabel *GDD_node_label_create(int id, GDD_Opcode *op) {
  GDD_NodeLabel *node = (GDD_NodeLabel *)safe_malloc(sizeof(GDD_NodeLabel));
  node->id = id;
  node->op = op;
  return node;
}

void GDD_node_label_delete(GDD_NodeLabel *node) {
  free(node);
}

GDD_NodeLabel *GDD_node_label_copy(GDD_NodeLabel *x) {
  GDD_NodeLabel *y = (GDD_NodeLabel *)safe_malloc(sizeof(GDD_NodeLabel));
  y->id = x->id;
  y->op = x->op;
  return y;
}

/** Copy a GDD edge label.

    @param[in] x = GDD edge label to copy 

    @return a (deep) copy of x (must be freed by delete) */
GDD_EdgeLabel *GDD_edge_label_copy(GDD_EdgeLabel *x) {
  GDD_EdgeLabel *y = (GDD_EdgeLabel *)safe_malloc(sizeof(GDD_EdgeLabel));
  y->dep = x->dep;
  y->latency = x->latency;
  y->distance = x->distance;
  if(x->regtype != NULL) {
    y->regtype = safe_strdup(x->regtype);
  } else {
    y->regtype = NULL;
  }
  return y;
}

/** Delete edges in a GDD and the associated edge labels.

    @param[in] g = GDD
    @param[in] edges = list of edges to delete 

    The list is cleared but not deleted. */
void GDD_delete_edges(SCEDA_Graph *g, SCEDA_List *edges) {
  SCEDA_ListIterator the_edges;
  SCEDA_list_iterator_init(edges, &the_edges);
  while(SCEDA_list_iterator_has_next(&the_edges)) {
    SCEDA_Edge *e = SCEDA_list_iterator_next(&the_edges);
    GDD_EdgeLabel *label;
    safe_call(SCEDA_graph_remove_edge(g, e, (void **)&label));
    GDD_edge_label_delete(label);
  }
  SCEDA_list_iterator_cleanup(&the_edges);
  SCEDA_list_clear(edges);
}

/** Test whether the given GDD edge label is a flow dependency of
    considered type.

    @param[in] e = GDD edge label
    @param[in] type = considered type
    
    @return TRUE iff e is a flow dependency of given type */
int GDD_edge_label_is_flowdep_of_type(GDD_EdgeLabel *edge, const char *type) {
  switch(edge->dep) {
  case GDD_FLOWDEP_REG:
    return (strcmp(edge->regtype, type) == 0);
    break;
  default:
    return FALSE;
    break;
  }
}

/** Returns the set of values of a given type.

    @param[in] g = GDD
    @param[in] type = considered type

    @return the set of values of the given type in the GDD */
SCEDA_HashSet *GDD_get_values_of_type(SCEDA_Graph *g, const char *type) {
  SCEDA_HashSet *vals = SCEDA_vertex_set_create();
  
  SCEDA_VerticesIterator g_vertice;
  SCEDA_vertices_iterator_init(g, &g_vertice);
  while(SCEDA_vertices_iterator_has_next(&g_vertice)) {
    SCEDA_Vertex *v = SCEDA_vertices_iterator_next(&g_vertice);
    if(GDD_vertex_is_value_of_type(v, type)) {
      SCEDA_hashset_add(vals, v);
    }
  }
  SCEDA_vertices_iterator_cleanup(&g_vertice);

  return vals;
}

/** Get the number of values of considered type.

    @param[in] g = GDD
    @param[in] type = considered type

    @return the number of values of given type in the GDD */
int GDD_nb_values(SCEDA_Graph *g, const char *type) {
  int nb = 0;

  SCEDA_VerticesIterator g_vertice;
  SCEDA_vertices_iterator_init(g, &g_vertice);
  while(SCEDA_vertices_iterator_has_next(&g_vertice)) {
    SCEDA_Vertex *v = SCEDA_vertices_iterator_next(&g_vertice);
    if(GDD_vertex_is_value_of_type(v, type)) {
      nb++;
    }
  }
  SCEDA_vertices_iterator_cleanup(&g_vertice);

  return nb;
}

/** Get the number of flow dependency edges in the GDD of considered
    type.

    @param[in] g = GDD
    @param[in] type = considered type

    @return the number of flow dependency edges of given type in the
    GDD */
int GDD_nb_flowdeps(SCEDA_Graph *g, const char *type) {
  int nb = 0;

  SCEDA_EdgesIterator g_edges;
  SCEDA_edges_iterator_init(g, &g_edges);
  while(SCEDA_edges_iterator_has_next(&g_edges)) {
    SCEDA_Edge *e = SCEDA_edges_iterator_next(&g_edges);
    if(GDD_edge_is_flowdep_of_type(e, type)) {
      nb++;
    }
  }
  SCEDA_edges_iterator_cleanup(&g_edges);

  return nb;
}

/** Returns the set of consumers of a given GDD vertex. 

    @param[in] v = vertex
    @param[in] type = considered type

    @return the set of consumers */
SCEDA_HashSet *GDD_consumers_of_type(SCEDA_Vertex *v, const char *type) {
  SCEDA_HashSet *cons = SCEDA_vertex_set_create();
  
  SCEDA_OutEdgesIterator out_edges;
  SCEDA_out_edges_iterator_init(v, &out_edges);
  while(SCEDA_out_edges_iterator_has_next(&out_edges)) {
    SCEDA_Edge *e = SCEDA_out_edges_iterator_next(&out_edges);
    if(GDD_edge_is_flowdep_of_type(e, type)) {
      SCEDA_Vertex *w = SCEDA_edge_target(e);
      SCEDA_hashset_add(cons, w);
    }
  }
  SCEDA_out_edges_iterator_cleanup(&out_edges);

  return cons;
}

/** Computes the maximal distance of flowdep edges between v_s and v_t.

    @param[in] v_s = source vertex
    @param[in] v_t = target vertex
    @param[in] type = considered type

    @return the maximal distance of flowdep edges (of given type)
    between v_s and v_t or INT_MIN if no such edges exist */
int GDD_max_distance_flowdep(SCEDA_Vertex *v_s, SCEDA_Vertex *v_t, const char *type) {
  int max = INT_MIN;

  SCEDA_EdgeClassIterator edges;
  SCEDA_edge_class_iterator_init(v_s, v_t, &edges);
  while(SCEDA_edge_class_iterator_has_next(&edges)) {
    SCEDA_Edge *e = SCEDA_edge_class_iterator_next(&edges);
    if(GDD_edge_is_flowdep_of_type(e, type)) {
      if(GDD_edge_distance(e) > max) {
	max = GDD_edge_distance(e);
      }
    }
  }
  SCEDA_edge_class_iterator_cleanup(&edges);

  return max;
}

/********************************/
/** Lexicographic positive test */
/********************************/
typedef struct {
  int n;
} LexPosCtxt;

static int GDD_lex_pos_cost(SCEDA_Edge *e, LexPosCtxt *ctxt) {
  return ctxt->n * GDD_edge_distance(e) - 1;
}

/** Tests whether the given GDD is lexicographic positive.

    @param[in] g = GDD
    @param[out] cycle = cycle that has a negative distance.

    @return TRUE iff g is lexicographic positive. When FALSE, a cycle
    with a negative distance is also returned. */
int GDD_is_lexicographic_positive(SCEDA_Graph *g, SCEDA_List **witness_cycle) {
  int is_lexicographic = TRUE;

  LexPosCtxt ctxt;
  ctxt.n = SCEDA_graph_vcount(g); 
  
  SCEDA_List *cycle = SCEDA_graph_neg_cycle_int(g, (SCEDA_int_edge_fun)GDD_lex_pos_cost, &ctxt);
  
  if(SCEDA_list_is_empty(cycle)) {
    SCEDA_list_delete(cycle);
  } else {
    is_lexicographic = FALSE;
    *witness_cycle = cycle;
  }

  return is_lexicographic;
}

/********************/
/** MII computation */
/********************/
static int GDD_MII_cost(SCEDA_Edge *e, void *ctxt) {
  return -GDD_edge_latency(e);
}

static int GDD_MII_time(SCEDA_Edge *e, void *ctxt) {
  return GDD_edge_distance(e);
}

/** Computes the MII ratio of the GDD and the cycle that realises the ratio.

    @param[in] g = GDD
    @param[out] MIInum = numerator
    @param[out] MIIden = denominator
    @param[out] cycle = list of edges

    @return 0 in case of success, -1 otherwise */
int GDD_MII_ratio_cycle(SCEDA_Graph *g, int *MIInum, int *MIIden, SCEDA_List **cycle) {
  if(SCEDA_graph_minimum_ratio_cycle(g, GDD_MII_cost, NULL, GDD_MII_time, NULL, MIInum, MIIden, cycle) == 0) {
    (*MIInum) = -(*MIInum);
    return 0;
  }
  return -1;
}

/** Compute the MII of the GDD. It is just ceil value of MII ratio.

    @param[in] g = GDD

    @return the MII or -1 in case of error (acyclic graph) */
int GDD_MII(SCEDA_Graph *g) {
  int MIInum;
  int MIIden;
  SCEDA_List *cycle;
  if(GDD_MII_ratio_cycle(g, &MIInum, &MIIden, &cycle) == 0) {
    SCEDA_list_delete(cycle);
    return (MIInum + MIIden - 1) / MIIden;
  } else {
    return -1;
  }
}

/************************/
/* Longest path in DAGs */
/************************/
static int opp_latency(SCEDA_Edge *e, void *data) {
  return -GDD_edge_latency(e);
}

/** Compute the longest path of the GDD.

    @param[in] g = GDD

    @return the longest path or -1 in case of error (non acyclic graph) */
int GDD_longest_path(SCEDA_Graph *g) {
  int lp = -1;

  GDD_Opcode *dummy_op = GDD_opcode_create("dummy_op", 0, 0);
  GDD_NodeLabel *dummy_node = GDD_node_label_create(-1, dummy_op);

  SCEDA_Vertex *source = SCEDA_graph_add_vertex(g, dummy_node);
  SCEDA_Vertex *sink = SCEDA_graph_add_vertex(g, dummy_node);
  
  SCEDA_List *added_edges = SCEDA_list_create(NULL);

  // add a unique source and a unique sink
  {
    SCEDA_VerticesIterator g_vertice;
    SCEDA_vertices_iterator_init(g, &g_vertice);
    while(SCEDA_vertices_iterator_has_next(&g_vertice)) {
      SCEDA_Vertex *v = SCEDA_vertices_iterator_next(&g_vertice);
      if(v != source) {
	if(SCEDA_vertex_in_deg(v) == 0) {
	  GDD_EdgeLabel *label = GDD_edge_label_create(NULL, "virtual", 0, 0);
	  SCEDA_Edge *e = SCEDA_graph_add_edge(g, source, v, label);
	  safe_call(SCEDA_list_add(added_edges, e));
	}
      }
      if(v != sink) {
	if(SCEDA_vertex_out_deg(v) == 0) {
	  GDD_EdgeLabel *label = GDD_edge_label_create(NULL,"virtual", GDD_vertex_latency(v), 0);
	  SCEDA_Edge *e = SCEDA_graph_add_edge(g, v, sink, label);
	  safe_call(SCEDA_list_add(added_edges, e));
	}
      }
    }
    SCEDA_vertices_iterator_cleanup(&g_vertice);
  }

  if(SCEDA_graph_compute_topological_order(g) == 0) {
    SCEDA_HashMap *paths = SCEDA_graph_shortest_path_from_in_dag(g, source, opp_latency, NULL);
    SCEDA_PathInfo *sink_path = SCEDA_hashmap_get(paths, sink);
    assert(sink_path->in_edge != NULL);
    lp = -sink_path->distance;
    SCEDA_hashmap_delete(paths);
  }

  // remove source and sink
  {
    GDD_delete_edges(g, added_edges);
    SCEDA_list_delete(added_edges);

    // GDD_Opcode *op;
    GDD_NodeLabel *node;
    safe_call(SCEDA_graph_remove_vertex(g, source, (void **)&node));
    safe_call(SCEDA_graph_remove_vertex(g, sink, (void **)&node));
    GDD_node_label_delete(dummy_node);
    GDD_opcode_delete(dummy_op);
  }

  return lp;
}
back to top