/*
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 <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <libxml/tree.h>
#include <libxml/parser.h>
#include "util.h"
#include "opcode.h"
#include "arch.h"
#include "ddg.h"
#include "ddg_xml.h"
/** Include xml utilities functions */
#include "xmlutil.h"
#include "boxed.h"
typedef struct {
GDD_Arch *arch;
SCEDA_Graph *graph;
SCEDA_HashMap *vertice; // id -> vertex
int keep_lambda;
int ii_min;
int undef;
int error;
} BuildCtxt;
static SCEDA_Vertex *get_nth_vertex(BuildCtxt *ctxt, int n) {
boxed(int) key = boxed_create(int, n);
SCEDA_Vertex *v = SCEDA_hashmap_get(ctxt->vertice, key);
if(v != NULL) {
/* fprintf(stderr,"fetching vertex #%d\n",n); */
boxed_delete(key);
return v;
}
/* fprintf(stderr,"creating vertex #%d\n",n); */
v = SCEDA_graph_add_vertex(ctxt->graph, NULL);
SCEDA_hashmap_put(ctxt->vertice, key, v, NULL);
ctxt->undef++;
return v;
}
static void readVertex(BuildCtxt *ctxt, const xmlChar **attrs) {
int i;
i = findAttr("id", attrs);
int v_id = atoi_xml(attrs[i]);
i = findAttr("type", attrs);
char *mnemo = (char *)attrs[i];
GDD_Opcode *op = GDD_arch_get_opcode(ctxt->arch, mnemo);
if(op == NULL) {
fprintf(stderr,"unknown mnemonic %s\n", mnemo);
ctxt->error = TRUE;
return;
}
SCEDA_Vertex *v = get_nth_vertex(ctxt, v_id);
if(SCEDA_vertex_get_data(GDD_NodeLabel *, v) == NULL) {
/* fprintf(stderr,"------------------\n"); */
/* fprintf(stderr,"binding vertex #%d to opcode\n",v_id); */
/* GDD_opcode_fprint(stderr, op); */
/* fprintf(stderr,"------------------\n"); */
SCEDA_vertex_set_data(v, GDD_node_label_create(v_id, op));
ctxt->undef--;
}
}
static void readEdge(BuildCtxt *ctxt, const xmlChar **attrs) {
int i;
i = findAttr("distance", attrs);
int distance = atoi_xml(attrs[i]);
if((distance == 0) || (ctxt->keep_lambda)) {
i = findAttr("src", attrs);
int vs_id = atoi_xml(attrs[i]);
SCEDA_Vertex *v_s = get_nth_vertex(ctxt, vs_id);
i = findAttr("dst", attrs);
int vt_id = atoi_xml(attrs[i]);
SCEDA_Vertex *v_t = get_nth_vertex(ctxt, vt_id);
i = findAttr("latency", attrs);
int latency = atoi_xml(attrs[i]);
i = findAttr("typedep", attrs);
char *dep = (char *)attrs[i];
if(strcmp(dep, "virtual") == 0) {
fprintf(stderr,"error: virtual dependency forbidden\n");
ctxt->error = TRUE;
return;
}
char *regtype = NULL;
i = findOptAttr("regtype", attrs);
if(i != -1) {
regtype = (char *)attrs[i];
if(strcmp(regtype, "(null)") == 0) {
regtype = NULL;
} else {
if(GDD_arch_get_regtype(ctxt->arch, regtype) <= 0) {
fprintf(stderr,"unknown register type %s\n",regtype);
ctxt->error = TRUE;
return;
}
}
}
GDD_EdgeLabel *e = GDD_edge_label_create(regtype, dep, latency, distance);
/* fprintf(stderr,"adding an edge from #%d to #%d\n", vs_id, vt_id); */
SCEDA_graph_add_edge(ctxt->graph, v_s, v_t, e);
} else {
fprintf(stderr,"ignoring interloop dependency\n");
}
}
// Appelée à chaque début d'élément
static void beginElement(void *user_data, const xmlChar *name, const xmlChar **attrs) {
BuildCtxt *ctxt = (BuildCtxt *)user_data;
if(ctxt->error) {
return;
}
if (xmlStrEqual(name, (xmlChar *)"operation")) {
readVertex(ctxt, attrs);
} else if (xmlStrEqual(name, (xmlChar *)"edge")) {
readEdge(ctxt, attrs);
} else if(xmlStrEqual(name, (xmlChar *)"siraloop")) {
int i;
i = findAttr("version", attrs);
int version = atoi_xml(attrs[i]);
if(version != 1) {
fprintf(stderr,"wrong ddg version: %d\n",version);
ctxt->error = TRUE;
return;
}
i = findAttr("II_min", attrs);
ctxt->ii_min = atoi_xml(attrs[i]);
} else {
fprintf(stderr,"ignoring %s tag\n", name);
}
}
static int boxed_int_match(boxed(int) x, boxed(int) y) {
return boxed_get(x) == boxed_get(y);
}
static unsigned int boxed_int_hash(boxed(int) x) {
return boxed_get(x);
}
/** Parse a GDD from a file stream.
@param[in] ignore_interloop = TRUE to ignore interloop dependency, FALSE otherwise
@param[in] arch = associated architecture
@param[in] stream = file stream
@param[out] ii_min = min II (filled by the function if not NULL)
@return the parsed GDD or NULL in case of error */
SCEDA_Graph *GDD_of_xml_stream(int ignore_interloop, GDD_Arch *arch, FILE *stream, int *ii_min) {
BuildCtxt ctxt;
ctxt.arch = arch;
ctxt.graph = SCEDA_graph_create((SCEDA_delete_fun)GDD_node_label_delete, (SCEDA_delete_fun)GDD_edge_label_delete);
ctxt.vertice = SCEDA_hashmap_create((SCEDA_delete_fun)boxed_delete,
NULL,
(SCEDA_match_fun)boxed_int_match,
(SCEDA_hash_fun)boxed_int_hash);
ctxt.keep_lambda = !ignore_interloop;
ctxt.ii_min = 0;
ctxt.undef = 0;
ctxt.error = FALSE;
xmlParserCtxtPtr xmlctxt;
xmlSAXHandler sax = { 0 };
// initialise SAX callbacks
sax.startElement = beginElement;
// create SAX context
if ((xmlctxt = xmlCreateIOParserCtxt(&sax, &ctxt, sax_read, sax_close, stream, XML_CHAR_ENCODING_NONE)) == NULL) {
fprintf(stderr,"error while creating SAX context\n");
SCEDA_hashmap_delete(ctxt.vertice);
SCEDA_graph_delete(ctxt.graph);
return NULL;
}
// parse stream
if(xmlParseDocument(xmlctxt) != 0) {
ctxt.error = TRUE;
}
//
if(ctxt.undef != 0) {
fprintf(stderr,"error: there remains an undefined vertex.\n");
ctxt.error = TRUE;
}
xmlFreeParserCtxt(xmlctxt);
if(ctxt.error) {
fprintf(stderr,"error while parsing ddg file\n");
SCEDA_hashmap_delete(ctxt.vertice);
SCEDA_graph_delete(ctxt.graph);
return NULL;
}
if(ii_min != NULL) {
*ii_min = ctxt.ii_min;
}
SCEDA_hashmap_delete(ctxt.vertice);
return ctxt.graph;
}
/** Parse a GDD from a file.
@param[in] ignore_interloop = TRUE to ignore interloop dependency, FALSE otherwise
@param[in] arch = associated architecture
@param[in] filename = filename
@param[out] ii_min = min II (filled by the function if not NULL)
@return the parsed GDD or NULL in case of error */
SCEDA_Graph *GDD_of_xml_file(int ignore_interloop, GDD_Arch *arch, const char *filename, int *ii_min) {
FILE *f = fopen(filename, "r");
if(f == NULL) {
fprintf(stderr,"error while opening %s\n", filename);
return NULL;
}
SCEDA_Graph *dag = GDD_of_xml_stream(ignore_interloop, arch, f, ii_min);
fclose(f);
return dag;
}
/** XML dump of a GDD to a stream
@param[in] stream = stream on which the dump will occur
@param[in] ddg = DDG
@param[in] ii_min = MII
*/
void GDD_to_xml_stream(FILE *stream, SCEDA_Graph *ddg, int ii_min) {
fprintf(stream,"<?xml version=\"1.0\"?>\n");
fprintf(stream,"<siraloop version=\"1\" II_min=\"%d\">\n",ii_min);
{
SCEDA_VerticesIterator vertices;
SCEDA_vertices_iterator_init(ddg, &vertices);
while(SCEDA_vertices_iterator_has_next(&vertices)) {
SCEDA_Vertex *v = SCEDA_vertices_iterator_next(&vertices);
int idx = GDD_vertex_get_id(v);
fprintf(stream,"\t<operation type=\"%s\" id=\"%d\"/>\n",GDD_vertex_mnemonic(v),idx);
}
SCEDA_vertices_iterator_cleanup(&vertices);
}
{
SCEDA_EdgesIterator edges;
SCEDA_edges_iterator_init(ddg, &edges);
while(SCEDA_edges_iterator_has_next(&edges)) {
SCEDA_Edge *e = SCEDA_edges_iterator_next(&edges);
SCEDA_Vertex *u = SCEDA_edge_source(e);
SCEDA_Vertex *v = SCEDA_edge_target(e);
int idx_u = GDD_vertex_get_id(u);
int idx_v = GDD_vertex_get_id(v);
char *depkind = "unknown";
char *regtype = "(null)";
switch(GDD_edge_depkind(e)) {
case GDD_FLOWDEP_REG:
depkind="flowdep_reg";
regtype = GDD_edge_regtype(e);
break;
case GDD_ANTIDEP_REG:
depkind="antidep_reg";
regtype = GDD_edge_regtype(e);
break;
case GDD_OUTPUTDEP_REG:
depkind="outputdep_reg";
regtype = GDD_edge_regtype(e);
break;
case GDD_INPUTDEP_REG:
depkind="inputdep_reg";
regtype = GDD_edge_regtype(e);
break;
case GDD_KILLERDEP:
depkind="killerdep";
regtype = GDD_edge_regtype(e);
break;
case GDD_REUSEDEP:
depkind="reusedep";
regtype = GDD_edge_regtype(e);
break;
case GDD_FLOWDEP_MEM:
depkind="flowdep_mem";
break;
case GDD_ANTIDEP_MEM:
depkind="antidep_mem";
break;
case GDD_OUTPUTDEP_MEM:
depkind="outputdep_mem";
break;
case GDD_INPUTDEP_MEM:
depkind="inputdep_mem";
break;
case GDD_SPILLDEP_MEM:
depkind="spilldep_mem";
break;
case GDD_OTHERDEP_MEM:
depkind="otherdep_mem";
break;
case GDD_SERIALDEP:
depkind="serial";
break;
default:
depkind="unknown";
}
fprintf(stream,"\t<edge src=\"%d\" dst=\"%d\" latency=\"%d\" distance=\"%d\" typedep=\"%s\" regtype=\"%s\"/>\n",idx_u,idx_v,GDD_edge_latency(e),GDD_edge_distance(e),depkind,regtype);
}
SCEDA_edges_iterator_cleanup(&edges);
}
fprintf(stream,"</siraloop>\n");
}