1 /*
  2  * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 
 26 package jdk.code.tools.dot;
 27 
 28 import jdk.code.tools.renderer.CommonRenderer;
 29 
 30 import java.io.Writer;
 31 import java.lang.reflect.code.Block;
 32 import java.lang.reflect.code.Body;
 33 import java.lang.reflect.code.Op;
 34 import java.lang.reflect.code.Value;
 35 import java.util.*;
 36 
 37 /**
 38  * Created by gfrost
 39  * http://www.graphviz.org/Documentation/dotguide.pdf
 40  */
 41 public class DotRenderer extends CommonRenderer<DotRenderer> {
 42     public DotRenderer() {
 43         super();
 44     }
 45 
 46     static String sysident(Object o) {
 47         return Integer.toString(System.identityHashCode(o));
 48     }
 49 
 50     DotRenderer end() {
 51         return out().cbrace().nl();
 52     }
 53 
 54     public DotRenderer start(String name) {
 55         return append("digraph").space().append(name).obrace().in().nl();
 56     }
 57 
 58     public DotRenderer rankdir(String s) {
 59         return append("rankdir").equal().append(s).semicolon().nl();
 60     }
 61 
 62     public DotRenderer concentrate() {
 63         return append("concentrate=true").nl();
 64     }
 65 
 66     public DotRenderer newrank() {
 67         return append("newrank=true").nl();
 68     }
 69 
 70     public DotRenderer edgesFirst() {
 71         return append("outputorder=edgesfirst").nl();
 72     }
 73 
 74 
 75     public <T extends CommonRenderer<T>> DotRenderer graph(NestedRendererSAM<GraphRenderer> nb) {
 76 
 77         nb.build(new GraphRenderer(this)).end();
 78         return self();
 79     }
 80 
 81 
 82     public static class GraphRenderer extends CommonRenderer<GraphRenderer> {
 83         public GraphRenderer(DotRenderer dotRenderer) {
 84             super(dotRenderer);
 85         }
 86 
 87         GraphRenderer end() {
 88             return self();
 89         }
 90 
 91         public GraphRenderer node(String name, String shape, NestedRendererSAM<NodeRenderer> sam) {
 92             sam.build(new NodeRenderer(this, name, shape)).end();
 93             return self();
 94         }
 95 
 96         public GraphRenderer node(String name, String shape) {
 97             return node(name, shape, (n) -> n);
 98         }
 99 
100         public GraphRenderer record(String name, NestedRendererSAM<NodeRenderer> sam) {
101             sam.build(new NodeRenderer(this, name, "record")).end();
102             return self();
103         }
104 
105         public GraphRenderer record(String name) {
106             return record(name, (n) -> n);
107         }
108 
109         public GraphRenderer ellipse(String name, NestedRendererSAM<NodeRenderer> sam) {
110             sam.build(new NodeRenderer(this, name, "ellipse")).end();
111             return self();
112         }
113 
114         public GraphRenderer ellipse(String name) {
115             return ellipse(name, (n) -> n);
116         }
117 
118         public GraphRenderer ellipse(Object o, NestedRendererSAM<NodeRenderer> sam) {
119             sam.build(new NodeRenderer(this, sysident(o), "ellipse")).end();
120             return self();
121         }
122 
123         public GraphRenderer ellipse(Object o) {
124             return ellipse(o, (n) -> n);
125         }
126 
127         public GraphRenderer circle(String name, NestedRendererSAM<NodeRenderer> sam) {
128             sam.build(new NodeRenderer(this, name, "circle")).end();
129             return self();
130         }
131 
132         public GraphRenderer circle(String name) {
133             return circle(name, (n) -> n);
134         }
135 
136         public GraphRenderer invertedtrapezium(String name, NestedRendererSAM<NodeRenderer> sam) {
137             sam.build(new NodeRenderer(this, name, "invtrapezium")).end();
138             return self();
139         }
140 
141         public GraphRenderer invertedtrapezium(String name) {
142             return invertedtrapezium(name, (n) -> n);
143         }
144 
145         public GraphRenderer invertedtrapezium(Object o, NestedRendererSAM<NodeRenderer> sam) {
146             sam.build(new NodeRenderer(this, sysident(o), "invtrapezium")).end();
147             return self();
148         }
149 
150         public GraphRenderer invertedtrapezium(Object o) {
151             return invertedtrapezium(o, (n) -> n);
152         }
153 
154         public GraphRenderer box(String name, NestedRendererSAM<NodeRenderer> sam) {
155             sam.build(new NodeRenderer(this, name, "box")).end();
156             return self();
157         }
158 
159         public GraphRenderer box(String name) {
160             return box(name, (n) -> n);
161         }
162 
163         public GraphRenderer box(Object o, NestedRendererSAM<NodeRenderer> sam) {
164             sam.build(new NodeRenderer(this, sysident(o), "box")).end();
165             return self();
166         }
167 
168         public GraphRenderer box(Object o) {
169             return box(o, (n) -> n);
170         }
171 
172         public GraphRenderer hexagon(String name, NestedRendererSAM<NodeRenderer> sam) {
173             sam.build(new NodeRenderer(this, name, "hexagon")).end();
174             return self();
175         }
176 
177         public GraphRenderer hexagon(String name) {
178             return hexagon(name, (n) -> n);
179         }
180 
181         public GraphRenderer hexagon(Object o, NestedRendererSAM<NodeRenderer> sam) {
182             sam.build(new NodeRenderer(this, sysident(o), "hexagon")).end();
183             return self();
184         }
185 
186         public GraphRenderer hexagon(Object o) {
187             return hexagon(o, (n) -> n);
188         }
189 
190         public static class NodeRenderer extends CommonRenderer<NodeRenderer> {
191             NodeRenderer(GraphRenderer graphRenderer, String name, String shape) {
192                 super(graphRenderer);
193                 append(name).osbrace().append("shape").equal().oquot().append(shape).cquot().space();
194                 // append(name).osbrace().append("shape").equal().append(shape).space();
195                 //   append(name).osbrace();//.append("shape").equal().append(shape).space();
196             }
197 
198             public NodeRenderer end() {
199                 return csbrace().semicolon().nl();
200             }
201 
202             NodeRenderer label(String label, NestedRendererSAM<LabelRenderer> sam) {
203                 LabelRenderer renderer = new LabelRenderer(this, label);
204                 sam.build(renderer).end();
205                 return self();
206             }
207 
208             NodeRenderer label(NestedRendererSAM<LabelRenderer> sam) {
209                 LabelRenderer renderer = new LabelRenderer(this, "");
210                 sam.build(renderer).end();
211                 return self();
212             }
213 
214             NodeRenderer label(String label) {
215                 return label(label, (l) -> l);
216             }
217 
218 
219             public NodeRenderer color(String color) {
220                 return append("color").equal().oquot().append(color).cquot().space();
221             }
222 
223             public NodeRenderer style(String style) {
224                 return append("style").equal().oquot().append(style).cquot().space();
225             }
226 
227             public static class LabelRenderer extends CommonRenderer<LabelRenderer> {
228                 int count = 0;
229 
230                 LabelRenderer(NodeRenderer nodeRenderer, String label) {
231                     super(nodeRenderer);
232                     append("label").equal().oquot().append(label);
233                 }
234 
235                 public LabelRenderer end() {
236                     return cquot().space();
237                 }
238 
239                 LabelRenderer port(String label, String text) {
240                     if (count > 0) {
241                         pipe();
242                     }
243                     count++;
244                     return lt().append(label).gt().append(text);
245                 }
246 
247                 LabelRenderer label(String label, String text) {
248                     if (count > 0) {
249                         pipe();
250                     }
251                     count++;
252                     return append(text);
253                 }
254 
255                 LabelRenderer box(NestedRendererSAM<BoxRenderer> sam) {
256                     sam.build(new BoxRenderer(this)).end();
257                     count = 0;
258                     return self();
259                 }
260 
261                 static class BoxRenderer extends CommonRenderer<BoxRenderer> {
262                     int count = 0;
263 
264                     BoxRenderer(LabelRenderer labelRenderer) {
265                         super(labelRenderer);
266                         pipe().obrace();
267                     }
268 
269                     BoxRenderer(BoxRenderer boxRenderer) {
270                         super(boxRenderer);
271                         pipe().obrace();
272                     }
273 
274                     BoxRenderer end() {
275                         return cbrace().pipe();
276                     }
277 
278                     BoxRenderer port(String label, String text) {
279                         if (count > 0) {
280                             pipe();
281                         }
282                         count++;
283                         return lt().append(label).gt().append(text);
284                     }
285 
286                     BoxRenderer label(String text) {
287                         if (count > 0) {
288                             pipe();
289                         }
290                         count++;
291                         return append(text);
292                     }
293 
294                     BoxRenderer box(NestedRendererSAM<BoxRenderer> sam) {
295                         sam.build(new BoxRenderer(this)).end();
296                         count = 0;
297                         return self();
298                     }
299                 }
300             }
301         }
302 
303         public GraphRenderer edge(String from, String to, NestedRendererSAM<EdgeRenderer> sam) {
304             sam.build(new EdgeRenderer(this, from, to)).end();
305             return self();
306         }
307 
308         public GraphRenderer edge(String from, String to) {
309             return edge(from, to, (n) -> n);
310         }
311 
312         public GraphRenderer edge(Object from, Object to, NestedRendererSAM<EdgeRenderer> sam) {
313             sam.build(new EdgeRenderer(this, sysident(from), sysident(to))).end();
314             return self();
315         }
316 
317         public GraphRenderer edge(Object from, Object to) {
318             return edge(from, to, (n) -> n);
319         }
320 
321         public static class EdgeRenderer extends CommonRenderer<EdgeRenderer> {
322             EdgeRenderer(GraphRenderer graphRenderer, String from, String to) {
323                 super(graphRenderer);
324                 append(from).rarrow().append(to).osbrace();
325             }
326 
327             public EdgeRenderer end() {
328                 return csbrace().semicolon().nl().self();
329             }
330 
331             EdgeRenderer label(String label, NestedRendererSAM<LabelRenderer> sam) {
332                 LabelRenderer renderer = new LabelRenderer(this, label);
333                 sam.build(renderer).end();
334                 return self();
335             }
336 
337 
338             EdgeRenderer label(String label) {
339                 return label(label, (l) -> l);
340             }
341 
342             public static class LabelRenderer extends CommonRenderer<LabelRenderer> {
343 
344                 LabelRenderer(EdgeRenderer edgeRenderer, String label) {
345                     super(edgeRenderer);
346                     append("label").equal().oquot().append(label);
347                 }
348 
349                 public LabelRenderer end() {
350                     return cquot().space();
351                 }
352 
353             }
354         }
355     }
356 
357     public static void representationTree(Op op, Writer w) {
358         new DotRenderer().writer(w).start("g").graph((g) -> {
359             op.traverse(null, (t, codeElement) -> switch (codeElement) {
360                 case Body b -> {
361                     g.hexagon(b, (n) -> n.label("").style("filled"));
362                     g.edge(b.parentOp(), b);
363                     yield null;
364                 }
365                 case Block b -> {
366                     g.box(b, (n) -> n.label(""));
367                     g.edge(b.parentBody(), b);
368                     yield null;
369                 }
370                 case Op o -> {
371                     if (o instanceof Op.Terminating) {
372                         g.ellipse(o, (n) -> n.label(o.opName()).style("filled"));
373                     } else {
374                         g.ellipse(o, (n) -> n.label(o.opName()));
375                     }
376                     if (o.parentBlock() != null) {
377                         g.edge(o.parentBlock(), o);
378                     }
379                     yield null;
380                 }
381             });
382             return g;
383         }).end();
384 
385     }
386 
387     public static void bodyGraph(Body body, Writer w) {
388         Block eb = body.entryBlock();
389         Deque<Block> stack = new ArrayDeque<>();
390         Set<Block> visited = new HashSet<>();
391         stack.push(eb);
392         new DotRenderer().writer(w).start("g").graph((g) -> {
393             while (!stack.isEmpty()) {
394                 Block b = stack.pop();
395                 if (!visited.add(b)) {
396                     continue;
397                 }
398 
399                 g.box(b, (box) -> box.label(""));
400 
401                 List<Block.Reference> successors = b.terminatingOp().successors();
402                 for (Block.Reference s : successors) {
403                     g.edge(b, s.targetBlock());
404 
405                     stack.push(s.targetBlock());
406                 }
407             }
408             return g;
409         }).end();
410 
411     }
412 
413     public static void bodyDominatorTree(Body body, Writer w) {
414         Block eb = body.entryBlock();
415         Map<Block, Block> idoms = body.immediateDominators();
416 
417         new DotRenderer().writer(w).start("g").graph((g) -> {
418             for (Map.Entry<Block, Block> e : idoms.entrySet()) {
419                 Block child = e.getKey();
420                 Block parent = e.getValue();
421 
422                 g.box(child, (b) -> b.label(""));
423 
424                 if (child != eb) {
425                     g.edge(parent, child);
426                 }
427             }
428             return g;
429         }).end();
430     }
431 
432     /**
433      * Generates a body data dependence graph for a given body.
434      *
435      * @param body              the body
436      * @param names             a map of block arguments to names
437      * @param traverseblockArgs true if a graph is produced, otherwise a DAG
438      * @param w                 the writer to write the sr.dot file
439      */
440     public static void dataDependenceGraph(Body body, Map<Block.Parameter, String> names,
441                                            boolean traverseblockArgs, Writer w) {
442 
443 
444         record Edge(Value from, Value to) {
445         }
446         new DotRenderer().writer(w).start("SR").graph((g) -> {
447             Set<Value> visted = new HashSet<>();
448             Set<Edge> vistedEdges = new HashSet<>();
449             Deque<Value> stack = new ArrayDeque<>(getValues(body));
450             while (!stack.isEmpty()) {
451                 Value v = stack.pop();
452                 if (!visted.add(v)) {
453                     continue;
454                 }
455 
456                 if (v instanceof Op.Result or) {
457                     if (!or.op().operands().isEmpty() || !(or.op() instanceof Op.Terminating)) {
458                         g.invertedtrapezium(v, (node) -> node.label(or.op().opName()));
459                     }
460                 } else if (v instanceof Block.Parameter ba) {
461                     String n = names.get(v);
462                     if (n != null) {
463                         g.invertedtrapezium(v, (node) -> node.label(n).style("filled"));
464                     } else {
465                         Block b = ba.declaringBlock();
466 
467                         g.box(v, (node) -> node.label("(" + b.parameters().indexOf(ba) + ")").style("filled"));
468                     }
469                 }
470 
471                 Set<Op.Result> uses = v.uses();
472                 stack.addAll(uses);
473                 for (Op.Result use : uses) {
474                     if (traverseblockArgs && use.op() instanceof Op.Terminating) {
475                         for (Block.Reference s : use.op().successors()) {
476                             int i = s.arguments().indexOf(v);
477                             if (i != -1) {
478                                 Block.Parameter ba = s.targetBlock().parameters().get(i);
479 
480                                 if (vistedEdges.add(new Edge(v, ba))) {
481                                     g.edge(v, ba);
482                                 }
483                                 stack.add(ba);
484                             }
485                         }
486                     }
487 
488                     if (use.op().operands().contains(v)) {
489                         if (vistedEdges.add(new Edge(v, use))) {
490                             g.edge(v, use);
491                         }
492                     }
493                 }
494             }
495             return g;
496         }).end();
497     }
498 
499     private static List<Value> getValues(Body r) {
500         return r.traverse(new ArrayList<>(), (values, codeElement) -> switch (codeElement) {
501             case Block b -> {
502                 values.addAll(b.parameters());
503                 yield values;
504             }
505             case Op op -> {
506                 values.add(op.result());
507                 yield values;
508             }
509             default -> values;
510         });
511     }
512 }