seafoam 0.13 → 0.14

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.
@@ -1,15 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tsort"
4
+
1
5
  module Seafoam
2
6
  module Passes
3
7
  # The Truffle pass applies if it looks like it was compiled by Truffle.
4
8
  class TrufflePass < Pass
5
- def self.applies?(graph)
6
- graph.props.values.any? do |v|
7
- TRIGGERS.any? { |t| v.to_s.include?(t) }
9
+ class << self
10
+ def applies?(graph)
11
+ graph.props.values.any? do |v|
12
+ TRIGGERS.any? { |t| v.to_s.include?(t) }
13
+ end
8
14
  end
9
15
  end
10
16
 
11
17
  def apply(graph)
12
- simplify_truffle_args graph if @options[:simplify_truffle_args]
18
+ simplify_truffle_args(graph) if @options[:simplify_truffle_args]
19
+ simplify_alloc(graph) if @options[:simplify_alloc]
13
20
  end
14
21
 
15
22
  private
@@ -18,41 +25,132 @@ module Seafoam
18
25
  # like a Graal parameter node.
19
26
  def simplify_truffle_args(graph)
20
27
  graph.nodes.dup.each_value do |node|
21
- next unless node.props.dig(:node_class, :node_class) == 'org.graalvm.compiler.nodes.java.LoadIndexedNode'
28
+ next unless node.node_class == "org.graalvm.compiler.nodes.java.LoadIndexedNode"
22
29
 
23
- index_node = node.inputs.find { |edge| edge.props[:name] == 'index' }.from
24
- array_node = Graal::Pi.follow_pi_object(node.inputs.find { |edge| edge.props[:name] == 'array' }.from)
30
+ index_node = node.inputs.find { |edge| edge.props[:name] == "index" }.from
31
+ array_node = Graal::Pi.follow_pi_object(node.inputs.find { |edge| edge.props[:name] == "array" }.from)
25
32
 
26
- next unless index_node.props.dig(:node_class, :node_class) == 'org.graalvm.compiler.nodes.ConstantNode'
27
- next unless array_node.props.dig(:node_class, :node_class) == 'org.graalvm.compiler.nodes.ParameterNode'
33
+ next unless index_node.node_class == "org.graalvm.compiler.nodes.ConstantNode"
34
+ next unless array_node.node_class == "org.graalvm.compiler.nodes.ParameterNode"
28
35
 
29
36
  node.props[:truffle_arg_load] = true
30
37
 
31
- index = index_node.props['rawvalue']
38
+ index = index_node.props["rawvalue"]
32
39
 
33
- arg_node = graph.create_node(graph.new_id, { synthetic: true, inlined: true, label: "T(#{index})", kind: 'input' })
40
+ arg_node = graph.create_node(graph.new_id,
41
+ { synthetic: true, inlined: true, label: "T(#{index})", kind: "input" })
34
42
 
35
43
  node.outputs.each do |output|
36
- next if output.props[:name] == 'next'
44
+ next if output.props[:name] == "next"
37
45
 
38
- graph.create_edge arg_node, output.to, output.props.dup
39
- graph.remove_edge output
46
+ graph.create_edge(arg_node, output.to, output.props.dup)
47
+ graph.remove_edge(output)
40
48
  end
41
49
  end
42
50
 
43
51
  graph.nodes.each_value.select { |node| node.props[:truffle_arg_load] }.each do |node|
44
- control_in = node.inputs.find { |edge| edge.props[:name] == 'next' }
45
- control_out = node.outputs.find { |edge| edge.props[:name] == 'next' }
46
- graph.create_edge control_in.from, control_out.to, { name: 'next' }
47
- graph.remove_edge control_in
48
- graph.remove_edge control_out
52
+ control_in = node.inputs.find { |edge| edge.props[:name] == "next" }
53
+ control_out = node.outputs.find { |edge| edge.props[:name] == "next" }
54
+ graph.create_edge(control_in.from, control_out.to, { name: "next" })
55
+ graph.remove_edge(control_in)
56
+ graph.remove_edge(control_out)
57
+ end
58
+ end
59
+
60
+ # Hide nodes that are uninteresting inputs to an allocation node. These
61
+ # are constants that are null or 0.
62
+ def simplify_alloc(graph)
63
+ commit_allocation_nodes = graph.nodes.each_value.select do |node|
64
+ node.node_class == "org.graalvm.compiler.nodes.virtual.CommitAllocationNode"
65
+ end
66
+
67
+ commit_allocation_nodes.each do |commit_allocation_node|
68
+ control_flow_pred = commit_allocation_node.inputs.first
69
+ control_flow_next = commit_allocation_node.outputs.first
70
+
71
+ objects = []
72
+ virtual_to_object = {}
73
+
74
+ # First step to fill virtual_to_object and avoid ordering issues
75
+ commit_allocation_node.props.each_pair do |key, value|
76
+ m = /^object\((\d+)\)$/.match(key)
77
+ next unless m
78
+
79
+ virtual_id = m[1].to_i
80
+
81
+ (m = /^(\w+(?:\[\])?)\[([0-9,]+)\]$/.match(value)) || raise(value)
82
+ class_name, values = m.captures
83
+ values = values.split(",").map(&:to_i)
84
+ virtual_node = graph.nodes[virtual_id]
85
+ if virtual_node.node_class == "org.graalvm.compiler.nodes.virtual.VirtualArrayNode"
86
+ label = "New #{class_name[0...-1]}#{virtual_node.props["length"]}]"
87
+ fields = values.size.times.to_a
88
+ else
89
+ label = "New #{class_name}"
90
+ fields = virtual_node.props["fields"].map { |field| field[:name] }
91
+ end
92
+ raise unless fields.size == values.size
93
+
94
+ new_node = graph.create_node(graph.new_id, { synthetic: true, label: label, kind: "alloc" })
95
+
96
+ object = [new_node, virtual_node, fields, values]
97
+ objects << object
98
+ virtual_to_object[virtual_id] = object
99
+ end
100
+
101
+ # Topological sort of the new nodes in the control flow according to data dependencies
102
+ # There can be cycles (e.g., instances referring one another),
103
+ # so we use TSort.strongly_connected_components instead of TSort.tsort.
104
+ objects = TSort.strongly_connected_components(
105
+ objects.method(:each),
106
+ lambda do |(_new_node, _virtual_node, _fields, values), &b|
107
+ values.each do |value_id|
108
+ usage = virtual_to_object[value_id]
109
+ b.call(usage) if usage
110
+ end
111
+ end
112
+ ).reduce(:concat)
113
+
114
+ prev = control_flow_pred.from
115
+ objects.each do |new_node, virtual_node, fields, values|
116
+ graph.create_edge(prev, new_node, control_flow_pred.props)
117
+
118
+ allocated_object_node = virtual_node.outputs.find do |output|
119
+ output.to.node_class == "org.graalvm.compiler.nodes.virtual.AllocatedObjectNode"
120
+ end
121
+ if allocated_object_node
122
+ allocated_object_node = allocated_object_node.to
123
+
124
+ allocated_object_node.outputs.each do |edge|
125
+ graph.create_edge(new_node, edge.to, edge.props)
126
+ end
127
+
128
+ allocated_object_node.props[:hidden] = true
129
+ end
130
+
131
+ fields.zip(values) do |field, value_id|
132
+ value_node = virtual_to_object[value_id]&.first || graph.nodes[value_id]
133
+ if @options[:hide_null_fields] &&
134
+ (value_node.node_class == "org.graalvm.compiler.nodes.ConstantNode") &&
135
+ ["Object[null]", "0"].include?(value_node.props["rawvalue"])
136
+ value_node.props[:hidden] = true
137
+ else
138
+ graph.create_edge(value_node, new_node, { name: field })
139
+ end
140
+ end
141
+
142
+ virtual_node.props[:hidden] = true
143
+
144
+ prev = new_node
145
+ end
146
+ graph.create_edge(prev, control_flow_next.to, control_flow_next.props)
147
+
148
+ commit_allocation_node.props[:hidden] = true
49
149
  end
50
150
  end
51
151
 
52
152
  # If we see these in the graph properties it's probably a Truffle graph.
53
- TRIGGERS = %w[
54
- TruffleCompiler
55
- ]
153
+ TRIGGERS = ["TruffleCompiler"]
56
154
  end
57
155
  end
58
156
  end
@@ -1,39 +1,43 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Seafoam
2
4
  # Passes are routines to read the graph and apply properties which tools,
3
5
  # such as the render command, can use to show more understandable output.
4
6
  module Passes
5
- # Apply all applicable passes to a graph.
6
- def self.apply(graph, options = {})
7
- passes.each do |pass|
8
- next unless pass.applies?(graph)
9
-
10
- # Record for information that the pass was applied this graph.
11
- passes_applied = graph.props[:passes_applied] ||= []
12
- passes_applied.push pass
13
-
14
- # Run the pass.
15
- instance = pass.new(options)
16
- instance.apply graph
7
+ class << self
8
+ # Apply all applicable passes to a graph.
9
+ def apply(graph, options = {})
10
+ passes.each do |pass|
11
+ next unless pass.applies?(graph)
12
+
13
+ # Record for information that the pass was applied this graph.
14
+ passes_applied = graph.props[:passes_applied] ||= []
15
+ passes_applied.push(pass)
16
+
17
+ # Run the pass.
18
+ instance = pass.new(options)
19
+ instance.apply(graph)
20
+ end
17
21
  end
18
- end
19
22
 
20
- # Get a list of all passes in the system.
21
- def self.passes
22
- # We have a defined order for passes to run - these passes at the start.
23
- pre_passes = [
24
- TrufflePass,
25
- GraalPass
26
- ]
23
+ # Get a list of all passes in the system.
24
+ def passes
25
+ # We have a defined order for passes to run - these passes at the start.
26
+ pre_passes = [
27
+ TrufflePass,
28
+ GraalPass,
29
+ ]
27
30
 
28
- # The fallback pass runs last.
29
- post_passes = [
30
- FallbackPass
31
- ]
31
+ # The fallback pass runs last.
32
+ post_passes = [
33
+ FallbackPass,
34
+ ]
32
35
 
33
- # Any extra passes in the middle.
34
- extra_passes = Pass::SUBCLASSES.dup - pre_passes - post_passes
36
+ # Any extra passes in the middle.
37
+ extra_passes = Pass::SUBCLASSES.dup - pre_passes - post_passes
35
38
 
36
- pre_passes + extra_passes + post_passes
39
+ pre_passes + extra_passes + post_passes
40
+ end
37
41
  end
38
42
  end
39
43
 
@@ -54,8 +58,11 @@ module Seafoam
54
58
  raise NotImplementedError
55
59
  end
56
60
 
57
- def self.inherited(pass)
58
- SUBCLASSES.push pass
61
+ class << self
62
+ def inherited(pass)
63
+ super
64
+ SUBCLASSES.push(pass)
65
+ end
59
66
  end
60
67
  end
61
68
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Seafoam
2
4
  # Spotlight can *light* nodes, which makes them visible, their adjacent nodes
3
5
  # visible by grey, and other nodes invisible. Multiple nodes can be *lit*.
@@ -9,11 +11,11 @@ module Seafoam
9
11
  # Mark a node as lit by the spotlight.
10
12
  def light(node)
11
13
  # This node is lit.
12
- node.props[:spotlight] = 'lit'
14
+ node.props[:spotlight] = "lit"
13
15
 
14
16
  # Adjacent nodes are shaded, if they haven't be lit themselvs.
15
17
  node.adjacent.each do |adjacent|
16
- adjacent.props[:spotlight] ||= 'shaded'
18
+ adjacent.props[:spotlight] ||= "shaded"
17
19
  end
18
20
  end
19
21
 
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Seafoam
2
4
  MAJOR_VERSION = 0
3
- MINOR_VERSION = 13
5
+ MINOR_VERSION = 14
4
6
  VERSION = "#{MAJOR_VERSION}.#{MINOR_VERSION}"
5
7
  end
data/lib/seafoam.rb CHANGED
@@ -1,20 +1,22 @@
1
- require 'seafoam/version'
2
- require 'seafoam/binary/io_binary_reader'
3
- require 'seafoam/bgv/bgv_parser'
4
- require 'seafoam/colors'
5
- require 'seafoam/graph'
6
- require 'seafoam/graal/graph_description'
7
- require 'seafoam/graal/source'
8
- require 'seafoam/graal/pi'
9
- require 'seafoam/passes'
10
- require 'seafoam/passes/truffle'
11
- require 'seafoam/passes/graal'
12
- require 'seafoam/passes/fallback'
13
- require 'seafoam/spotlight'
14
- require 'seafoam/isabelle_writer'
15
- require 'seafoam/json_writer'
16
- require 'seafoam/graphviz_writer'
17
- require 'seafoam/mermaid_writer'
18
- require 'seafoam/markdown_writer'
19
- require 'seafoam/commands'
20
- require 'seafoam/formatters/formatters'
1
+ # frozen_string_literal: true
2
+
3
+ require "seafoam/version"
4
+ require "seafoam/binary/io_binary_reader"
5
+ require "seafoam/bgv/bgv_parser"
6
+ require "seafoam/colors"
7
+ require "seafoam/graph"
8
+ require "seafoam/graal/graph_description"
9
+ require "seafoam/graal/source"
10
+ require "seafoam/graal/pi"
11
+ require "seafoam/passes"
12
+ require "seafoam/passes/truffle"
13
+ require "seafoam/passes/graal"
14
+ require "seafoam/passes/fallback"
15
+ require "seafoam/spotlight"
16
+ require "seafoam/isabelle_writer"
17
+ require "seafoam/json_writer"
18
+ require "seafoam/graphviz_writer"
19
+ require "seafoam/mermaid_writer"
20
+ require "seafoam/markdown_writer"
21
+ require "seafoam/commands"
22
+ require "seafoam/formatters/formatters"
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.13'
4
+ version: '0.14'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Seaton
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-16 00:00:00.000000000 Z
11
+ date: 2022-09-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -39,21 +39,21 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '3.8'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rubocop
42
+ name: rubocop-shopify
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0.74'
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
- version: '0.74'
55
- description:
56
- email:
54
+ version: 2.9.0
55
+ description:
56
+ email:
57
57
  executables:
58
58
  - seafoam
59
59
  - bgv2json
@@ -92,7 +92,7 @@ homepage: https://github.com/Shopify/seafoam
92
92
  licenses:
93
93
  - MIT
94
94
  metadata: {}
95
- post_install_message:
95
+ post_install_message:
96
96
  rdoc_options: []
97
97
  require_paths:
98
98
  - lib
@@ -100,15 +100,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - ">="
102
102
  - !ruby/object:Gem::Version
103
- version: 2.5.9
103
+ version: 2.7.0
104
104
  required_rubygems_version: !ruby/object:Gem::Requirement
105
105
  requirements:
106
106
  - - ">="
107
107
  - !ruby/object:Gem::Version
108
108
  version: '0'
109
109
  requirements: []
110
- rubygems_version: 3.3.3
111
- signing_key:
110
+ rubygems_version: 3.3.7
111
+ signing_key:
112
112
  specification_version: 4
113
113
  summary: A tool for working with compiler graphs
114
114
  test_files: []