seafoam 0.14 → 0.16

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 44f9af9b54052ddbfc03bc99833eb0af6e2e5f2bd0a5612a77ea10e3d30b3fca
4
- data.tar.gz: 7d1613577c3e033c69149eb51845540b88adc0c05bda3327c59a41c700647181
3
+ metadata.gz: 953c4b8d8e9c803df82cf0806f64ad775016ff3aaac16e96b602e15b28ec9fe4
4
+ data.tar.gz: a933e3faf9effa97b4a48ae38b7ce48ebaa6eba50db5b675442ad2431c93be83
5
5
  SHA512:
6
- metadata.gz: f35b3f93409e92be806e793817461900f66d7d1a8e91e6ae95fc52f66fa61955d7c48f0de1899ad15b35e88fa2b6b4cbb70fed976accb9f20ee7725f201828fe
7
- data.tar.gz: 03d4b654e6f2001128aa711ccddb4cccf09aed480d65578e0e49edb3d59c2acb5b594e3ef4f32b972e9821862aa24b9daa12c4fb7f1cef838a46c43c2c3b4fa9
6
+ metadata.gz: 369ab75e67c67eaf58d4628875579b84e132a2462212f95c656c8d4c69031297a3f3c3c6652e1dd08bebc367077a6b0fdeb2d56c2c115908d55255a0be54457c
7
+ data.tar.gz: e6f2fca576c55ce24ad5d27d67b88f4b1f9e70e68eb76bee473492a8ee1148db4ed3b7c6294bc615c5399d94c0dc7e2009ab13b39ee96940b042e08b6e0518a5
@@ -59,7 +59,7 @@ module Seafoam
59
59
  # Move to the next graph in the file, and return its index and ID, or nil if
60
60
  # there are no more graphs.
61
61
  def read_graph_preheader
62
- return nil unless read_groups
62
+ return unless read_groups
63
63
 
64
64
  # Already read BEGIN_GRAPH
65
65
  index = @index
@@ -161,7 +161,7 @@ module Seafoam
161
161
  def graph_name(graph_header)
162
162
  groups_names = graph_header[:group].map { |g| g[:short_name] }
163
163
  count = 0
164
- name = graph_header[:format].sub(/%s/) do
164
+ name = graph_header[:format].sub("%s") do
165
165
  arg = graph_header[:args][count]
166
166
  count += 1
167
167
  arg
@@ -350,10 +350,20 @@ module Seafoam
350
350
  def describe(name, formatter_module, *args)
351
351
  file, graph_index, *rest = parse_name(name)
352
352
 
353
+ pass_options = DEFAULT_PASS_OPTIONS.dup
354
+ until args.empty?
355
+ arg = args.shift
356
+ case arg
357
+ when "--no-simplify"
358
+ pass_options.merge!(NO_SIMPLIFY_PASS_OPTIONS)
359
+ else
360
+ raise ArgumentError, "unexpected option #{arg}"
361
+ end
362
+ end
363
+
353
364
  if graph_index.nil? || !rest.all?(&:nil?)
354
365
  raise ArgumentError, "describe only works with a graph"
355
366
  end
356
- raise ArgumentError, "describe does not take arguments" unless args.empty?
357
367
 
358
368
  parser = BGV::BGVParser.new(file)
359
369
  parser.read_file_header
@@ -371,26 +381,34 @@ module Seafoam
371
381
  end
372
382
 
373
383
  graph = parser.read_graph
384
+ Passes.apply(graph, pass_options)
385
+
374
386
  description = Seafoam::Graal::GraphDescription.new
375
387
 
376
388
  graph.nodes.each_value do |node|
377
- node_class = node.node_class
389
+ next if node.props[:hidden]
378
390
 
379
- simple_node_class = node_class[/([^.]+)$/, 1]
380
- description.node_counts[simple_node_class] += 1
381
-
382
- case node_class
383
- when "org.graalvm.compiler.nodes.IfNode"
384
- description.branches = true
385
- when "org.graalvm.compiler.nodes.LoopBeginNode"
386
- description.loops = true
387
- when "org.graalvm.compiler.nodes.InvokeNode", "org.graalvm.compiler.nodes.InvokeWithExceptionNode"
388
- description.calls = true
391
+ node_class = node.node_class
392
+ if node_class
393
+ simple_node_class = node_class[/([^.]+)$/, 1]
394
+ description.node_counts[simple_node_class] += 1
395
+
396
+ case node_class
397
+ when "org.graalvm.compiler.nodes.IfNode", "jdk.graal.compiler.nodes.IfNode"
398
+ description.branches = true
399
+ when "org.graalvm.compiler.nodes.LoopBeginNode", "jdk.graal.compiler.nodes.LoopBeginNode"
400
+ description.loops = true
401
+ when "org.graalvm.compiler.nodes.InvokeNode", "org.graalvm.compiler.nodes.InvokeWithExceptionNode",
402
+ "jdk.graal.compiler.nodes.InvokeNode", "jdk.graal.compiler.nodes.InvokeWithExceptionNode"
403
+ description.calls = true
404
+ end
405
+ elsif node.props[:synthetic_class]
406
+ description.node_counts["*" + node.props[:synthetic_class]] += 1
389
407
  end
390
- end
391
408
 
392
- description.deopts = graph.nodes[0].outputs.map(&:to)
393
- .all? { |t| t.node_class == "org.graalvm.compiler.nodes.DeoptimizeNode" }
409
+ description.deopts = graph.nodes[0].outputs.map(&:to)
410
+ .all? { |t| t.node_class.end_with?(".compiler.nodes.DeoptimizeNode") }
411
+ end
394
412
 
395
413
  formatter = formatter_module::DescribeFormatter.new(graph, description)
396
414
  @out.puts formatter.format
@@ -405,16 +423,7 @@ module Seafoam
405
423
  raise ArgumentError, "render needs at least a graph" unless graph_index
406
424
  raise ArgumentError, "render only works with a graph" unless rest == [nil, nil]
407
425
 
408
- pass_options = {
409
- simplify_truffle_args: true,
410
- hide_frame_state: true,
411
- hide_pi: true,
412
- hide_begin_end: true,
413
- hide_floating: false,
414
- reduce_edges: true,
415
- simplify_alloc: true,
416
- hide_null_fields: true,
417
- }
426
+ pass_options = DEFAULT_PASS_OPTIONS.dup
418
427
  spotlight_nodes = nil
419
428
  args = args.dup
420
429
  out_file = nil
@@ -456,6 +465,8 @@ module Seafoam
456
465
  spotlight_nodes = spotlight_arg.split(",").map { |n| Integer(n) }
457
466
  when "--full-truffle-args"
458
467
  pass_options[:simplify_truffle_args] = false
468
+ when "--show-reachability-fences"
469
+ pass_options[:hide_reachability_fences] = false
459
470
  when "--no-simplify-alloc"
460
471
  pass_options[:simplify_alloc] = false
461
472
  when "--show-null-fields"
@@ -470,6 +481,8 @@ module Seafoam
470
481
  pass_options[:hide_floating] = true
471
482
  when "--no-reduce-edges"
472
483
  pass_options[:reduce_edges] = false
484
+ when "--no-simplify"
485
+ pass_options.merge!(NO_SIMPLIFY_PASS_OPTIONS)
473
486
  when "--draw-blocks"
474
487
  draw_blocks = true
475
488
  when "--option"
@@ -477,7 +490,7 @@ module Seafoam
477
490
  raise ArgumentError, "no key for --option" unless key
478
491
 
479
492
  value = args.shift
480
- raise ArgumentError, "no value for --option #{key}" unless out_file
493
+ raise ArgumentError, "no value for --option #{key}" unless value
481
494
 
482
495
  value = { "true" => true, "false" => "false" }.fetch(key, value)
483
496
  pass_options[key.to_sym] = value
@@ -644,6 +657,7 @@ module Seafoam
644
657
  @out.puts " --show-begin-end"
645
658
  @out.puts " --hide-floating"
646
659
  @out.puts " --no-reduce-edges"
660
+ @out.puts " --no-simplify"
647
661
  @out.puts " --draw-blocks"
648
662
  @out.puts " --option key value"
649
663
  @out.puts " --help"
@@ -693,5 +707,27 @@ module Seafoam
693
707
  end
694
708
  # Don't worry if it fails.
695
709
  end
710
+
711
+ DEFAULT_PASS_OPTIONS = {
712
+ simplify_truffle_args: true,
713
+ hide_reachability_fences: true,
714
+ hide_frame_state: true,
715
+ hide_pi: true,
716
+ hide_begin_end: true,
717
+ hide_floating: false,
718
+ reduce_edges: true,
719
+ simplify_alloc: true,
720
+ hide_null_fields: true,
721
+ }
722
+
723
+ NO_SIMPLIFY_PASS_OPTIONS = {
724
+ simplify_truffle_args: false,
725
+ hide_reachability_fences: false,
726
+ simplify_alloc: false,
727
+ hide_null_fields: false,
728
+ hide_pi: false,
729
+ hide_begin_end: false,
730
+ reduce_edges: false,
731
+ }
696
732
  end
697
733
  end
@@ -9,7 +9,7 @@ module Seafoam
9
9
  class DescribeFormatter < Seafoam::Formatters::Base::DescribeFormatter
10
10
  def format
11
11
  ret = Seafoam::Graal::GraphDescription::ATTRIBUTES.map { |attr| [attr, description.send(attr)] }.to_h
12
- ret[:node_count] = graph.nodes.size
12
+ ret[:node_count] = graph.nodes.values.count { |n| !n.props[:hidden] }
13
13
  ret[:node_counts] = description.sorted_node_counts
14
14
 
15
15
  ret.to_json
@@ -7,9 +7,10 @@ module Seafoam
7
7
  class DescribeFormatter < Seafoam::Formatters::Base::DescribeFormatter
8
8
  def format
9
9
  notes = Seafoam::Graal::GraphDescription::ATTRIBUTES.select { |attr| description.send(attr) }
10
+ unhidden_count = graph.nodes.values.count { |n| !n.props[:hidden] }
10
11
  node_counts = description.sorted_node_counts.map { |node_class, count| "#{node_class}: #{count}" }.join("\n")
11
12
 
12
- ["#{graph.nodes.size} nodes", *notes].join(", ") + "\n#{node_counts}"
13
+ ["#{unhidden_count} nodes", *notes].join(", ") + "\n#{node_counts}"
13
14
  end
14
15
  end
15
16
 
@@ -16,6 +16,8 @@ module Seafoam
16
16
  PI_NODES = [
17
17
  "org.graalvm.compiler.nodes.PiNode",
18
18
  "org.graalvm.compiler.nodes.PiArrayNode",
19
+ "jdk.graal.compiler.nodes.PiNode",
20
+ "jdk.graal.compiler.nodes.PiArrayNode",
19
21
  ]
20
22
  end
21
23
  end
data/lib/seafoam/graph.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "set"
4
+
3
5
  module Seafoam
4
6
  # A graph, with properties, nodes, and edges. We don't encapsulate the graph
5
7
  # too much - be careful.
@@ -61,7 +63,7 @@ module Seafoam
61
63
  end
62
64
 
63
65
  def node_class
64
- @props.dig(:node_class, :node_class)
66
+ @props.dig(:node_class, :node_class) || ""
65
67
  end
66
68
 
67
69
  # All edges - input and output.
@@ -87,6 +89,18 @@ module Seafoam
87
89
  def inspect
88
90
  "<Node #{id} #{node_class}>"
89
91
  end
92
+
93
+ def visit(&block)
94
+ block.call(self)
95
+ end
96
+
97
+ def visit_outputs(search_strategy, &block)
98
+ if search_strategy == :bfs
99
+ BFS.new(self).search(&block)
100
+ else
101
+ raise "Unknown search strategy: #{search_strategy}"
102
+ end
103
+ end
90
104
  end
91
105
 
92
106
  # A directed edge, with a node it's from and a node it's going to, and
@@ -16,14 +16,14 @@ module Seafoam
16
16
  graph.nodes.each_value do |node|
17
17
  nodes.push(
18
18
  id: node.id,
19
- props: node.props
19
+ props: node.props,
20
20
  )
21
21
 
22
22
  node.outputs.each do |edge|
23
23
  edges.push(
24
24
  from: edge.from.id,
25
25
  to: edge.to.id,
26
- props: edge.props
26
+ props: edge.props,
27
27
  )
28
28
  end
29
29
  end
@@ -35,7 +35,7 @@ module Seafoam
35
35
  edges: edges,
36
36
  }
37
37
 
38
- @out.puts JSON.pretty_generate(prepare_json(object))
38
+ @out.puts JSON.pretty_generate(self.class.prepare_json(object))
39
39
  end
40
40
 
41
41
  class << self
@@ -38,7 +38,7 @@ module Seafoam
38
38
  when "diamond"
39
39
  shape = ["{{", "}}"]
40
40
  end
41
- @stream.puts "#{indent}#{id}#{shape[0]}#{attrs[:label].inspect}#{shape[1]}"
41
+ @stream.puts "#{indent}#{id}#{shape[0]}#{attrs[:label].gsub('"', "#quot;").inspect}#{shape[1]}"
42
42
  @stream.puts "#{indent}style #{id} fill:#{attrs[:fillcolor]},stroke:#{attrs[:color]},color:#{attrs[:fontcolor]};"
43
43
  end
44
44
 
@@ -42,7 +42,7 @@ module Seafoam
42
42
  # For constant nodes, the rawvalue is a truncated version of the
43
43
  # actual value, which is fully qualified. Instead, produce a simple
44
44
  # version of the value and don't truncate it.
45
- if node_class == "org.graalvm.compiler.nodes.ConstantNode"
45
+ if node_class.end_with?(".compiler.nodes.ConstantNode")
46
46
  if node.props["value"] =~ /Object\[Instance<(\w+\.)+(\w*)>\]/
47
47
  node.props["rawvalue"] = "instance:#{Regexp.last_match(2)}"
48
48
  end
@@ -59,63 +59,63 @@ module Seafoam
59
59
  end
60
60
 
61
61
  # The template for InvokeNode could be simpler.
62
- if node_class == "org.graalvm.compiler.nodes.InvokeNode"
62
+ if node_class.end_with?(".compiler.nodes.InvokeNode")
63
63
  name_template = "Call {p#targetMethod/s}"
64
64
  end
65
65
 
66
66
  # The template for InvokeWithExceptionNode could be simpler.
67
- if node_class == "org.graalvm.compiler.nodes.InvokeWithExceptionNode"
67
+ if node_class.end_with?(".compiler.nodes.InvokeWithExceptionNode")
68
68
  name_template = "Call {p#targetMethod/s} !"
69
69
  end
70
70
 
71
71
  # The template for CommitAllocationNode could be simpler.
72
- if node_class == "org.graalvm.compiler.nodes.virtual.CommitAllocationNode"
72
+ if node_class.end_with?(".compiler.nodes.virtual.CommitAllocationNode")
73
73
  name_template = "Alloc"
74
74
  end
75
75
 
76
76
  # The template for org.graalvm.compiler.nodes.virtual.VirtualArrayNode
77
77
  # includes an ID that we don't normally need.
78
- if node_class == "org.graalvm.compiler.nodes.virtual.VirtualArrayNode"
78
+ if node_class.end_with?(".compiler.nodes.virtual.VirtualArrayNode")
79
79
  name_template = "VirtualArray {p#componentType/s}[{p#length}]"
80
80
  end
81
81
 
82
82
  # The template for LoadField could be simpler.
83
- if node_class == "org.graalvm.compiler.nodes.java.LoadFieldNode"
83
+ if node_class.end_with?(".compiler.nodes.java.LoadFieldNode")
84
84
  name_template = "LoadField {x#field}"
85
85
  end
86
86
 
87
87
  # The template for StoreField could be simpler.
88
- if node_class == "org.graalvm.compiler.nodes.java.StoreFieldNode"
88
+ if node_class.end_with?(".compiler.nodes.java.StoreFieldNode")
89
89
  name_template = "StoreField {x#field}"
90
90
  end
91
91
 
92
92
  # We want to see keys for IntegerSwitchNode.
93
- if node_class == "org.graalvm.compiler.nodes.extended.IntegerSwitchNode"
93
+ if node_class.end_with?(".compiler.nodes.extended.IntegerSwitchNode")
94
94
  name_template = "IntegerSwitch {p#keys}"
95
95
  end
96
96
 
97
97
  # Use a symbol for PiNode.
98
- if node_class == "org.graalvm.compiler.nodes.PiNode"
98
+ if node_class.end_with?(".compiler.nodes.PiNode")
99
99
  name_template = "π"
100
100
  end
101
101
 
102
102
  # Use a symbol for PiArrayNode.
103
- if node_class == "org.graalvm.compiler.nodes.PiArrayNode"
103
+ if node_class.end_with?(".compiler.nodes.PiArrayNode")
104
104
  name_template = "[π]"
105
105
  end
106
106
 
107
107
  # Use a symbol for PhiNode.
108
- if node_class == "org.graalvm.compiler.nodes.ValuePhiNode"
108
+ if node_class.end_with?(".compiler.nodes.ValuePhiNode")
109
109
  name_template = "ϕ"
110
110
  end
111
111
 
112
112
  # Better template for frame states.
113
- if node_class == "org.graalvm.compiler.nodes.FrameState"
113
+ if node_class.end_with?(".compiler.nodes.FrameState")
114
114
  name_template = "FrameState {x#state}"
115
115
  end
116
116
 
117
117
  # Show the stamp in an InstanceOfNode.
118
- if node_class == "org.graalvm.compiler.nodes.java.InstanceOfNode"
118
+ if node_class.end_with?(".compiler.nodes.java.InstanceOfNode")
119
119
  name_template = "InstanceOf {x#simpleStamp}"
120
120
  end
121
121
 
@@ -194,6 +194,49 @@ module Seafoam
194
194
  "org.graalvm.compiler.replacements.nodes.ReadRegisterNode" => "memory",
195
195
  "org.graalvm.compiler.replacements.nodes.WriteRegisterNode" => "memory",
196
196
  "org.graalvm.compiler.word.WordCastNode" => "memory",
197
+ "jdk.graal.compiler.nodes.BeginNode" => "control",
198
+ "jdk.graal.compiler.nodes.ConstantNode" => "input",
199
+ "jdk.graal.compiler.nodes.DeoptimizeNode" => "control",
200
+ "jdk.graal.compiler.nodes.EndNode" => "control",
201
+ "jdk.graal.compiler.nodes.extended.IntegerSwitchNode" => "control",
202
+ "jdk.graal.compiler.nodes.extended.UnsafeMemoryLoadNode" => "memory",
203
+ "jdk.graal.compiler.nodes.extended.UnsafeMemoryStoreNode" => "memory",
204
+ "jdk.graal.compiler.nodes.FixedGuardNode" => "guard",
205
+ "jdk.graal.compiler.nodes.FrameState" => "info",
206
+ "jdk.graal.compiler.nodes.GuardNode" => "guard",
207
+ "jdk.graal.compiler.nodes.IfNode" => "control",
208
+ "jdk.graal.compiler.nodes.InvokeNode" => "call",
209
+ "jdk.graal.compiler.nodes.InvokeWithExceptionNode" => "call",
210
+ "jdk.graal.compiler.nodes.java.ArrayLengthNode" => "memory",
211
+ "jdk.graal.compiler.nodes.java.LoadFieldNode" => "memory",
212
+ "jdk.graal.compiler.nodes.java.LoadIndexedNode" => "memory",
213
+ "jdk.graal.compiler.nodes.java.MonitorEnterNode" => "sync",
214
+ "jdk.graal.compiler.nodes.java.MonitorExitNode" => "sync",
215
+ "jdk.graal.compiler.nodes.java.NewArrayNode" => "alloc",
216
+ "jdk.graal.compiler.nodes.java.NewInstanceNode" => "alloc",
217
+ "jdk.graal.compiler.nodes.java.RawMonitorEnterNode" => "sync",
218
+ "jdk.graal.compiler.nodes.java.StoreFieldNode" => "memory",
219
+ "jdk.graal.compiler.nodes.java.StoreIndexedNode" => "memory",
220
+ "jdk.graal.compiler.nodes.KillingBeginNode" => "control",
221
+ "jdk.graal.compiler.nodes.LoopBeginNode" => "control",
222
+ "jdk.graal.compiler.nodes.LoopEndNode" => "control",
223
+ "jdk.graal.compiler.nodes.LoopExitNode" => "control",
224
+ "jdk.graal.compiler.nodes.memory.ReadNode" => "memory",
225
+ "jdk.graal.compiler.nodes.memory.WriteNode" => "memory",
226
+ "jdk.graal.compiler.nodes.MergeNode" => "control",
227
+ "jdk.graal.compiler.nodes.ParameterNode" => "input",
228
+ "jdk.graal.compiler.nodes.PrefetchAllocateNode" => "alloc",
229
+ "jdk.graal.compiler.nodes.ReturnNode" => "control",
230
+ "jdk.graal.compiler.nodes.StartNode" => "control",
231
+ "jdk.graal.compiler.nodes.UnwindNode" => "control",
232
+ "jdk.graal.compiler.nodes.virtual.AllocatedObjectNode" => "virtual",
233
+ "jdk.graal.compiler.nodes.virtual.CommitAllocationNode" => "alloc",
234
+ "jdk.graal.compiler.nodes.virtual.VirtualArrayNode" => "virtual",
235
+ "jdk.graal.compiler.nodes.VirtualObjectState" => "info",
236
+ "jdk.graal.compiler.replacements.nodes.ArrayEqualsNode" => "memory",
237
+ "jdk.graal.compiler.replacements.nodes.ReadRegisterNode" => "memory",
238
+ "jdk.graal.compiler.replacements.nodes.WriteRegisterNode" => "memory",
239
+ "jdk.graal.compiler.word.WordCastNode" => "memory",
197
240
  }
198
241
 
199
242
  # Render a Graal 'name template'.
@@ -208,15 +251,19 @@ module Seafoam
208
251
  e.props[:name] == Regexp.last_match(1)
209
252
  end.map { |e| e.from.id }.join(", ")
210
253
  end
211
- string = string.gsub(/\{x#field\}/) do |_|
254
+ string = string.gsub("{x#field}") do |_|
212
255
  "#{node.props.dig("field", :field_class).split(".").last}.#{node.props.dig("field", :name)}"
213
256
  end
214
- string = string.gsub(/\{x#state\}/) do |_|
215
- "#{node.props.dig("code",
216
- :declaring_class)}##{node.props.dig("code",
217
- :method_name)} #{node.props["sourceFile"]}:#{node.props["sourceLine"]}"
257
+ string = string.gsub("{x#state}") do |_|
258
+ "#{node.props.dig(
259
+ "code",
260
+ :declaring_class,
261
+ )}##{node.props.dig(
262
+ "code",
263
+ :method_name,
264
+ )} #{node.props["sourceFile"]}:#{node.props["sourceLine"]}"
218
265
  end
219
- string = string.gsub(/\{x#simpleStamp\}/) do |_|
266
+ string = string.gsub("{x#simpleStamp}") do |_|
220
267
  stamp = node.props.dig("checkedStamp")
221
268
  if stamp =~ /a!?# L(.*);/
222
269
  Regexp.last_match(1)
@@ -230,7 +277,7 @@ module Seafoam
230
277
  # Annotate edges with their label and kind.
231
278
  def apply_edges(graph)
232
279
  graph.edges.each do |edge|
233
- if edge.to.node_class == "org.graalvm.compiler.nodes.ValuePhiNode" && edge.props[:name] == "values"
280
+ if edge.to.node_class.end_with?(".compiler.nodes.ValuePhiNode") && edge.props[:name] == "values"
234
281
  merge_node = edge.to.edges.find { |e| e.props[:name] == "merge" }.from
235
282
  control_into_merge = ["ends", "loopBegin"]
236
283
  merge_node_control_edges_in = merge_node.edges.select do |e|
@@ -278,11 +325,11 @@ module Seafoam
278
325
  # (break) to the LoopBeginNode. Both are drawn reversed.
279
326
  when "loopBegin"
280
327
  case edge.to.node_class
281
- when "org.graalvm.compiler.nodes.LoopEndNode"
328
+ when "org.graalvm.compiler.nodes.LoopEndNode", "jdk.graal.compiler.nodes.LoopEndNode"
282
329
  # If it's from the LoopEnd then it's the control edge to follow.
283
330
  edge.props[:kind] = "loop"
284
331
  edge.props[:reverse] = true
285
- when "org.graalvm.compiler.nodes.LoopExitNode"
332
+ when "org.graalvm.compiler.nodes.LoopExitNode", "jdk.graal.compiler.nodes.LoopExitNode"
286
333
  # If it's from the LoopExit then it's just for information - it's
287
334
  # not control flow to follow.
288
335
  edge.props[:kind] = "info"
@@ -417,16 +464,30 @@ module Seafoam
417
464
  end
418
465
 
419
466
  # If we see these in the graph properties it's probably a Graal graph.
420
- TRIGGERS = ["HostedGraphBuilderPhase", "GraalCompiler", "TruffleCompiler"]
467
+ TRIGGERS = ["HostedGraphBuilderPhase", "GraalCompiler", "TruffleCompiler", "SubstrateCompilation"]
421
468
 
422
469
  # Simple input node classes that may be inlined.
423
- SIMPLE_INPUTS = ["org.graalvm.compiler.nodes.ConstantNode", "org.graalvm.compiler.nodes.ParameterNode"]
470
+ SIMPLE_INPUTS = [
471
+ "org.graalvm.compiler.nodes.ConstantNode",
472
+ "org.graalvm.compiler.nodes.ParameterNode",
473
+ "jdk.graal.compiler.nodes.ConstantNode",
474
+ "jdk.graal.compiler.nodes.ParameterNode",
475
+ ]
424
476
 
425
477
  # Nodes just to maintain frame state.
426
- FRAME_STATE_NODES = ["org.graalvm.compiler.nodes.FrameState",
427
- "org.graalvm.compiler.virtual.nodes.MaterializedObjectState",]
428
-
429
- BEGIN_END_NODES = ["org.graalvm.compiler.nodes.BeginNode", "org.graalvm.compiler.nodes.EndNode"]
478
+ FRAME_STATE_NODES = [
479
+ "org.graalvm.compiler.nodes.FrameState",
480
+ "org.graalvm.compiler.virtual.nodes.MaterializedObjectState",
481
+ "jdk.graal.compiler.nodes.FrameState",
482
+ "jdk.graal.compiler.virtual.nodes.MaterializedObjectState",
483
+ ]
484
+
485
+ BEGIN_END_NODES = [
486
+ "org.graalvm.compiler.nodes.BeginNode",
487
+ "org.graalvm.compiler.nodes.EndNode",
488
+ "jdk.graal.compiler.nodes.BeginNode",
489
+ "jdk.graal.compiler.nodes.EndNode",
490
+ ]
430
491
  end
431
492
  end
432
493
  end
@@ -17,6 +17,7 @@ module Seafoam
17
17
  def apply(graph)
18
18
  simplify_truffle_args(graph) if @options[:simplify_truffle_args]
19
19
  simplify_alloc(graph) if @options[:simplify_alloc]
20
+ hide_reachability_fences(graph) if @options[:hide_reachability_fences]
20
21
  end
21
22
 
22
23
  private
@@ -25,27 +26,34 @@ module Seafoam
25
26
  # like a Graal parameter node.
26
27
  def simplify_truffle_args(graph)
27
28
  graph.nodes.dup.each_value do |node|
28
- next unless node.node_class == "org.graalvm.compiler.nodes.java.LoadIndexedNode"
29
+ next unless node.node_class.end_with?(".compiler.nodes.java.LoadIndexedNode")
29
30
 
30
31
  index_node = node.inputs.find { |edge| edge.props[:name] == "index" }.from
31
32
  array_node = Graal::Pi.follow_pi_object(node.inputs.find { |edge| edge.props[:name] == "array" }.from)
32
33
 
33
- next unless index_node.node_class == "org.graalvm.compiler.nodes.ConstantNode"
34
- next unless array_node.node_class == "org.graalvm.compiler.nodes.ParameterNode"
34
+ next unless index_node.node_class.end_with?(".compiler.nodes.ConstantNode")
35
+ next unless array_node.node_class.end_with?(".compiler.nodes.ParameterNode")
35
36
 
36
37
  node.props[:truffle_arg_load] = true
37
38
 
38
- index = index_node.props["rawvalue"]
39
+ lang_formatter = Seafoam::Passes::TruffleTranslators.get_translator(node)
40
+ index = lang_formatter.translate_argument_load(index_node.props["rawvalue"].to_i)
39
41
 
40
- arg_node = graph.create_node(graph.new_id,
41
- { synthetic: true, inlined: true, label: "T(#{index})", kind: "input" })
42
+ arg_node = graph.create_node(
43
+ graph.new_id,
44
+ { synthetic: true, synthetic_class: "TruffleArgument", inlined: true, label: "T(#{index})", kind: "input" },
45
+ )
46
+
47
+ edges_to_remove = []
42
48
 
43
49
  node.outputs.each do |output|
44
50
  next if output.props[:name] == "next"
45
51
 
46
52
  graph.create_edge(arg_node, output.to, output.props.dup)
47
- graph.remove_edge(output)
53
+ edges_to_remove << output
48
54
  end
55
+
56
+ edges_to_remove.each { |edge| graph.remove_edge(edge) }
49
57
  end
50
58
 
51
59
  graph.nodes.each_value.select { |node| node.props[:truffle_arg_load] }.each do |node|
@@ -61,7 +69,7 @@ module Seafoam
61
69
  # are constants that are null or 0.
62
70
  def simplify_alloc(graph)
63
71
  commit_allocation_nodes = graph.nodes.each_value.select do |node|
64
- node.node_class == "org.graalvm.compiler.nodes.virtual.CommitAllocationNode"
72
+ node.node_class.end_with?(".compiler.nodes.virtual.CommitAllocationNode")
65
73
  end
66
74
 
67
75
  commit_allocation_nodes.each do |commit_allocation_node|
@@ -78,11 +86,16 @@ module Seafoam
78
86
 
79
87
  virtual_id = m[1].to_i
80
88
 
81
- (m = /^(\w+(?:\[\])?)\[([0-9,]+)\]$/.match(value)) || raise(value)
89
+ m = /^([[:alnum:]$]+(?:\[\])?)\[([0-9,]+)\]$/.match(value)
90
+
91
+ unless m
92
+ raise "Unexpected value in allocation node: '#{value}'"
93
+ end
94
+
82
95
  class_name, values = m.captures
83
96
  values = values.split(",").map(&:to_i)
84
97
  virtual_node = graph.nodes[virtual_id]
85
- if virtual_node.node_class == "org.graalvm.compiler.nodes.virtual.VirtualArrayNode"
98
+ if virtual_node.node_class.end_with?(".compiler.nodes.virtual.VirtualArrayNode")
86
99
  label = "New #{class_name[0...-1]}#{virtual_node.props["length"]}]"
87
100
  fields = values.size.times.to_a
88
101
  else
@@ -91,7 +104,10 @@ module Seafoam
91
104
  end
92
105
  raise unless fields.size == values.size
93
106
 
94
- new_node = graph.create_node(graph.new_id, { synthetic: true, label: label, kind: "alloc" })
107
+ new_node = graph.create_node(
108
+ graph.new_id,
109
+ { synthetic: true, synthetic_class: "TruffleNew", label: label, kind: "alloc" },
110
+ )
95
111
 
96
112
  object = [new_node, virtual_node, fields, values]
97
113
  objects << object
@@ -108,7 +124,7 @@ module Seafoam
108
124
  usage = virtual_to_object[value_id]
109
125
  b.call(usage) if usage
110
126
  end
111
- end
127
+ end,
112
128
  ).reduce(:concat)
113
129
 
114
130
  prev = control_flow_pred.from
@@ -116,7 +132,7 @@ module Seafoam
116
132
  graph.create_edge(prev, new_node, control_flow_pred.props)
117
133
 
118
134
  allocated_object_node = virtual_node.outputs.find do |output|
119
- output.to.node_class == "org.graalvm.compiler.nodes.virtual.AllocatedObjectNode"
135
+ output.to.node_class.end_with?(".compiler.nodes.virtual.AllocatedObjectNode")
120
136
  end
121
137
  if allocated_object_node
122
138
  allocated_object_node = allocated_object_node.to
@@ -131,7 +147,7 @@ module Seafoam
131
147
  fields.zip(values) do |field, value_id|
132
148
  value_node = virtual_to_object[value_id]&.first || graph.nodes[value_id]
133
149
  if @options[:hide_null_fields] &&
134
- (value_node.node_class == "org.graalvm.compiler.nodes.ConstantNode") &&
150
+ value_node.node_class.end_with?(".compiler.nodes.ConstantNode") &&
135
151
  ["Object[null]", "0"].include?(value_node.props["rawvalue"])
136
152
  value_node.props[:hidden] = true
137
153
  else
@@ -149,8 +165,24 @@ module Seafoam
149
165
  end
150
166
  end
151
167
 
168
+ # Hide reachability fences - they're just really boring.
169
+ def hide_reachability_fences(graph)
170
+ graph.nodes.each_value do |node|
171
+ next unless node.node_class.end_with?(".compiler.nodes.java.ReachabilityFenceNode")
172
+
173
+ pred = node.inputs.find { |edge| edge.props[:name] == "next" }
174
+ succ = node.outputs.find { |edge| edge.props[:name] == "next" }
175
+
176
+ graph.create_edge(pred.from, succ.to, pred.props.merge({ synthetic: true }))
177
+
178
+ node.props[:hidden] = true
179
+ pred.props[:hidden] = true
180
+ succ.props[:hidden] = true
181
+ end
182
+ end
183
+
152
184
  # If we see these in the graph properties it's probably a Truffle graph.
153
- TRIGGERS = ["TruffleCompiler"]
185
+ TRIGGERS = ["TruffleCompiler", "TruffleFinal"]
154
186
  end
155
187
  end
156
188
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Seafoam
4
+ module Passes
5
+ module TruffleTranslators
6
+ class Default
7
+ def translate_argument_load(index)
8
+ index.to_s
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+
5
+ module Seafoam
6
+ module Passes
7
+ module TruffleTranslators
8
+ autoload :Default, "seafoam/passes/truffle_translators/default"
9
+ autoload :TruffleRuby, "seafoam/passes/truffle_translators/truffleruby"
10
+
11
+ TRUFFLE_LANGUAGES = {
12
+ "org.truffleruby" => "TruffleRuby",
13
+ }
14
+
15
+ class << self
16
+ def get_translator(node)
17
+ translator = node.visit_outputs(:bfs) do |entry|
18
+ declaring_class = entry.props["code"] || entry.props.dig("nodeSourcePosition", :method)
19
+
20
+ if declaring_class
21
+ subpackage = declaring_class[:declaring_class].match(/^(\w+\.\w+)\./)[1]
22
+ translator = TRUFFLE_LANGUAGES[subpackage]
23
+
24
+ break const_get(translator).new if translator
25
+ end
26
+ end
27
+
28
+ translator || Default.new
29
+ end
30
+ end
31
+
32
+ class Default
33
+ def translate_argument_load(index)
34
+ index.to_s
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Seafoam
4
+ module Passes
5
+ module TruffleTranslators
6
+ class TruffleRuby < Default
7
+ TRUFFLERUBY_ARGS = [
8
+ "DECLARATION_FRAME",
9
+ "CALLER_SPECIAL_VARIABLES",
10
+ "METHOD",
11
+ "DECLARATION_CONTEXT",
12
+ "FRAME_ON_STACK_MARKER",
13
+ "SELF",
14
+ "BLOCK",
15
+ "DESCRIPTOR",
16
+ ]
17
+
18
+ def translate_argument_load(index)
19
+ index >= TRUFFLERUBY_ARGS.size ? "args[#{index - TRUFFLERUBY_ARGS.size}]" : TRUFFLERUBY_ARGS[index]
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+
5
+ module Seafoam
6
+ class BFS
7
+ attr_reader :root
8
+
9
+ def initialize(root)
10
+ @root = root
11
+ end
12
+
13
+ def search(&block)
14
+ queue = root.outputs.collect(&:to)
15
+ visited = Set.new
16
+
17
+ until queue.empty?
18
+ entry = queue.shift
19
+ visited << entry
20
+
21
+ result = entry.visit(&block)
22
+
23
+ if result
24
+ return result
25
+ else
26
+ entry.outputs.collect(&:to).each do |child|
27
+ queue << child unless visited.include?(child)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Seafoam
4
4
  MAJOR_VERSION = 0
5
- MINOR_VERSION = 14
5
+ MINOR_VERSION = 16
6
6
  VERSION = "#{MAJOR_VERSION}.#{MINOR_VERSION}"
7
7
  end
data/lib/seafoam.rb CHANGED
@@ -9,6 +9,7 @@ require "seafoam/graal/graph_description"
9
9
  require "seafoam/graal/source"
10
10
  require "seafoam/graal/pi"
11
11
  require "seafoam/passes"
12
+ require "seafoam/passes/truffle_translators/translators"
12
13
  require "seafoam/passes/truffle"
13
14
  require "seafoam/passes/graal"
14
15
  require "seafoam/passes/fallback"
@@ -20,3 +21,4 @@ require "seafoam/mermaid_writer"
20
21
  require "seafoam/markdown_writer"
21
22
  require "seafoam/commands"
22
23
  require "seafoam/formatters/formatters"
24
+ require "seafoam/search"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: seafoam
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.14'
4
+ version: '0.16'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Seaton
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-09-02 00:00:00.000000000 Z
11
+ date: 2024-03-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -42,14 +42,14 @@ dependencies:
42
42
  name: rubocop-shopify
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
47
  version: 2.9.0
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: 2.9.0
55
55
  description:
@@ -86,12 +86,19 @@ files:
86
86
  - lib/seafoam/passes/fallback.rb
87
87
  - lib/seafoam/passes/graal.rb
88
88
  - lib/seafoam/passes/truffle.rb
89
+ - lib/seafoam/passes/truffle_translators/default.rb
90
+ - lib/seafoam/passes/truffle_translators/translators.rb
91
+ - lib/seafoam/passes/truffle_translators/truffleruby.rb
92
+ - lib/seafoam/search.rb
89
93
  - lib/seafoam/spotlight.rb
90
94
  - lib/seafoam/version.rb
91
95
  homepage: https://github.com/Shopify/seafoam
92
96
  licenses:
93
97
  - MIT
94
- metadata: {}
98
+ metadata:
99
+ bug_tracker_uri: https://github.com/Shopify/seafoam/issues
100
+ source_code_uri: https://github.com/Shopify/seafoam
101
+ allowed_push_host: https://rubygems.org
95
102
  post_install_message:
96
103
  rdoc_options: []
97
104
  require_paths:
@@ -107,7 +114,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
107
114
  - !ruby/object:Gem::Version
108
115
  version: '0'
109
116
  requirements: []
110
- rubygems_version: 3.3.7
117
+ rubygems_version: 3.5.6
111
118
  signing_key:
112
119
  specification_version: 4
113
120
  summary: A tool for working with compiler graphs