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

   This file is part of RS.
   
   RS 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.
   
   RS 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 RS.  If not, see
   <http://www.gnu.org/licenses/>.
*/
#include <SCEDA/list.h>
#include <SCEDA/graph_dag.h>
#include <SCEDA/graph_path.h>

#include "vsa.h"
#include "util.h"

#include "greedy.h"
#include "dvk.h"
#include "pkill.h"

/** Build (a copy of) G_k

    @param[in] ddg = GDD_DAG G
    @param[in] type = considered type
    @param[in] pkg = potential killers graph 
    @param[in] killing_map = killing function
    @param[out] g_to_g_extk = correspondance table between vertices of G and vertices of G_k
    @param[out] g_extk_to_g = correspondance table between vertices of G_k and vertices of G
    
    @return G_k
*/
static SCEDA_Graph *RS_ddag_extk(GDD_DAG *ddg, const char *type, SCEDA_Graph *pkg, SCEDA_HashMap *killing_map, SCEDA_HashMap **g_to_g_extk, SCEDA_HashMap **g_extk_to_g) {
  SCEDA_Graph *g = GDD_dag_get_ddg(ddg);
  
  SCEDA_Graph *g_extk = SCEDA_graph_create((SCEDA_delete_fun)GDD_node_label_delete, (SCEDA_delete_fun)GDD_edge_label_delete);

  SCEDA_HashMap *g_to_gk = SCEDA_vertex_map_create(NULL);
  SCEDA_HashMap *gk_to_g = SCEDA_vertex_map_create(NULL);
  *g_to_g_extk = g_to_gk;
  *g_extk_to_g = gk_to_g;

  // copy G vertices
  {
    SCEDA_VerticesIterator g_vertices;
    SCEDA_vertices_iterator_init(g, &g_vertices);
    while(SCEDA_vertices_iterator_has_next(&g_vertices)) {
      SCEDA_Vertex *v = SCEDA_vertices_iterator_next(&g_vertices);
      SCEDA_Vertex *v_extk = SCEDA_graph_add_vertex(g_extk, GDD_node_label_copy(GDD_vertex_get_node_label(v)));
      SCEDA_hashmap_put(g_to_gk, v, v_extk, NULL);
      SCEDA_hashmap_put(gk_to_g, v_extk, v, NULL);
    }
    SCEDA_vertices_iterator_cleanup(&g_vertices);
  }

  // duplicate edges
  {
    SCEDA_EdgesIterator edges;
    SCEDA_edges_iterator_init(g, &edges);
    while(SCEDA_edges_iterator_has_next(&edges)) {
      SCEDA_Edge *e = SCEDA_edges_iterator_next(&edges);
      SCEDA_Vertex *vs = SCEDA_edge_source(e);
      SCEDA_Vertex *vt = SCEDA_edge_target(e);
      SCEDA_Vertex *vs_extk = SCEDA_hashmap_get(g_to_gk, vs);
      SCEDA_Vertex *vt_extk = SCEDA_hashmap_get(g_to_gk, vt);
      GDD_EdgeLabel *lab_e_extk = GDD_edge_label_copy(GDD_edge_get_edge_label(e));
      SCEDA_graph_add_edge(g_extk, vs_extk, vt_extk, lab_e_extk);
    }
    SCEDA_edges_iterator_cleanup(&edges);
  }
  
  //
  SCEDA_HashMap *g_to_pkg = SCEDA_vertex_map_create(NULL);
  {
    SCEDA_VerticesIterator pkg_vertices;
    SCEDA_vertices_iterator_init(pkg, &pkg_vertices);
    while(SCEDA_vertices_iterator_has_next(&pkg_vertices)) {
      SCEDA_Vertex *v_pk = SCEDA_vertices_iterator_next(&pkg_vertices);
      SCEDA_Vertex *v = SCEDA_vertex_get_data(SCEDA_Vertex *, v_pk);
      SCEDA_hashmap_put(g_to_pkg, v, v_pk, NULL);
    }
    SCEDA_vertices_iterator_cleanup(&pkg_vertices);
  }

  // add new edges
  {
    SCEDA_HashMapIterator killers;
    SCEDA_hashmap_iterator_init(killing_map, &killers);
    while(SCEDA_hashmap_iterator_has_next(&killers)) {
      SCEDA_Vertex *u;
      SCEDA_Vertex *ku = SCEDA_hashmap_iterator_next(&killers, &u);
      //
      SCEDA_Vertex *ku_extk = SCEDA_hashmap_get(g_to_gk, ku);
      //
      SCEDA_Vertex *u_pk = SCEDA_hashmap_get(g_to_pkg, u);
      SCEDA_VertexSuccIterator pkillers;
      SCEDA_vertex_succ_iterator_init(u_pk, &pkillers);
      while(SCEDA_vertex_succ_iterator_has_next(&pkillers)) {
	SCEDA_Vertex *v_pk = SCEDA_vertex_succ_iterator_next(&pkillers);
	SCEDA_Vertex *v = SCEDA_vertex_get_data(SCEDA_Vertex *, v_pk);
	if(v == ku) {
	  continue;
	}
	SCEDA_Vertex *v_extk = SCEDA_hashmap_get(g_to_gk, v);
	int delta = GDD_vertex_delta_r(v, type) - GDD_vertex_delta_r(ku, type) + 1;
	GDD_EdgeLabel *label = GDD_edge_label_create(NULL, "serial", delta, 0);
	SCEDA_graph_add_edge(g_extk, v_extk, ku_extk, label);
      }
      SCEDA_vertex_succ_iterator_cleanup(&pkillers);
    }
    SCEDA_hashmap_iterator_cleanup(&killers);
  }
  
  SCEDA_hashmap_delete(g_to_pkg);

  //  assert(SCEDA_graph_is_acyclic(g_extk));

  return g_extk;
}

/** Algorithm 1 */
static int opp_latency(SCEDA_Edge *e, void *data) {
  return -GDD_edge_latency(e);
}

static int reachable_from(SCEDA_Vertex *v, SCEDA_HashMap *from_u) {
  SCEDA_PathInfo *info_u_v = SCEDA_hashmap_get(from_u, v);
  return (info_u_v->in_edge != NULL);
}

/** Check condition 
    v < u < k(v) && 
    lp(v,u) >= delta_w(v) - delta_w(u) &&
    lp(u,k(v)) > delta_w(u) - delta_r(k(v)) */
static int clause(const char *type, SCEDA_Vertex *u, SCEDA_Vertex *v, SCEDA_Vertex *ku, SCEDA_Vertex *kv, SCEDA_HashMap *from_u, SCEDA_HashMap *from_v) {
  SCEDA_PathInfo *info_v_u = SCEDA_hashmap_get(from_v, u);
  if((info_v_u->in_edge == NULL) && (info_v_u->distance != 0)) {
    return FALSE;
  }
  int lp_v_u = -info_v_u->distance;
  if(lp_v_u < GDD_vertex_delta_w(v, type) - GDD_vertex_delta_w(u, type)) {
    return FALSE;
  }
  SCEDA_PathInfo *info_u_kv = SCEDA_hashmap_get(from_u,kv);
  if((info_u_kv->in_edge == NULL) && (info_u_kv->distance != 0)) {
    return FALSE;
  }
  int lp_u_kv = -info_u_kv->distance;
  if(lp_u_kv <= GDD_vertex_delta_w(u, type) - GDD_vertex_delta_r(kv, type)) {
    return FALSE;
  }
  return TRUE;
}

static int vsa_aux(const char *type, SCEDA_Vertex *u, SCEDA_Vertex *v, SCEDA_Vertex *ku, SCEDA_Vertex *kv, SCEDA_HashMap *from_u, SCEDA_HashMap *from_v) {
  return (clause(type, u, v, ku, kv, from_u, from_v) || clause(type, v, u, kv, ku, from_v, from_u));
}

/* This is algorithm 1 of register saturation article (page 13) 

   @param[in] type = type
   @param[in] gk = G_k 
   @param[in] killing_map = killing function
   @param[in] amk = saturating values (in G_k) 

   G_k is modified to ensure that values of amk are simultaneously
   alive */
static void ensure_vsa(const char *type, SCEDA_Graph *gk, SCEDA_HashMap *killing_map, SCEDA_List *amk) {
  SCEDA_ListIterator us;
  SCEDA_list_iterator_init(amk, &us);
  while(SCEDA_list_iterator_has_next(&us)) {
    SCEDA_Vertex *u = SCEDA_list_iterator_next(&us);
    SCEDA_Vertex *ku = SCEDA_hashmap_get(killing_map, u);

    SCEDA_ListIterator vs;
    SCEDA_list_iterator_init(amk, &vs);
    while(SCEDA_list_iterator_has_next(&vs)) {
      SCEDA_Vertex *v = SCEDA_list_iterator_next(&vs);
      if(u == v) {
	continue;
      }

      SCEDA_Vertex *kv = SCEDA_hashmap_get(killing_map, v);
      if(ku == kv) {
	continue;
      }

      safe_call(SCEDA_graph_compute_topological_order(gk));
      SCEDA_HashMap *from_u = SCEDA_graph_shortest_path_from_in_dag(gk, u, opp_latency, NULL);
      SCEDA_HashMap *from_v = SCEDA_graph_shortest_path_from_in_dag(gk, v, opp_latency, NULL);
      
      if(!vsa_aux(type, u, v, ku, kv, from_u, from_v)) {
	if(reachable_from(v, from_u)) {
	  // u < v
	  GDD_EdgeLabel *e_u_v = GDD_edge_label_create(NULL, "serial", GDD_vertex_delta_w(u, type) - GDD_vertex_delta_w(v, type), 0);
	  GDD_EdgeLabel *e_v_ku = GDD_edge_label_create(NULL, "serial", GDD_vertex_delta_w(v, type) - GDD_vertex_delta_r(ku, type) + 1, 0);
	  SCEDA_graph_add_edge(gk, u, v, e_u_v);
	  SCEDA_graph_add_edge(gk, v, ku, e_v_ku);
	} else if(reachable_from(u, from_v)) {
	  // v < u
	  GDD_EdgeLabel *e_v_u = GDD_edge_label_create(NULL, "serial", GDD_vertex_delta_w(v, type) - GDD_vertex_delta_w(u, type), 0);
	  GDD_EdgeLabel *e_u_kv = GDD_edge_label_create(NULL, "serial", GDD_vertex_delta_w(u, type) - GDD_vertex_delta_r(kv, type) + 1, 0);
	  SCEDA_graph_add_edge(gk, v, u, e_v_u);
	  SCEDA_graph_add_edge(gk, u, kv, e_u_kv);
	} else {
	  // u || v
	  SCEDA_HashMap *from_ku = SCEDA_graph_shortest_path_from_in_dag(gk, ku, opp_latency, NULL);
	  SCEDA_HashMap *from_kv = SCEDA_graph_shortest_path_from_in_dag(gk, kv, opp_latency, NULL);
	  if(!reachable_from(v, from_ku)) {
	    GDD_EdgeLabel *e_u_v = GDD_edge_label_create(NULL, "serial", GDD_vertex_delta_w(u, type) - GDD_vertex_delta_w(v, type), 0);
	    GDD_EdgeLabel *e_v_ku = GDD_edge_label_create(NULL, "serial", GDD_vertex_delta_w(v, type) - GDD_vertex_delta_r(ku, type) + 1, 0);
	    SCEDA_graph_add_edge(gk, u, v, e_u_v);
	    SCEDA_graph_add_edge(gk, v, ku, e_v_ku);
	  } else if(!reachable_from(u, from_kv)) {
	    GDD_EdgeLabel *e_v_u = GDD_edge_label_create(NULL, "serial", GDD_vertex_delta_w(v, type) - GDD_vertex_delta_w(u, type), 0);
	    GDD_EdgeLabel *e_u_kv = GDD_edge_label_create(NULL, "serial", GDD_vertex_delta_w(u, type) - GDD_vertex_delta_r(kv, type) + 1, 0);
	    SCEDA_graph_add_edge(gk, v, u, e_v_u);
	    SCEDA_graph_add_edge(gk, u, kv, e_u_kv);
	  } else {
	    assert(FALSE);
	  }
	  SCEDA_hashmap_delete(from_kv);
	  SCEDA_hashmap_delete(from_ku);
	}
      }

      SCEDA_hashmap_delete(from_v);
      SCEDA_hashmap_delete(from_u);
    }
    SCEDA_list_iterator_cleanup(&vs);
  }
  SCEDA_list_iterator_cleanup(&us);
}

/** Build a DDG which is an extension of the original acyclic DDG such
    that it ensures a saturating schedule for the given killing
    function.

    Vertices and edges labels of the original graph are copied.

    @param[in] ddg = GDD_DAG
    @param[in] type = considered type
    @param[in] killing = killing function
    @param[out] dummy_op = opcode created to represent the exit node
    for values without killers (must be deleted manually)

    @return a DDG */
SCEDA_Graph *RS_ensure_saturating_schedule(GDD_DAG *ddg, const char *type, SCEDA_HashMap *killing, GDD_Opcode **dummy_op) {
  SCEDA_Graph *pkg = RS_ddag_pkill(ddg, type);
  SCEDA_List *amk = RS_ddag_saturating_values(ddg, type, pkg, killing); 

  SCEDA_HashMap *g_to_gk;
  SCEDA_HashMap *gk_to_g;

  SCEDA_Graph *gk = RS_ddag_extk(ddg, type, pkg, killing, &g_to_gk, &gk_to_g);

  int idx_exit = 0;
  {
    SCEDA_VerticesIterator vertices;
    SCEDA_vertices_iterator_init(gk, &vertices);
    while(SCEDA_vertices_iterator_has_next(&vertices)) {
      SCEDA_Vertex *v = SCEDA_vertices_iterator_next(&vertices);
      int idx = GDD_vertex_get_id(v);
      if(idx > idx_exit) {
	idx_exit = idx;
      }
    }
    SCEDA_vertices_iterator_cleanup(&vertices);

    idx_exit++;
  }

  *dummy_op = GDD_opcode_create("dummy_op", 0, 0);
  SCEDA_Vertex *gk_exit = SCEDA_graph_add_vertex(gk, GDD_node_label_create(idx_exit, *dummy_op));
  { 
    SCEDA_VerticesIterator vertices;
    SCEDA_vertices_iterator_init(gk, &vertices);
    while(SCEDA_vertices_iterator_has_next(&vertices)) {
      SCEDA_Vertex *vk = SCEDA_vertices_iterator_next(&vertices);
      if(vk != gk_exit) {
	if(SCEDA_vertex_out_deg(vk) == 0) {
	  SCEDA_graph_add_edge(gk, vk, gk_exit, GDD_edge_label_create(NULL, "serial", GDD_vertex_latency(vk), 0));
	}
      }
    }
    SCEDA_vertices_iterator_cleanup(&vertices);
  }

  SCEDA_List *gk_amk = SCEDA_list_create(NULL);
  SCEDA_HashMap *gk_killing = SCEDA_vertex_map_create(NULL);

  // convert AMk and killing function to G_k vertices
  {
    SCEDA_ListIterator satvals;
    SCEDA_list_iterator_init(amk, &satvals);
    while(SCEDA_list_iterator_has_next(&satvals)) {
      SCEDA_Vertex *v = SCEDA_list_iterator_next(&satvals);
      SCEDA_Vertex *gk_v = SCEDA_hashmap_get(g_to_gk, v);
      
      SCEDA_Vertex *kv = SCEDA_hashmap_get(killing, v);
      SCEDA_Vertex *gk_kv = gk_exit;
      if(kv != NULL) {
	gk_kv = SCEDA_hashmap_get(g_to_gk, kv);
      }

      SCEDA_hashmap_put(gk_killing, gk_v, gk_kv, NULL);
      safe_call(SCEDA_list_add(gk_amk, gk_v));
    }
    SCEDA_list_iterator_cleanup(&satvals);
  }

  //

  ensure_vsa(type, gk, gk_killing, gk_amk);

  //

  SCEDA_hashmap_delete(gk_killing);
  SCEDA_list_delete(gk_amk);

  SCEDA_hashmap_delete(g_to_gk);
  SCEDA_hashmap_delete(gk_to_g);

  SCEDA_list_delete(amk);
  SCEDA_graph_delete(pkg);

  return gk;
}
back to top