seafoam 0.6 → 0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/bgv2isabelle +1 -5
- data/bin/bgv2json +1 -5
- data/bin/cfg2asm +1 -5
- data/bin/seafoam +1 -5
- data/lib/seafoam/bgv/bgv_parser.rb +10 -2
- data/lib/seafoam/commands.rb +107 -32
- data/lib/seafoam/graal/pi.rb +18 -0
- data/lib/seafoam/graph.rb +33 -1
- data/lib/seafoam/graphviz_writer.rb +24 -2
- data/lib/seafoam/json_writer.rb +1 -3
- data/lib/seafoam/{annotators → passes}/fallback.rb +4 -4
- data/lib/seafoam/{annotators → passes}/graal.rb +43 -15
- data/lib/seafoam/passes/truffle.rb +58 -0
- data/lib/seafoam/passes.rb +61 -0
- data/lib/seafoam/version.rb +1 -1
- data/lib/seafoam.rb +5 -4
- metadata +26 -62
- data/.github/probots.yml +0 -2
- data/.github/workflows/workflows.yml +0 -39
- data/.gitignore +0 -7
- data/.rubocop.yml +0 -37
- data/.ruby-version +0 -1
- data/.seafoam/config +0 -1
- data/CODE_OF_CONDUCT.md +0 -128
- data/CONTRIBUTING.md +0 -5
- data/Gemfile +0 -2
- data/LICENSE.md +0 -7
- data/README.md +0 -378
- data/demos/box-unbox-stats +0 -65
- data/docs/annotators.md +0 -43
- data/docs/bgv.md +0 -293
- data/docs/getting-graphs.md +0 -59
- data/docs/images/igv.png +0 -0
- data/docs/images/seafoam.png +0 -0
- data/docs/images/spotlight-igv.png +0 -0
- data/docs/images/spotlight-seafoam.png +0 -0
- data/docs/json.md +0 -35
- data/examples/Fib.java +0 -24
- data/examples/MatMult.java +0 -39
- data/examples/fib-java.bgv.gz +0 -0
- data/examples/fib.js +0 -15
- data/examples/fib.rb +0 -15
- data/examples/identity.rb +0 -13
- data/examples/java/Irreducible.class +0 -0
- data/examples/java/Irreducible.j +0 -35
- data/examples/java/IrreducibleDecompiled.java +0 -21
- data/examples/java/JavaExamples.java +0 -418
- data/examples/matmult.rb +0 -29
- data/examples/overflow.rb +0 -13
- data/examples/ruby/clamps.rb +0 -20
- data/examples/ruby/graal.patch +0 -15
- data/examples/ruby/ruby_examples.rb +0 -278
- data/lib/seafoam/annotators.rb +0 -54
- data/lib/seafoam/config.rb +0 -34
- data/seafoam.gemspec +0 -21
- data/spec/seafoam/annotators/fallback_spec.rb +0 -69
- data/spec/seafoam/annotators/graal_spec.rb +0 -96
- data/spec/seafoam/annotators_spec.rb +0 -61
- data/spec/seafoam/bgv/bgv_parser_spec.rb +0 -157
- data/spec/seafoam/binary/io_binary_reader_spec.rb +0 -176
- data/spec/seafoam/cfg/cfg_parser_spec.rb +0 -21
- data/spec/seafoam/cfg/disassembler_spec.rb +0 -32
- data/spec/seafoam/command_spec.rb +0 -316
- data/spec/seafoam/graph_spec.rb +0 -172
- data/spec/seafoam/graphviz_writer_spec.rb +0 -63
- data/spec/seafoam/json_writer_spec.rb +0 -14
- data/spec/seafoam/spec_helpers.rb +0 -34
- data/spec/seafoam/spotlight_spec.rb +0 -38
- data/tools/render-all +0 -36
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f8665fe768d4900820ca676269cd25028306e8253c2109c71ff4cb31dd7c6e44
|
4
|
+
data.tar.gz: fd8c52e5cf3daafa9b2673265067b743cc644755c3772c02904c2b815b3a93d2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 815e7031027718cea7a933b7fc5ad822f754f05e3ae5bdc2066775183a9ca98c841b54e682a148c4437a29b1f95c4ede5833a2a1f2995d2c55296a800e71b269
|
7
|
+
data.tar.gz: 462aaad5134c8b6cce229e8456742151354333c5e7c02283e2e90917fa7ad238f3f4f71770519357117f42b1579928ddb0bba3f04b31ddef68d7e41e92f8c016
|
data/bin/bgv2isabelle
CHANGED
@@ -5,12 +5,8 @@ require 'seafoam'
|
|
5
5
|
# This is the 'bgv2isabelle' command line entry point.
|
6
6
|
|
7
7
|
begin
|
8
|
-
# Load configuraiton.
|
9
|
-
config = Seafoam::Config.new
|
10
|
-
config.load_config
|
11
|
-
|
12
8
|
# Run the command line.
|
13
|
-
commands = Seafoam::Commands.new($stdout
|
9
|
+
commands = Seafoam::Commands.new($stdout)
|
14
10
|
commands.bgv2isabelle(*ARGV)
|
15
11
|
rescue StandardError => e
|
16
12
|
if $DEBUG
|
data/bin/bgv2json
CHANGED
@@ -5,12 +5,8 @@ require 'seafoam'
|
|
5
5
|
# This is the 'bgv2json' command line entry point.
|
6
6
|
|
7
7
|
begin
|
8
|
-
# Load configuraiton.
|
9
|
-
config = Seafoam::Config.new
|
10
|
-
config.load_config
|
11
|
-
|
12
8
|
# Run the command line.
|
13
|
-
commands = Seafoam::Commands.new($stdout
|
9
|
+
commands = Seafoam::Commands.new($stdout)
|
14
10
|
commands.bgv2json(*ARGV)
|
15
11
|
rescue StandardError => e
|
16
12
|
if $DEBUG
|
data/bin/cfg2asm
CHANGED
@@ -5,12 +5,8 @@ require 'seafoam'
|
|
5
5
|
# This is the 'cfg2asm' command line entry point.
|
6
6
|
|
7
7
|
begin
|
8
|
-
# Load configuraiton.
|
9
|
-
config = Seafoam::Config.new
|
10
|
-
config.load_config
|
11
|
-
|
12
8
|
# Run the command line.
|
13
|
-
commands = Seafoam::Commands.new($stdout
|
9
|
+
commands = Seafoam::Commands.new($stdout)
|
14
10
|
commands.cfg2asm(*ARGV)
|
15
11
|
rescue StandardError => e
|
16
12
|
if $DEBUG
|
data/bin/seafoam
CHANGED
@@ -5,12 +5,8 @@ require 'seafoam'
|
|
5
5
|
# This is the 'seafoam' command line entry point.
|
6
6
|
|
7
7
|
begin
|
8
|
-
# Load configuraiton.
|
9
|
-
config = Seafoam::Config.new
|
10
|
-
config.load_config
|
11
|
-
|
12
8
|
# Run the command line.
|
13
|
-
commands = Seafoam::Commands.new($stdout
|
9
|
+
commands = Seafoam::Commands.new($stdout)
|
14
10
|
commands.seafoam(*ARGV)
|
15
11
|
rescue StandardError => e
|
16
12
|
if $DEBUG
|
@@ -127,13 +127,21 @@ module Seafoam
|
|
127
127
|
end
|
128
128
|
end
|
129
129
|
end
|
130
|
-
|
130
|
+
|
131
|
+
# Read block information.
|
132
|
+
@reader.read_sint32.times do
|
133
|
+
block_id = @reader.read_sint32
|
134
|
+
block_nodes = @reader.read_sint32.times.map { @reader.read_sint32 }
|
135
|
+
# Followers aren't used but could be.
|
136
|
+
@reader.read_sint32.times.map { @reader.read_sint32 }
|
137
|
+
graph.create_block block_id, block_nodes
|
138
|
+
end
|
131
139
|
graph
|
132
140
|
end
|
133
141
|
|
134
142
|
# Skip over a graph, having read or skipped its headers.
|
135
143
|
def skip_graph
|
136
|
-
# Already read BEGIN_GRAPH, id, format, args, and props
|
144
|
+
# Already read BEGIN_GRAPH, id, format, args, and props.
|
137
145
|
@reader.read_sint32.times do
|
138
146
|
@reader.skip_int32
|
139
147
|
node_class = read_pool_object
|
data/lib/seafoam/commands.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
require 'json'
|
2
|
+
require 'set'
|
2
3
|
|
3
4
|
module Seafoam
|
4
5
|
# Implementations of the command-line commands that you can run in Seafoam.
|
5
6
|
class Commands
|
6
|
-
def initialize(out
|
7
|
+
def initialize(out)
|
7
8
|
@out = out
|
8
|
-
@config = config
|
9
9
|
end
|
10
10
|
|
11
11
|
# Run the general seafoam command.
|
@@ -15,24 +15,7 @@ module Seafoam
|
|
15
15
|
when nil, 'help', '-h', '--help', '-help'
|
16
16
|
raise ArgumentError, "unexpected arguments #{args.join(' ')}" unless args.empty?
|
17
17
|
|
18
|
-
|
19
|
-
@out.puts ' file.bgv list'
|
20
|
-
@out.puts ' file.bgv[:graph][:node[-edge]] search term...'
|
21
|
-
@out.puts ' file.bgv[:graph][:node[-edge]] edges'
|
22
|
-
@out.puts ' file.bgv[:graph][:node[-edge]] props'
|
23
|
-
@out.puts ' file.bgv:graph:node source'
|
24
|
-
@out.puts ' file.bgv:graph render'
|
25
|
-
@out.puts ' --spotlight n,n,n...'
|
26
|
-
@out.puts ' --out graph.pdf'
|
27
|
-
@out.puts ' graph.svg'
|
28
|
-
@out.puts ' graph.png'
|
29
|
-
@out.puts ' graph.dot'
|
30
|
-
@out.puts ' --show-frame-state'
|
31
|
-
@out.puts ' --hide-floating'
|
32
|
-
@out.puts ' --no-reduce-edges'
|
33
|
-
@out.puts ' --option key value'
|
34
|
-
@out.puts ' --help'
|
35
|
-
@out.puts ' --version'
|
18
|
+
help(*args)
|
36
19
|
when 'version', '-v', '-version', '--version'
|
37
20
|
version(*args)
|
38
21
|
else
|
@@ -57,6 +40,8 @@ module Seafoam
|
|
57
40
|
render name, *args
|
58
41
|
when 'debug'
|
59
42
|
debug name, *args
|
43
|
+
when 'describe'
|
44
|
+
describe name, *args
|
60
45
|
else
|
61
46
|
raise ArgumentError, "unknown command #{command}"
|
62
47
|
end
|
@@ -262,7 +247,7 @@ module Seafoam
|
|
262
247
|
end
|
263
248
|
|
264
249
|
def search_object(tag, object, terms)
|
265
|
-
full_text = JSON.generate(object)
|
250
|
+
full_text = JSON.generate(JSONWriter.prepare_json(object))
|
266
251
|
full_text_down = full_text.downcase
|
267
252
|
start = 0
|
268
253
|
terms.each do |t|
|
@@ -298,7 +283,7 @@ module Seafoam
|
|
298
283
|
parser.read_graph_header
|
299
284
|
graph = parser.read_graph
|
300
285
|
if node_id
|
301
|
-
|
286
|
+
Passes.apply graph
|
302
287
|
node = graph.nodes[node_id]
|
303
288
|
raise ArgumentError, 'node not found' unless node
|
304
289
|
|
@@ -391,14 +376,66 @@ module Seafoam
|
|
391
376
|
end
|
392
377
|
end
|
393
378
|
|
379
|
+
# seafoam file.bgv:n describe
|
380
|
+
def describe(name, *args)
|
381
|
+
file, graph_index, *rest = parse_name(name)
|
382
|
+
|
383
|
+
if graph_index.nil? || !rest.all?(&:nil?)
|
384
|
+
raise ArgumentError, 'describe only works with a graph'
|
385
|
+
end
|
386
|
+
raise ArgumentError, 'describe does not take arguments' unless args.empty?
|
387
|
+
|
388
|
+
parser = BGV::BGVParser.new(file)
|
389
|
+
parser.read_file_header
|
390
|
+
parser.skip_document_props
|
391
|
+
|
392
|
+
loop do
|
393
|
+
index, = parser.read_graph_preheader
|
394
|
+
break unless index
|
395
|
+
|
396
|
+
parser.skip_graph_header
|
397
|
+
|
398
|
+
if index != graph_index
|
399
|
+
parser.skip_graph
|
400
|
+
next
|
401
|
+
end
|
402
|
+
|
403
|
+
graph = parser.read_graph
|
404
|
+
notes = Set.new
|
405
|
+
|
406
|
+
graph.nodes.each_value do |node|
|
407
|
+
node_class = node.props.dig(:node_class, :node_class)
|
408
|
+
case node_class
|
409
|
+
when 'org.graalvm.compiler.nodes.IfNode'
|
410
|
+
notes.add 'branches'
|
411
|
+
when 'org.graalvm.compiler.nodes.LoopBeginNode'
|
412
|
+
notes.add 'loops'
|
413
|
+
when 'org.graalvm.compiler.nodes.InvokeNode', 'org.graalvm.compiler.nodes.InvokeWithExceptionNode'
|
414
|
+
notes.add 'calls'
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
notes.add 'deopts' if graph.nodes[0].outputs.map(&:to)
|
419
|
+
.all? { |t| t.props.dig(:node_class, :node_class) == 'org.graalvm.compiler.nodes.DeoptimizeNode' }
|
420
|
+
|
421
|
+
notes.add 'linear' unless notes.include?('branches') || notes.include?('loops')
|
422
|
+
|
423
|
+
@out.puts ["#{graph.nodes.size} nodes", *notes].join(', ')
|
424
|
+
|
425
|
+
break
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
394
429
|
# seafoam file.bgv:n render options...
|
395
430
|
def render(name, *args)
|
396
431
|
file, graph_index, *rest = parse_name(name)
|
397
432
|
raise ArgumentError, 'render needs at least a graph' unless graph_index
|
398
433
|
raise ArgumentError, 'render only works with a graph' unless rest == [nil, nil]
|
399
434
|
|
400
|
-
|
435
|
+
pass_options = {
|
436
|
+
simplify_truffle_args: true,
|
401
437
|
hide_frame_state: true,
|
438
|
+
hide_pi: true,
|
402
439
|
hide_floating: false,
|
403
440
|
reduce_edges: true
|
404
441
|
}
|
@@ -406,6 +443,7 @@ module Seafoam
|
|
406
443
|
args = args.dup
|
407
444
|
out_file = nil
|
408
445
|
explicit_out_file = false
|
446
|
+
draw_blocks = false
|
409
447
|
until args.empty?
|
410
448
|
arg = args.shift
|
411
449
|
case arg
|
@@ -418,12 +456,18 @@ module Seafoam
|
|
418
456
|
raise ArgumentError, 'no list for --spotlight' unless spotlight_arg
|
419
457
|
|
420
458
|
spotlight_nodes = spotlight_arg.split(',').map { |n| Integer(n) }
|
459
|
+
when '--full-truffle-args'
|
460
|
+
pass_options[:simplify_truffle_args] = false
|
421
461
|
when '--show-frame-state'
|
422
|
-
|
462
|
+
pass_options[:hide_frame_state] = false
|
463
|
+
when '--show-pi'
|
464
|
+
pass_options[:hide_pi] = false
|
423
465
|
when '--hide-floating'
|
424
|
-
|
466
|
+
pass_options[:hide_floating] = true
|
425
467
|
when '--no-reduce-edges'
|
426
|
-
|
468
|
+
pass_options[:reduce_edges] = false
|
469
|
+
when '--draw-blocks'
|
470
|
+
draw_blocks = true
|
427
471
|
when '--option'
|
428
472
|
key = args.shift
|
429
473
|
raise ArgumentError, 'no key for --option' unless key
|
@@ -432,7 +476,7 @@ module Seafoam
|
|
432
476
|
raise ArgumentError, "no value for --option #{key}" unless out_file
|
433
477
|
|
434
478
|
value = { 'true' => true, 'false' => 'false' }.fetch(key, value)
|
435
|
-
|
479
|
+
pass_options[key.to_sym] = value
|
436
480
|
else
|
437
481
|
raise ArgumentError, "unexpected option #{arg}"
|
438
482
|
end
|
@@ -455,7 +499,7 @@ module Seafoam
|
|
455
499
|
with_graph(file, graph_index) do |parser|
|
456
500
|
parser.skip_graph_header
|
457
501
|
graph = parser.read_graph
|
458
|
-
|
502
|
+
Passes.apply graph, pass_options
|
459
503
|
if spotlight_nodes
|
460
504
|
spotlight = Spotlight.new(graph)
|
461
505
|
spotlight_nodes.each do |node_id|
|
@@ -469,14 +513,14 @@ module Seafoam
|
|
469
513
|
if out_format == :dot
|
470
514
|
File.open(out_file, 'w') do |stream|
|
471
515
|
writer = GraphvizWriter.new(stream)
|
472
|
-
writer.write_graph graph
|
516
|
+
writer.write_graph graph, false, draw_blocks
|
473
517
|
end
|
474
518
|
else
|
475
519
|
begin
|
476
520
|
IO.popen(['dot', "-T#{out_format}", '-o', out_file], 'w') do |stream|
|
477
521
|
writer = GraphvizWriter.new(stream)
|
478
522
|
hidpi = out_format == :png
|
479
|
-
writer.write_graph graph, hidpi
|
523
|
+
writer.write_graph graph, hidpi, draw_blocks
|
480
524
|
end
|
481
525
|
rescue Errno::ENOENT
|
482
526
|
raise 'Could not run Graphviz - is it installed?'
|
@@ -565,6 +609,32 @@ module Seafoam
|
|
565
609
|
raise ArgumentError, 'graph not found' unless graph_found
|
566
610
|
end
|
567
611
|
|
612
|
+
# Prints help.
|
613
|
+
def help(*_args)
|
614
|
+
@out.puts 'seafoam file.bgv info'
|
615
|
+
@out.puts ' file.bgv list'
|
616
|
+
@out.puts ' file.bgv[:graph][:node[-edge]] search term...'
|
617
|
+
@out.puts ' file.bgv[:graph][:node[-edge]] edges'
|
618
|
+
@out.puts ' file.bgv[:graph][:node[-edge]] props'
|
619
|
+
@out.puts ' file.bgv:graph:node source'
|
620
|
+
@out.puts ' file.bgv:graph describe'
|
621
|
+
@out.puts ' file.bgv:graph render'
|
622
|
+
@out.puts ' --spotlight n,n,n...'
|
623
|
+
@out.puts ' --out graph.pdf'
|
624
|
+
@out.puts ' graph.svg'
|
625
|
+
@out.puts ' graph.png'
|
626
|
+
@out.puts ' graph.dot'
|
627
|
+
@out.puts ' --full-truffle-args'
|
628
|
+
@out.puts ' --show-frame-state'
|
629
|
+
@out.puts ' --show-pi'
|
630
|
+
@out.puts ' --hide-floating'
|
631
|
+
@out.puts ' --no-reduce-edges'
|
632
|
+
@out.puts ' --draw-blocks'
|
633
|
+
@out.puts ' --option key value'
|
634
|
+
@out.puts ' --help'
|
635
|
+
@out.puts ' --version'
|
636
|
+
end
|
637
|
+
|
568
638
|
# Prints the version.
|
569
639
|
def version(*args)
|
570
640
|
raise ArgumentError, "unexpected arguments #{args.join(' ')}" unless args.empty?
|
@@ -598,10 +668,15 @@ module Seafoam
|
|
598
668
|
|
599
669
|
# Open a file for the user if possible.
|
600
670
|
def autoopen(file)
|
601
|
-
|
671
|
+
return unless @out.tty?
|
672
|
+
|
673
|
+
case RUBY_PLATFORM
|
674
|
+
when /darwin/
|
602
675
|
system 'open', file
|
603
|
-
|
676
|
+
when /linux/
|
677
|
+
system 'xdg-open', file
|
604
678
|
end
|
679
|
+
# Don't worry if it fails.
|
605
680
|
end
|
606
681
|
end
|
607
682
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Seafoam
|
2
|
+
module Graal
|
3
|
+
# Routines for understanding pi nodes in Graal.
|
4
|
+
module Pi
|
5
|
+
# Find the actual value behind potentially a chain of pi nodes.
|
6
|
+
def self.follow_pi_object(node)
|
7
|
+
node = node.edges.find { |edge| edge.props[:name] == 'object' }.from while PI_NODES.include?(node.props.dig(:node_class, :node_class))
|
8
|
+
node
|
9
|
+
end
|
10
|
+
|
11
|
+
# Pi nodes add type information.
|
12
|
+
PI_NODES = [
|
13
|
+
'org.graalvm.compiler.nodes.PiNode',
|
14
|
+
'org.graalvm.compiler.nodes.PiArrayNode'
|
15
|
+
]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/seafoam/graph.rb
CHANGED
@@ -2,12 +2,14 @@ module Seafoam
|
|
2
2
|
# A graph, with properties, nodes, and edges. We don't encapsulate the graph
|
3
3
|
# too much - be careful.
|
4
4
|
class Graph
|
5
|
-
attr_reader :props, :nodes, :edges
|
5
|
+
attr_reader :props, :nodes, :edges, :blocks, :new_id
|
6
6
|
|
7
7
|
def initialize(props = nil)
|
8
8
|
@props = props || {}
|
9
9
|
@nodes = {}
|
10
10
|
@edges = []
|
11
|
+
@blocks = []
|
12
|
+
@new_id = 0
|
11
13
|
end
|
12
14
|
|
13
15
|
# Create a node.
|
@@ -15,6 +17,7 @@ module Seafoam
|
|
15
17
|
props ||= {}
|
16
18
|
node = Node.new(id, props)
|
17
19
|
@nodes[id] = node
|
20
|
+
@new_id = id + 1
|
18
21
|
node
|
19
22
|
end
|
20
23
|
|
@@ -27,6 +30,20 @@ module Seafoam
|
|
27
30
|
to.inputs.push edge
|
28
31
|
edge
|
29
32
|
end
|
33
|
+
|
34
|
+
# Add a new basic block with given id and node id list.
|
35
|
+
def create_block(id, node_ids)
|
36
|
+
nodes = node_ids.select { |n| @nodes.key? n }.map { |n| @nodes[n] }
|
37
|
+
block = Block.new(id, nodes)
|
38
|
+
@blocks.push block
|
39
|
+
block
|
40
|
+
end
|
41
|
+
|
42
|
+
def remove_edge(edge)
|
43
|
+
edge.from.outputs.delete edge
|
44
|
+
edge.to.inputs.delete edge
|
45
|
+
edges.delete edge
|
46
|
+
end
|
30
47
|
end
|
31
48
|
|
32
49
|
# A node, with properties, input edges, and output edges.
|
@@ -88,4 +105,19 @@ module Seafoam
|
|
88
105
|
"<Edge #{from.id} -> #{to.id}>"
|
89
106
|
end
|
90
107
|
end
|
108
|
+
|
109
|
+
# A control-flow basic block
|
110
|
+
class Block
|
111
|
+
attr_reader :id, :nodes
|
112
|
+
|
113
|
+
def initialize(id, nodes)
|
114
|
+
@id = id
|
115
|
+
@nodes = nodes
|
116
|
+
end
|
117
|
+
|
118
|
+
# Inspect.
|
119
|
+
def inspect
|
120
|
+
"<Block #{id}>"
|
121
|
+
end
|
122
|
+
end
|
91
123
|
end
|
@@ -7,15 +7,16 @@ module Seafoam
|
|
7
7
|
end
|
8
8
|
|
9
9
|
# Write a graph.
|
10
|
-
def write_graph(graph, hidpi = false)
|
10
|
+
def write_graph(graph, hidpi = false, draw_blocks = false)
|
11
11
|
inline_attrs = {}
|
12
12
|
attrs = {}
|
13
13
|
attrs[:dpi] = 200 if hidpi
|
14
|
-
attrs[:bgcolor] = '
|
14
|
+
attrs[:bgcolor] = 'white'
|
15
15
|
@stream.puts 'digraph G {'
|
16
16
|
@stream.puts " graph #{write_attrs(attrs)};"
|
17
17
|
write_nodes inline_attrs, graph
|
18
18
|
write_edges inline_attrs, graph
|
19
|
+
write_blocks graph if draw_blocks
|
19
20
|
@stream.puts '}'
|
20
21
|
end
|
21
22
|
|
@@ -99,6 +100,9 @@ module Seafoam
|
|
99
100
|
# from a shaded node.
|
100
101
|
next if edge.to.props[:hidden] && edge.from.props[:spotlight] != 'shaded'
|
101
102
|
|
103
|
+
# Skip the edge if it's hidden itself
|
104
|
+
next if edge.props[:hidden]
|
105
|
+
|
102
106
|
write_edge inline_attrs, edge
|
103
107
|
end
|
104
108
|
end
|
@@ -166,6 +170,24 @@ module Seafoam
|
|
166
170
|
end
|
167
171
|
end
|
168
172
|
|
173
|
+
# Write basic block outlines.
|
174
|
+
def write_blocks(graph)
|
175
|
+
graph.blocks.each do |block|
|
176
|
+
@stream.puts " subgraph cluster_block#{block.id} {"
|
177
|
+
@stream.puts ' fontname = "Arial";'
|
178
|
+
@stream.puts " label = \"B#{block.id}\";"
|
179
|
+
@stream.puts ' style=dotted;'
|
180
|
+
|
181
|
+
block.nodes.each do |node|
|
182
|
+
next if node.props[:hidden] || node.props[:inlined]
|
183
|
+
|
184
|
+
@stream.puts " node#{node.id};"
|
185
|
+
end
|
186
|
+
|
187
|
+
@stream.puts ' }'
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
169
191
|
# Return attributes for a node or edge modified to 'shade' them in terms
|
170
192
|
# the spotlight functionality - so basically make them light grey.
|
171
193
|
def shade(attrs)
|
data/lib/seafoam/json_writer.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
module Seafoam
|
2
|
-
module
|
3
|
-
# The fallback
|
2
|
+
module Passes
|
3
|
+
# The fallback pass always applies, and adds some basic properties.
|
4
4
|
# Works for example with Truffle AST and call graphs, but also means anyone
|
5
5
|
# can emit a graph with 'label' properties and we can do something useful
|
6
6
|
# with it.
|
7
|
-
class
|
7
|
+
class FallbackPass < Pass
|
8
8
|
def self.applies?(_graph)
|
9
9
|
true
|
10
10
|
end
|
11
11
|
|
12
|
-
def
|
12
|
+
def apply(graph)
|
13
13
|
graph.nodes.each_value do |node|
|
14
14
|
if node.props[:label].nil? && node.props['label']
|
15
15
|
node.props[:label] = node.props['label']
|
@@ -1,18 +1,19 @@
|
|
1
1
|
module Seafoam
|
2
|
-
module
|
3
|
-
# The Graal
|
2
|
+
module Passes
|
3
|
+
# The Graal pass applies if it looks like it was compiled by Graal or
|
4
4
|
# Truffle.
|
5
|
-
class
|
5
|
+
class GraalPass < Pass
|
6
6
|
def self.applies?(graph)
|
7
7
|
graph.props.values.any? do |v|
|
8
8
|
TRIGGERS.any? { |t| v.to_s.include?(t) }
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
-
def
|
13
|
-
|
14
|
-
|
12
|
+
def apply(graph)
|
13
|
+
apply_nodes graph
|
14
|
+
apply_edges graph
|
15
15
|
hide_frame_state graph if @options[:hide_frame_state]
|
16
|
+
hide_pi graph if @options[:hide_pi]
|
16
17
|
hide_floating graph if @options[:hide_floating]
|
17
18
|
reduce_edges graph if @options[:reduce_edges]
|
18
19
|
hide_unused_nodes graph
|
@@ -21,8 +22,10 @@ module Seafoam
|
|
21
22
|
private
|
22
23
|
|
23
24
|
# Annotate nodes with their label and kind
|
24
|
-
def
|
25
|
+
def apply_nodes(graph)
|
25
26
|
graph.nodes.each_value do |node|
|
27
|
+
next if node.props[:label]
|
28
|
+
|
26
29
|
# The Java class of the node.
|
27
30
|
node_class = node.props.dig(:node_class, :node_class)
|
28
31
|
|
@@ -93,6 +96,11 @@ module Seafoam
|
|
93
96
|
name_template = 'π'
|
94
97
|
end
|
95
98
|
|
99
|
+
# Use a symbol for PiArrayNode.
|
100
|
+
if node_class == 'org.graalvm.compiler.nodes.PiArrayNode'
|
101
|
+
name_template = '[π]'
|
102
|
+
end
|
103
|
+
|
96
104
|
# Use a symbol for PhiNode.
|
97
105
|
if node_class == 'org.graalvm.compiler.nodes.ValuePhiNode'
|
98
106
|
name_template = 'ϕ'
|
@@ -208,7 +216,7 @@ module Seafoam
|
|
208
216
|
end
|
209
217
|
|
210
218
|
# Annotate edges with their label and kind.
|
211
|
-
def
|
219
|
+
def apply_edges(graph)
|
212
220
|
graph.edges.each do |edge|
|
213
221
|
if edge.to.props.dig(:node_class, :node_class) == 'org.graalvm.compiler.nodes.ValuePhiNode' && edge.props[:name] == 'values'
|
214
222
|
merge_node = edge.to.edges.find { |e| e.props[:name] == 'merge' }.from
|
@@ -254,8 +262,6 @@ module Seafoam
|
|
254
262
|
# loopBegin edges point from LoopEndNode (continue) and LoopExitNode
|
255
263
|
# (break) to the LoopBeginNode. Both are drawn reversed.
|
256
264
|
when 'loopBegin'
|
257
|
-
edge.props[:hidden] = true
|
258
|
-
|
259
265
|
case edge.to.props.dig(:node_class, :node_class)
|
260
266
|
when 'org.graalvm.compiler.nodes.LoopEndNode'
|
261
267
|
# If it's from the LoopEnd then it's the control edge to follow.
|
@@ -311,6 +317,24 @@ module Seafoam
|
|
311
317
|
end
|
312
318
|
end
|
313
319
|
|
320
|
+
# Hide pi nodes - they add information for optimisation, but not generally
|
321
|
+
# useful day-to-day for understanding the graph. Connect the user to the
|
322
|
+
# actual object, which may be several steps away.
|
323
|
+
def hide_pi(graph)
|
324
|
+
loop do
|
325
|
+
umodified = true
|
326
|
+
graph.edges.each do |edge|
|
327
|
+
next unless Graal::Pi::PI_NODES.include?(edge.from.props.dig(:node_class, :node_class))
|
328
|
+
|
329
|
+
object = Graal::Pi.follow_pi_object(edge.from)
|
330
|
+
graph.create_edge object, edge.to, edge.props.merge({ synthetic: true })
|
331
|
+
graph.remove_edge edge
|
332
|
+
umodified = false
|
333
|
+
end
|
334
|
+
break if umodified
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
314
338
|
# Hide floating nodes. This highlights just the control flow backbone.
|
315
339
|
def hide_floating(graph)
|
316
340
|
graph.nodes.each_value do |node|
|
@@ -332,15 +356,19 @@ module Seafoam
|
|
332
356
|
end
|
333
357
|
end
|
334
358
|
|
335
|
-
# Hide nodes that have no non-hidden users and no control flow
|
336
|
-
# would display as a node floating unconnected to the rest of
|
337
|
-
# otherwise. An exception is made for node with an anchor edge
|
338
|
-
# as some guards are anchored like this.
|
359
|
+
# Hide nodes that have no non-hidden users or edges and no control flow
|
360
|
+
# in. These would display as a node floating unconnected to the rest of
|
361
|
+
# the graph otherwise. An exception is made for node with an anchor edge
|
362
|
+
# coming in, as some guards are anchored like this.
|
339
363
|
def hide_unused_nodes(graph)
|
340
364
|
loop do
|
341
365
|
modified = false
|
342
366
|
graph.nodes.each_value do |node|
|
343
|
-
|
367
|
+
# Call trees are like Graal graphs but don't have these edges -
|
368
|
+
# don't hide them.
|
369
|
+
next if node.props['truffleCallees']
|
370
|
+
|
371
|
+
next unless node.outputs.all? { |edge| edge.to.props[:hidden] || edge.props[:hidden] } &&
|
344
372
|
node.inputs.none? { |edge| edge.props[:kind] == 'control' } &&
|
345
373
|
node.inputs.none? { |edge| edge.props[:name] == 'anchor' }
|
346
374
|
|