furnace 0.4.0.beta.1 → 0.4.0.beta.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.travis.yml +6 -0
- data/INSTRUMENT.md +250 -0
- data/Rakefile +1 -1
- data/lib/furnace.rb +10 -6
- data/lib/furnace/ast/node.rb +15 -1
- data/lib/furnace/awesome_printer.rb +121 -0
- data/lib/furnace/context.rb +0 -0
- data/lib/furnace/ssa.rb +7 -25
- data/lib/furnace/ssa/argument.rb +14 -10
- data/lib/furnace/ssa/basic_block.rb +70 -25
- data/lib/furnace/ssa/builder.rb +11 -7
- data/lib/furnace/ssa/constant.rb +19 -11
- data/lib/furnace/ssa/event_stream.rb +145 -0
- data/lib/furnace/ssa/function.rb +78 -57
- data/lib/furnace/ssa/generic_instruction.rb +12 -5
- data/lib/furnace/ssa/instruction.rb +50 -35
- data/lib/furnace/ssa/instruction_syntax.rb +9 -25
- data/lib/furnace/ssa/instructions/branch.rb +2 -2
- data/lib/furnace/ssa/instructions/phi.rb +12 -10
- data/lib/furnace/ssa/instructions/return.rb +3 -3
- data/lib/furnace/ssa/instructions/return_value.rb +11 -0
- data/lib/furnace/ssa/instrumentation.rb +33 -0
- data/lib/furnace/ssa/module.rb +5 -1
- data/lib/furnace/ssa/named_value.rb +31 -10
- data/lib/furnace/ssa/terminator_instruction.rb +6 -6
- data/lib/furnace/ssa/types/basic_block.rb +4 -8
- data/lib/furnace/ssa/types/function.rb +4 -8
- data/lib/furnace/ssa/user.rb +14 -15
- data/lib/furnace/ssa/value.rb +9 -5
- data/lib/furnace/transform/iterative.rb +20 -4
- data/lib/furnace/type.rb +9 -0
- data/lib/furnace/type/bottom.rb +11 -0
- data/lib/furnace/type/top.rb +72 -0
- data/lib/furnace/type/value.rb +13 -0
- data/lib/furnace/type/variable.rb +61 -0
- data/lib/furnace/version.rb +1 -1
- data/test/{test_helper.rb → helper.rb} +28 -3
- data/test/{ast_test.rb → test_ast.rb} +13 -1
- data/test/test_awesome_printer.rb +148 -0
- data/test/{ssa_test.rb → test_ssa.rb} +276 -315
- data/test/{transform_test.rb → test_transform.rb} +2 -2
- data/test/test_type.rb +138 -0
- metadata +83 -105
- data/lib/furnace/graphviz.rb +0 -49
- data/lib/furnace/ssa/generic_type.rb +0 -16
- data/lib/furnace/ssa/pretty_printer.rb +0 -113
- data/lib/furnace/ssa/type.rb +0 -27
- data/lib/furnace/ssa/types/void.rb +0 -15
data/lib/furnace/ssa/function.rb
CHANGED
@@ -1,40 +1,42 @@
|
|
1
1
|
module Furnace
|
2
2
|
class SSA::Function
|
3
3
|
attr_reader :original_name
|
4
|
-
|
4
|
+
attr_reader :name
|
5
5
|
attr_reader :arguments
|
6
|
-
|
6
|
+
attr_reader :return_type
|
7
7
|
|
8
8
|
attr_accessor :entry
|
9
9
|
|
10
|
-
def initialize(name=nil, arguments=[], return_type=
|
11
|
-
@original_name
|
12
|
-
@name
|
13
|
-
|
14
|
-
@
|
10
|
+
def initialize(name=nil, arguments=[], return_type=Type::Bottom.new)
|
11
|
+
@original_name = name
|
12
|
+
@name = name
|
13
|
+
@return_type = return_type.to_type
|
14
|
+
@arguments = arguments
|
15
15
|
|
16
|
-
@basic_blocks
|
16
|
+
@basic_blocks = Set.new
|
17
17
|
|
18
|
-
@name_prefixes
|
19
|
-
@next_name
|
18
|
+
@name_prefixes = [""].to_set
|
19
|
+
@next_name = 0
|
20
|
+
|
21
|
+
SSA.instrument(self)
|
20
22
|
end
|
21
23
|
|
22
24
|
def initialize_copy(original)
|
23
|
-
@
|
25
|
+
@name_prefixes = [""].to_set
|
26
|
+
@name = @original_name
|
24
27
|
|
25
28
|
value_map = Hash.new do |value_map, value|
|
26
29
|
new_value = value.dup
|
27
30
|
value_map[value] = new_value
|
28
31
|
|
29
32
|
if new_value.is_a? SSA::User
|
30
|
-
new_value.function = self
|
31
33
|
new_value.operands = value.translate_operands(value_map)
|
32
34
|
end
|
33
35
|
|
34
36
|
new_value
|
35
37
|
end
|
36
38
|
|
37
|
-
|
39
|
+
self.arguments = @arguments.map do |arg|
|
38
40
|
new_arg = arg.dup
|
39
41
|
new_arg.function = self
|
40
42
|
value_map[arg] = new_arg
|
@@ -42,13 +44,16 @@ module Furnace
|
|
42
44
|
new_arg
|
43
45
|
end
|
44
46
|
|
45
|
-
|
47
|
+
old_basic_blocks = @basic_blocks
|
48
|
+
@basic_blocks = Set[]
|
49
|
+
|
50
|
+
old_basic_blocks.each do |bb|
|
46
51
|
new_bb = bb.dup
|
47
52
|
new_bb.function = self
|
48
53
|
|
49
54
|
value_map[bb] = new_bb
|
50
55
|
|
51
|
-
new_bb
|
56
|
+
add new_bb
|
52
57
|
end
|
53
58
|
|
54
59
|
@entry = value_map[@entry]
|
@@ -64,8 +69,26 @@ module Furnace
|
|
64
69
|
end
|
65
70
|
end
|
66
71
|
|
72
|
+
def name=(name)
|
73
|
+
@name = name
|
74
|
+
|
75
|
+
SSA.instrument(self)
|
76
|
+
end
|
77
|
+
|
67
78
|
def arguments=(arguments)
|
68
79
|
@arguments = sanitize_arguments(arguments)
|
80
|
+
|
81
|
+
@arguments.each do |arg|
|
82
|
+
arg.function = self
|
83
|
+
end
|
84
|
+
|
85
|
+
SSA.instrument(self)
|
86
|
+
end
|
87
|
+
|
88
|
+
def return_type=(return_type)
|
89
|
+
@return_type = return_type.to_type
|
90
|
+
|
91
|
+
SSA.instrument(self)
|
69
92
|
end
|
70
93
|
|
71
94
|
def make_name(prefix=nil)
|
@@ -89,6 +112,10 @@ module Furnace
|
|
89
112
|
|
90
113
|
alias each_basic_block each
|
91
114
|
|
115
|
+
def size
|
116
|
+
@basic_blocks.count
|
117
|
+
end
|
118
|
+
|
92
119
|
def include?(name)
|
93
120
|
@basic_blocks.any? { |n| n.name == name }
|
94
121
|
end
|
@@ -102,23 +129,43 @@ module Furnace
|
|
102
129
|
end
|
103
130
|
|
104
131
|
def add(block)
|
132
|
+
block.function = self
|
105
133
|
@basic_blocks.add block
|
134
|
+
|
135
|
+
SSA.instrument(self)
|
106
136
|
end
|
107
137
|
|
108
138
|
alias << add
|
109
139
|
|
110
140
|
def remove(block)
|
111
141
|
@basic_blocks.delete block
|
142
|
+
block.detach
|
143
|
+
|
144
|
+
SSA.instrument(self)
|
112
145
|
end
|
113
146
|
|
114
|
-
def each_instruction(
|
115
|
-
return to_enum(:each_instruction,
|
147
|
+
def each_instruction(*types, &proc)
|
148
|
+
return to_enum(:each_instruction, *types) if proc.nil?
|
116
149
|
|
117
150
|
each do |block|
|
118
|
-
block.each(
|
151
|
+
block.each(*types, &proc)
|
119
152
|
end
|
120
153
|
end
|
121
154
|
|
155
|
+
def replace_type_with(type, replacement)
|
156
|
+
@arguments.each do |arg|
|
157
|
+
arg.replace_type_with(type, replacement)
|
158
|
+
end
|
159
|
+
|
160
|
+
each_instruction do |insn|
|
161
|
+
insn.replace_type_with(type, replacement)
|
162
|
+
end
|
163
|
+
|
164
|
+
self.return_type = return_type.replace_type_with(type, replacement)
|
165
|
+
|
166
|
+
self
|
167
|
+
end
|
168
|
+
|
122
169
|
def predecessors_for(name)
|
123
170
|
predecessors = Set[]
|
124
171
|
|
@@ -132,51 +179,25 @@ module Furnace
|
|
132
179
|
end
|
133
180
|
|
134
181
|
def self.to_type
|
135
|
-
SSA::FunctionType.
|
182
|
+
SSA::FunctionType.new
|
136
183
|
end
|
137
184
|
|
138
185
|
def to_value
|
139
186
|
SSA::Constant.new(self.class.to_type, @name)
|
140
187
|
end
|
141
188
|
|
142
|
-
def
|
143
|
-
p.keyword
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
basic_block.pretty_print(p)
|
152
|
-
p.newline
|
153
|
-
end
|
154
|
-
|
155
|
-
p.text "}"
|
156
|
-
p.newline
|
189
|
+
def awesome_print(p=AwesomePrinter.new)
|
190
|
+
p.keyword('function').
|
191
|
+
nest(@return_type).
|
192
|
+
text(@name).
|
193
|
+
collection('(', ', ', ') {', @arguments).
|
194
|
+
newline.
|
195
|
+
collection(@basic_blocks).
|
196
|
+
append('}').
|
197
|
+
newline
|
157
198
|
end
|
158
199
|
|
159
|
-
alias inspect
|
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
|
200
|
+
alias inspect awesome_print
|
180
201
|
|
181
202
|
protected
|
182
203
|
|
@@ -185,7 +206,7 @@ module Furnace
|
|
185
206
|
if !argument.is_a?(SSA::Argument)
|
186
207
|
raise ArgumentError, "function #{@name} arguments: #{argument.inspect} (at #{index}) is not an Argument"
|
187
208
|
end
|
188
|
-
end
|
209
|
+
end.dup.freeze
|
189
210
|
end
|
190
211
|
end
|
191
|
-
end
|
212
|
+
end
|
@@ -2,13 +2,20 @@ module Furnace
|
|
2
2
|
class SSA::GenericInstruction < SSA::Instruction
|
3
3
|
attr_reader :type
|
4
4
|
|
5
|
-
def initialize(
|
6
|
-
|
7
|
-
|
5
|
+
def initialize(type, operands=[], name=nil)
|
6
|
+
@type = type.to_type
|
7
|
+
|
8
|
+
super(operands, name)
|
8
9
|
end
|
9
10
|
|
10
11
|
def type=(type)
|
11
|
-
@type
|
12
|
+
@type = type.to_type
|
13
|
+
|
14
|
+
SSA.instrument(self)
|
15
|
+
end
|
16
|
+
|
17
|
+
def replace_type_with(type, replacement)
|
18
|
+
self.type = self.type.replace_type_with(type, replacement)
|
12
19
|
end
|
13
20
|
end
|
14
|
-
end
|
21
|
+
end
|
@@ -8,26 +8,39 @@ module Furnace
|
|
8
8
|
SSA::InstructionSyntax.new(self).evaluate(&block)
|
9
9
|
end
|
10
10
|
|
11
|
-
|
11
|
+
attr_reader :basic_block
|
12
12
|
|
13
|
-
def
|
14
|
-
|
15
|
-
@basic_block = basic_block
|
13
|
+
def opcode
|
14
|
+
self.class.opcode
|
16
15
|
end
|
17
16
|
|
18
|
-
def
|
19
|
-
|
17
|
+
def basic_block=(basic_block)
|
18
|
+
if @basic_block && @basic_block != basic_block
|
19
|
+
@basic_block.remove self
|
20
|
+
end
|
20
21
|
|
21
|
-
|
22
|
+
if basic_block
|
23
|
+
self.function = basic_block.function
|
24
|
+
end
|
25
|
+
|
26
|
+
@basic_block = basic_block
|
27
|
+
|
28
|
+
SSA.instrument(self)
|
22
29
|
end
|
23
30
|
|
24
|
-
def
|
25
|
-
|
31
|
+
def detach
|
32
|
+
@basic_block = nil
|
33
|
+
|
34
|
+
super
|
26
35
|
end
|
27
36
|
|
28
37
|
def remove
|
29
|
-
@basic_block.remove self
|
30
|
-
|
38
|
+
@basic_block.remove self if @basic_block
|
39
|
+
end
|
40
|
+
|
41
|
+
def erase
|
42
|
+
remove
|
43
|
+
drop_references
|
31
44
|
end
|
32
45
|
|
33
46
|
def replace_with(value)
|
@@ -35,12 +48,15 @@ module Furnace
|
|
35
48
|
|
36
49
|
if value.is_a? SSA::Instruction
|
37
50
|
@basic_block.replace self, value
|
38
|
-
|
51
|
+
drop_references
|
39
52
|
else
|
40
|
-
|
53
|
+
erase
|
41
54
|
end
|
42
55
|
end
|
43
56
|
|
57
|
+
def replace_type_with(type, replacement)
|
58
|
+
end
|
59
|
+
|
44
60
|
def has_side_effects?
|
45
61
|
false
|
46
62
|
end
|
@@ -49,44 +65,43 @@ module Furnace
|
|
49
65
|
false
|
50
66
|
end
|
51
67
|
|
52
|
-
def
|
53
|
-
unless type ==
|
54
|
-
p.type
|
55
|
-
|
56
|
-
|
68
|
+
def awesome_print(p=AwesomePrinter.new)
|
69
|
+
unless type == Type::Bottom.new
|
70
|
+
p.nest(type).
|
71
|
+
name(name).
|
72
|
+
text('=')
|
57
73
|
end
|
58
74
|
|
59
|
-
|
60
|
-
p.keyword opcode
|
61
|
-
else
|
62
|
-
p.keyword_invalid opcode
|
63
|
-
end
|
75
|
+
p.keyword(opcode)
|
64
76
|
|
65
|
-
|
66
|
-
|
77
|
+
awesome_print_parameters(p)
|
78
|
+
awesome_print_operands(p)
|
67
79
|
|
68
80
|
p
|
69
81
|
end
|
70
82
|
|
71
|
-
def
|
72
|
-
if type ==
|
73
|
-
p.type
|
83
|
+
def awesome_print_as_value(p=AwesomePrinter.new)
|
84
|
+
if type == Type::Bottom.new
|
85
|
+
p.nest(type)
|
74
86
|
else
|
75
87
|
super
|
76
88
|
end
|
77
89
|
end
|
78
90
|
|
79
|
-
|
80
|
-
|
81
|
-
def pretty_parameters(p)
|
91
|
+
def awesome_print_parameters(p=AwesomePrinter.new)
|
92
|
+
p
|
82
93
|
end
|
83
94
|
|
84
|
-
def
|
95
|
+
def awesome_print_operands(p=AwesomePrinter.new)
|
85
96
|
if @operands
|
86
|
-
p.
|
97
|
+
p.collection('', ', ', '', @operands) do |operand|
|
98
|
+
operand.awesome_print_as_value(p)
|
99
|
+
end
|
87
100
|
else
|
88
|
-
p.text
|
101
|
+
p.text('<DETACHED>')
|
89
102
|
end
|
90
103
|
end
|
104
|
+
|
105
|
+
protected :function=
|
91
106
|
end
|
92
|
-
end
|
107
|
+
end
|
@@ -2,7 +2,7 @@ module Furnace
|
|
2
2
|
class SSA::InstructionSyntax
|
3
3
|
def initialize(klass)
|
4
4
|
@klass = klass
|
5
|
-
@operands =
|
5
|
+
@operands = []
|
6
6
|
@splat = nil
|
7
7
|
end
|
8
8
|
|
@@ -12,11 +12,10 @@ module Furnace
|
|
12
12
|
codegen
|
13
13
|
end
|
14
14
|
|
15
|
-
def operand(name
|
15
|
+
def operand(name)
|
16
16
|
check_for_splat
|
17
17
|
|
18
|
-
|
19
|
-
@operands[name.to_sym] = type
|
18
|
+
@operands << name.to_sym
|
20
19
|
end
|
21
20
|
|
22
21
|
def splat(name)
|
@@ -37,7 +36,7 @@ module Furnace
|
|
37
36
|
operands, splat = @operands, @splat
|
38
37
|
|
39
38
|
@klass.class_eval do
|
40
|
-
operands.each_with_index do |
|
39
|
+
operands.each_with_index do |operand, index|
|
41
40
|
define_method(operand) do
|
42
41
|
@operands[index]
|
43
42
|
end
|
@@ -45,10 +44,14 @@ module Furnace
|
|
45
44
|
define_method(:"#{operand}=") do |value|
|
46
45
|
value = value.to_value
|
47
46
|
|
47
|
+
return if @operands[index] == value
|
48
|
+
|
48
49
|
@operands[index].remove_use self if @operands[index]
|
49
50
|
@operands[index] = value
|
50
51
|
value.add_use self if value
|
51
52
|
|
53
|
+
SSA.instrument(self)
|
54
|
+
|
52
55
|
value
|
53
56
|
end
|
54
57
|
end
|
@@ -82,28 +85,9 @@ module Furnace
|
|
82
85
|
@operands = values
|
83
86
|
end
|
84
87
|
|
85
|
-
verify!
|
86
|
-
|
87
88
|
values
|
88
89
|
end
|
89
|
-
|
90
|
-
define_method(:verify!) do |ignore_nil_types=true|
|
91
|
-
return if @operands.nil?
|
92
|
-
|
93
|
-
operands.each_with_index do |(operand, type), index|
|
94
|
-
next if type.nil?
|
95
|
-
|
96
|
-
value = send operand
|
97
|
-
next if ignore_nil_types && value.type.nil?
|
98
|
-
|
99
|
-
if value.type.nil? || !value.type.subtype_of?(type)
|
100
|
-
raise TypeError, "Wrong type for operand ##{index + 1} `#{operand}': #{SSA.inspect_type type} is expected, #{SSA.inspect_type value.type} is present"
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
nil
|
105
|
-
end
|
106
90
|
end
|
107
91
|
end
|
108
92
|
end
|
109
|
-
end
|
93
|
+
end
|