furnace 0.4.0.beta.1 → 0.4.0.beta.2

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.
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