seafoam 0.5 → 0.9
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 +53 -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 -13
- 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 +32 -66
- 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: 49d090779b04322f8c82aae0cf11ea2e39cd4cef1f3d9dc2b51de1f27e565f9c
|
|
4
|
+
data.tar.gz: 121f1c7bb327c30f1a388960012c43dab49e0f1079d5a420811ab2332de9663b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dd13ee2efe4fe4118fc5f6bdb56c55b3c1c5f28b86a3d37ce3cf4fc72bcdcbeb161968260b62626047b9f9f9eedec53cbc743d79512a11bd77b195f054c54262
|
|
7
|
+
data.tar.gz: 67e06a4e89fdf9e6297e3d68da7f7b0e7d4f1de95a97c9be17c12e6c9114476ee6e747c6306c3bc472e549317f20c19c315c9789d1814c68fdd85501327c432b
|
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
|
@@ -3,9 +3,8 @@ require 'json'
|
|
|
3
3
|
module Seafoam
|
|
4
4
|
# Implementations of the command-line commands that you can run in Seafoam.
|
|
5
5
|
class Commands
|
|
6
|
-
def initialize(out
|
|
6
|
+
def initialize(out)
|
|
7
7
|
@out = out
|
|
8
|
-
@config = config
|
|
9
8
|
end
|
|
10
9
|
|
|
11
10
|
# Run the general seafoam command.
|
|
@@ -15,24 +14,7 @@ module Seafoam
|
|
|
15
14
|
when nil, 'help', '-h', '--help', '-help'
|
|
16
15
|
raise ArgumentError, "unexpected arguments #{args.join(' ')}" unless args.empty?
|
|
17
16
|
|
|
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'
|
|
17
|
+
help(*args)
|
|
36
18
|
when 'version', '-v', '-version', '--version'
|
|
37
19
|
version(*args)
|
|
38
20
|
else
|
|
@@ -262,7 +244,7 @@ module Seafoam
|
|
|
262
244
|
end
|
|
263
245
|
|
|
264
246
|
def search_object(tag, object, terms)
|
|
265
|
-
full_text = JSON.generate(object)
|
|
247
|
+
full_text = JSON.generate(JSONWriter.prepare_json(object))
|
|
266
248
|
full_text_down = full_text.downcase
|
|
267
249
|
start = 0
|
|
268
250
|
terms.each do |t|
|
|
@@ -298,7 +280,7 @@ module Seafoam
|
|
|
298
280
|
parser.read_graph_header
|
|
299
281
|
graph = parser.read_graph
|
|
300
282
|
if node_id
|
|
301
|
-
|
|
283
|
+
Passes.apply graph
|
|
302
284
|
node = graph.nodes[node_id]
|
|
303
285
|
raise ArgumentError, 'node not found' unless node
|
|
304
286
|
|
|
@@ -397,8 +379,10 @@ module Seafoam
|
|
|
397
379
|
raise ArgumentError, 'render needs at least a graph' unless graph_index
|
|
398
380
|
raise ArgumentError, 'render only works with a graph' unless rest == [nil, nil]
|
|
399
381
|
|
|
400
|
-
|
|
382
|
+
pass_options = {
|
|
383
|
+
simplify_truffle_args: true,
|
|
401
384
|
hide_frame_state: true,
|
|
385
|
+
hide_pi: true,
|
|
402
386
|
hide_floating: false,
|
|
403
387
|
reduce_edges: true
|
|
404
388
|
}
|
|
@@ -406,6 +390,7 @@ module Seafoam
|
|
|
406
390
|
args = args.dup
|
|
407
391
|
out_file = nil
|
|
408
392
|
explicit_out_file = false
|
|
393
|
+
draw_blocks = false
|
|
409
394
|
until args.empty?
|
|
410
395
|
arg = args.shift
|
|
411
396
|
case arg
|
|
@@ -418,12 +403,18 @@ module Seafoam
|
|
|
418
403
|
raise ArgumentError, 'no list for --spotlight' unless spotlight_arg
|
|
419
404
|
|
|
420
405
|
spotlight_nodes = spotlight_arg.split(',').map { |n| Integer(n) }
|
|
406
|
+
when '--full-truffle-args'
|
|
407
|
+
pass_options[:simplify_truffle_args] = false
|
|
421
408
|
when '--show-frame-state'
|
|
422
|
-
|
|
409
|
+
pass_options[:hide_frame_state] = false
|
|
410
|
+
when '--show-pi'
|
|
411
|
+
pass_options[:hide_pi] = false
|
|
423
412
|
when '--hide-floating'
|
|
424
|
-
|
|
413
|
+
pass_options[:hide_floating] = true
|
|
425
414
|
when '--no-reduce-edges'
|
|
426
|
-
|
|
415
|
+
pass_options[:reduce_edges] = false
|
|
416
|
+
when '--draw-blocks'
|
|
417
|
+
draw_blocks = true
|
|
427
418
|
when '--option'
|
|
428
419
|
key = args.shift
|
|
429
420
|
raise ArgumentError, 'no key for --option' unless key
|
|
@@ -432,7 +423,7 @@ module Seafoam
|
|
|
432
423
|
raise ArgumentError, "no value for --option #{key}" unless out_file
|
|
433
424
|
|
|
434
425
|
value = { 'true' => true, 'false' => 'false' }.fetch(key, value)
|
|
435
|
-
|
|
426
|
+
pass_options[key.to_sym] = value
|
|
436
427
|
else
|
|
437
428
|
raise ArgumentError, "unexpected option #{arg}"
|
|
438
429
|
end
|
|
@@ -455,7 +446,7 @@ module Seafoam
|
|
|
455
446
|
with_graph(file, graph_index) do |parser|
|
|
456
447
|
parser.skip_graph_header
|
|
457
448
|
graph = parser.read_graph
|
|
458
|
-
|
|
449
|
+
Passes.apply graph, pass_options
|
|
459
450
|
if spotlight_nodes
|
|
460
451
|
spotlight = Spotlight.new(graph)
|
|
461
452
|
spotlight_nodes.each do |node_id|
|
|
@@ -469,14 +460,14 @@ module Seafoam
|
|
|
469
460
|
if out_format == :dot
|
|
470
461
|
File.open(out_file, 'w') do |stream|
|
|
471
462
|
writer = GraphvizWriter.new(stream)
|
|
472
|
-
writer.write_graph graph
|
|
463
|
+
writer.write_graph graph, false, draw_blocks
|
|
473
464
|
end
|
|
474
465
|
else
|
|
475
466
|
begin
|
|
476
467
|
IO.popen(['dot', "-T#{out_format}", '-o', out_file], 'w') do |stream|
|
|
477
468
|
writer = GraphvizWriter.new(stream)
|
|
478
469
|
hidpi = out_format == :png
|
|
479
|
-
writer.write_graph graph, hidpi
|
|
470
|
+
writer.write_graph graph, hidpi, draw_blocks
|
|
480
471
|
end
|
|
481
472
|
rescue Errno::ENOENT
|
|
482
473
|
raise 'Could not run Graphviz - is it installed?'
|
|
@@ -565,6 +556,31 @@ module Seafoam
|
|
|
565
556
|
raise ArgumentError, 'graph not found' unless graph_found
|
|
566
557
|
end
|
|
567
558
|
|
|
559
|
+
# Prints help.
|
|
560
|
+
def help(*_args)
|
|
561
|
+
@out.puts 'seafoam file.bgv info'
|
|
562
|
+
@out.puts ' file.bgv list'
|
|
563
|
+
@out.puts ' file.bgv[:graph][:node[-edge]] search term...'
|
|
564
|
+
@out.puts ' file.bgv[:graph][:node[-edge]] edges'
|
|
565
|
+
@out.puts ' file.bgv[:graph][:node[-edge]] props'
|
|
566
|
+
@out.puts ' file.bgv:graph:node source'
|
|
567
|
+
@out.puts ' file.bgv:graph render'
|
|
568
|
+
@out.puts ' --spotlight n,n,n...'
|
|
569
|
+
@out.puts ' --out graph.pdf'
|
|
570
|
+
@out.puts ' graph.svg'
|
|
571
|
+
@out.puts ' graph.png'
|
|
572
|
+
@out.puts ' graph.dot'
|
|
573
|
+
@out.puts ' --full-truffle-args'
|
|
574
|
+
@out.puts ' --show-frame-state'
|
|
575
|
+
@out.puts ' --show-pi'
|
|
576
|
+
@out.puts ' --hide-floating'
|
|
577
|
+
@out.puts ' --no-reduce-edges'
|
|
578
|
+
@out.puts ' --draw-blocks'
|
|
579
|
+
@out.puts ' --option key value'
|
|
580
|
+
@out.puts ' --help'
|
|
581
|
+
@out.puts ' --version'
|
|
582
|
+
end
|
|
583
|
+
|
|
568
584
|
# Prints the version.
|
|
569
585
|
def version(*args)
|
|
570
586
|
raise ArgumentError, "unexpected arguments #{args.join(' ')}" unless args.empty?
|
|
@@ -598,10 +614,15 @@ module Seafoam
|
|
|
598
614
|
|
|
599
615
|
# Open a file for the user if possible.
|
|
600
616
|
def autoopen(file)
|
|
601
|
-
|
|
617
|
+
return unless @out.tty?
|
|
618
|
+
|
|
619
|
+
case RUBY_PLATFORM
|
|
620
|
+
when /darwin/
|
|
602
621
|
system 'open', file
|
|
603
|
-
|
|
622
|
+
when /linux/
|
|
623
|
+
system 'xdg-open', file
|
|
604
624
|
end
|
|
625
|
+
# Don't worry if it fails.
|
|
605
626
|
end
|
|
606
627
|
end
|
|
607
628
|
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
|
|
@@ -311,6 +319,24 @@ module Seafoam
|
|
|
311
319
|
end
|
|
312
320
|
end
|
|
313
321
|
|
|
322
|
+
# Hide pi nodes - they add information for optimisation, but not generally
|
|
323
|
+
# useful day-to-day for understanding the graph. Connect the user to the
|
|
324
|
+
# actual object, which may be several steps away.
|
|
325
|
+
def hide_pi(graph)
|
|
326
|
+
loop do
|
|
327
|
+
umodified = true
|
|
328
|
+
graph.edges.each do |edge|
|
|
329
|
+
next unless Graal::Pi::PI_NODES.include?(edge.from.props.dig(:node_class, :node_class))
|
|
330
|
+
|
|
331
|
+
object = Graal::Pi.follow_pi_object(edge.from)
|
|
332
|
+
graph.create_edge object, edge.to, edge.props.merge({ synthetic: true })
|
|
333
|
+
graph.remove_edge edge
|
|
334
|
+
umodified = false
|
|
335
|
+
end
|
|
336
|
+
break if umodified
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
|
|
314
340
|
# Hide floating nodes. This highlights just the control flow backbone.
|
|
315
341
|
def hide_floating(graph)
|
|
316
342
|
graph.nodes.each_value do |node|
|
|
@@ -332,15 +358,19 @@ module Seafoam
|
|
|
332
358
|
end
|
|
333
359
|
end
|
|
334
360
|
|
|
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.
|
|
361
|
+
# Hide nodes that have no non-hidden users or edges and no control flow
|
|
362
|
+
# in. These would display as a node floating unconnected to the rest of
|
|
363
|
+
# the graph otherwise. An exception is made for node with an anchor edge
|
|
364
|
+
# coming in, as some guards are anchored like this.
|
|
339
365
|
def hide_unused_nodes(graph)
|
|
340
366
|
loop do
|
|
341
367
|
modified = false
|
|
342
368
|
graph.nodes.each_value do |node|
|
|
343
|
-
|
|
369
|
+
# Call trees are like Graal graphs but don't have these edges -
|
|
370
|
+
# don't hide them.
|
|
371
|
+
next if node.props['truffleCallees']
|
|
372
|
+
|
|
373
|
+
next unless node.outputs.all? { |edge| edge.to.props[:hidden] || edge.props[:hidden] } &&
|
|
344
374
|
node.inputs.none? { |edge| edge.props[:kind] == 'control' } &&
|
|
345
375
|
node.inputs.none? { |edge| edge.props[:name] == 'anchor' }
|
|
346
376
|
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
module Seafoam
|
|
2
|
+
module Passes
|
|
3
|
+
# The Truffle pass applies if it looks like it was compiled by Truffle.
|
|
4
|
+
class TrufflePass < Pass
|
|
5
|
+
def self.applies?(graph)
|
|
6
|
+
graph.props.values.any? do |v|
|
|
7
|
+
TRIGGERS.any? { |t| v.to_s.include?(t) }
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def apply(graph)
|
|
12
|
+
simplify_truffle_args graph if @options[:simplify_truffle_args]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
# Simplify a Truffle argument load into a single, inlined, node very much
|
|
18
|
+
# like a Graal parameter node.
|
|
19
|
+
def simplify_truffle_args(graph)
|
|
20
|
+
graph.nodes.dup.each_value do |node|
|
|
21
|
+
next unless node.props.dig(:node_class, :node_class) == 'org.graalvm.compiler.nodes.java.LoadIndexedNode'
|
|
22
|
+
|
|
23
|
+
index_node = node.inputs.find { |edge| edge.props[:name] == 'index' }.from
|
|
24
|
+
array_node = Graal::Pi.follow_pi_object(node.inputs.find { |edge| edge.props[:name] == 'array' }.from)
|
|
25
|
+
|
|
26
|
+
next unless index_node.props.dig(:node_class, :node_class) == 'org.graalvm.compiler.nodes.ConstantNode'
|
|
27
|
+
next unless array_node.props.dig(:node_class, :node_class) == 'org.graalvm.compiler.nodes.ParameterNode'
|
|
28
|
+
|
|
29
|
+
node.props[:truffle_arg_load] = true
|
|
30
|
+
|
|
31
|
+
index = index_node.props['rawvalue']
|
|
32
|
+
|
|
33
|
+
arg_node = graph.create_node(graph.new_id, { synthetic: true, inlined: true, label: "T(#{index})", kind: 'input' })
|
|
34
|
+
|
|
35
|
+
node.outputs.each do |output|
|
|
36
|
+
next if output.props[:name] == 'next'
|
|
37
|
+
|
|
38
|
+
graph.create_edge arg_node, output.to, output.props.dup
|
|
39
|
+
graph.remove_edge output
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
graph.nodes.each_value.select { |node| node.props[:truffle_arg_load] }.each do |node|
|
|
44
|
+
control_in = node.inputs.find { |edge| edge.props[:name] == 'next' }
|
|
45
|
+
control_out = node.outputs.find { |edge| edge.props[:name] == 'next' }
|
|
46
|
+
graph.create_edge control_in.from, control_out.to, { name: 'next' }
|
|
47
|
+
graph.remove_edge control_in
|
|
48
|
+
graph.remove_edge control_out
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# If we see these in the graph properties it's probably a Truffle graph.
|
|
53
|
+
TRIGGERS = %w[
|
|
54
|
+
TruffleCompiler
|
|
55
|
+
]
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module Seafoam
|
|
2
|
+
# Passes are routines to read the graph and apply properties which tools,
|
|
3
|
+
# such as the render command, can use to show more understandable output.
|
|
4
|
+
module Passes
|
|
5
|
+
# Apply all applicable passes to a graph.
|
|
6
|
+
def self.apply(graph, options = {})
|
|
7
|
+
passes.each do |pass|
|
|
8
|
+
next unless pass.applies?(graph)
|
|
9
|
+
|
|
10
|
+
# Record for information that the pass was applied this graph.
|
|
11
|
+
passes_applied = graph.props[:passes_applied] ||= []
|
|
12
|
+
passes_applied.push pass
|
|
13
|
+
|
|
14
|
+
# Run the pass.
|
|
15
|
+
instance = pass.new(options)
|
|
16
|
+
instance.apply graph
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Get a list of all passes in the system.
|
|
21
|
+
def self.passes
|
|
22
|
+
# We have a defined order for passes to run - these passes at the start.
|
|
23
|
+
pre_passes = [
|
|
24
|
+
TrufflePass,
|
|
25
|
+
GraalPass
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
# The fallback pass runs last.
|
|
29
|
+
post_passes = [
|
|
30
|
+
FallbackPass
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
# Any extra passes in the middle.
|
|
34
|
+
extra_passes = Pass::SUBCLASSES.dup - pre_passes - post_passes
|
|
35
|
+
|
|
36
|
+
pre_passes + extra_passes + post_passes
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# The base class for all passes. You must subclass this to be recognized
|
|
41
|
+
# as an pass.
|
|
42
|
+
class Pass
|
|
43
|
+
SUBCLASSES = []
|
|
44
|
+
|
|
45
|
+
def initialize(options = {})
|
|
46
|
+
@options = options
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def applies?(_graph)
|
|
50
|
+
raise NotImplementedError
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def apply(_graph)
|
|
54
|
+
raise NotImplementedError
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def self.inherited(pass)
|
|
58
|
+
SUBCLASSES.push pass
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
data/lib/seafoam/version.rb
CHANGED