https://hal.archives-ouvertes.fr/hal-03445804
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;
}