seafoam 0.13 → 0.14

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