furnace 0.3.1 → 0.4.0.beta.1
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.
- data/.gitignore +1 -0
- data/.travis.yml +4 -0
- data/Gemfile +1 -2
- data/{LICENSE → LICENSE.MIT} +14 -14
- data/Rakefile +1 -5
- data/furnace.gemspec +7 -3
- data/lib/furnace/ast/node.rb +14 -0
- data/lib/furnace/ast/processor.rb +7 -7
- data/lib/furnace/ssa/argument.rb +23 -0
- data/lib/furnace/ssa/basic_block.rb +117 -0
- data/lib/furnace/ssa/builder.rb +100 -0
- data/lib/furnace/ssa/constant.rb +43 -0
- data/lib/furnace/ssa/function.rb +191 -0
- data/lib/furnace/ssa/generic_instruction.rb +14 -0
- data/lib/furnace/ssa/generic_type.rb +16 -0
- data/lib/furnace/ssa/instruction.rb +92 -0
- data/lib/furnace/ssa/instruction_syntax.rb +109 -0
- data/lib/furnace/ssa/instructions/branch.rb +11 -0
- data/lib/furnace/ssa/instructions/phi.rb +63 -0
- data/lib/furnace/ssa/instructions/return.rb +11 -0
- data/lib/furnace/ssa/module.rb +52 -0
- data/lib/furnace/ssa/named_value.rb +25 -0
- data/lib/furnace/ssa/pretty_printer.rb +113 -0
- data/lib/furnace/ssa/terminator_instruction.rb +24 -0
- data/lib/furnace/ssa/type.rb +27 -0
- data/lib/furnace/ssa/types/basic_block.rb +11 -0
- data/lib/furnace/ssa/types/function.rb +11 -0
- data/lib/furnace/ssa/types/void.rb +15 -0
- data/lib/furnace/ssa/user.rb +84 -0
- data/lib/furnace/ssa/value.rb +62 -0
- data/lib/furnace/ssa.rb +66 -0
- data/lib/furnace/transform/iterative.rb +27 -0
- data/lib/furnace/transform/pipeline.rb +3 -3
- data/lib/furnace/version.rb +1 -1
- data/lib/furnace.rb +3 -3
- data/test/ast_test.rb +32 -3
- data/test/ssa_test.rb +1129 -0
- data/test/test_helper.rb +17 -28
- data/test/transform_test.rb +74 -0
- metadata +136 -58
- data/lib/furnace/cfg/algorithms.rb +0 -193
- data/lib/furnace/cfg/graph.rb +0 -99
- data/lib/furnace/cfg/node.rb +0 -78
- data/lib/furnace/cfg.rb +0 -7
- data/lib/furnace/transform/iterative_process.rb +0 -26
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/{LICENSE → LICENSE.MIT}
RENAMED
@@ -1,20 +1,20 @@
|
|
1
|
-
Copyright (c) 2011-
|
1
|
+
Copyright (c) 2011-2013 Peter Zotov <whitequark@whitequark.org>
|
2
2
|
|
3
|
-
Permission is hereby granted, free of charge, to any person obtaining a
|
4
|
-
copy of this software and associated documentation files (the
|
5
|
-
"Software"), to deal in the Software without restriction, including
|
6
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
-
permit persons to whom the Software is furnished to do so, subject to
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a
|
4
|
+
copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
9
|
the following conditions:
|
10
10
|
|
11
|
-
The above copyright notice and this permission notice shall be included
|
11
|
+
The above copyright notice and this permission notice shall be included
|
12
12
|
in all copies or substantial portions of the Software.
|
13
13
|
|
14
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
15
|
-
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
17
|
-
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
18
|
-
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
19
|
-
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
15
|
+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
17
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
18
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
19
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
20
20
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
CHANGED
@@ -5,11 +5,7 @@ task :default => :test
|
|
5
5
|
|
6
6
|
desc "Run test suite"
|
7
7
|
task :test do
|
8
|
-
|
9
|
-
Bacon.summary_at_exit
|
10
|
-
Dir["test/**/*_test.rb"].each do |file|
|
11
|
-
load file
|
12
|
-
end
|
8
|
+
sh "bacon test/*_test.rb"
|
13
9
|
end
|
14
10
|
|
15
11
|
PAGES_REPO = 'git@github.com:whitequark/furnace'
|
data/furnace.gemspec
CHANGED
@@ -17,8 +17,12 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
18
|
s.require_paths = ["lib"]
|
19
19
|
|
20
|
-
s.
|
21
|
-
|
20
|
+
s.add_dependency 'ansi'
|
21
|
+
|
22
|
+
s.add_development_dependency 'rake', '~> 10.0'
|
23
|
+
s.add_development_dependency 'bacon', '~> 1.2'
|
24
|
+
s.add_development_dependency 'bacon-colored_output'
|
25
|
+
s.add_development_dependency 'simplecov'
|
22
26
|
s.add_development_dependency 'yard'
|
23
|
-
s.add_development_dependency '
|
27
|
+
s.add_development_dependency 'kramdown'
|
24
28
|
end
|
data/lib/furnace/ast/node.rb
CHANGED
@@ -130,6 +130,20 @@ module Furnace::AST
|
|
130
130
|
"(#{fancy_type} ...)"
|
131
131
|
end
|
132
132
|
|
133
|
+
# Returns {#children}. This is very useful in order to decompose nodes
|
134
|
+
# concisely. For example:
|
135
|
+
#
|
136
|
+
# node = s(:gasgn, :$foo, s(:integer, 1))
|
137
|
+
# s
|
138
|
+
# var_name, value = *node
|
139
|
+
# p var_name # => :$foo
|
140
|
+
# p value # => (integer 1)
|
141
|
+
#
|
142
|
+
# @return [Array]
|
143
|
+
def to_a
|
144
|
+
children
|
145
|
+
end
|
146
|
+
|
133
147
|
# Converts `self` to a pretty-printed s-expression.
|
134
148
|
#
|
135
149
|
# @param [Integer] indent Base indentation level.
|
@@ -37,7 +37,7 @@ module Furnace::AST
|
|
37
37
|
# def process_binary_op(node)
|
38
38
|
# # Children aren't decomposed automatically; it is suggested to use Ruby
|
39
39
|
# # multiple assignment expansion, as it is very convenient here.
|
40
|
-
# left_expr, right_expr = node
|
40
|
+
# left_expr, right_expr = *node
|
41
41
|
#
|
42
42
|
# # AST::Node#updated won't change node type if nil is passed as a first
|
43
43
|
# # argument, which allows to reuse the same handler for multiple node types
|
@@ -54,11 +54,11 @@ module Furnace::AST
|
|
54
54
|
# def on_negate(node)
|
55
55
|
# # It is also possible to use #process_all for more compact code
|
56
56
|
# # if every child is a Node.
|
57
|
-
# node.updated(nil, process_all(node
|
57
|
+
# node.updated(nil, process_all(node))
|
58
58
|
# end
|
59
59
|
#
|
60
60
|
# def on_store(node)
|
61
|
-
# expr, variable_name = node
|
61
|
+
# expr, variable_name = *node
|
62
62
|
#
|
63
63
|
# # Note that variable_name is not a Node and thus isn't passed to #process.
|
64
64
|
# node.updated(nil, [
|
@@ -74,7 +74,7 @@ module Furnace::AST
|
|
74
74
|
# end
|
75
75
|
#
|
76
76
|
# def on_each(node)
|
77
|
-
# node.updated(nil, process_all(node
|
77
|
+
# node.updated(nil, process_all(node))
|
78
78
|
# end
|
79
79
|
# end
|
80
80
|
#
|
@@ -95,7 +95,7 @@ module Furnace::AST
|
|
95
95
|
# def compute_op(node)
|
96
96
|
# # First, node children are processed and then unpacked to local
|
97
97
|
# # variables.
|
98
|
-
# nodes = process_all(node
|
98
|
+
# nodes = process_all(node)
|
99
99
|
#
|
100
100
|
# if nodes.all? { |node| node.type == :integer }
|
101
101
|
# # If each of those nodes represents a literal, we can fold this
|
@@ -186,7 +186,7 @@ module Furnace::AST
|
|
186
186
|
# # (divide
|
187
187
|
# # (integer 1)
|
188
188
|
# # (divide (integer 1) (integer 0))
|
189
|
-
# left, right = process_all(node
|
189
|
+
# left, right = process_all(node)
|
190
190
|
#
|
191
191
|
# if right.type == :integer &&
|
192
192
|
# right.children.first == 0
|
@@ -250,7 +250,7 @@ module Furnace::AST
|
|
250
250
|
# @param [Array<AST::Node>] nodes
|
251
251
|
# @return [Array<AST::Node>]
|
252
252
|
def process_all(nodes)
|
253
|
-
nodes.map do |node|
|
253
|
+
nodes.to_a.map do |node|
|
254
254
|
process node
|
255
255
|
end
|
256
256
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Furnace
|
2
|
+
class SSA::Argument < SSA::NamedValue
|
3
|
+
attr_reader :type
|
4
|
+
|
5
|
+
def initialize(function, type, name)
|
6
|
+
super(function, name)
|
7
|
+
self.type = type
|
8
|
+
end
|
9
|
+
|
10
|
+
def type=(type)
|
11
|
+
@type = type.to_type if type
|
12
|
+
end
|
13
|
+
|
14
|
+
def has_side_effects?
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
18
|
+
def pretty_print(p=SSA::PrettyPrinter.new)
|
19
|
+
p.type type
|
20
|
+
p.name name
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module Furnace
|
2
|
+
class SSA::BasicBlock < SSA::NamedValue
|
3
|
+
def initialize(function, name=nil, insns=[])
|
4
|
+
super(function, name)
|
5
|
+
@instructions = insns.to_a
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize_copy(original)
|
9
|
+
super
|
10
|
+
|
11
|
+
@instructions = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_a
|
15
|
+
@instructions.dup
|
16
|
+
end
|
17
|
+
|
18
|
+
def include?(instruction)
|
19
|
+
@instructions.include? instruction
|
20
|
+
end
|
21
|
+
|
22
|
+
def each(type=nil, &proc)
|
23
|
+
if type.nil?
|
24
|
+
@instructions.each(&proc)
|
25
|
+
else
|
26
|
+
return to_enum(:each, type) if proc.nil?
|
27
|
+
|
28
|
+
@instructions.each do |insn|
|
29
|
+
if insn.instance_of? type
|
30
|
+
yield insn
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
alias each_instruction each
|
37
|
+
|
38
|
+
def append(instruction)
|
39
|
+
@instructions.push instruction
|
40
|
+
end
|
41
|
+
|
42
|
+
alias << append
|
43
|
+
|
44
|
+
def insert(before, instruction)
|
45
|
+
unless index = @instructions.index(before)
|
46
|
+
raise ArgumentError, "Instruction #{before} is not found"
|
47
|
+
end
|
48
|
+
|
49
|
+
@instructions.insert index, instruction
|
50
|
+
end
|
51
|
+
|
52
|
+
def replace(instruction, replace_with)
|
53
|
+
insert instruction, replace_with
|
54
|
+
remove instruction
|
55
|
+
end
|
56
|
+
|
57
|
+
def remove(instruction)
|
58
|
+
@instructions.delete instruction
|
59
|
+
end
|
60
|
+
|
61
|
+
def terminator
|
62
|
+
@instructions.last
|
63
|
+
end
|
64
|
+
|
65
|
+
def successor_names
|
66
|
+
terminator.successors
|
67
|
+
end
|
68
|
+
|
69
|
+
def successors
|
70
|
+
successor_names.map do |label|
|
71
|
+
@function.find(label)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def predecessor_names
|
76
|
+
predecessors.map(&:name)
|
77
|
+
end
|
78
|
+
|
79
|
+
def predecessors
|
80
|
+
@function.predecessors_for(@name)
|
81
|
+
end
|
82
|
+
|
83
|
+
def exits?
|
84
|
+
terminator.exits?
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.to_type
|
88
|
+
SSA::BasicBlockType.instance
|
89
|
+
end
|
90
|
+
|
91
|
+
def type
|
92
|
+
self.class.to_type
|
93
|
+
end
|
94
|
+
|
95
|
+
def constant?
|
96
|
+
true
|
97
|
+
end
|
98
|
+
|
99
|
+
def pretty_print(p=SSA::PrettyPrinter.new)
|
100
|
+
p.text @name, ":"
|
101
|
+
p.newline
|
102
|
+
|
103
|
+
each do |insn|
|
104
|
+
p << ' '
|
105
|
+
insn.pretty_print(p)
|
106
|
+
p.newline
|
107
|
+
end
|
108
|
+
|
109
|
+
p
|
110
|
+
end
|
111
|
+
|
112
|
+
def inspect_as_value(p=SSA::PrettyPrinter.new)
|
113
|
+
p.keyword 'label'
|
114
|
+
p.name name
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module Furnace
|
2
|
+
class SSA::Builder
|
3
|
+
attr_reader :function
|
4
|
+
attr_accessor :block
|
5
|
+
|
6
|
+
def self.scope
|
7
|
+
SSA
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(name, arguments=[], return_type=nil)
|
11
|
+
@function = SSA::Function.new(name, [], return_type)
|
12
|
+
@function.arguments = arguments.map do |(type, name)|
|
13
|
+
SSA::Argument.new(@function, type, name)
|
14
|
+
end
|
15
|
+
|
16
|
+
@block = @function.entry = add_block
|
17
|
+
end
|
18
|
+
|
19
|
+
def lookup_insn(opcode)
|
20
|
+
self.class.scope.const_get SSA.opcode_to_class_name(opcode)
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_block
|
24
|
+
block = SSA::BasicBlock.new(@function)
|
25
|
+
@function.add block
|
26
|
+
|
27
|
+
if block_given?
|
28
|
+
branch block
|
29
|
+
@block = block
|
30
|
+
|
31
|
+
yield
|
32
|
+
else
|
33
|
+
block
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def append(instruction, *args)
|
38
|
+
insn = lookup_insn(instruction).new(@block, *args)
|
39
|
+
@block.append insn
|
40
|
+
|
41
|
+
insn
|
42
|
+
end
|
43
|
+
|
44
|
+
def branch(target)
|
45
|
+
append(:branch, [ target ])
|
46
|
+
end
|
47
|
+
|
48
|
+
def phi(type, mapping)
|
49
|
+
append(:phi, type, Hash[mapping])
|
50
|
+
end
|
51
|
+
|
52
|
+
def fork(post_block)
|
53
|
+
old_block = @block
|
54
|
+
new_block = add_block
|
55
|
+
|
56
|
+
@block = new_block
|
57
|
+
|
58
|
+
value = yield old_block
|
59
|
+
|
60
|
+
branch post_block
|
61
|
+
|
62
|
+
[ new_block, value ]
|
63
|
+
ensure
|
64
|
+
@block = old_block
|
65
|
+
end
|
66
|
+
|
67
|
+
def control_flow_op(instruction, type=nil, uses)
|
68
|
+
cond_block = @block
|
69
|
+
post_block = add_block
|
70
|
+
|
71
|
+
mapping = yield cond_block, post_block
|
72
|
+
|
73
|
+
targets = mapping.map { |(target, _)| target }
|
74
|
+
append(instruction, uses + targets)
|
75
|
+
|
76
|
+
@block = post_block
|
77
|
+
phi(type, mapping.map do |(target, value)|
|
78
|
+
if target == post_block
|
79
|
+
[cond_block, value]
|
80
|
+
else
|
81
|
+
[target, value]
|
82
|
+
end
|
83
|
+
end)
|
84
|
+
end
|
85
|
+
|
86
|
+
def return(value)
|
87
|
+
append(:return, [ value ])
|
88
|
+
end
|
89
|
+
|
90
|
+
def method_missing(opcode, *args)
|
91
|
+
class_name = SSA.opcode_to_class_name(opcode)
|
92
|
+
|
93
|
+
if self.class.scope.const_defined? class_name
|
94
|
+
append opcode, *args
|
95
|
+
else
|
96
|
+
super
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Furnace
|
2
|
+
class SSA::Constant < SSA::Value
|
3
|
+
attr_reader :type
|
4
|
+
attr_accessor :value
|
5
|
+
|
6
|
+
def initialize(type, value)
|
7
|
+
super()
|
8
|
+
|
9
|
+
self.type = type
|
10
|
+
@value = value
|
11
|
+
end
|
12
|
+
|
13
|
+
def type=(type)
|
14
|
+
@type = type.to_type
|
15
|
+
end
|
16
|
+
|
17
|
+
def constant?
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
def ==(other)
|
22
|
+
if other.respond_to? :to_value
|
23
|
+
other_value = other.to_value
|
24
|
+
|
25
|
+
other_value.constant? &&
|
26
|
+
other_value.type == type &&
|
27
|
+
other_value.value == value
|
28
|
+
else
|
29
|
+
false
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def inspect_as_value(p=SSA::PrettyPrinter.new)
|
34
|
+
p.type type
|
35
|
+
p.text @value.inspect unless type == SSA.void
|
36
|
+
p
|
37
|
+
end
|
38
|
+
|
39
|
+
def inspect
|
40
|
+
pretty_print
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
module Furnace
|
2
|
+
class SSA::Function
|
3
|
+
attr_reader :original_name
|
4
|
+
attr_accessor :name
|
5
|
+
attr_reader :arguments
|
6
|
+
attr_accessor :return_type
|
7
|
+
|
8
|
+
attr_accessor :entry
|
9
|
+
|
10
|
+
def initialize(name=nil, arguments=[], return_type=SSA.void)
|
11
|
+
@original_name = name
|
12
|
+
@name = name
|
13
|
+
self.arguments = arguments
|
14
|
+
@return_type = return_type
|
15
|
+
|
16
|
+
@basic_blocks = Set.new
|
17
|
+
|
18
|
+
@name_prefixes = [""].to_set
|
19
|
+
@next_name = 0
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize_copy(original)
|
23
|
+
@name = @original_name
|
24
|
+
|
25
|
+
value_map = Hash.new do |value_map, value|
|
26
|
+
new_value = value.dup
|
27
|
+
value_map[value] = new_value
|
28
|
+
|
29
|
+
if new_value.is_a? SSA::User
|
30
|
+
new_value.function = self
|
31
|
+
new_value.operands = value.translate_operands(value_map)
|
32
|
+
end
|
33
|
+
|
34
|
+
new_value
|
35
|
+
end
|
36
|
+
|
37
|
+
@arguments = @arguments.map do |arg|
|
38
|
+
new_arg = arg.dup
|
39
|
+
new_arg.function = self
|
40
|
+
value_map[arg] = new_arg
|
41
|
+
|
42
|
+
new_arg
|
43
|
+
end
|
44
|
+
|
45
|
+
@basic_blocks = @basic_blocks.map do |bb|
|
46
|
+
new_bb = bb.dup
|
47
|
+
new_bb.function = self
|
48
|
+
|
49
|
+
value_map[bb] = new_bb
|
50
|
+
|
51
|
+
new_bb
|
52
|
+
end
|
53
|
+
|
54
|
+
@entry = value_map[@entry]
|
55
|
+
|
56
|
+
original.each do |bb|
|
57
|
+
new_bb = value_map[bb]
|
58
|
+
|
59
|
+
bb.each do |insn|
|
60
|
+
new_insn = value_map[insn]
|
61
|
+
new_insn.basic_block = new_bb
|
62
|
+
new_bb.append new_insn
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def arguments=(arguments)
|
68
|
+
@arguments = sanitize_arguments(arguments)
|
69
|
+
end
|
70
|
+
|
71
|
+
def make_name(prefix=nil)
|
72
|
+
if prefix.nil?
|
73
|
+
(@next_name += 1).to_s
|
74
|
+
else
|
75
|
+
prefix = prefix.to_s
|
76
|
+
|
77
|
+
if @name_prefixes.include? prefix
|
78
|
+
"#{prefix}.#{@next_name += 1}"
|
79
|
+
else
|
80
|
+
@name_prefixes.add prefix
|
81
|
+
prefix
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def each(&proc)
|
87
|
+
@basic_blocks.each(&proc)
|
88
|
+
end
|
89
|
+
|
90
|
+
alias each_basic_block each
|
91
|
+
|
92
|
+
def include?(name)
|
93
|
+
@basic_blocks.any? { |n| n.name == name }
|
94
|
+
end
|
95
|
+
|
96
|
+
def find(name)
|
97
|
+
if block = @basic_blocks.find { |n| n.name == name }
|
98
|
+
block
|
99
|
+
else
|
100
|
+
raise ArgumentError, "Cannot find basic block #{name}"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def add(block)
|
105
|
+
@basic_blocks.add block
|
106
|
+
end
|
107
|
+
|
108
|
+
alias << add
|
109
|
+
|
110
|
+
def remove(block)
|
111
|
+
@basic_blocks.delete block
|
112
|
+
end
|
113
|
+
|
114
|
+
def each_instruction(type=nil, &proc)
|
115
|
+
return to_enum(:each_instruction, type) if proc.nil?
|
116
|
+
|
117
|
+
each do |block|
|
118
|
+
block.each(type, &proc)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def predecessors_for(name)
|
123
|
+
predecessors = Set[]
|
124
|
+
|
125
|
+
each do |block|
|
126
|
+
if block.successor_names.include? name
|
127
|
+
predecessors << block
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
predecessors
|
132
|
+
end
|
133
|
+
|
134
|
+
def self.to_type
|
135
|
+
SSA::FunctionType.instance
|
136
|
+
end
|
137
|
+
|
138
|
+
def to_value
|
139
|
+
SSA::Constant.new(self.class.to_type, @name)
|
140
|
+
end
|
141
|
+
|
142
|
+
def pretty_print(p=SSA::PrettyPrinter.new)
|
143
|
+
p.keyword 'function'
|
144
|
+
p.type @return_type
|
145
|
+
p.text @name, '('
|
146
|
+
p.objects @arguments
|
147
|
+
p.text ') {'
|
148
|
+
p.newline
|
149
|
+
|
150
|
+
each do |basic_block|
|
151
|
+
basic_block.pretty_print(p)
|
152
|
+
p.newline
|
153
|
+
end
|
154
|
+
|
155
|
+
p.text "}"
|
156
|
+
p.newline
|
157
|
+
end
|
158
|
+
|
159
|
+
alias inspect pretty_print
|
160
|
+
|
161
|
+
def to_graphviz
|
162
|
+
Graphviz.new do |graph|
|
163
|
+
@basic_blocks.each do |block|
|
164
|
+
options = {}
|
165
|
+
|
166
|
+
if @entry == block
|
167
|
+
options.merge!({ color: 'green' })
|
168
|
+
elsif block.returns?
|
169
|
+
options.merge!({ color: 'red' })
|
170
|
+
end
|
171
|
+
|
172
|
+
graph.node block.name, block.inspect, options
|
173
|
+
|
174
|
+
block.successor_names.each do |name|
|
175
|
+
graph.edge block.name, name
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
protected
|
182
|
+
|
183
|
+
def sanitize_arguments(arguments)
|
184
|
+
arguments.each_with_index do |argument, index|
|
185
|
+
if !argument.is_a?(SSA::Argument)
|
186
|
+
raise ArgumentError, "function #{@name} arguments: #{argument.inspect} (at #{index}) is not an Argument"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Furnace
|
2
|
+
class SSA::GenericInstruction < SSA::Instruction
|
3
|
+
attr_reader :type
|
4
|
+
|
5
|
+
def initialize(basic_block, type=nil, uses=[], name=basic_block.function.make_name)
|
6
|
+
super(basic_block, uses, name)
|
7
|
+
self.type = type
|
8
|
+
end
|
9
|
+
|
10
|
+
def type=(type)
|
11
|
+
@type = type.to_type if type
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|