seafoam 0.13 → 0.15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/bgv2isabelle +3 -2
- data/bin/bgv2json +3 -2
- data/bin/seafoam +3 -2
- data/lib/seafoam/bgv/bgv_parser.rb +58 -50
- data/lib/seafoam/binary/io_binary_reader.rb +19 -17
- data/lib/seafoam/colors.rb +17 -11
- data/lib/seafoam/commands.rb +226 -167
- data/lib/seafoam/formatters/base.rb +2 -0
- data/lib/seafoam/formatters/formatters.rb +5 -3
- data/lib/seafoam/formatters/json.rb +9 -6
- data/lib/seafoam/formatters/text.rb +8 -4
- data/lib/seafoam/graal/graph_description.rb +9 -1
- data/lib/seafoam/graal/pi.rb +10 -6
- data/lib/seafoam/graal/source.rb +13 -9
- data/lib/seafoam/graph.rb +29 -9
- data/lib/seafoam/graphviz_writer.rb +129 -99
- data/lib/seafoam/isabelle_writer.rb +9 -7
- data/lib/seafoam/json_writer.rb +19 -15
- data/lib/seafoam/markdown_writer.rb +5 -3
- data/lib/seafoam/mermaid_writer.rb +36 -24
- data/lib/seafoam/passes/fallback.rb +10 -6
- data/lib/seafoam/passes/graal.rb +194 -160
- data/lib/seafoam/passes/truffle.rb +152 -22
- 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/passes.rb +36 -29
- data/lib/seafoam/search.rb +33 -0
- data/lib/seafoam/spotlight.rb +4 -2
- data/lib/seafoam/version.rb +3 -1
- data/lib/seafoam.rb +24 -20
- metadata +21 -14
@@ -1,8 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Seafoam
|
2
4
|
# Formatters are the mechanism by which `seafoam` command output is presented to the user.
|
3
5
|
module Formatters
|
4
|
-
autoload :Base,
|
5
|
-
autoload :Json,
|
6
|
-
autoload :Text,
|
6
|
+
autoload :Base, "seafoam/formatters/base"
|
7
|
+
autoload :Json, "seafoam/formatters/json"
|
8
|
+
autoload :Text, "seafoam/formatters/text"
|
7
9
|
end
|
8
10
|
end
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
2
4
|
|
3
5
|
module Seafoam
|
4
6
|
module Formatters
|
@@ -7,7 +9,8 @@ module Seafoam
|
|
7
9
|
class DescribeFormatter < Seafoam::Formatters::Base::DescribeFormatter
|
8
10
|
def format
|
9
11
|
ret = Seafoam::Graal::GraphDescription::ATTRIBUTES.map { |attr| [attr, description.send(attr)] }.to_h
|
10
|
-
ret[:node_count] = graph.nodes.
|
12
|
+
ret[:node_count] = graph.nodes.values.count { |n| !n.props[:hidden] }
|
13
|
+
ret[:node_counts] = description.sorted_node_counts
|
11
14
|
|
12
15
|
ret.to_json
|
13
16
|
end
|
@@ -22,7 +25,7 @@ module Seafoam
|
|
22
25
|
def render_node_entry(node)
|
23
26
|
{
|
24
27
|
input: node.inputs.map { |input| build_edge(input) },
|
25
|
-
output: node.outputs.map { |output| build_edge(output) }
|
28
|
+
output: node.outputs.map { |output| build_edge(output) },
|
26
29
|
}.to_json
|
27
30
|
end
|
28
31
|
|
@@ -44,7 +47,7 @@ module Seafoam
|
|
44
47
|
def format
|
45
48
|
{
|
46
49
|
major_version: major_version,
|
47
|
-
minor_version: minor_version
|
50
|
+
minor_version: minor_version,
|
48
51
|
}.to_json
|
49
52
|
end
|
50
53
|
end
|
@@ -56,7 +59,7 @@ module Seafoam
|
|
56
59
|
{
|
57
60
|
graph_index: entry.index,
|
58
61
|
graph_file: entry.file,
|
59
|
-
graph_name_components: entry.graph_name_components
|
62
|
+
graph_name_components: entry.graph_name_components,
|
60
63
|
}
|
61
64
|
end.to_json
|
62
65
|
end
|
@@ -73,7 +76,7 @@ module Seafoam
|
|
73
76
|
name = method[:method_name]
|
74
77
|
{
|
75
78
|
class: declaring_class,
|
76
|
-
method: name
|
79
|
+
method: name,
|
77
80
|
}
|
78
81
|
end
|
79
82
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Seafoam
|
2
4
|
module Formatters
|
3
5
|
module Text
|
@@ -5,8 +7,10 @@ module Seafoam
|
|
5
7
|
class DescribeFormatter < Seafoam::Formatters::Base::DescribeFormatter
|
6
8
|
def format
|
7
9
|
notes = Seafoam::Graal::GraphDescription::ATTRIBUTES.select { |attr| description.send(attr) }
|
10
|
+
unhidden_count = graph.nodes.values.count { |n| !n.props[:hidden] }
|
11
|
+
node_counts = description.sorted_node_counts.map { |node_class, count| "#{node_class}: #{count}" }.join("\n")
|
8
12
|
|
9
|
-
["#{
|
13
|
+
["#{unhidden_count} nodes", *notes].join(", ") + "\n#{node_counts}"
|
10
14
|
end
|
11
15
|
end
|
12
16
|
|
@@ -19,12 +23,12 @@ module Seafoam
|
|
19
23
|
end
|
20
24
|
|
21
25
|
def render_node_entry(node)
|
22
|
-
ret = [
|
26
|
+
ret = ["Input:"]
|
23
27
|
ret += node.inputs.map do |input|
|
24
28
|
" #{node.id_and_label} <-(#{input.props[:label]}) #{input.from.id_and_label}"
|
25
29
|
end
|
26
30
|
|
27
|
-
ret <<
|
31
|
+
ret << "Output:"
|
28
32
|
ret += node.outputs.map do |output|
|
29
33
|
" #{node.id_and_label} ->(#{output.props[:label]}) #{output.to.id_and_label}"
|
30
34
|
end
|
@@ -48,7 +52,7 @@ module Seafoam
|
|
48
52
|
class ListFormatter < Seafoam::Formatters::Base::ListFormatter
|
49
53
|
def format
|
50
54
|
entries.map do |entry|
|
51
|
-
"#{entry.file}:#{entry.index} #{entry.graph_name_components.join(
|
55
|
+
"#{entry.file}:#{entry.index} #{entry.graph_name_components.join("/")}"
|
52
56
|
end.join("\n")
|
53
57
|
end
|
54
58
|
end
|
@@ -1,21 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Seafoam
|
2
4
|
module Graal
|
3
5
|
# Provides a high level description of a Graal graph's features.
|
4
6
|
class GraphDescription
|
5
|
-
ATTRIBUTES =
|
7
|
+
ATTRIBUTES = [:branches, :calls, :deopts, :linear, :loops]
|
6
8
|
|
7
9
|
ATTRIBUTES.each { |attr| attr_accessor(attr) unless attr == :linear }
|
10
|
+
attr_reader :node_counts
|
8
11
|
|
9
12
|
def initialize
|
10
13
|
@branches = false
|
11
14
|
@calls = false
|
12
15
|
@deopts = false
|
13
16
|
@loops = false
|
17
|
+
@node_counts = Hash.new(0)
|
14
18
|
end
|
15
19
|
|
16
20
|
def linear
|
17
21
|
!branches && !loops
|
18
22
|
end
|
23
|
+
|
24
|
+
def sorted_node_counts
|
25
|
+
@node_counts.to_a.sort_by { |node_class, count| [-count, node_class] }.to_h
|
26
|
+
end
|
19
27
|
end
|
20
28
|
end
|
21
29
|
end
|
data/lib/seafoam/graal/pi.rb
CHANGED
@@ -1,17 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Seafoam
|
2
4
|
module Graal
|
3
5
|
# Routines for understanding pi nodes in Graal.
|
4
6
|
module Pi
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
7
|
+
class << self
|
8
|
+
# Find the actual value behind potentially a chain of pi nodes.
|
9
|
+
def follow_pi_object(node)
|
10
|
+
node = node.edges.find { |edge| edge.props[:name] == "object" }.from while PI_NODES.include?(node.node_class)
|
11
|
+
node
|
12
|
+
end
|
9
13
|
end
|
10
14
|
|
11
15
|
# Pi nodes add type information.
|
12
16
|
PI_NODES = [
|
13
|
-
|
14
|
-
|
17
|
+
"org.graalvm.compiler.nodes.PiNode",
|
18
|
+
"org.graalvm.compiler.nodes.PiArrayNode",
|
15
19
|
]
|
16
20
|
end
|
17
21
|
end
|
data/lib/seafoam/graal/source.rb
CHANGED
@@ -1,18 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Seafoam
|
2
4
|
module Graal
|
3
5
|
# Routines for understanding source positions in Graal.
|
4
6
|
module Source
|
5
|
-
|
6
|
-
|
7
|
+
class << self
|
8
|
+
def walk(source_position, &block)
|
9
|
+
results = []
|
7
10
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
caller = source_position
|
12
|
+
while caller
|
13
|
+
method = caller[:method]
|
14
|
+
results.push(block.call(method))
|
15
|
+
caller = caller[:caller]
|
16
|
+
end
|
14
17
|
|
15
|
-
|
18
|
+
results
|
19
|
+
end
|
16
20
|
end
|
17
21
|
end
|
18
22
|
end
|
data/lib/seafoam/graph.rb
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "set"
|
4
|
+
|
1
5
|
module Seafoam
|
2
6
|
# A graph, with properties, nodes, and edges. We don't encapsulate the graph
|
3
7
|
# too much - be careful.
|
@@ -25,24 +29,24 @@ module Seafoam
|
|
25
29
|
def create_edge(from, to, props = nil)
|
26
30
|
props ||= {}
|
27
31
|
edge = Edge.new(from, to, props)
|
28
|
-
@edges.push
|
29
|
-
from.outputs.push
|
30
|
-
to.inputs.push
|
32
|
+
@edges.push(edge)
|
33
|
+
from.outputs.push(edge)
|
34
|
+
to.inputs.push(edge)
|
31
35
|
edge
|
32
36
|
end
|
33
37
|
|
34
38
|
# Add a new basic block with given id and node id list.
|
35
39
|
def create_block(id, node_ids)
|
36
|
-
nodes = node_ids.select { |n| @nodes.key?
|
40
|
+
nodes = node_ids.select { |n| @nodes.key?(n) }.map { |n| @nodes[n] }
|
37
41
|
block = Block.new(id, nodes)
|
38
|
-
@blocks.push
|
42
|
+
@blocks.push(block)
|
39
43
|
block
|
40
44
|
end
|
41
45
|
|
42
46
|
def remove_edge(edge)
|
43
|
-
edge.from.outputs.delete
|
44
|
-
edge.to.inputs.delete
|
45
|
-
edges.delete
|
47
|
+
edge.from.outputs.delete(edge)
|
48
|
+
edge.to.inputs.delete(edge)
|
49
|
+
edges.delete(edge)
|
46
50
|
end
|
47
51
|
end
|
48
52
|
|
@@ -58,6 +62,10 @@ module Seafoam
|
|
58
62
|
@props = props
|
59
63
|
end
|
60
64
|
|
65
|
+
def node_class
|
66
|
+
@props.dig(:node_class, :node_class)
|
67
|
+
end
|
68
|
+
|
61
69
|
# All edges - input and output.
|
62
70
|
def edges
|
63
71
|
inputs + outputs
|
@@ -79,7 +87,19 @@ module Seafoam
|
|
79
87
|
|
80
88
|
# Inspect.
|
81
89
|
def inspect
|
82
|
-
"<Node #{id}>"
|
90
|
+
"<Node #{id} #{node_class}>"
|
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
|
83
103
|
end
|
84
104
|
end
|
85
105
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Seafoam
|
2
4
|
# A writer from graphs to the Graphviz DOT format, including all the
|
3
5
|
# formatting.
|
@@ -11,25 +13,64 @@ module Seafoam
|
|
11
13
|
inline_attrs = {}
|
12
14
|
attrs = {}
|
13
15
|
attrs[:dpi] = 200 if hidpi
|
14
|
-
attrs[:bgcolor] =
|
15
|
-
|
16
|
+
attrs[:bgcolor] = "white"
|
17
|
+
start_graph(attrs)
|
18
|
+
write_nodes(inline_attrs, graph, draw_blocks)
|
19
|
+
write_edges(inline_attrs, graph)
|
20
|
+
end_graph
|
21
|
+
end
|
22
|
+
|
23
|
+
def start_graph(attrs)
|
24
|
+
@stream.puts "digraph G {"
|
16
25
|
@stream.puts " graph #{write_attrs(attrs)};"
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
@stream.puts
|
26
|
+
end
|
27
|
+
|
28
|
+
def end_graph
|
29
|
+
@stream.puts "}"
|
21
30
|
end
|
22
31
|
|
23
32
|
private
|
24
33
|
|
25
34
|
# Write node declarations.
|
26
|
-
def write_nodes(inline_attrs, graph)
|
27
|
-
|
28
|
-
|
35
|
+
def write_nodes(inline_attrs, graph, draw_blocks)
|
36
|
+
drawn_in_blocks = []
|
37
|
+
|
38
|
+
if draw_blocks
|
39
|
+
graph.blocks.each do |block|
|
40
|
+
next if block.nodes.all? { |n| n.props[:hidden] || n.props[:inlined] }
|
41
|
+
|
42
|
+
start_subgraph(block.id)
|
43
|
+
|
44
|
+
block.nodes.each do |node|
|
45
|
+
next if node.props[:hidden] || node.props[:inlined]
|
46
|
+
|
47
|
+
write_node(" ", inline_attrs, node)
|
48
|
+
drawn_in_blocks.push(node)
|
49
|
+
end
|
50
|
+
|
51
|
+
end_subgraph
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
(graph.nodes.values - drawn_in_blocks).each do |node|
|
56
|
+
write_node(" ", inline_attrs, node)
|
29
57
|
end
|
30
58
|
end
|
31
59
|
|
32
|
-
def
|
60
|
+
def start_subgraph(id)
|
61
|
+
@stream.puts " subgraph cluster_block#{id} {"
|
62
|
+
@stream.puts ' fontname = "Arial";'
|
63
|
+
@stream.puts " label = \"B#{id}\";"
|
64
|
+
@stream.puts " style = filled;"
|
65
|
+
@stream.puts " color = #{DARK_YELLOW.inspect};"
|
66
|
+
@stream.puts " fillcolor = #{LIGHT_YELLOW.inspect};"
|
67
|
+
end
|
68
|
+
|
69
|
+
def end_subgraph
|
70
|
+
@stream.puts " }"
|
71
|
+
end
|
72
|
+
|
73
|
+
def write_node(indent, inline_attrs, node)
|
33
74
|
# We're going to build up a hash of Graphviz drawing attributes.
|
34
75
|
attrs = {}
|
35
76
|
|
@@ -39,71 +80,75 @@ module Seafoam
|
|
39
80
|
# If the node has any adjacent nodes that are not hidden, and are
|
40
81
|
# shaded, then we need to declare the node but make it invisible so
|
41
82
|
# the edge appears, pointing off into space, but the node does not.
|
42
|
-
if node.adjacent.any? { |a| !a.props[:hidden] && a.props[:spotlight] ==
|
43
|
-
attrs[:style] =
|
44
|
-
attrs[:label] =
|
45
|
-
|
83
|
+
if node.adjacent.any? { |a| !a.props[:hidden] && a.props[:spotlight] == "shaded" }
|
84
|
+
attrs[:style] = "invis"
|
85
|
+
attrs[:label] = ""
|
86
|
+
output_node(indent, "node#{node.id}", attrs)
|
46
87
|
end
|
47
88
|
else
|
48
89
|
# This is a visible node.
|
49
90
|
|
50
91
|
# Give it a label.
|
51
|
-
if node.props[:label]
|
52
|
-
|
92
|
+
attrs[:label] = if node.props[:label]
|
93
|
+
"#{node.id} #{node.props[:label]}"
|
53
94
|
else
|
54
95
|
# If we really still don't have a label, just use the ID.
|
55
|
-
|
96
|
+
node.id.to_s
|
56
97
|
end
|
57
98
|
|
58
99
|
# Basic attributes for a node.
|
59
|
-
attrs[:shape] =
|
60
|
-
attrs[:fontname] =
|
61
|
-
attrs[:style] =
|
62
|
-
attrs[:color] =
|
100
|
+
attrs[:shape] = "rectangle"
|
101
|
+
attrs[:fontname] = "Arial"
|
102
|
+
attrs[:style] = "filled"
|
103
|
+
attrs[:color] = "black"
|
63
104
|
|
64
105
|
# Color depends on the kind of node.
|
65
106
|
back_color, fore_color = NODE_COLORS[node.props[:kind]]
|
66
107
|
attrs[:fillcolor] = back_color
|
67
108
|
attrs[:fontcolor] = fore_color
|
68
109
|
|
110
|
+
# If the node is shaded, convert the attributes to the shaded
|
111
|
+
# version.
|
112
|
+
attrs = shade(attrs) if node.props[:spotlight] == "shaded"
|
113
|
+
|
69
114
|
if node.props[:inlined]
|
70
115
|
# If the node is to be inlined then draw it smaller and a different
|
71
116
|
# shape.
|
72
|
-
attrs[:shape] =
|
73
|
-
attrs[:fontsize] =
|
117
|
+
attrs[:shape] = "oval"
|
118
|
+
attrs[:fontsize] = "8"
|
74
119
|
|
75
120
|
# Just record these attributes for where it's used by other nodes
|
76
121
|
# so it can be drawn above them - don't actually declare a node.
|
77
122
|
inline_attrs[node.id] = attrs
|
78
123
|
else
|
79
|
-
attrs[:shape] =
|
80
|
-
|
81
|
-
# If the node is shaded, convert the attributes to the shaded
|
82
|
-
# version.
|
83
|
-
attrs = shade(attrs) if node.props[:spotlight] == 'shaded'
|
124
|
+
attrs[:shape] = "diamond" if node.props[:kind] == "calc"
|
84
125
|
|
85
126
|
# Declare the node.
|
86
|
-
|
127
|
+
output_node(indent, "node#{node.id}", attrs)
|
87
128
|
end
|
88
129
|
end
|
89
130
|
end
|
90
131
|
|
132
|
+
def output_node(indent, id, attrs)
|
133
|
+
@stream.puts "#{indent}#{id} #{write_attrs(attrs)};"
|
134
|
+
end
|
135
|
+
|
91
136
|
# Write edge declarations.
|
92
137
|
|
93
138
|
def write_edges(inline_attrs, graph)
|
94
139
|
graph.edges.each do |edge|
|
95
140
|
# Skip the edge if it's from a node that is hidden and it doesn't point
|
96
141
|
# to a shaded node.
|
97
|
-
next if edge.from.props[:hidden] && edge.to.props[:spotlight] !=
|
142
|
+
next if edge.from.props[:hidden] && edge.to.props[:spotlight] != "shaded"
|
98
143
|
|
99
144
|
# Skip the edge if it's to a node that is hidden and it doesn't come
|
100
145
|
# from a shaded node.
|
101
|
-
next if edge.to.props[:hidden] && edge.from.props[:spotlight] !=
|
146
|
+
next if edge.to.props[:hidden] && edge.from.props[:spotlight] != "shaded"
|
102
147
|
|
103
148
|
# Skip the edge if it's hidden itself
|
104
149
|
next if edge.props[:hidden]
|
105
150
|
|
106
|
-
write_edge
|
151
|
+
write_edge(inline_attrs, edge)
|
107
152
|
end
|
108
153
|
end
|
109
154
|
|
@@ -118,74 +163,64 @@ module Seafoam
|
|
118
163
|
attrs[:label] = label
|
119
164
|
|
120
165
|
# Basic edge attributes.
|
121
|
-
attrs[:fontname] =
|
166
|
+
attrs[:fontname] = "arial"
|
122
167
|
color = EDGE_COLORS[edge.props[:kind]]
|
123
168
|
attrs[:color] = EDGE_COLORS[edge.props[:kind]]
|
124
169
|
attrs[:fontcolor] = color
|
125
170
|
|
126
171
|
# Properties depending on the kind of edge.
|
127
172
|
case edge.props[:kind]
|
128
|
-
when
|
173
|
+
when "control"
|
129
174
|
attrs[:penwidth] = 2
|
130
|
-
when
|
175
|
+
when "loop"
|
131
176
|
attrs[:penwidth] = 4
|
132
|
-
when
|
133
|
-
attrs[:style] =
|
177
|
+
when "info"
|
178
|
+
attrs[:style] = "dashed"
|
134
179
|
end
|
135
180
|
|
136
181
|
# Reversed edges.
|
137
|
-
attrs[:dir] =
|
182
|
+
attrs[:dir] = "back" if edge.props[:reverse]
|
138
183
|
|
139
184
|
# Convert attributes to shaded if any edges involved are shaded.
|
140
|
-
attrs = shade(attrs) if edge.nodes.any? { |n| n.props[:spotlight] ==
|
185
|
+
attrs = shade(attrs) if edge.nodes.any? { |n| n.props[:spotlight] == "shaded" }
|
141
186
|
|
142
187
|
# Does this edge come from an inlined node?
|
143
188
|
|
144
189
|
if edge.from.props[:inlined]
|
145
|
-
#
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
#
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
190
|
+
# Don't draw inlined nodes to hidden nodes.
|
191
|
+
unless edge.to.props[:hidden]
|
192
|
+
|
193
|
+
# An inlined edge is drawn as a new version of the from-node and an
|
194
|
+
# edge from that new version directly to the to-node. With only one
|
195
|
+
# user it's a short edge and the from-node is show directly above the
|
196
|
+
# to-node.
|
197
|
+
|
198
|
+
node_attrs = if edge.to.props[:spotlight] == "shaded"
|
199
|
+
# Draw inlined edges to shaded nodes as invisible.
|
200
|
+
{ label: "", style: "invis" }
|
201
|
+
else
|
202
|
+
# Get attributes from when we went through nodes earlier.
|
203
|
+
inline_attrs[edge.from.id]
|
204
|
+
end
|
205
|
+
|
206
|
+
# Inlined nodes skip the arrow for simplicity.
|
207
|
+
attrs[:arrowhead] = "none"
|
208
|
+
attrs[:fontsize] = "8"
|
209
|
+
|
210
|
+
# Declare a new node just for this user.
|
211
|
+
output_node(" ", "inline#{edge.from.id}x#{edge.to.id}", node_attrs)
|
212
|
+
|
213
|
+
# Declare the edge.
|
214
|
+
output_edge("inline#{edge.from.id}x#{edge.to.id}", "node#{edge.to.id}", attrs)
|
156
215
|
end
|
157
|
-
|
158
|
-
# Inlined nodes skip the arrow for simplicity.
|
159
|
-
attrs[:arrowhead] = 'none'
|
160
|
-
attrs[:fontsize] = '8'
|
161
|
-
|
162
|
-
# Declare a new node just for this user.
|
163
|
-
@stream.puts " inline#{edge.from.id}x#{edge.to.id} #{write_attrs(node_attrs)};"
|
164
|
-
|
165
|
-
# Declare the edge.
|
166
|
-
@stream.puts " inline#{edge.from.id}x#{edge.to.id} -> node#{edge.to.id} #{write_attrs(attrs)};"
|
167
216
|
else
|
168
217
|
# Declare the edge.
|
169
|
-
|
218
|
+
output_edge("node#{edge.from.id}", "node#{edge.to.id}", attrs)
|
170
219
|
end
|
171
220
|
end
|
172
221
|
|
173
|
-
|
174
|
-
|
175
|
-
graph.blocks.each do |block|
|
176
|
-
@stream.puts " subgraph cluster_block#{block.id} {"
|
177
|
-
@stream.puts ' fontname = "Arial";'
|
178
|
-
@stream.puts " label = \"B#{block.id}\";"
|
179
|
-
@stream.puts ' style=dotted;'
|
180
|
-
|
181
|
-
block.nodes.each do |node|
|
182
|
-
next if node.props[:hidden] || node.props[:inlined]
|
183
|
-
|
184
|
-
@stream.puts " node#{node.id};"
|
185
|
-
end
|
186
|
-
|
187
|
-
@stream.puts ' }'
|
188
|
-
end
|
222
|
+
def output_edge(from, to, attrs)
|
223
|
+
@stream.puts " #{from} -> #{to} #{write_attrs(attrs)};"
|
189
224
|
end
|
190
225
|
|
191
226
|
# Return attributes for a node or edge modified to 'shade' them in terms
|
@@ -200,36 +235,31 @@ module Seafoam
|
|
200
235
|
|
201
236
|
# Write a hash of key-value attributes into the DOT format.
|
202
237
|
def write_attrs(attrs)
|
203
|
-
|
204
|
-
end
|
205
|
-
|
206
|
-
# Quote and escape a string.
|
207
|
-
def quote(string)
|
208
|
-
string = string.to_s
|
209
|
-
string = string.gsub('\\', '\\\\')
|
210
|
-
string = string.gsub('"', '\\"')
|
211
|
-
"\"#{string}\""
|
238
|
+
"[" + attrs.reject { |_k, v| v.nil? }.map { |k, v| "#{k}=#{v.inspect}" }.join(",") + "]"
|
212
239
|
end
|
213
240
|
|
214
241
|
# Color theme.
|
215
242
|
|
216
243
|
EDGE_COLORS = {
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
244
|
+
"info" => BIG_STONE,
|
245
|
+
"control" => AMARANTH,
|
246
|
+
"loop" => AMARANTH,
|
247
|
+
"data" => KEPPEL,
|
248
|
+
"other" => BLACK,
|
222
249
|
}
|
223
250
|
|
224
251
|
NODE_COLORS = {
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
252
|
+
"info" => [DUST, BLACK],
|
253
|
+
"input" => [WHITE_ICE, BLACK],
|
254
|
+
"control" => [CARISSMA, BLACK],
|
255
|
+
"memory" => [LIGHT_PURPLE, BLACK],
|
256
|
+
"call" => [AMARANTH, WHITE],
|
257
|
+
"sync" => [AMARANTH, WHITE],
|
258
|
+
"alloc" => [AMARANTH, WHITE],
|
259
|
+
"virtual" => [BIG_STONE, WHITE],
|
260
|
+
"guard" => [ORANGE, BLACK],
|
261
|
+
"calc" => [KEPPEL, BLACK],
|
262
|
+
"other" => [DUST, BLACK],
|
233
263
|
}
|
234
264
|
end
|
235
265
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Seafoam
|
2
4
|
# Write graphs in the Isabelle file format.
|
3
5
|
class IsabelleWriter
|
@@ -28,19 +30,19 @@ module Seafoam
|
|
28
30
|
|
29
31
|
graph.nodes.each_value do |node|
|
30
32
|
node_class = node.props[:node_class][:node_class]
|
31
|
-
case node_class
|
32
|
-
when
|
33
|
-
|
34
|
-
when
|
35
|
-
|
33
|
+
desc = case node_class
|
34
|
+
when "org.graalvm.compiler.nodes.ConstantNode"
|
35
|
+
"(ConstantNode #{node.props["rawvalue"]})"
|
36
|
+
when "org.graalvm.compiler.nodes.ParameterNode"
|
37
|
+
"(ParameterNode #{node.props["index"]})"
|
36
38
|
else
|
37
|
-
|
39
|
+
node_class.split(".").last
|
38
40
|
end
|
39
41
|
inputs = node.inputs.map(&:from).map(&:id)
|
40
42
|
outputs = node.outputs.map(&:to).map(&:id)
|
41
43
|
@out.puts " (add_node #{node.id} #{desc} #{inputs.inspect} #{outputs.inspect}"
|
42
44
|
end
|
43
|
-
@out.puts
|
45
|
+
@out.puts " empty_graph" + (")" * graph.nodes.size)
|
44
46
|
end
|
45
47
|
end
|
46
48
|
end
|