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