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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.travis.yml +6 -0
  3. data/INSTRUMENT.md +250 -0
  4. data/Rakefile +1 -1
  5. data/lib/furnace.rb +10 -6
  6. data/lib/furnace/ast/node.rb +15 -1
  7. data/lib/furnace/awesome_printer.rb +121 -0
  8. data/lib/furnace/context.rb +0 -0
  9. data/lib/furnace/ssa.rb +7 -25
  10. data/lib/furnace/ssa/argument.rb +14 -10
  11. data/lib/furnace/ssa/basic_block.rb +70 -25
  12. data/lib/furnace/ssa/builder.rb +11 -7
  13. data/lib/furnace/ssa/constant.rb +19 -11
  14. data/lib/furnace/ssa/event_stream.rb +145 -0
  15. data/lib/furnace/ssa/function.rb +78 -57
  16. data/lib/furnace/ssa/generic_instruction.rb +12 -5
  17. data/lib/furnace/ssa/instruction.rb +50 -35
  18. data/lib/furnace/ssa/instruction_syntax.rb +9 -25
  19. data/lib/furnace/ssa/instructions/branch.rb +2 -2
  20. data/lib/furnace/ssa/instructions/phi.rb +12 -10
  21. data/lib/furnace/ssa/instructions/return.rb +3 -3
  22. data/lib/furnace/ssa/instructions/return_value.rb +11 -0
  23. data/lib/furnace/ssa/instrumentation.rb +33 -0
  24. data/lib/furnace/ssa/module.rb +5 -1
  25. data/lib/furnace/ssa/named_value.rb +31 -10
  26. data/lib/furnace/ssa/terminator_instruction.rb +6 -6
  27. data/lib/furnace/ssa/types/basic_block.rb +4 -8
  28. data/lib/furnace/ssa/types/function.rb +4 -8
  29. data/lib/furnace/ssa/user.rb +14 -15
  30. data/lib/furnace/ssa/value.rb +9 -5
  31. data/lib/furnace/transform/iterative.rb +20 -4
  32. data/lib/furnace/type.rb +9 -0
  33. data/lib/furnace/type/bottom.rb +11 -0
  34. data/lib/furnace/type/top.rb +72 -0
  35. data/lib/furnace/type/value.rb +13 -0
  36. data/lib/furnace/type/variable.rb +61 -0
  37. data/lib/furnace/version.rb +1 -1
  38. data/test/{test_helper.rb → helper.rb} +28 -3
  39. data/test/{ast_test.rb → test_ast.rb} +13 -1
  40. data/test/test_awesome_printer.rb +148 -0
  41. data/test/{ssa_test.rb → test_ssa.rb} +276 -315
  42. data/test/{transform_test.rb → test_transform.rb} +2 -2
  43. data/test/test_type.rb +138 -0
  44. metadata +83 -105
  45. data/lib/furnace/graphviz.rb +0 -49
  46. data/lib/furnace/ssa/generic_type.rb +0 -16
  47. data/lib/furnace/ssa/pretty_printer.rb +0 -113
  48. data/lib/furnace/ssa/type.rb +0 -27
  49. data/lib/furnace/ssa/types/void.rb +0 -15
@@ -1,40 +1,42 @@
1
1
  module Furnace
2
2
  class SSA::Function
3
3
  attr_reader :original_name
4
- attr_accessor :name
4
+ attr_reader :name
5
5
  attr_reader :arguments
6
- attr_accessor :return_type
6
+ attr_reader :return_type
7
7
 
8
8
  attr_accessor :entry
9
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
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 = Set.new
16
+ @basic_blocks = Set.new
17
17
 
18
- @name_prefixes = [""].to_set
19
- @next_name = 0
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
- @name = @original_name
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
- @arguments = @arguments.map do |arg|
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
- @basic_blocks = @basic_blocks.map do |bb|
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(type=nil, &proc)
115
- return to_enum(:each_instruction, type) if proc.nil?
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(type, &proc)
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.instance
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 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
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 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
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(basic_block, type=nil, uses=[], name=basic_block.function.make_name)
6
- super(basic_block, uses, name)
7
- self.type = type
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 = type.to_type if 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
- attr_accessor :basic_block
11
+ attr_reader :basic_block
12
12
 
13
- def initialize(basic_block, operands=[], name=nil)
14
- super(basic_block.function, operands, name)
15
- @basic_block = basic_block
13
+ def opcode
14
+ self.class.opcode
16
15
  end
17
16
 
18
- def initialize_copy(original)
19
- super
17
+ def basic_block=(basic_block)
18
+ if @basic_block && @basic_block != basic_block
19
+ @basic_block.remove self
20
+ end
20
21
 
21
- @operands = nil
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 opcode
25
- self.class.opcode
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
- detach
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
- detach
51
+ drop_references
39
52
  else
40
- remove
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 pretty_print(p=SSA::PrettyPrinter.new)
53
- unless type == SSA.void
54
- p.type type
55
- p.name name
56
- p.text '='
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
- if valid?
60
- p.keyword opcode
61
- else
62
- p.keyword_invalid opcode
63
- end
75
+ p.keyword(opcode)
64
76
 
65
- pretty_parameters(p)
66
- pretty_operands(p)
77
+ awesome_print_parameters(p)
78
+ awesome_print_operands(p)
67
79
 
68
80
  p
69
81
  end
70
82
 
71
- def inspect_as_value(p=SSA::PrettyPrinter.new)
72
- if type == SSA.void
73
- p.type 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
- protected
80
-
81
- def pretty_parameters(p)
91
+ def awesome_print_parameters(p=AwesomePrinter.new)
92
+ p
82
93
  end
83
94
 
84
- def pretty_operands(p)
95
+ def awesome_print_operands(p=AwesomePrinter.new)
85
96
  if @operands
86
- p.values @operands
97
+ p.collection('', ', ', '', @operands) do |operand|
98
+ operand.awesome_print_as_value(p)
99
+ end
87
100
  else
88
- p.text '<DETACHED>'
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, type=nil)
15
+ def operand(name)
16
16
  check_for_splat
17
17
 
18
- type = type.to_type unless type.nil?
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 |(operand, type), index|
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