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