+/* Adds a new node info dumper callback. */
+void *dump_add_node_info_callback(dump_node_info_cb_t *cb, void *data)
+{
+ hook_entry_t *info = xmalloc(sizeof(*info));
+
+ info->hook._hook_node_info = cb;
+ info->context = data;
+ register_hook(hook_node_info, info);
+
+ return info;
+}
+
+/* Remove a previously added info dumper callback. */
+void dump_remv_node_info_callback(void *handle)
+{
+ hook_entry_t *info = handle;
+ unregister_hook(hook_node_info, info);
+ xfree(info);
+}
+
+/**
+ * Dump the node information of a node n to a file F.
+ */
+static INLINE int dump_node_info(FILE *F, ir_node *n)
+{
+ int bad = 0;
+ const ir_op_ops *ops = get_op_ops(get_irn_op(n));
+
+ fprintf(F, " info1: \"");
+ bad = dump_irnode_to_file(F, n);
+ /* call the dump_node operation if available */
+ if (ops->dump_node)
+ bad = ops->dump_node(n, F, dump_node_info_txt);
+
+ /* allow additional info to be added */
+ hook_node_info(F, n);
+ fprintf(F, "\"\n");
+
+ return bad;
+}
+
+/**
+ * checks whether a node is "constant-like" ie can be treated "block-less"
+ */
+static INLINE
+int is_constlike_node(ir_node *n) {
+ ir_opcode code = get_irn_opcode(n);
+ return (code == iro_Const || code == iro_Bad || code == iro_NoMem || code == iro_SymConst || code == iro_Unknown);
+}
+
+
+/** outputs the predecessors of n, that are constants, local. I.e.,
+ generates a copy of the constant predecessors for each node called with. */
+static void dump_const_node_local(FILE *F, ir_node *n) {
+ int i;
+ if (!get_opt_dump_const_local()) return;
+
+ /* Use visited flag to avoid outputting nodes twice.
+ initialize it first. */
+ for (i = 0; i < get_irn_arity(n); i++) {
+ ir_node *con = get_irn_n(n, i);
+ if (is_constlike_node(con)) {
+ set_irn_visited(con, get_irg_visited(current_ir_graph) - 1);
+ }
+ }
+
+ for (i = 0; i < get_irn_arity(n); i++) {
+ ir_node *con = get_irn_n(n, i);
+ if (is_constlike_node(con) && irn_not_visited(con)) {
+ int bad = 0;
+
+ mark_irn_visited(con);
+ /* Generate a new name for the node by appending the names of
+ n and const. */
+ fprintf(F, "node: {title: "); PRINT_CONSTID(n, con);
+ fprintf(F, " label: \"");
+ bad |= dump_node_label(F, con);
+ fprintf(F, "\" ");
+ bad |= dump_node_info(F, con);
+ dump_node_vcgattr(F, n, con, bad);
+ fprintf(F, "}\n");
+ }
+ }
+}
+
+/** If the block of an edge is a const_like node, dump it local with an edge */
+static void dump_const_block_local(FILE *F, ir_node *n) {
+ ir_node *blk;
+
+ if (!get_opt_dump_const_local()) return;
+
+ blk = get_nodes_block(n);
+ if (is_constlike_node(blk)) {
+ int bad = 0;
+
+ /* Generate a new name for the node by appending the names of
+ n and blk. */
+ fprintf(F, "node: {title: \""); PRINT_CONSTBLKID(n, blk);
+ fprintf(F, "\" label: \"");
+ bad |= dump_node_label(F, blk);
+ fprintf(F, "\" ");
+ bad |= dump_node_info(F, blk);
+ dump_node_vcgattr(F, n, blk, bad);
+ fprintf(F, "}\n");
+
+ fprintf(F, "edge: { sourcename: \"");
+ PRINT_NODEID(n);
+ fprintf(F, "\" targetname: \""); PRINT_CONSTBLKID(n,blk);
+
+ if (dump_edge_vcgattr_hook) {
+ fprintf(F, "\" ");
+ if (dump_edge_vcgattr_hook(F, n, -1)) {
+ fprintf(F, "}\n");
+ return;
+ } else {
+ fprintf(F, " " BLOCK_EDGE_ATTR "}\n");
+ return;
+ }
+ }
+
+ fprintf(F, "\" " BLOCK_EDGE_ATTR "}\n");
+ }
+}
+
+/**
+ * prints the error message of a node to a file F as info2.
+ */
+static void print_node_error(FILE *F, const char *err_msg)
+{
+ if (! err_msg)
+ return;
+
+ fprintf(F, " info2: \"%s\"", err_msg);
+}
+
+/**
+ * prints debug messages of a node to file F as info3.
+ */
+static void print_dbg_info(FILE *F, dbg_info *dbg)
+{
+ char buf[1024];
+
+ if (__dbg_info_snprint) {
+ buf[0] = '\0';
+ if (__dbg_info_snprint(buf, sizeof(buf), dbg) > 0)
+ fprintf(F, " info3: \"%s\"\n", buf);
+ }
+}
+
+/**
+ * Dump a node
+ */
+static void dump_node(FILE *F, ir_node *n)
+{
+ int bad = 0;
+ const char *p;
+
+ if (get_opt_dump_const_local() && is_constlike_node(n))
+ return;
+
+ /* dump this node */
+ fprintf(F, "node: {title: \""); PRINT_NODEID(n); fprintf(F, "\" label: \"");
+
+ bad = ! irn_vrfy_irg_dump(n, current_ir_graph, &p);
+ bad |= dump_node_label(F, n);
+ dump_node_ana_vals(F, n);
+ //dump_node_ana_info(F, n);
+ fprintf(F, "\" ");
+ bad |= dump_node_info(F, n);
+ print_node_error(F, p);
+ print_dbg_info(F, get_irn_dbg_info(n));
+ dump_node_vcgattr(F, n, NULL, bad);
+ fprintf(F, "}\n");
+ dump_const_node_local(F, n);
+
+ if(dump_node_edge_hook)
+ dump_node_edge_hook(F, n);
+#if DO_HEAPANALYSIS
+ dump_irn_chi_term(F, n);
+ dump_irn_state(F, n);
+#endif
+}
+
+/** dump the edge to the block this node belongs to */
+static void
+dump_ir_block_edge(FILE *F, ir_node *n) {
+ if (get_opt_dump_const_local() && is_constlike_node(n)) return;
+ if (is_no_Block(n)) {
+ ir_node *block = get_nodes_block(n);
+
+ if (get_opt_dump_const_local() && is_constlike_node(block)) {
+ dump_const_block_local(F, n);
+ } else {
+ fprintf(F, "edge: { sourcename: \"");
+ PRINT_NODEID(n);
+ fprintf(F, "\" targetname: ");
+ fprintf(F, "\""); PRINT_NODEID(block); fprintf(F, "\"");
+
+ if (dump_edge_vcgattr_hook) {
+ fprintf(F, " ");
+ if (dump_edge_vcgattr_hook(F, n, -1)) {
+ fprintf(F, "}\n");
+ return;
+ } else {
+ fprintf(F, " " BLOCK_EDGE_ATTR "}\n");
+ return;
+ }
+ }
+
+ fprintf(F, " " BLOCK_EDGE_ATTR "}\n");
+ }
+ }
+}
+
+static void
+print_data_edge_vcgattr(FILE *F, ir_node *from, int to) {
+ /*
+ * do not use get_nodes_block() here, will fail
+ * if the irg is not pinned.
+ */
+ if (get_irn_n(from, -1) == get_irn_n(get_irn_n(from, to), -1))
+ fprintf(F, INTRA_DATA_EDGE_ATTR);
+ else
+ fprintf(F, INTER_DATA_EDGE_ATTR);
+}
+
+static void
+print_mem_edge_vcgattr(FILE *F, ir_node *from, int to) {
+ /*
+ * do not use get_nodes_block() here, will fail
+ * if the irg is not pinned.
+ */
+ if (get_irn_n(from, -1) == get_irn_n(get_irn_n(from, to), -1))
+ fprintf(F, INTRA_MEM_EDGE_ATTR);
+ else
+ fprintf(F, INTER_MEM_EDGE_ATTR);
+}
+
+/** Print the vcg attributes for the edge from node from to it's to's input */
+static void print_edge_vcgattr(FILE *F, ir_node *from, int to) {
+ assert(from);
+
+ if (dump_edge_vcgattr_hook)
+ if (dump_edge_vcgattr_hook(F, from, to))
+ return;
+
+ if (dump_backedge_information_flag && is_backedge(from, to))
+ fprintf(F, BACK_EDGE_ATTR);
+
+ switch (get_irn_opcode(from)) {
+ case iro_Block:
+ fprintf(F, CF_EDGE_ATTR);
+ break;
+ case iro_Start: break;
+ case iro_End:
+ if (to >= 0) {
+ if (get_irn_mode(get_End_keepalive(from, to)) == mode_BB)
+ fprintf(F, KEEP_ALIVE_CF_EDGE_ATTR);
+ else
+ fprintf(F, KEEP_ALIVE_DF_EDGE_ATTR);
+ }
+ break;
+ default:
+ if (is_Proj(from)) {
+ if (get_irn_mode(from) == mode_M)
+ print_mem_edge_vcgattr(F, from, to);
+ else if (get_irn_mode(from) == mode_X)
+ fprintf(F, CF_EDGE_ATTR);
+ else
+ print_data_edge_vcgattr(F, from, to);
+ }
+ else if (get_irn_mode(get_irn_n(from, to)) == mode_M)
+ print_mem_edge_vcgattr(F, from, to);
+ else if (get_irn_mode(get_irn_n(from, to)) == mode_X)
+ fprintf(F, CF_EDGE_ATTR);
+ else
+ print_data_edge_vcgattr(F, from, to);
+ }
+}
+
+/** dump edges to our inputs */
+static void dump_ir_data_edges(FILE *F, ir_node *n) {
+ int i;
+ unsigned long visited = get_irn_visited(n);
+
+ if ((get_irn_op(n) == op_End) && (!dump_keepalive))
+ return;
+
+ /* dump the dependency edges. */
+ for (i = 0; i < get_irn_deps(n); ++i) {
+ ir_node *dep = get_irn_dep(n, i);
+
+ if(dep) {
+ fprintf(F, "edge: {sourcename: \"");
+ PRINT_NODEID(n);
+ fprintf(F, "\" targetname: ");
+ if ((get_opt_dump_const_local()) && is_constlike_node(dep)) {
+ PRINT_CONSTID(n, dep);
+ } else {
+ fprintf(F, "\"");
+ PRINT_NODEID(dep);
+ fprintf(F, "\"");
+ }
+ fprintf(F, " label: \"%d\" ", i);
+ fprintf(F, " color: darkgreen}\n");
+ }
+ }
+
+ for (i = 0; i < get_irn_arity(n); i++) {
+ ir_node * pred = get_irn_n(n, i);
+ assert(pred);
+
+ if ((get_interprocedural_view() && get_irn_visited(pred) < visited))
+ continue; /* pred not dumped */
+
+ if (dump_backedge_information_flag && is_backedge(n, i))
+ fprintf(F, "backedge: {sourcename: \"");
+ else
+ fprintf(F, "edge: {sourcename: \"");
+ PRINT_NODEID(n);
+ fprintf(F, "\" targetname: ");
+ if ((get_opt_dump_const_local()) && is_constlike_node(pred)) {
+ PRINT_CONSTID(n, pred);
+ } else {
+ fprintf(F, "\""); PRINT_NODEID(pred); fprintf(F, "\"");
+ }
+ fprintf(F, " label: \"%d\" ", i);
+ print_edge_vcgattr(F, n, i);
+ fprintf(F, "}\n");
+ }
+}
+
+/** Dumps a node and its edges but not the block edge
+ */
+static INLINE void
+dump_node_wo_blockedge(ir_node *n, void *env) {
+ FILE *F = env;
+ dump_node(F, n);
+ dump_ir_data_edges(F, n);
+}
+
+/** Dumps a node and its edges.
+ */
+static void
+dump_whole_node(ir_node *n, void *env) {
+ FILE *F = env;
+ dump_node_wo_blockedge(n, env);
+ if (!node_floats(n)) dump_ir_block_edge(F, n);
+}