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.
- data/.gitignore +1 -0
- data/.travis.yml +4 -0
- data/Gemfile +1 -2
- data/{LICENSE → LICENSE.MIT} +14 -14
- data/Rakefile +1 -5
- data/furnace.gemspec +7 -3
- data/lib/furnace/ast/node.rb +14 -0
- data/lib/furnace/ast/processor.rb +7 -7
- data/lib/furnace/ssa/argument.rb +23 -0
- data/lib/furnace/ssa/basic_block.rb +117 -0
- data/lib/furnace/ssa/builder.rb +100 -0
- data/lib/furnace/ssa/constant.rb +43 -0
- data/lib/furnace/ssa/function.rb +191 -0
- data/lib/furnace/ssa/generic_instruction.rb +14 -0
- data/lib/furnace/ssa/generic_type.rb +16 -0
- data/lib/furnace/ssa/instruction.rb +92 -0
- data/lib/furnace/ssa/instruction_syntax.rb +109 -0
- data/lib/furnace/ssa/instructions/branch.rb +11 -0
- data/lib/furnace/ssa/instructions/phi.rb +63 -0
- data/lib/furnace/ssa/instructions/return.rb +11 -0
- data/lib/furnace/ssa/module.rb +52 -0
- data/lib/furnace/ssa/named_value.rb +25 -0
- data/lib/furnace/ssa/pretty_printer.rb +113 -0
- data/lib/furnace/ssa/terminator_instruction.rb +24 -0
- data/lib/furnace/ssa/type.rb +27 -0
- data/lib/furnace/ssa/types/basic_block.rb +11 -0
- data/lib/furnace/ssa/types/function.rb +11 -0
- data/lib/furnace/ssa/types/void.rb +15 -0
- data/lib/furnace/ssa/user.rb +84 -0
- data/lib/furnace/ssa/value.rb +62 -0
- data/lib/furnace/ssa.rb +66 -0
- data/lib/furnace/transform/iterative.rb +27 -0
- data/lib/furnace/transform/pipeline.rb +3 -3
- data/lib/furnace/version.rb +1 -1
- data/lib/furnace.rb +3 -3
- data/test/ast_test.rb +32 -3
- data/test/ssa_test.rb +1129 -0
- data/test/test_helper.rb +17 -28
- data/test/transform_test.rb +74 -0
- metadata +136 -58
- data/lib/furnace/cfg/algorithms.rb +0 -193
- data/lib/furnace/cfg/graph.rb +0 -99
- data/lib/furnace/cfg/node.rb +0 -78
- data/lib/furnace/cfg.rb +0 -7
- 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
|
data/lib/furnace/ssa.rb
ADDED
@@ -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(
|
8
|
+
def run(context)
|
9
9
|
@stages.each do |stage|
|
10
10
|
break if stage.nil?
|
11
11
|
|
12
|
-
|
12
|
+
stage.run context
|
13
13
|
end
|
14
14
|
|
15
|
-
|
15
|
+
true
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|
data/lib/furnace/version.rb
CHANGED
data/lib/furnace.rb
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
# data structure being used:
|
7
7
|
#
|
8
8
|
# * Abstract syntax trees: {AST}
|
9
|
-
# *
|
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/
|
17
|
+
require "furnace/ssa"
|
18
18
|
|
19
19
|
require "furnace/transform/pipeline"
|
20
|
-
require "furnace/transform/
|
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
|
-
|
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
|