seafoam 0.6 → 0.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/bin/bgv2isabelle +1 -5
  3. data/bin/bgv2json +1 -5
  4. data/bin/cfg2asm +1 -5
  5. data/bin/seafoam +1 -5
  6. data/lib/seafoam/bgv/bgv_parser.rb +10 -2
  7. data/lib/seafoam/commands.rb +107 -32
  8. data/lib/seafoam/graal/pi.rb +18 -0
  9. data/lib/seafoam/graph.rb +33 -1
  10. data/lib/seafoam/graphviz_writer.rb +24 -2
  11. data/lib/seafoam/json_writer.rb +1 -3
  12. data/lib/seafoam/{annotators → passes}/fallback.rb +4 -4
  13. data/lib/seafoam/{annotators → passes}/graal.rb +43 -15
  14. data/lib/seafoam/passes/truffle.rb +58 -0
  15. data/lib/seafoam/passes.rb +61 -0
  16. data/lib/seafoam/version.rb +1 -1
  17. data/lib/seafoam.rb +5 -4
  18. metadata +26 -62
  19. data/.github/probots.yml +0 -2
  20. data/.github/workflows/workflows.yml +0 -39
  21. data/.gitignore +0 -7
  22. data/.rubocop.yml +0 -37
  23. data/.ruby-version +0 -1
  24. data/.seafoam/config +0 -1
  25. data/CODE_OF_CONDUCT.md +0 -128
  26. data/CONTRIBUTING.md +0 -5
  27. data/Gemfile +0 -2
  28. data/LICENSE.md +0 -7
  29. data/README.md +0 -378
  30. data/demos/box-unbox-stats +0 -65
  31. data/docs/annotators.md +0 -43
  32. data/docs/bgv.md +0 -293
  33. data/docs/getting-graphs.md +0 -59
  34. data/docs/images/igv.png +0 -0
  35. data/docs/images/seafoam.png +0 -0
  36. data/docs/images/spotlight-igv.png +0 -0
  37. data/docs/images/spotlight-seafoam.png +0 -0
  38. data/docs/json.md +0 -35
  39. data/examples/Fib.java +0 -24
  40. data/examples/MatMult.java +0 -39
  41. data/examples/fib-java.bgv.gz +0 -0
  42. data/examples/fib.js +0 -15
  43. data/examples/fib.rb +0 -15
  44. data/examples/identity.rb +0 -13
  45. data/examples/java/Irreducible.class +0 -0
  46. data/examples/java/Irreducible.j +0 -35
  47. data/examples/java/IrreducibleDecompiled.java +0 -21
  48. data/examples/java/JavaExamples.java +0 -418
  49. data/examples/matmult.rb +0 -29
  50. data/examples/overflow.rb +0 -13
  51. data/examples/ruby/clamps.rb +0 -20
  52. data/examples/ruby/graal.patch +0 -15
  53. data/examples/ruby/ruby_examples.rb +0 -278
  54. data/lib/seafoam/annotators.rb +0 -54
  55. data/lib/seafoam/config.rb +0 -34
  56. data/seafoam.gemspec +0 -21
  57. data/spec/seafoam/annotators/fallback_spec.rb +0 -69
  58. data/spec/seafoam/annotators/graal_spec.rb +0 -96
  59. data/spec/seafoam/annotators_spec.rb +0 -61
  60. data/spec/seafoam/bgv/bgv_parser_spec.rb +0 -157
  61. data/spec/seafoam/binary/io_binary_reader_spec.rb +0 -176
  62. data/spec/seafoam/cfg/cfg_parser_spec.rb +0 -21
  63. data/spec/seafoam/cfg/disassembler_spec.rb +0 -32
  64. data/spec/seafoam/command_spec.rb +0 -316
  65. data/spec/seafoam/graph_spec.rb +0 -172
  66. data/spec/seafoam/graphviz_writer_spec.rb +0 -63
  67. data/spec/seafoam/json_writer_spec.rb +0 -14
  68. data/spec/seafoam/spec_helpers.rb +0 -34
  69. data/spec/seafoam/spotlight_spec.rb +0 -38
  70. data/tools/render-all +0 -36
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 69f3387c345f401ca95791c9f5b69d57b1ab123e60abcb31b9160b8734b5507e
4
- data.tar.gz: 154f61a73ddb2ff1cf271c8d57fbf9422ddbb46fa3e0dc6d1256d1f4c2c0addc
3
+ metadata.gz: f8665fe768d4900820ca676269cd25028306e8253c2109c71ff4cb31dd7c6e44
4
+ data.tar.gz: fd8c52e5cf3daafa9b2673265067b743cc644755c3772c02904c2b815b3a93d2
5
5
  SHA512:
6
- metadata.gz: 29067885733970836583c0060a933ad13ec78ba35a01c6462ea0af4ac7df2137f8d0202dcdb7f57078aab0aba30c2da08973c9f31415191c9229f0a7adaf816b
7
- data.tar.gz: 69b2872db8937ed1bd73eefee880139055ee9a40b3a3cd50c041f3c623307780ff5e72d3ad2eaf9882d4234b58b9668021270f768c767229e930d12613483f8d
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, config)
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, config)
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, config)
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, config)
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
- skip_blocks
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
@@ -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, config)
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
- @out.puts 'seafoam file.bgv info'
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
- Annotators.apply graph
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
- annotator_options = {
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
- annotator_options[:hide_frame_state] = false
462
+ pass_options[:hide_frame_state] = false
463
+ when '--show-pi'
464
+ pass_options[:hide_pi] = false
423
465
  when '--hide-floating'
424
- annotator_options[:hide_floating] = true
466
+ pass_options[:hide_floating] = true
425
467
  when '--no-reduce-edges'
426
- annotator_options[:reduce_edges] = false
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
- annotator_options[key.to_sym] = value
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
- Annotators.apply graph, annotator_options
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
- if RUBY_PLATFORM.include?('darwin') && @out.tty?
671
+ return unless @out.tty?
672
+
673
+ case RUBY_PLATFORM
674
+ when /darwin/
602
675
  system 'open', file
603
- # Don't worry if it fails.
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] = 'transparent'
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)
@@ -36,9 +36,7 @@ module Seafoam
36
36
  @out.puts JSON.pretty_generate(prepare_json(object))
37
37
  end
38
38
 
39
- private
40
-
41
- def prepare_json(object)
39
+ def self.prepare_json(object)
42
40
  case object
43
41
  when Float
44
42
  if object.nan?
@@ -1,15 +1,15 @@
1
1
  module Seafoam
2
- module Annotators
3
- # The fallback annotator always applies, and adds some basic annotations.
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 FallbackAnnotator < Annotator
7
+ class FallbackPass < Pass
8
8
  def self.applies?(_graph)
9
9
  true
10
10
  end
11
11
 
12
- def annotate(graph)
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 Annotators
3
- # The Graal annotator applies if it looks like it was compiled by Graal or
2
+ module Passes
3
+ # The Graal pass applies if it looks like it was compiled by Graal or
4
4
  # Truffle.
5
- class GraalAnnotator < Annotator
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 annotate(graph)
13
- annotate_nodes graph
14
- annotate_edges graph
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 annotate_nodes(graph)
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 annotate_edges(graph)
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 in. These
336
- # would display as a node floating unconnected to the rest of the graph
337
- # otherwise. An exception is made for node with an anchor edge coming in,
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
- next unless node.outputs.all? { |edge| edge.to.props[:hidden] } &&
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