furnace 0.0.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 +5 -0
- data/.rvmrc +61 -0
- data/Gemfile +4 -0
- data/Rakefile +1 -0
- data/bin/furnace +22 -0
- data/furnace.gemspec +19 -0
- data/lib/furnace/anf/edge.rb +6 -0
- data/lib/furnace/anf/graph.rb +60 -0
- data/lib/furnace/anf/if_node.rb +17 -0
- data/lib/furnace/anf/in_node.rb +17 -0
- data/lib/furnace/anf/let_node.rb +37 -0
- data/lib/furnace/anf/node.rb +31 -0
- data/lib/furnace/anf/return_node.rb +17 -0
- data/lib/furnace/ast/node.rb +70 -0
- data/lib/furnace/ast/symbolic_node.rb +36 -0
- data/lib/furnace/ast/visitor.rb +42 -0
- data/lib/furnace/cfg/edge.rb +36 -0
- data/lib/furnace/cfg/graph.rb +59 -0
- data/lib/furnace/cfg/node.rb +39 -0
- data/lib/furnace/graphviz.rb +30 -0
- data/lib/furnace/transform/generic/anf_build.rb +153 -0
- data/lib/furnace/transform/generic/cfg_build.rb +89 -0
- data/lib/furnace/transform/generic/cfg_normalize.rb +41 -0
- data/lib/furnace/transform/generic/label_normalize.rb +60 -0
- data/lib/furnace/transform/optimizing/fold_constants.rb +19 -0
- data/lib/furnace/transform/pipeline.rb +17 -0
- data/lib/furnace/transform/rubinius/ast_build.rb +53 -0
- data/lib/furnace/transform/rubinius/ast_normalize.rb +170 -0
- data/lib/furnace/version.rb +3 -0
- data/lib/furnace.rb +37 -0
- metadata +95 -0
@@ -0,0 +1,153 @@
|
|
1
|
+
module Furnace
|
2
|
+
module Transform
|
3
|
+
module Generic
|
4
|
+
class ANFBuild
|
5
|
+
include AST::Visitor
|
6
|
+
|
7
|
+
def transform(cfg, method)
|
8
|
+
@method_locals = method.local_names
|
9
|
+
|
10
|
+
@last_label = -1
|
11
|
+
@anf_nodes = Hash.new { |k,v| v }
|
12
|
+
@anf_edges = []
|
13
|
+
|
14
|
+
@anf = ANF::Graph.new
|
15
|
+
|
16
|
+
cfg.nodes.each do |node|
|
17
|
+
@locals = {}
|
18
|
+
@node = node
|
19
|
+
|
20
|
+
@last_anf_node = nil
|
21
|
+
|
22
|
+
# At this point we can have no more than two edges.
|
23
|
+
@default_edge = node.default_leaving_edge
|
24
|
+
@other_edge = node.leaving_edge(node.operations.last.metadata[:label])
|
25
|
+
|
26
|
+
# Transform the AST for each node to ANF, removing redundant root nodes
|
27
|
+
# in the process.
|
28
|
+
node.operations.delete_if do |operation|
|
29
|
+
visit operation
|
30
|
+
|
31
|
+
operation.type == :remove
|
32
|
+
end
|
33
|
+
|
34
|
+
# If there were no nodes created, fallback to default CFG edge.
|
35
|
+
@last_anf_node ||= @default_edge.target_label
|
36
|
+
|
37
|
+
# If some operations were done just for side effect, add an InNode.
|
38
|
+
if node.operations.any?
|
39
|
+
anf_in = ANF::InNode.new(@anf, node.operations)
|
40
|
+
@anf.nodes.add anf_in
|
41
|
+
|
42
|
+
@anf_edges << [ anf_in, @last_anf_node ]
|
43
|
+
@last_anf_node = anf_in
|
44
|
+
end
|
45
|
+
|
46
|
+
# If any locals were rebound, add a LetNode.
|
47
|
+
if @locals.any? || node.operations.any?
|
48
|
+
anf_let = ANF::LetNode.new(@anf, passed_locals)
|
49
|
+
@anf.nodes.add anf_let
|
50
|
+
|
51
|
+
@anf_edges << [ anf_let, @last_anf_node ]
|
52
|
+
@last_anf_node = anf_let
|
53
|
+
end
|
54
|
+
|
55
|
+
@anf_nodes[node.label] = @last_anf_node
|
56
|
+
end
|
57
|
+
|
58
|
+
# The root is a CFG node with label (ip) 0.
|
59
|
+
@anf.root = @anf_nodes[0]
|
60
|
+
|
61
|
+
@anf_edges.each do |(source_label, target_label, param)|
|
62
|
+
@anf.edges.add ANF::Edge.new(@anf_nodes[source_label],
|
63
|
+
@anf_nodes[target_label],
|
64
|
+
param)
|
65
|
+
end
|
66
|
+
|
67
|
+
[ @anf, method ]
|
68
|
+
end
|
69
|
+
|
70
|
+
def passed_locals
|
71
|
+
map = @method_locals.map do |name|
|
72
|
+
# Is the name rebound?
|
73
|
+
if @locals.include?(name)
|
74
|
+
[ name, @locals[name] ]
|
75
|
+
# Is it the middle of function?
|
76
|
+
elsif @node.entering_edges.any?
|
77
|
+
[ name, AST::LocalVariable.new(name) ]
|
78
|
+
# Locals default to nil.
|
79
|
+
else
|
80
|
+
[ name, nil ]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
Hash[*map.flatten]
|
85
|
+
end
|
86
|
+
|
87
|
+
# (set-lvar :var value)
|
88
|
+
def on_set_lvar(ast_node)
|
89
|
+
@locals[ast_node.children.first] = ast_node.children.last
|
90
|
+
|
91
|
+
ast_node.update(:remove)
|
92
|
+
end
|
93
|
+
|
94
|
+
# (jump-if compare_to condition)
|
95
|
+
def on_jump_if(ast_node)
|
96
|
+
if ast_node.children.first == true
|
97
|
+
true_edge, false_edge = @other_edge, @default_edge
|
98
|
+
else
|
99
|
+
true_edge, false_edge = @default_edge, @other_edge
|
100
|
+
end
|
101
|
+
|
102
|
+
true_node = ANF::LetNode.new(@anf, passed_locals)
|
103
|
+
false_node = ANF::LetNode.new(@anf, passed_locals)
|
104
|
+
@last_anf_node = ANF::IfNode.new(@anf, ast_node.children.last)
|
105
|
+
|
106
|
+
@anf.nodes.merge [ true_node, false_node, @last_anf_node ]
|
107
|
+
|
108
|
+
@anf_edges << [ @last_anf_node, true_node, true ] <<
|
109
|
+
[ true_node, true_edge.target_label ]
|
110
|
+
@anf_edges << [ @last_anf_node, false_node, false ] <<
|
111
|
+
[ false_node, false_edge.target_label ]
|
112
|
+
|
113
|
+
ast_node.update(:remove)
|
114
|
+
end
|
115
|
+
|
116
|
+
# (return expression)
|
117
|
+
def on_return(ast_node)
|
118
|
+
@last_anf_node = ANF::ReturnNode.new(@anf, ast_node.children.last)
|
119
|
+
|
120
|
+
@anf.nodes.add @last_anf_node
|
121
|
+
|
122
|
+
ast_node.update(:remove)
|
123
|
+
end
|
124
|
+
|
125
|
+
# (get-lvar :x) -> %x
|
126
|
+
def on_get_lvar(node)
|
127
|
+
node.update(:expand, AST::LocalVariable.new(node.children.first))
|
128
|
+
end
|
129
|
+
|
130
|
+
# AST node labels do not make sense.
|
131
|
+
def on_any(ast_node)
|
132
|
+
ast_node.metadata.delete :label
|
133
|
+
end
|
134
|
+
|
135
|
+
def expand_node(node)
|
136
|
+
node.update(:expand)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Immediates do not have to carry metadata anymore.
|
140
|
+
alias :on_true :expand_node
|
141
|
+
alias :on_false :expand_node
|
142
|
+
alias :on_nil :expand_node
|
143
|
+
alias :on_fixnum :expand_node
|
144
|
+
alias :on_literal :expand_node
|
145
|
+
|
146
|
+
# We have a near infinite supply of small, unoccupied labels.
|
147
|
+
def make_label
|
148
|
+
@last_label -= 1
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Furnace
|
2
|
+
module Transform
|
3
|
+
module Generic
|
4
|
+
class VariableTracer
|
5
|
+
include AST::Visitor
|
6
|
+
|
7
|
+
attr_reader :read_set, :write_set
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@read_set = Set.new
|
11
|
+
@write_set = Set.new
|
12
|
+
@conflict = false
|
13
|
+
end
|
14
|
+
|
15
|
+
def reset
|
16
|
+
@read_set.clear
|
17
|
+
@write_set.clear
|
18
|
+
@conflict = false
|
19
|
+
end
|
20
|
+
|
21
|
+
def conflict?
|
22
|
+
@conflict
|
23
|
+
end
|
24
|
+
|
25
|
+
def on_set_lvar(node)
|
26
|
+
var = node.children.first
|
27
|
+
|
28
|
+
@write_set.add var
|
29
|
+
@conflict ||= @read_set.include?(var)
|
30
|
+
end
|
31
|
+
|
32
|
+
def on_get_lvar(node)
|
33
|
+
var = node.children.first
|
34
|
+
|
35
|
+
@read_set.add var
|
36
|
+
@conflict ||= @write_set.include?(var)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class CFGBuild
|
41
|
+
def transform(ast, target_map, method)
|
42
|
+
cfg = CFG::Graph.new
|
43
|
+
|
44
|
+
tracer = VariableTracer.new
|
45
|
+
|
46
|
+
ast.children.each do |child|
|
47
|
+
label = child.metadata[:label]
|
48
|
+
|
49
|
+
# Transfer control to the next operation directly if this
|
50
|
+
# is a jump target.
|
51
|
+
if target_map.include? label
|
52
|
+
cfg.transfer({ nil => label })
|
53
|
+
end
|
54
|
+
|
55
|
+
# Our CFG must also be easily convertible to ANF/SSA.
|
56
|
+
# Split blocks if a non-SSA variable operation is encountered.
|
57
|
+
tracer.visit child
|
58
|
+
|
59
|
+
if tracer.conflict?
|
60
|
+
cfg.transfer({ nil => child.metadata[:label] })
|
61
|
+
end
|
62
|
+
|
63
|
+
# Expand current operation.
|
64
|
+
cfg.expand label, child
|
65
|
+
|
66
|
+
# Transfer control non-sequentaly if needed.
|
67
|
+
if child.type == :jump
|
68
|
+
cfg.transfer({ label => child.children[0] })
|
69
|
+
elsif child.type == :jump_if
|
70
|
+
cfg.transfer({ label => child.children[0],
|
71
|
+
nil => child.next.metadata[:label] })
|
72
|
+
elsif child.type == :return
|
73
|
+
cfg.transfer({ })
|
74
|
+
elsif tracer.conflict?
|
75
|
+
# Reset tracer below.
|
76
|
+
else
|
77
|
+
next
|
78
|
+
end
|
79
|
+
|
80
|
+
# There was a conflict or a control transfer. Reset the tracer.
|
81
|
+
tracer.reset
|
82
|
+
end
|
83
|
+
|
84
|
+
[ cfg, method ]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Furnace
|
2
|
+
module Transform
|
3
|
+
module Generic
|
4
|
+
class CFGNormalize
|
5
|
+
def transform(cfg, method)
|
6
|
+
cfg.nodes.each do |node|
|
7
|
+
# If a last operation is an unconditional jump, optimize it out.
|
8
|
+
last = node.operations.last
|
9
|
+
if last.type == :jump
|
10
|
+
edge = node.leaving_edge(last.metadata[:label])
|
11
|
+
|
12
|
+
node.operations.delete last
|
13
|
+
|
14
|
+
cfg.edges.delete edge
|
15
|
+
cfg.edges.add CFG::Edge.new(cfg, nil, node.label, edge.target.label)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Remove no-ops.
|
19
|
+
node.operations.delete_if { |op| op.type == :nop }
|
20
|
+
end
|
21
|
+
|
22
|
+
# Remove empty nodes.
|
23
|
+
cfg.nodes.delete_if do |node|
|
24
|
+
if node.operations.empty?
|
25
|
+
node.entering_edges.each do |edge|
|
26
|
+
edge.target = node.default_leaving_edge.target
|
27
|
+
end
|
28
|
+
cfg.edges.subtract node.leaving_edges
|
29
|
+
|
30
|
+
true
|
31
|
+
else
|
32
|
+
false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
[ cfg, method ]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Furnace
|
2
|
+
module Transform
|
3
|
+
module Generic
|
4
|
+
class LabelNormalize
|
5
|
+
include AST::Visitor
|
6
|
+
|
7
|
+
def transform(ast, method)
|
8
|
+
# Find the minimal label in each operation sub-tree.
|
9
|
+
# It's the operation entry point.
|
10
|
+
visit ast
|
11
|
+
|
12
|
+
# Traverse operations in reverse order and bypass all jump_target's,
|
13
|
+
# recording the forwarded address in label_map.
|
14
|
+
label_map = {}
|
15
|
+
|
16
|
+
last_real_operation = nil
|
17
|
+
ast.children.reverse.each do |child|
|
18
|
+
if child.type == :jump_target
|
19
|
+
label = last_real_operation
|
20
|
+
else
|
21
|
+
label = child.metadata[:label]
|
22
|
+
last_real_operation = label
|
23
|
+
end
|
24
|
+
|
25
|
+
label_map[child.metadata[:label]] = label
|
26
|
+
end
|
27
|
+
|
28
|
+
# Remove jump_target's.
|
29
|
+
ast.children.reject! { |c| c.type == :jump_target }
|
30
|
+
|
31
|
+
# Find all jumpable labels and substitute the addresses to forward
|
32
|
+
# through jump_target's.
|
33
|
+
target_map = []
|
34
|
+
|
35
|
+
ast.children.each do |child|
|
36
|
+
if child.type == :jump || child.type == :jump_if
|
37
|
+
forwarded_target = label_map[child.children[0]]
|
38
|
+
child.children[0] = forwarded_target
|
39
|
+
|
40
|
+
target_map << forwarded_target
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
[ ast, target_map, method ]
|
45
|
+
end
|
46
|
+
|
47
|
+
def on_any(node)
|
48
|
+
return if node.type == :root
|
49
|
+
|
50
|
+
child_nodes = node.children.select { |c| c.is_a? AST::Node }
|
51
|
+
|
52
|
+
new_label = child_nodes.map { |c| c.metadata[:label] }.compact.min
|
53
|
+
node.metadata[:label] = new_label if new_label
|
54
|
+
|
55
|
+
child_nodes.each { |c| c.metadata.delete :label }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Furnace
|
2
|
+
module Transform
|
3
|
+
module Optimizing
|
4
|
+
class FoldConstants
|
5
|
+
def transform(anf, method)
|
6
|
+
anf.nodes.each do |node|
|
7
|
+
if node.is_a? ANF::LetNode
|
8
|
+
node.try_propagate
|
9
|
+
node.try_eliminate
|
10
|
+
end
|
11
|
+
end
|
12
|
+
anf.eliminate_dead_code
|
13
|
+
|
14
|
+
[ anf, method ]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Furnace
|
2
|
+
module Transform
|
3
|
+
module Rubinius
|
4
|
+
class ASTBuild
|
5
|
+
def transform(method)
|
6
|
+
stack = []
|
7
|
+
map = {}
|
8
|
+
serial = 0
|
9
|
+
|
10
|
+
ast = AST::Node.new(:root)
|
11
|
+
|
12
|
+
method.decode.each do |opcode|
|
13
|
+
ins = opcode.instruction
|
14
|
+
|
15
|
+
node = AST::Node.new("rbx_#{ins.opcode}")
|
16
|
+
node.metadata[:label] = opcode.ip
|
17
|
+
node.children += opcode.args
|
18
|
+
|
19
|
+
# Compute the real value of consumed values.
|
20
|
+
case ins.stack_consumed
|
21
|
+
when Fixnum
|
22
|
+
consumed = ins.stack_consumed
|
23
|
+
when Array
|
24
|
+
#p ins.stack_consumed, opcode.args
|
25
|
+
consumed = ins.stack_consumed[0] + opcode.args.last
|
26
|
+
end
|
27
|
+
|
28
|
+
# Pop consumed values and attach to current node.
|
29
|
+
consumed.times.map { map[stack.pop] }.reverse.each do |child|
|
30
|
+
child.parent = node
|
31
|
+
node.children << child
|
32
|
+
end
|
33
|
+
|
34
|
+
# Push back and map the results.
|
35
|
+
if ins.stack_produced == 0 || ins.opcode == :ret
|
36
|
+
node.parent = ast
|
37
|
+
ast.children << node
|
38
|
+
elsif ins.stack_produced == 1
|
39
|
+
map[serial] = node
|
40
|
+
stack.push serial
|
41
|
+
|
42
|
+
serial += 1
|
43
|
+
else
|
44
|
+
raise RuntimeError, "don't know what to do with opcode #{opcode.inspect}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
[ ast, method ]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
module Furnace
|
2
|
+
module Transform
|
3
|
+
module Rubinius
|
4
|
+
class ASTNormalize
|
5
|
+
include AST::Visitor
|
6
|
+
|
7
|
+
def transform(ast, method)
|
8
|
+
@locals = method.local_names
|
9
|
+
@literals = method.literals
|
10
|
+
|
11
|
+
visit ast
|
12
|
+
|
13
|
+
[ ast, method ]
|
14
|
+
end
|
15
|
+
|
16
|
+
# (rbx-meta-push-0) -> 0
|
17
|
+
def on_rbx_meta_push_0(node)
|
18
|
+
node.update(:fixnum, [0], :constant => true)
|
19
|
+
end
|
20
|
+
|
21
|
+
# (rbx-meta-push-1) -> 1
|
22
|
+
def on_rbx_meta_push_1(node)
|
23
|
+
node.update(:fixnum, [1], :constant => true)
|
24
|
+
end
|
25
|
+
|
26
|
+
# (rbx-meta-push-2) -> 2
|
27
|
+
def on_rbx_meta_push_2(node)
|
28
|
+
node.update(:fixnum, [2], :constant => true)
|
29
|
+
end
|
30
|
+
|
31
|
+
# (rbx-push-int f) -> f
|
32
|
+
def on_rbx_push_int(node)
|
33
|
+
node.update(:fixnum, nil, :constant => true)
|
34
|
+
end
|
35
|
+
|
36
|
+
# (rbx-push-literal l) -> l
|
37
|
+
def on_rbx_push_literal(node)
|
38
|
+
node.update(:literal, nil, :constant => true)
|
39
|
+
end
|
40
|
+
|
41
|
+
# (rbx-push-nil) -> (nil)
|
42
|
+
def on_rbx_push_nil(node)
|
43
|
+
node.update(:nil, nil, :constant => true)
|
44
|
+
end
|
45
|
+
|
46
|
+
# (rbx-string-dup s) -> (dup s)
|
47
|
+
def on_rbx_string_dup(node)
|
48
|
+
node.update(:dup)
|
49
|
+
end
|
50
|
+
|
51
|
+
# (rbx-pop x) -> x
|
52
|
+
def on_rbx_pop(node)
|
53
|
+
child = node.children.first
|
54
|
+
child.update(:nop) if child.metadata[:constant]
|
55
|
+
|
56
|
+
node.update(:expand, [
|
57
|
+
child,
|
58
|
+
AST::Node.new(:jump_target, [], node.metadata)
|
59
|
+
], nil)
|
60
|
+
end
|
61
|
+
|
62
|
+
# (rbx-*) -> .
|
63
|
+
def make_jump_target(node)
|
64
|
+
node.update(:jump_target)
|
65
|
+
end
|
66
|
+
alias :on_rbx_check_interrupts :make_jump_target
|
67
|
+
alias :on_rbx_allow_private :make_jump_target
|
68
|
+
|
69
|
+
# (rbx-push-self) -> (self)
|
70
|
+
def on_rbx_push_self(node)
|
71
|
+
node.update(:self)
|
72
|
+
end
|
73
|
+
|
74
|
+
# (rbx-push-local n) -> (get-local n)
|
75
|
+
def on_rbx_push_local(node)
|
76
|
+
node.update(:get_lvar, [ @locals[node.children.first] ])
|
77
|
+
end
|
78
|
+
|
79
|
+
# (rbx-set-local n v) -> (set-local n v)
|
80
|
+
def on_rbx_set_local(node)
|
81
|
+
node.update(:set_lvar, [ @locals[node.children.first], node.children.last ])
|
82
|
+
end
|
83
|
+
|
84
|
+
# (rbx-push-ivar n) -> (get-ivar n)
|
85
|
+
def on_rbx_push_ivar(node)
|
86
|
+
node.update(:get_ivar, [ @literals[node.children.first] ])
|
87
|
+
end
|
88
|
+
|
89
|
+
# (rbx-set-ivar n v) -> (set-ivar n v)
|
90
|
+
def on_rbx_set_ivar(node)
|
91
|
+
node.update(:set_ivar, [ @literals[node.children.first], node.children.last ])
|
92
|
+
end
|
93
|
+
|
94
|
+
# (rbx-push-const-fast n x) -> (const n)
|
95
|
+
def on_rbx_push_const_fast(node)
|
96
|
+
node.update(:const, [ node.children.first ])
|
97
|
+
end
|
98
|
+
|
99
|
+
# (rbx-find-const n c) -> (const n c)
|
100
|
+
def on_rbx_find_const(node)
|
101
|
+
node.update(:const, [ @literals[node.children.first], node.children.last ])
|
102
|
+
end
|
103
|
+
|
104
|
+
# (rbx-ret x) -> (return x)
|
105
|
+
def on_rbx_ret(node)
|
106
|
+
node.update(:return)
|
107
|
+
end
|
108
|
+
|
109
|
+
# (rbx-send-method msg receiver) -> (send msg receiver)
|
110
|
+
def on_rbx_send_method(node)
|
111
|
+
node.update(:send, [
|
112
|
+
AST::MethodName.new(node.children[0]),
|
113
|
+
node.children[1]
|
114
|
+
])
|
115
|
+
end
|
116
|
+
|
117
|
+
# (rbx-send-stack msg count receiver args...) -> (send msg receiver args...)
|
118
|
+
def on_rbx_send_stack(node)
|
119
|
+
node.update(:send, [
|
120
|
+
AST::MethodName.new(node.children[0]), # message
|
121
|
+
node.children[2], # receiver
|
122
|
+
*node.children[3..-1] # args
|
123
|
+
])
|
124
|
+
end
|
125
|
+
|
126
|
+
# (rbx-send-stack-with-block msg count receiver args... block) -> (send-with-block msg receiver args... block)
|
127
|
+
def on_rbx_send_stack_with_block(node)
|
128
|
+
node.update(:send_with_block, [
|
129
|
+
AST::MethodName.new(node.children[0]), # message
|
130
|
+
node.children[2], # receiver
|
131
|
+
*node.children[3..-1] # args
|
132
|
+
])
|
133
|
+
end
|
134
|
+
|
135
|
+
# (rbx-create-block block) -> (lambda block)
|
136
|
+
def on_rbx_create_block(node)
|
137
|
+
$block = node.children.first
|
138
|
+
node.update(:lambda, [ "FAIL" ])
|
139
|
+
end
|
140
|
+
|
141
|
+
# (rbx-meta-* op receiver arg) -> (send op receiver arg)
|
142
|
+
def on_rbx_send_op_any(node)
|
143
|
+
node.update(:send, [
|
144
|
+
AST::MethodName.new(node.children[0]),
|
145
|
+
*node.children[1..-1]
|
146
|
+
])
|
147
|
+
end
|
148
|
+
alias :on_rbx_meta_send_op_plus :on_rbx_send_op_any
|
149
|
+
alias :on_rbx_meta_send_op_minus :on_rbx_send_op_any
|
150
|
+
alias :on_rbx_meta_send_op_gt :on_rbx_send_op_any
|
151
|
+
alias :on_rbx_meta_send_op_lt :on_rbx_send_op_any
|
152
|
+
alias :on_rbx_meta_to_s :on_rbx_send_op_any
|
153
|
+
|
154
|
+
# (rbx-goto-if-* block condition) -> (jump-if block value condition)
|
155
|
+
def on_rbx_goto_if_false(node)
|
156
|
+
node.update(:jump_if, [ node.children[0], false, node.children[1] ])
|
157
|
+
end
|
158
|
+
|
159
|
+
def on_rbx_goto_if_true(node)
|
160
|
+
node.update(:jump_if, [ node.children[0], true, node.children[1] ])
|
161
|
+
end
|
162
|
+
|
163
|
+
# (rbx-goto block) -> (jump block)
|
164
|
+
def on_rbx_goto(node)
|
165
|
+
node.update(:jump)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
data/lib/furnace.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require "furnace/version"
|
2
|
+
|
3
|
+
require "set"
|
4
|
+
|
5
|
+
require "furnace/ast/node"
|
6
|
+
require "furnace/ast/symbolic_node"
|
7
|
+
require "furnace/ast/visitor"
|
8
|
+
|
9
|
+
require "furnace/cfg/node"
|
10
|
+
require "furnace/cfg/edge"
|
11
|
+
require "furnace/cfg/graph"
|
12
|
+
|
13
|
+
require "furnace/anf/node"
|
14
|
+
require "furnace/anf/edge"
|
15
|
+
require "furnace/anf/let_node"
|
16
|
+
require "furnace/anf/in_node"
|
17
|
+
require "furnace/anf/if_node"
|
18
|
+
require "furnace/anf/return_node"
|
19
|
+
require "furnace/anf/graph"
|
20
|
+
|
21
|
+
require "furnace/transform/pipeline"
|
22
|
+
|
23
|
+
require "furnace/transform/rubinius/ast_build"
|
24
|
+
require "furnace/transform/rubinius/ast_normalize"
|
25
|
+
|
26
|
+
require "furnace/transform/generic/label_normalize"
|
27
|
+
require "furnace/transform/generic/cfg_build"
|
28
|
+
require "furnace/transform/generic/cfg_normalize"
|
29
|
+
require "furnace/transform/generic/anf_build"
|
30
|
+
|
31
|
+
require "furnace/transform/optimizing/fold_constants"
|
32
|
+
|
33
|
+
require "furnace/graphviz"
|
34
|
+
|
35
|
+
if RUBY_ENGINE != "rbx"
|
36
|
+
raise "Sorry, Furnace only works on Rubinius."
|
37
|
+
end
|