furnace 0.3.1 → 0.4.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|