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