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 +4 -4
- data/lib/seafoam/bgv/bgv_parser.rb +2 -2
- data/lib/seafoam/commands.rb +62 -26
- data/lib/seafoam/formatters/json.rb +1 -1
- data/lib/seafoam/formatters/text.rb +2 -1
- data/lib/seafoam/graal/pi.rb +2 -0
- data/lib/seafoam/graph.rb +15 -1
- data/lib/seafoam/json_writer.rb +3 -3
- data/lib/seafoam/mermaid_writer.rb +1 -1
- data/lib/seafoam/passes/graal.rb +89 -28
- data/lib/seafoam/passes/truffle.rb +47 -15
- data/lib/seafoam/passes/truffle_translators/default.rb +13 -0
- data/lib/seafoam/passes/truffle_translators/translators.rb +39 -0
- data/lib/seafoam/passes/truffle_translators/truffleruby.rb +24 -0
- data/lib/seafoam/search.rb +33 -0
- data/lib/seafoam/version.rb +1 -1
- data/lib/seafoam.rb +2 -0
- metadata +13 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 953c4b8d8e9c803df82cf0806f64ad775016ff3aaac16e96b602e15b28ec9fe4
|
4
|
+
data.tar.gz: a933e3faf9effa97b4a48ae38b7ce48ebaa6eba50db5b675442ad2431c93be83
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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(
|
164
|
+
name = graph_header[:format].sub("%s") do
|
165
165
|
arg = graph_header[:args][count]
|
166
166
|
count += 1
|
167
167
|
arg
|
data/lib/seafoam/commands.rb
CHANGED
@@ -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
|
-
|
389
|
+
next if node.props[:hidden]
|
378
390
|
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
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
|
-
|
393
|
-
|
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
|
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.
|
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
|
-
["#{
|
13
|
+
["#{unhidden_count} nodes", *notes].join(", ") + "\n#{node_counts}"
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
data/lib/seafoam/graal/pi.rb
CHANGED
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
|
data/lib/seafoam/json_writer.rb
CHANGED
@@ -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
|
|
data/lib/seafoam/passes/graal.rb
CHANGED
@@ -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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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(
|
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(
|
215
|
-
"#{node.props.dig(
|
216
|
-
|
217
|
-
|
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(
|
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
|
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 = [
|
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 = [
|
427
|
-
|
428
|
-
|
429
|
-
|
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
|
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
|
34
|
-
next unless array_node.node_class
|
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
|
-
|
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(
|
41
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
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(
|
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
|
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
|
-
|
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,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
|
data/lib/seafoam/version.rb
CHANGED
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.
|
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:
|
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.
|
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
|