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.
Files changed (45) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +4 -0
  3. data/Gemfile +1 -2
  4. data/{LICENSE → LICENSE.MIT} +14 -14
  5. data/Rakefile +1 -5
  6. data/furnace.gemspec +7 -3
  7. data/lib/furnace/ast/node.rb +14 -0
  8. data/lib/furnace/ast/processor.rb +7 -7
  9. data/lib/furnace/ssa/argument.rb +23 -0
  10. data/lib/furnace/ssa/basic_block.rb +117 -0
  11. data/lib/furnace/ssa/builder.rb +100 -0
  12. data/lib/furnace/ssa/constant.rb +43 -0
  13. data/lib/furnace/ssa/function.rb +191 -0
  14. data/lib/furnace/ssa/generic_instruction.rb +14 -0
  15. data/lib/furnace/ssa/generic_type.rb +16 -0
  16. data/lib/furnace/ssa/instruction.rb +92 -0
  17. data/lib/furnace/ssa/instruction_syntax.rb +109 -0
  18. data/lib/furnace/ssa/instructions/branch.rb +11 -0
  19. data/lib/furnace/ssa/instructions/phi.rb +63 -0
  20. data/lib/furnace/ssa/instructions/return.rb +11 -0
  21. data/lib/furnace/ssa/module.rb +52 -0
  22. data/lib/furnace/ssa/named_value.rb +25 -0
  23. data/lib/furnace/ssa/pretty_printer.rb +113 -0
  24. data/lib/furnace/ssa/terminator_instruction.rb +24 -0
  25. data/lib/furnace/ssa/type.rb +27 -0
  26. data/lib/furnace/ssa/types/basic_block.rb +11 -0
  27. data/lib/furnace/ssa/types/function.rb +11 -0
  28. data/lib/furnace/ssa/types/void.rb +15 -0
  29. data/lib/furnace/ssa/user.rb +84 -0
  30. data/lib/furnace/ssa/value.rb +62 -0
  31. data/lib/furnace/ssa.rb +66 -0
  32. data/lib/furnace/transform/iterative.rb +27 -0
  33. data/lib/furnace/transform/pipeline.rb +3 -3
  34. data/lib/furnace/version.rb +1 -1
  35. data/lib/furnace.rb +3 -3
  36. data/test/ast_test.rb +32 -3
  37. data/test/ssa_test.rb +1129 -0
  38. data/test/test_helper.rb +17 -28
  39. data/test/transform_test.rb +74 -0
  40. metadata +136 -58
  41. data/lib/furnace/cfg/algorithms.rb +0 -193
  42. data/lib/furnace/cfg/graph.rb +0 -99
  43. data/lib/furnace/cfg/node.rb +0 -78
  44. data/lib/furnace/cfg.rb +0 -7
  45. data/lib/furnace/transform/iterative_process.rb +0 -26
@@ -0,0 +1,62 @@
1
+ module Furnace
2
+ class SSA::Value
3
+ def initialize
4
+ @uses = Set.new
5
+ end
6
+
7
+ def initialize_copy(original)
8
+ @uses = Set.new
9
+ end
10
+
11
+ def type
12
+ SSA.void
13
+ end
14
+
15
+ def constant?
16
+ false
17
+ end
18
+
19
+ def add_use(use)
20
+ @uses.add use
21
+ end
22
+
23
+ def remove_use(use)
24
+ @uses.delete use
25
+ end
26
+
27
+ def each_use(&block)
28
+ @uses.each(&block)
29
+ end
30
+
31
+ def use_count
32
+ @uses.count
33
+ end
34
+
35
+ def used?
36
+ @uses.any?
37
+ end
38
+
39
+ def replace_all_uses_with(value)
40
+ each_use do |user|
41
+ user.replace_uses_of self, value
42
+ end
43
+ end
44
+
45
+ def to_value
46
+ self
47
+ end
48
+
49
+ def ==(other)
50
+ other.respond_to?(:to_value) &&
51
+ equal?(other.to_value)
52
+ end
53
+
54
+ def pretty_print(p=SSA::PrettyPrinter.new)
55
+ inspect_as_value(p)
56
+ end
57
+
58
+ def inspect_as_value(p=SSA::PrettyPrinter.new)
59
+ p.text inspect
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,66 @@
1
+ module Furnace
2
+ module SSA
3
+ def self.void
4
+ SSA::VoidType.instance
5
+ end
6
+
7
+ def self.void_value
8
+ SSA::VoidType.value
9
+ end
10
+
11
+ def self.class_name_to_opcode(klass)
12
+ klass.to_s.split('::').last.gsub(/([a-z]|^)([A-Z])/) do
13
+ if $1.empty?
14
+ $2.downcase
15
+ else
16
+ "#{$1}_#{$2.downcase}"
17
+ end
18
+ end.gsub(/_insn$/, '')
19
+ end
20
+
21
+ def self.opcode_to_class_name(opcode)
22
+ opcode.to_s.gsub(/(?:([a-z])_|^)([a-z])/) do
23
+ if $1.nil?
24
+ $2.upcase
25
+ else
26
+ "#{$1}#{$2.upcase}"
27
+ end
28
+ end + 'Insn'
29
+ end
30
+
31
+ def self.inspect_type(type)
32
+ type ? type.inspect : '<?>'
33
+ end
34
+ end
35
+
36
+ require_relative 'ssa/pretty_printer'
37
+
38
+ require_relative 'ssa/type'
39
+ require_relative 'ssa/generic_type'
40
+
41
+ require_relative 'ssa/types/void'
42
+ require_relative 'ssa/types/basic_block'
43
+ require_relative 'ssa/types/function'
44
+
45
+ require_relative 'ssa/value'
46
+ require_relative 'ssa/constant'
47
+ require_relative 'ssa/named_value'
48
+ require_relative 'ssa/argument'
49
+ require_relative 'ssa/user'
50
+
51
+ require_relative 'ssa/instruction'
52
+ require_relative 'ssa/instruction_syntax'
53
+ require_relative 'ssa/generic_instruction'
54
+ require_relative 'ssa/terminator_instruction'
55
+
56
+ require_relative 'ssa/basic_block'
57
+ require_relative 'ssa/function'
58
+
59
+ require_relative 'ssa/instructions/phi'
60
+ require_relative 'ssa/instructions/branch'
61
+ require_relative 'ssa/instructions/return'
62
+
63
+ require_relative 'ssa/module'
64
+
65
+ require_relative 'ssa/builder'
66
+ end
@@ -0,0 +1,27 @@
1
+ module Furnace
2
+ module Transform
3
+ class Iterative
4
+ def initialize(stages)
5
+ @stages = stages
6
+ end
7
+
8
+ def run(context)
9
+ self_changed = false
10
+
11
+ loop do
12
+ changed = false
13
+
14
+ @stages.each do |stage|
15
+ return self_changed if stage.nil?
16
+
17
+ if stage.run(context)
18
+ self_changed = changed = true
19
+ end
20
+ end
21
+
22
+ return self_changed unless changed
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -5,14 +5,14 @@ module Furnace
5
5
  @stages = stages
6
6
  end
7
7
 
8
- def run(*sequence)
8
+ def run(context)
9
9
  @stages.each do |stage|
10
10
  break if stage.nil?
11
11
 
12
- sequence = stage.transform *sequence
12
+ stage.run context
13
13
  end
14
14
 
15
- sequence
15
+ true
16
16
  end
17
17
  end
18
18
  end
@@ -1,3 +1,3 @@
1
1
  module Furnace
2
- VERSION = "0.3.1"
2
+ VERSION = "0.4.0.beta.1"
3
3
  end
data/lib/furnace.rb CHANGED
@@ -6,7 +6,7 @@
6
6
  # data structure being used:
7
7
  #
8
8
  # * Abstract syntax trees: {AST}
9
- # * Control flow graphs: {CFG}
9
+ # * Static single assignment representation: {SSA}
10
10
  # * Transformations: {Transform}
11
11
  #
12
12
  # Additionally, a simple {Graphviz} adapter is provided.
@@ -14,10 +14,10 @@ module Furnace
14
14
  require "furnace/version"
15
15
 
16
16
  require "furnace/ast"
17
- require "furnace/cfg"
17
+ require "furnace/ssa"
18
18
 
19
19
  require "furnace/transform/pipeline"
20
- require "furnace/transform/iterative_process"
20
+ require "furnace/transform/iterative"
21
21
 
22
22
  require "furnace/graphviz"
23
23
  end
data/test/ast_test.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  require_relative 'test_helper'
2
2
 
3
3
  describe AST::Node do
4
+ extend Furnace::AST::Sexp
5
+
4
6
  class MetaNode < AST::Node
5
7
  attr_reader :meta
6
8
  end
@@ -60,6 +62,17 @@ describe AST::Node do
60
62
  AST::Node.new(:a, [ :sym,
61
63
  AST::Node.new(:b, [ @node, @node ])
62
64
  ]).to_sexp.should.equal "(a :sym\n (b\n (node 0 1)\n (node 0 1)))"
65
+ AST::Node.new(:a, [
66
+ { :foo => :bar, :baz => 100, :val => AST::Node.new(:val, [ @node ]) }, @node
67
+ ]).to_sexp.should == <<-END.rstrip
68
+ (a {
69
+ :foo => :bar
70
+ :baz => 100
71
+ :val => (val
72
+ (node 0 1))
73
+ }
74
+ (node 0 1))
75
+ END
63
76
  end
64
77
 
65
78
  it 'should return self in to_ast' do
@@ -86,9 +99,19 @@ describe AST::Node do
86
99
  end
87
100
  @node.should.equal mock_node
88
101
  end
102
+
103
+ it 'should allow to decompose nodes with a, b = *node' do
104
+ node = s(:gasgn, :$foo, s(:integer, 1))
105
+
106
+ var_name, value = *node
107
+ var_name.should.equal :$foo
108
+ value.should.equal s(:integer, 1)
109
+ end
89
110
  end
90
111
 
91
112
  describe AST::Processor do
113
+ extend Furnace::AST::Sexp
114
+
92
115
  def have_sexp(text)
93
116
  text = text.lines.map { |line| line.sub /^ +\|(.+)/, '\1' }.join.rstrip
94
117
  lambda { |ast| ast.to_sexp == text }
@@ -162,8 +185,6 @@ describe AST::Processor do
162
185
  SEXP
163
186
  end
164
187
 
165
- extend Furnace::AST::Sexp
166
-
167
188
  it 'should build sexps' do
168
189
  s(:add,
169
190
  s(:integer, 1),
@@ -182,4 +203,12 @@ describe AST::Processor do
182
203
  -> { @processor.process(nil) }.should.raise NoMethodError, %r|to_ast|
183
204
  -> { @processor.process([]) }.should.raise NoMethodError, %r|to_ast|
184
205
  end
185
- end
206
+
207
+ it 'should allow to visit nodes with process_all(node)' do
208
+ @processor.process_all s(:foo, s(:bar), s(:integer, 1))
209
+ @processor.counts.should.equal({
210
+ bar: 1,
211
+ integer: 1,
212
+ })
213
+ end
214
+ end