seafoam 0.5 → 0.9

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 +53 -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 -13
  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 +32 -66
  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: '08ef3412a15721b9bfd6184645579568626e2079ff1aaaeb9d9181270c25209f'
4
- data.tar.gz: a139c2adb73d4912131b886a61482d2c67ee2090f1117a51669cfe334202ea37
3
+ metadata.gz: 49d090779b04322f8c82aae0cf11ea2e39cd4cef1f3d9dc2b51de1f27e565f9c
4
+ data.tar.gz: 121f1c7bb327c30f1a388960012c43dab49e0f1079d5a420811ab2332de9663b
5
5
  SHA512:
6
- metadata.gz: 1939c6d23e19bb930475a33c7b1fd690c2e3c57b6fcfaed3eafd3c01d4c2e2775398f796f6a22c62012e6133a27dc4575df0f3afc96785efd1fdfbd3a8203203
7
- data.tar.gz: 9fb0c9552c3df825ab9dde3b76480d08a4ef5587ccb85f1e9024e0fff85fd71b8707bd0d7cecccde69d35a6662e736ee1fbeb332f95745ca8e34a40d260da702
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, 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
@@ -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, config)
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
- @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'
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
- Annotators.apply graph
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
- annotator_options = {
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
- annotator_options[:hide_frame_state] = false
409
+ pass_options[:hide_frame_state] = false
410
+ when '--show-pi'
411
+ pass_options[:hide_pi] = false
423
412
  when '--hide-floating'
424
- annotator_options[:hide_floating] = true
413
+ pass_options[:hide_floating] = true
425
414
  when '--no-reduce-edges'
426
- annotator_options[:reduce_edges] = false
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
- annotator_options[key.to_sym] = value
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
- Annotators.apply graph, annotator_options
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
- if RUBY_PLATFORM.include?('darwin') && @out.tty?
617
+ return unless @out.tty?
618
+
619
+ case RUBY_PLATFORM
620
+ when /darwin/
602
621
  system 'open', file
603
- # Don't worry if it fails.
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] = '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
@@ -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 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.
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
- next unless node.outputs.all? { |edge| edge.to.props[:hidden] } &&
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
@@ -1,5 +1,5 @@
1
1
  module Seafoam
2
2
  MAJOR_VERSION = 0
3
- MINOR_VERSION = 5
3
+ MINOR_VERSION = 9
4
4
  VERSION = "#{MAJOR_VERSION}.#{MINOR_VERSION}"
5
5
  end