seafoam 0.14 → 0.16

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