seafoam 0.5 → 0.9
Sign up to get free protection for your applications and to get access to all the features.
- 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