furnace 0.4.0.beta.1 → 0.4.0.beta.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.travis.yml +6 -0
- data/INSTRUMENT.md +250 -0
- data/Rakefile +1 -1
- data/lib/furnace.rb +10 -6
- data/lib/furnace/ast/node.rb +15 -1
- data/lib/furnace/awesome_printer.rb +121 -0
- data/lib/furnace/context.rb +0 -0
- data/lib/furnace/ssa.rb +7 -25
- data/lib/furnace/ssa/argument.rb +14 -10
- data/lib/furnace/ssa/basic_block.rb +70 -25
- data/lib/furnace/ssa/builder.rb +11 -7
- data/lib/furnace/ssa/constant.rb +19 -11
- data/lib/furnace/ssa/event_stream.rb +145 -0
- data/lib/furnace/ssa/function.rb +78 -57
- data/lib/furnace/ssa/generic_instruction.rb +12 -5
- data/lib/furnace/ssa/instruction.rb +50 -35
- data/lib/furnace/ssa/instruction_syntax.rb +9 -25
- data/lib/furnace/ssa/instructions/branch.rb +2 -2
- data/lib/furnace/ssa/instructions/phi.rb +12 -10
- data/lib/furnace/ssa/instructions/return.rb +3 -3
- data/lib/furnace/ssa/instructions/return_value.rb +11 -0
- data/lib/furnace/ssa/instrumentation.rb +33 -0
- data/lib/furnace/ssa/module.rb +5 -1
- data/lib/furnace/ssa/named_value.rb +31 -10
- data/lib/furnace/ssa/terminator_instruction.rb +6 -6
- data/lib/furnace/ssa/types/basic_block.rb +4 -8
- data/lib/furnace/ssa/types/function.rb +4 -8
- data/lib/furnace/ssa/user.rb +14 -15
- data/lib/furnace/ssa/value.rb +9 -5
- data/lib/furnace/transform/iterative.rb +20 -4
- data/lib/furnace/type.rb +9 -0
- data/lib/furnace/type/bottom.rb +11 -0
- data/lib/furnace/type/top.rb +72 -0
- data/lib/furnace/type/value.rb +13 -0
- data/lib/furnace/type/variable.rb +61 -0
- data/lib/furnace/version.rb +1 -1
- data/test/{test_helper.rb → helper.rb} +28 -3
- data/test/{ast_test.rb → test_ast.rb} +13 -1
- data/test/test_awesome_printer.rb +148 -0
- data/test/{ssa_test.rb → test_ssa.rb} +276 -315
- data/test/{transform_test.rb → test_transform.rb} +2 -2
- data/test/test_type.rb +138 -0
- metadata +83 -105
- data/lib/furnace/graphviz.rb +0 -49
- data/lib/furnace/ssa/generic_type.rb +0 -16
- data/lib/furnace/ssa/pretty_printer.rb +0 -113
- data/lib/furnace/ssa/type.rb +0 -27
- data/lib/furnace/ssa/types/void.rb +0 -15
@@ -0,0 +1,61 @@
|
|
1
|
+
module Furnace
|
2
|
+
class Type::Variable
|
3
|
+
def initialize
|
4
|
+
freeze
|
5
|
+
end
|
6
|
+
|
7
|
+
def to_type
|
8
|
+
self
|
9
|
+
end
|
10
|
+
|
11
|
+
def subtype_of?(other)
|
12
|
+
other.instance_of?(Type::Top) ||
|
13
|
+
self == other
|
14
|
+
end
|
15
|
+
|
16
|
+
def supertype_of?(other)
|
17
|
+
other.subtype_of?(self)
|
18
|
+
end
|
19
|
+
|
20
|
+
def variable?
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
def replace_type_with(type, replacement)
|
25
|
+
if self == type
|
26
|
+
replacement.to_type
|
27
|
+
else
|
28
|
+
self
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def specialize(other)
|
33
|
+
{ self => other }
|
34
|
+
end
|
35
|
+
|
36
|
+
def awesome_print(p=AwesomePrinter.new)
|
37
|
+
p.type_variable self
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Type::Variable::Annotator
|
42
|
+
def initialize
|
43
|
+
@last_annotation = "a"
|
44
|
+
|
45
|
+
@annotations = Hash.new do |hash, var|
|
46
|
+
unless var.is_a? Type::Variable
|
47
|
+
raise ArgumentError, "#{self.class} cannot annotate #{var.class}"
|
48
|
+
end
|
49
|
+
|
50
|
+
annotation = @last_annotation
|
51
|
+
@last_annotation = @last_annotation.succ
|
52
|
+
|
53
|
+
hash[var] = annotation
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def annotate(var)
|
58
|
+
@annotations[var]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/furnace/version.rb
CHANGED
@@ -19,7 +19,32 @@ end
|
|
19
19
|
require 'simplecov'
|
20
20
|
SimpleCov.start
|
21
21
|
|
22
|
-
$LOAD_PATH << File.expand_path('../../lib', __FILE__)
|
23
|
-
|
24
22
|
require 'furnace'
|
25
|
-
include Furnace
|
23
|
+
include Furnace
|
24
|
+
|
25
|
+
class RubyType < Type::Top
|
26
|
+
attr_reader :ruby_type
|
27
|
+
|
28
|
+
def initialize(ruby_type)
|
29
|
+
@ruby_type = ruby_type
|
30
|
+
end
|
31
|
+
|
32
|
+
def ==(other)
|
33
|
+
other.instance_of?(RubyType) &&
|
34
|
+
@ruby_type == other.ruby_type
|
35
|
+
end
|
36
|
+
|
37
|
+
def hash
|
38
|
+
[self.class, @ruby_type].hash
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_s
|
42
|
+
"^#{@ruby_type}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class Class
|
47
|
+
def to_type
|
48
|
+
RubyType.new(self)
|
49
|
+
end
|
50
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require_relative '
|
1
|
+
require_relative 'helper'
|
2
2
|
|
3
3
|
describe AST::Node do
|
4
4
|
extend Furnace::AST::Sexp
|
@@ -107,6 +107,18 @@ describe AST::Node do
|
|
107
107
|
var_name.should.equal :$foo
|
108
108
|
value.should.equal s(:integer, 1)
|
109
109
|
end
|
110
|
+
|
111
|
+
it 'should concatenate with arrays' do
|
112
|
+
node = s(:gasgn, :$foo)
|
113
|
+
(node + [s(:integer, 1)]).
|
114
|
+
should.equal s(:gasgn, :$foo, s(:integer, 1))
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'should append elements' do
|
118
|
+
node = s(:array)
|
119
|
+
(node << s(:integer, 1) << s(:string, "foo")).
|
120
|
+
should.equal s(:array, s(:integer, 1), s(:string, "foo"))
|
121
|
+
end
|
110
122
|
end
|
111
123
|
|
112
124
|
describe AST::Processor do
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require_relative 'helper'
|
2
|
+
|
3
|
+
describe AwesomePrinter do
|
4
|
+
AwesomePrinter.colorize = false
|
5
|
+
|
6
|
+
it 'outputs chunks' do
|
7
|
+
AwesomePrinter.new do |p|
|
8
|
+
p.text 'foo'
|
9
|
+
end.should == 'foo'
|
10
|
+
|
11
|
+
AwesomePrinter.new do |p|
|
12
|
+
Integer.to_type.awesome_print(p)
|
13
|
+
end.should == '^Integer'
|
14
|
+
|
15
|
+
AwesomePrinter.new do |p|
|
16
|
+
p.keyword 'bar'
|
17
|
+
end.should == 'bar'
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'supports matching by =~' do
|
21
|
+
AwesomePrinter.new do |p|
|
22
|
+
p.text 'foo'
|
23
|
+
end.should =~ /foo/
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'ensures space between chunks' do
|
27
|
+
AwesomePrinter.new do |p|
|
28
|
+
p.text 'foo'
|
29
|
+
p.keyword 'doh'
|
30
|
+
p.text 'bar'
|
31
|
+
end.should == 'foo doh bar'
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'adds no space before and after #append' do
|
35
|
+
AwesomePrinter.new do |p|
|
36
|
+
p.text 'foo'
|
37
|
+
p.append 'bar'
|
38
|
+
p.text 'baz'
|
39
|
+
end.should == 'foobarbaz'
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'adds no space after #newline' do
|
43
|
+
AwesomePrinter.new do |p|
|
44
|
+
p.text 'foo'
|
45
|
+
p.newline
|
46
|
+
p.text 'bar'
|
47
|
+
end.should == "foo\nbar"
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'converts objects to chunks with to_s' do
|
51
|
+
AwesomePrinter.new do |p|
|
52
|
+
p.text :foo
|
53
|
+
p.text 1
|
54
|
+
p.keyword :bar
|
55
|
+
end.should == 'foo 1 bar'
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'when nesting, delegates to #awesome_print and then #to_s' do
|
59
|
+
with_awesome_print = Object.new.tap do |o|
|
60
|
+
def o.awesome_print(p)
|
61
|
+
p.text "awesome print"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
with_inspect = Object.new.tap do |o|
|
66
|
+
def o.to_s
|
67
|
+
"awesome to_s"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
AwesomePrinter.new do |p|
|
72
|
+
p.nest with_awesome_print
|
73
|
+
p.nest with_inspect
|
74
|
+
end.should == 'awesome print awesome to_s'
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'prints %names' do
|
78
|
+
AwesomePrinter.new do |p|
|
79
|
+
p.name 'foo'
|
80
|
+
end.should == '%foo'
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'prints type variables' do
|
84
|
+
a, b = 2.times.map { Type::Variable.new }
|
85
|
+
|
86
|
+
AwesomePrinter.new do |p|
|
87
|
+
p.type_variable a
|
88
|
+
p.type_variable b
|
89
|
+
p.type_variable a
|
90
|
+
end.should == '~a ~b ~a'
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'prints type variables with passed Annotator' do
|
94
|
+
a, b = 2.times.map { Type::Variable.new }
|
95
|
+
|
96
|
+
ann = Type::Variable::Annotator.new
|
97
|
+
ann.annotate a
|
98
|
+
|
99
|
+
AwesomePrinter.new(false, ann) do |p|
|
100
|
+
p.type_variable b
|
101
|
+
end.should == '~b'
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'prints collections' do
|
105
|
+
AwesomePrinter.new do |p|
|
106
|
+
p.collection(%w(a b c))
|
107
|
+
end.should == 'abc'
|
108
|
+
|
109
|
+
AwesomePrinter.new do |p|
|
110
|
+
p.collection('<', '; ', '>', %w(a b c))
|
111
|
+
end.should == '<a; b; c>'
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'ensures space before collections' do
|
115
|
+
AwesomePrinter.new do |p|
|
116
|
+
p.text 'foo'
|
117
|
+
p.collection(%w(a b c))
|
118
|
+
end.should == 'foo abc'
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'prints collections with custom iterator' do
|
122
|
+
AwesomePrinter.new do |p|
|
123
|
+
p.collection(%w(abc def)) do |e|
|
124
|
+
p.text e.reverse
|
125
|
+
end
|
126
|
+
end.should == 'cbafed'
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'chains' do
|
130
|
+
AwesomePrinter.new do |p|
|
131
|
+
p.append("foo").should == p
|
132
|
+
p.text("foo").should == p
|
133
|
+
p.newline.should == p
|
134
|
+
p.nest("foo").should == p
|
135
|
+
p.name("foo").should == p
|
136
|
+
p.type("foo").should == p
|
137
|
+
p.type_variable(Type::Variable.new).should == p
|
138
|
+
p.keyword("foo").should == p
|
139
|
+
p.collection(%w(a b)).should == p
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'adds colors if requested' do
|
144
|
+
AwesomePrinter.new(true) do |p|
|
145
|
+
p.keyword :bar
|
146
|
+
end.should == "\e[1;37mbar\e[0m"
|
147
|
+
end
|
148
|
+
end
|
@@ -1,27 +1,7 @@
|
|
1
|
-
require_relative '
|
1
|
+
require_relative 'helper'
|
2
2
|
|
3
3
|
describe SSA do
|
4
|
-
|
5
|
-
|
6
|
-
class SSARubyType < SSA::GenericType
|
7
|
-
def initialize(ruby_type)
|
8
|
-
@ruby_type = ruby_type
|
9
|
-
end
|
10
|
-
|
11
|
-
def parameters
|
12
|
-
[@ruby_type]
|
13
|
-
end
|
14
|
-
|
15
|
-
def inspect
|
16
|
-
"^#{@ruby_type}"
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
class Class
|
21
|
-
def to_type
|
22
|
-
SSARubyType.new(self)
|
23
|
-
end
|
24
|
-
end
|
4
|
+
AwesomePrinter.colorize = false
|
25
5
|
|
26
6
|
class BindingInsn < SSA::Instruction
|
27
7
|
def type
|
@@ -31,7 +11,11 @@ describe SSA do
|
|
31
11
|
|
32
12
|
class DupInsn < SSA::Instruction
|
33
13
|
def type
|
34
|
-
operands
|
14
|
+
if operands
|
15
|
+
operands.first.type
|
16
|
+
else
|
17
|
+
Type::Top.new
|
18
|
+
end
|
35
19
|
end
|
36
20
|
end
|
37
21
|
|
@@ -47,8 +31,8 @@ describe SSA do
|
|
47
31
|
class CondBranchInsn < SSA::TerminatorInstruction
|
48
32
|
syntax do |s|
|
49
33
|
s.operand :condition
|
50
|
-
s.operand :if_true
|
51
|
-
s.operand :if_false
|
34
|
+
s.operand :if_true
|
35
|
+
s.operand :if_false
|
52
36
|
end
|
53
37
|
|
54
38
|
def exits?
|
@@ -56,6 +40,9 @@ describe SSA do
|
|
56
40
|
end
|
57
41
|
end
|
58
42
|
|
43
|
+
class IsBInsn < SSA::Instruction
|
44
|
+
end
|
45
|
+
|
59
46
|
module TestScope
|
60
47
|
include SSA
|
61
48
|
|
@@ -74,129 +61,35 @@ describe SSA do
|
|
74
61
|
|
75
62
|
before do
|
76
63
|
@function = SSA::Function.new('foo')
|
77
|
-
@basic_block = SSA::BasicBlock.new
|
64
|
+
@basic_block = SSA::BasicBlock.new
|
78
65
|
@function.add @basic_block
|
79
66
|
@function.entry = @basic_block
|
80
67
|
end
|
81
68
|
|
82
|
-
def insn_noary
|
83
|
-
BindingInsn.new
|
69
|
+
def insn_noary
|
70
|
+
BindingInsn.new
|
84
71
|
end
|
85
72
|
|
86
|
-
def insn_unary(
|
87
|
-
DupInsn.new(
|
73
|
+
def insn_unary(what)
|
74
|
+
DupInsn.new([what])
|
88
75
|
end
|
89
76
|
|
90
|
-
def insn_binary(
|
91
|
-
TupleConcatInsn.new(
|
77
|
+
def insn_binary(left, right)
|
78
|
+
TupleConcatInsn.new([left, right])
|
92
79
|
end
|
93
80
|
|
94
81
|
it 'converts class names to opcodes' do
|
95
82
|
SSA.class_name_to_opcode(DupInsn).should == 'dup'
|
96
83
|
SSA.class_name_to_opcode(TupleConcatInsn).should == 'tuple_concat'
|
97
84
|
SSA.class_name_to_opcode(TestScope::NestedInsn).should == 'nested'
|
85
|
+
SSA.class_name_to_opcode(IsBInsn).should == 'is_b'
|
98
86
|
end
|
99
87
|
|
100
88
|
it 'converts opcodes to class names' do
|
101
89
|
SSA.opcode_to_class_name('foo').should == 'FooInsn'
|
102
90
|
SSA.opcode_to_class_name('foo_bar').should == 'FooBarInsn'
|
103
91
|
SSA.opcode_to_class_name(:foo_bar).should == 'FooBarInsn'
|
104
|
-
|
105
|
-
|
106
|
-
describe SSA::PrettyPrinter do
|
107
|
-
it 'outputs chunks' do
|
108
|
-
SSA::PrettyPrinter.new do |p|
|
109
|
-
p.text 'foo'
|
110
|
-
end.to_s.should == 'foo'
|
111
|
-
|
112
|
-
SSA::PrettyPrinter.new do |p|
|
113
|
-
p.type Integer.to_type
|
114
|
-
end.to_s.should == '^Integer'
|
115
|
-
|
116
|
-
SSA::PrettyPrinter.new do |p|
|
117
|
-
p.keyword 'bar'
|
118
|
-
end.to_s.should == 'bar'
|
119
|
-
end
|
120
|
-
|
121
|
-
it 'ensures space between chunks' do
|
122
|
-
SSA::PrettyPrinter.new do |p|
|
123
|
-
p.text 'foo'
|
124
|
-
p.keyword 'doh'
|
125
|
-
p.text 'bar'
|
126
|
-
end.to_s.should == 'foo doh bar'
|
127
|
-
end
|
128
|
-
|
129
|
-
it 'adds no space inside #text chunk' do
|
130
|
-
SSA::PrettyPrinter.new do |p|
|
131
|
-
p.text 'foo', 'bar'
|
132
|
-
p.keyword 'squick'
|
133
|
-
end.to_s.should == 'foobar squick'
|
134
|
-
end
|
135
|
-
|
136
|
-
it 'adds no space after newline' do
|
137
|
-
SSA::PrettyPrinter.new do |p|
|
138
|
-
p.text 'foo'
|
139
|
-
p.newline
|
140
|
-
p.text 'bar'
|
141
|
-
end.to_s.should == "foo\nbar"
|
142
|
-
end
|
143
|
-
|
144
|
-
it 'converts objects to chunks with to_s' do
|
145
|
-
SSA::PrettyPrinter.new do |p|
|
146
|
-
p.text :foo
|
147
|
-
p.text 1
|
148
|
-
p.keyword :bar
|
149
|
-
end.to_s.should == 'foo 1 bar'
|
150
|
-
end
|
151
|
-
|
152
|
-
it 'adds colors if requested' do
|
153
|
-
SSA::PrettyPrinter.new(true) do |p|
|
154
|
-
p.keyword :bar
|
155
|
-
end.to_s.should == "\e[1;37mbar\e[0m"
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
describe SSA::Type do
|
160
|
-
before do
|
161
|
-
@type = SSA::Type.new
|
162
|
-
end
|
163
|
-
|
164
|
-
it 'converts to type' do
|
165
|
-
@type.to_type.should.be.equal @type
|
166
|
-
end
|
167
|
-
|
168
|
-
it 'is a monotype' do
|
169
|
-
@type.should.be.monotype
|
170
|
-
end
|
171
|
-
|
172
|
-
it 'should be ==, eql?, subtype_of? and have the same hash as another instance' do
|
173
|
-
other = SSA::Type.new
|
174
|
-
@type.should == other
|
175
|
-
@type.should.be.eql other
|
176
|
-
@type.should.be.subtype_of other
|
177
|
-
@type.hash.should == other.hash
|
178
|
-
end
|
179
|
-
|
180
|
-
describe SSA::GenericType do
|
181
|
-
before do
|
182
|
-
@type = Integer.to_type
|
183
|
-
end
|
184
|
-
|
185
|
-
it 'should compare its parameters' do
|
186
|
-
otheri = Integer.to_type
|
187
|
-
otherf = Float.to_type
|
188
|
-
|
189
|
-
@type.should == otheri
|
190
|
-
@type.should.be.eql otheri
|
191
|
-
@type.should.be.subtype_of otheri
|
192
|
-
@type.hash.should == otheri.hash
|
193
|
-
|
194
|
-
@type.should.not == otherf
|
195
|
-
@type.should.not.be.eql otherf
|
196
|
-
@type.should.not.be.subtype_of otherf
|
197
|
-
@type.hash.should.not == otherf.hash
|
198
|
-
end
|
199
|
-
end
|
92
|
+
SSA.opcode_to_class_name(:is_b).should == 'IsBInsn'
|
200
93
|
end
|
201
94
|
|
202
95
|
describe SSA::Value do
|
@@ -204,8 +97,8 @@ describe SSA do
|
|
204
97
|
@val = SSA::Value.new
|
205
98
|
end
|
206
99
|
|
207
|
-
it 'has
|
208
|
-
@val.type.should ==
|
100
|
+
it 'has bottom type' do
|
101
|
+
@val.type.should == Type::Bottom.new
|
209
102
|
end
|
210
103
|
|
211
104
|
it 'is not constant' do
|
@@ -221,7 +114,7 @@ describe SSA do
|
|
221
114
|
end
|
222
115
|
|
223
116
|
it 'pretty prints' do
|
224
|
-
@val.
|
117
|
+
@val.awesome_print.should =~ %r{#<Furnace::SSA::Value}
|
225
118
|
end
|
226
119
|
|
227
120
|
it 'has an use list' do
|
@@ -244,7 +137,7 @@ describe SSA do
|
|
244
137
|
it 'can have all of its uses replaced' do
|
245
138
|
val1, val2 = 2.times.map { SSA::Value.new }
|
246
139
|
|
247
|
-
user = SSA::User.new(
|
140
|
+
user = SSA::User.new([val1], 'foo')
|
248
141
|
|
249
142
|
val1.replace_all_uses_with(val2)
|
250
143
|
|
@@ -259,7 +152,7 @@ describe SSA do
|
|
259
152
|
end
|
260
153
|
|
261
154
|
it 'pretty prints' do
|
262
|
-
@imm.
|
155
|
+
@imm.awesome_print.should == '^Integer 1'
|
263
156
|
end
|
264
157
|
|
265
158
|
it 'converts to value' do
|
@@ -279,49 +172,32 @@ describe SSA do
|
|
279
172
|
end
|
280
173
|
end
|
281
174
|
|
282
|
-
describe SSA::VoidType do
|
283
|
-
it 'allows to retrieve a constant' do
|
284
|
-
const = SSA.void_value
|
285
|
-
const.should.be.constant
|
286
|
-
const.type.should.be.instance_of SSA::VoidType
|
287
|
-
end
|
288
|
-
|
289
|
-
it 'inspects as void' do
|
290
|
-
SSA.void.inspect.should == 'void'
|
291
|
-
end
|
292
|
-
|
293
|
-
it 'inspects as void in constants' do
|
294
|
-
SSA.void_value.inspect.should == 'void'
|
295
|
-
end
|
296
|
-
end
|
297
|
-
|
298
175
|
describe SSA::NamedValue do
|
299
176
|
it 'receives unique names' do
|
300
|
-
values = 5.times.map
|
301
|
-
|
177
|
+
values = 5.times.map do
|
178
|
+
v = SSA::NamedValue.new(nil)
|
179
|
+
v.function = @function
|
180
|
+
v
|
181
|
+
end
|
182
|
+
|
183
|
+
values.map(&:name).uniq.size.should == 5
|
302
184
|
end
|
303
185
|
|
304
186
|
it 'receives unique names even if explicitly specified name conflicts' do
|
305
|
-
i1 = SSA::NamedValue.new(
|
306
|
-
|
307
|
-
i2.name.should.not == i1.name
|
187
|
+
i1 = SSA::NamedValue.new("foo")
|
188
|
+
i1.function = @function
|
308
189
|
|
309
|
-
i2
|
190
|
+
i2 = SSA::NamedValue.new("foo")
|
191
|
+
i2.name.should == i1.name
|
192
|
+
|
193
|
+
i2.function = @function
|
310
194
|
i2.name.should.not == i1.name
|
311
195
|
end
|
312
196
|
end
|
313
197
|
|
314
198
|
describe SSA::Argument do
|
315
199
|
before do
|
316
|
-
@val = SSA::Argument.new(
|
317
|
-
end
|
318
|
-
|
319
|
-
it 'pretty prints' do
|
320
|
-
SSA::Argument.new(@function, nil, 'baz').pretty_print.
|
321
|
-
should == '<?> %baz'
|
322
|
-
|
323
|
-
SSA::Argument.new(@function, Integer, 'bar').pretty_print.
|
324
|
-
should == '^Integer %bar'
|
200
|
+
@val = SSA::Argument.new(Integer, 'foo')
|
325
201
|
end
|
326
202
|
|
327
203
|
it 'converts to value' do
|
@@ -334,15 +210,24 @@ describe SSA do
|
|
334
210
|
end
|
335
211
|
|
336
212
|
it 'compares by identity' do
|
337
|
-
@val.should.not == SSA::Argument.new(
|
213
|
+
@val.should.not == SSA::Argument.new(Integer, 'foo')
|
338
214
|
end
|
339
215
|
|
340
216
|
it 'is not constant' do
|
341
217
|
@val.should.not.be.constant
|
342
218
|
end
|
343
219
|
|
344
|
-
it '
|
345
|
-
|
220
|
+
it 'replaces its type' do
|
221
|
+
ty = Type::Variable.new
|
222
|
+
|
223
|
+
val = SSA::Argument.new(ty, 'foo')
|
224
|
+
val.replace_type_with(ty, Integer.to_type)
|
225
|
+
val.type.should == Integer.to_type
|
226
|
+
end
|
227
|
+
|
228
|
+
it 'pretty prints' do
|
229
|
+
@val.awesome_print.
|
230
|
+
should == '^Integer %foo'
|
346
231
|
end
|
347
232
|
end
|
348
233
|
|
@@ -350,14 +235,14 @@ describe SSA do
|
|
350
235
|
it 'enumerates operands' do
|
351
236
|
val1, val2 = 2.times.map { SSA::Value.new }
|
352
237
|
|
353
|
-
user = SSA::User.new(
|
238
|
+
user = SSA::User.new([val1, val2])
|
354
239
|
user.should.enumerate :each_operand, [val1, val2]
|
355
240
|
end
|
356
241
|
|
357
242
|
it 'populates use lists' do
|
358
243
|
val = SSA::Value.new
|
359
244
|
|
360
|
-
user = SSA::User.new
|
245
|
+
user = SSA::User.new
|
361
246
|
val.should.enumerate :each_use, []
|
362
247
|
|
363
248
|
user.operands = [val]
|
@@ -367,7 +252,7 @@ describe SSA do
|
|
367
252
|
it 'updates use lists' do
|
368
253
|
val1, val2 = 2.times.map { SSA::Value.new }
|
369
254
|
|
370
|
-
user = SSA::User.new
|
255
|
+
user = SSA::User.new
|
371
256
|
val1.should.enumerate :each_use, []
|
372
257
|
val2.should.enumerate :each_use, []
|
373
258
|
|
@@ -382,17 +267,17 @@ describe SSA do
|
|
382
267
|
|
383
268
|
it 'detaches from values' do
|
384
269
|
val = SSA::Value.new
|
385
|
-
user = SSA::User.new(
|
270
|
+
user = SSA::User.new([val])
|
386
271
|
|
387
272
|
val.should.enumerate :each_use, [user]
|
388
|
-
user.
|
273
|
+
user.drop_references
|
389
274
|
val.should.enumerate :each_use, []
|
390
275
|
end
|
391
276
|
|
392
277
|
it 'can replace uses of values' do
|
393
278
|
val1, val2 = 2.times.map { SSA::Value.new }
|
394
279
|
|
395
|
-
user = SSA::User.new(
|
280
|
+
user = SSA::User.new([val1])
|
396
281
|
user.replace_uses_of(val1, val2)
|
397
282
|
|
398
283
|
val1.should.enumerate :each_use, []
|
@@ -402,7 +287,7 @@ describe SSA do
|
|
402
287
|
it 'barfs on #replace_uses_of if the value is not used' do
|
403
288
|
val1, val2 = 2.times.map { SSA::Value.new }
|
404
289
|
|
405
|
-
user = SSA::User.new(
|
290
|
+
user = SSA::User.new([val1])
|
406
291
|
|
407
292
|
-> { user.replace_uses_of(val2, val1) }.should.raise ArgumentError
|
408
293
|
end
|
@@ -410,106 +295,132 @@ describe SSA do
|
|
410
295
|
|
411
296
|
describe SSA::Instruction do
|
412
297
|
it 'is not terminator' do
|
413
|
-
i = insn_noary
|
298
|
+
i = insn_noary
|
414
299
|
i.should.not.be.terminator
|
415
300
|
end
|
416
301
|
|
417
302
|
it 'does not have side effects' do
|
418
|
-
i = insn_noary
|
303
|
+
i = insn_noary
|
419
304
|
i.has_side_effects?.should == false
|
420
305
|
end
|
421
306
|
|
422
307
|
it 'removes itself from basic block' do
|
423
|
-
i = insn_noary
|
308
|
+
i = insn_noary
|
424
309
|
@basic_block.append i
|
425
310
|
|
426
311
|
i.remove
|
427
312
|
@basic_block.to_a.should.be.empty
|
313
|
+
i.operands.should == []
|
314
|
+
end
|
315
|
+
|
316
|
+
it 'erases itself from basic block' do
|
317
|
+
i = insn_noary
|
318
|
+
@basic_block.append i
|
319
|
+
|
320
|
+
i.erase
|
321
|
+
@basic_block.to_a.should.be.empty
|
322
|
+
i.operands.should == nil
|
428
323
|
end
|
429
324
|
|
430
325
|
it 'replaces uses of itself with instructions' do
|
431
|
-
i1 = insn_noary
|
326
|
+
i1 = insn_noary
|
432
327
|
@basic_block.append i1
|
433
328
|
|
434
|
-
i2 = insn_unary(
|
329
|
+
i2 = insn_unary(i1)
|
435
330
|
@basic_block.append i2
|
436
331
|
|
437
|
-
i1a = insn_noary
|
332
|
+
i1a = insn_noary
|
438
333
|
i1.replace_with i1a
|
334
|
+
i1.operands.should == nil
|
439
335
|
|
440
336
|
@basic_block.to_a.should == [i1a, i2]
|
441
337
|
i2.operands.should == [i1a]
|
442
338
|
end
|
443
339
|
|
444
340
|
it 'replaces uses of itself with constants' do
|
445
|
-
i1 = insn_noary
|
341
|
+
i1 = insn_noary
|
446
342
|
@basic_block.append i1
|
447
343
|
|
448
|
-
i2 = insn_unary(
|
344
|
+
i2 = insn_unary(i1)
|
449
345
|
@basic_block.append i2
|
450
346
|
|
451
347
|
c1 = SSA::Constant.new(Integer, 1)
|
452
348
|
i1.replace_with c1
|
349
|
+
i1.operands.should == nil
|
453
350
|
|
454
351
|
@basic_block.to_a.should == [i2]
|
455
352
|
i2.operands.should == [c1]
|
456
353
|
end
|
457
354
|
|
458
355
|
it 'pretty prints' do
|
459
|
-
dup = DupInsn.new(
|
460
|
-
dup.
|
461
|
-
dup.
|
356
|
+
dup = DupInsn.new([SSA::Constant.new(Integer, 1)])
|
357
|
+
dup.basic_block = @basic_block
|
358
|
+
dup.awesome_print.should =~ /\^Integer %\d+ = dup \^Integer 1/
|
359
|
+
dup.awesome_print_as_value.should =~ /^%\d+/
|
462
360
|
|
463
|
-
concat = TupleConcatInsn.new(
|
361
|
+
concat = TupleConcatInsn.new(
|
464
362
|
[SSA::Constant.new(Array, [1]), SSA::Constant.new(Array, [2,3])])
|
465
|
-
concat.
|
466
|
-
concat.
|
363
|
+
concat.basic_block = @basic_block
|
364
|
+
concat.awesome_print.should =~ /\^Array %\d+ = tuple_concat \^Array \[1\], \^Array \[2, 3\]/
|
365
|
+
concat.awesome_print_as_value.should =~ /^%\d+/
|
467
366
|
|
468
|
-
zero_arity = BindingInsn.new
|
469
|
-
zero_arity.
|
470
|
-
zero_arity.
|
367
|
+
zero_arity = BindingInsn.new
|
368
|
+
zero_arity.basic_block = @basic_block
|
369
|
+
zero_arity.awesome_print.should =~ /\^Binding %\d+ = binding/
|
370
|
+
zero_arity.awesome_print_as_value.should =~ /^%\d+/
|
471
371
|
|
472
|
-
zero_all = TestScope::NestedInsn.new
|
473
|
-
zero_all.
|
474
|
-
zero_all.
|
372
|
+
zero_all = TestScope::NestedInsn.new
|
373
|
+
zero_all.basic_block = @basic_block
|
374
|
+
zero_all.awesome_print.should == 'nested'
|
375
|
+
zero_all.awesome_print_as_value.should == 'bottom'
|
475
376
|
end
|
476
377
|
|
477
378
|
describe SSA::GenericInstruction do
|
478
379
|
it 'has settable type' do
|
479
|
-
i = GenericInsn.new(
|
480
|
-
i.
|
380
|
+
i = GenericInsn.new(Integer, [])
|
381
|
+
i.basic_block = @basic_block
|
382
|
+
i.awesome_print.should =~ /\^Integer %\d+ = generic/
|
481
383
|
i.type = Binding
|
482
|
-
i.
|
384
|
+
i.awesome_print.should =~ /\^Binding %\d+ = generic/
|
385
|
+
end
|
386
|
+
|
387
|
+
it 'replaces its type' do
|
388
|
+
ty = Type::Variable.new
|
389
|
+
|
390
|
+
i = GenericInsn.new(ty, [])
|
391
|
+
i.replace_type_with(ty, Integer.to_type)
|
392
|
+
i.type.should == Integer.to_type
|
483
393
|
end
|
484
394
|
|
485
395
|
describe SSA::PhiInsn do
|
486
396
|
it 'accepts operand hash' do
|
487
397
|
-> {
|
488
|
-
phi = SSA::PhiInsn.new(
|
398
|
+
phi = SSA::PhiInsn.new(Integer,
|
489
399
|
{ @basic_block => SSA::Constant.new(Integer, 1) })
|
490
400
|
}.should.not.raise
|
491
401
|
end
|
492
402
|
|
493
403
|
it 'enumerates operands' do
|
494
404
|
val1, val2 = 2.times.map { SSA::Value.new }
|
495
|
-
bb1, bb2 = 2.times.map { SSA::BasicBlock.new
|
405
|
+
bb1, bb2 = 2.times.map { SSA::BasicBlock.new }
|
496
406
|
|
497
|
-
phi = SSA::PhiInsn.new(
|
407
|
+
phi = SSA::PhiInsn.new(Integer,
|
498
408
|
{ bb1 => val1, bb2 => val2 })
|
499
409
|
phi.should.enumerate :each_operand, [val1, val2, bb1, bb2]
|
500
410
|
end
|
501
411
|
|
502
412
|
it 'pretty prints' do
|
503
|
-
phi = SSA::PhiInsn.new(
|
413
|
+
phi = SSA::PhiInsn.new(Integer,
|
504
414
|
{ @basic_block => SSA::Constant.new(Integer, 1) })
|
505
|
-
phi.
|
506
|
-
|
415
|
+
phi.basic_block = @basic_block
|
416
|
+
phi.awesome_print.should =~
|
417
|
+
/\^Integer %\d = phi %\d => \^Integer 1/
|
507
418
|
end
|
508
419
|
|
509
420
|
it 'maintains use chains' do
|
510
421
|
val = SSA::Value.new
|
511
|
-
phi = SSA::PhiInsn.new(
|
512
|
-
|
422
|
+
phi = SSA::PhiInsn.new(Integer,
|
423
|
+
{ @basic_block => val })
|
513
424
|
val.should.enumerate :each_use, [phi]
|
514
425
|
@basic_block.should.enumerate :each_use, [phi]
|
515
426
|
end
|
@@ -517,8 +428,8 @@ describe SSA do
|
|
517
428
|
it 'can replace uses of values' do
|
518
429
|
val1, val2 = 2.times.map { SSA::Value.new }
|
519
430
|
|
520
|
-
phi = SSA::PhiInsn.new(
|
521
|
-
|
431
|
+
phi = SSA::PhiInsn.new(Integer,
|
432
|
+
{ @basic_block => val1 })
|
522
433
|
phi.replace_uses_of(val1, val2)
|
523
434
|
|
524
435
|
val1.should.enumerate :each_use, []
|
@@ -527,9 +438,9 @@ describe SSA do
|
|
527
438
|
|
528
439
|
it 'can replace uses of basic blocks' do
|
529
440
|
val = SSA::Value.new
|
530
|
-
bb2 = SSA::BasicBlock.new
|
441
|
+
bb2 = SSA::BasicBlock.new
|
531
442
|
|
532
|
-
phi = SSA::PhiInsn.new(
|
443
|
+
phi = SSA::PhiInsn.new(Integer,
|
533
444
|
{ @basic_block => val })
|
534
445
|
phi.replace_uses_of(@basic_block, bb2)
|
535
446
|
|
@@ -541,7 +452,7 @@ describe SSA do
|
|
541
452
|
it 'barfs on #replace_uses_of if the value is not used' do
|
542
453
|
val1, val2 = 2.times.map { SSA::Value.new }
|
543
454
|
|
544
|
-
phi = SSA::PhiInsn.new(
|
455
|
+
phi = SSA::PhiInsn.new(Integer,
|
545
456
|
{ @basic_block => val1 })
|
546
457
|
|
547
458
|
-> { phi.replace_uses_of(val2, val1) }.should.raise ArgumentError
|
@@ -551,31 +462,54 @@ describe SSA do
|
|
551
462
|
|
552
463
|
describe SSA::TerminatorInstruction do
|
553
464
|
it 'is a terminator' do
|
554
|
-
i = SSA::TerminatorInstruction.new(
|
465
|
+
i = SSA::TerminatorInstruction.new([])
|
555
466
|
i.should.be.terminator
|
556
467
|
end
|
557
468
|
|
558
|
-
it 'has side effects' do
|
559
|
-
i = SSA::TerminatorInstruction.new(
|
469
|
+
it 'has side effects if exits?' do
|
470
|
+
i = SSA::TerminatorInstruction.new([])
|
471
|
+
|
472
|
+
def i.exits?; true; end
|
560
473
|
i.has_side_effects?.should == true
|
561
474
|
end
|
562
475
|
|
563
476
|
it 'requires to implement #exits?' do
|
564
|
-
i = SSA::TerminatorInstruction.new(
|
477
|
+
i = SSA::TerminatorInstruction.new([])
|
565
478
|
-> { i.exits? }.should.raise NotImplementedError
|
566
479
|
end
|
567
480
|
|
568
481
|
describe SSA::BranchInsn do
|
569
482
|
it 'does not exit the method' do
|
570
|
-
i = SSA::BranchInsn.new(
|
483
|
+
i = SSA::BranchInsn.new([@basic_block])
|
571
484
|
i.exits?.should == false
|
572
485
|
end
|
573
486
|
end
|
574
487
|
|
575
488
|
describe SSA::ReturnInsn do
|
489
|
+
before do
|
490
|
+
@i = SSA::ReturnInsn.new
|
491
|
+
end
|
492
|
+
|
493
|
+
it 'exits the method' do
|
494
|
+
@i.exits?.should == true
|
495
|
+
end
|
496
|
+
|
497
|
+
it 'returns bottom in #value_type' do
|
498
|
+
@i.value_type.should == Type::Bottom.new
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|
502
|
+
describe SSA::ReturnValueInsn do
|
503
|
+
before do
|
504
|
+
@i = SSA::ReturnValueInsn.new([SSA::Constant.new(Integer, 1)])
|
505
|
+
end
|
506
|
+
|
576
507
|
it 'exits the method' do
|
577
|
-
i
|
578
|
-
|
508
|
+
@i.exits?.should == true
|
509
|
+
end
|
510
|
+
|
511
|
+
it 'returns value type in #value_type' do
|
512
|
+
@i.value_type.should == Integer.to_type
|
579
513
|
end
|
580
514
|
end
|
581
515
|
end
|
@@ -587,18 +521,18 @@ describe SSA do
|
|
587
521
|
end
|
588
522
|
|
589
523
|
it 'has the type of BasicBlock' do
|
590
|
-
@basic_block.type.should == SSA::
|
524
|
+
@basic_block.type.should == SSA::BasicBlockType.new
|
591
525
|
end
|
592
526
|
|
593
527
|
it 'pretty prints' do
|
594
|
-
@basic_block.append insn_noary
|
595
|
-
@basic_block.append insn_noary
|
596
|
-
@basic_block.
|
597
|
-
"1:\n ^Binding %2 = binding\n ^Binding %3 = binding\n"
|
528
|
+
@basic_block.append insn_noary
|
529
|
+
@basic_block.append insn_noary
|
530
|
+
@basic_block.awesome_print.should ==
|
531
|
+
"1:\n ^Binding %2 = binding\n ^Binding %3 = binding\n\n"
|
598
532
|
end
|
599
533
|
|
600
534
|
it 'inspects as value' do
|
601
|
-
@basic_block.
|
535
|
+
@basic_block.awesome_print_as_value.should == 'label %1'
|
602
536
|
end
|
603
537
|
|
604
538
|
it 'is constant' do
|
@@ -606,14 +540,21 @@ describe SSA do
|
|
606
540
|
end
|
607
541
|
|
608
542
|
it 'can append instructions' do
|
609
|
-
i1, i2 = 2.times.map { insn_noary
|
543
|
+
i1, i2 = 2.times.map { insn_noary }
|
610
544
|
@basic_block.append i1
|
611
545
|
@basic_block.append i2
|
612
546
|
@basic_block.to_a.should == [i1, i2]
|
613
547
|
end
|
614
548
|
|
549
|
+
it 'can prepend instructions' do
|
550
|
+
i1, i2 = 2.times.map { insn_noary }
|
551
|
+
@basic_block.prepend i1
|
552
|
+
@basic_block.prepend i2
|
553
|
+
@basic_block.to_a.should == [i2, i1]
|
554
|
+
end
|
555
|
+
|
615
556
|
it 'can insert instructions' do
|
616
|
-
i1, i2, i3 = 3.times.map { insn_noary
|
557
|
+
i1, i2, i3 = 3.times.map { insn_noary }
|
617
558
|
@basic_block.append i1
|
618
559
|
-> { @basic_block.insert i3, i2 }.should.raise ArgumentError, %r|is not found|
|
619
560
|
@basic_block.append i3
|
@@ -621,38 +562,49 @@ describe SSA do
|
|
621
562
|
@basic_block.to_a.should == [i1, i2, i3]
|
622
563
|
end
|
623
564
|
|
565
|
+
it 'can splice instructions' do
|
566
|
+
i1, i2, i3 = 3.times.map { insn_noary }
|
567
|
+
@basic_block.append i1
|
568
|
+
@basic_block.append i2
|
569
|
+
@basic_block.append i3
|
570
|
+
@basic_block.splice(i2).should == [i3]
|
571
|
+
@basic_block.to_a.should == [i1, i2]
|
572
|
+
|
573
|
+
-> { @basic_block.splice(i3) }.should.raise(ArgumentError, %r|is not found|)
|
574
|
+
end
|
575
|
+
|
624
576
|
it 'is not affected by changes to #to_a value' do
|
625
|
-
i1 = insn_noary
|
577
|
+
i1 = insn_noary
|
626
578
|
@basic_block.append i1
|
627
579
|
@basic_block.to_a.clear
|
628
580
|
@basic_block.to_a.size.should == 1
|
629
581
|
end
|
630
582
|
|
631
583
|
it 'enumerates instructions' do
|
632
|
-
i1 = insn_noary
|
584
|
+
i1 = insn_noary
|
633
585
|
@basic_block.append i1
|
634
586
|
@basic_block.should.enumerate :each, [i1]
|
635
587
|
end
|
636
588
|
|
637
589
|
it 'enumerates instructions by type' do
|
638
|
-
i1 = BindingInsn.new
|
590
|
+
i1 = BindingInsn.new
|
639
591
|
@basic_block.append i1
|
640
592
|
|
641
|
-
i2 = GenericInsn.new(
|
593
|
+
i2 = GenericInsn.new(Integer)
|
642
594
|
@basic_block.append i2
|
643
595
|
|
644
596
|
@basic_block.each(BindingInsn).should.enumerate :each, [i1]
|
645
597
|
end
|
646
598
|
|
647
599
|
it 'can check for presence of instructions' do
|
648
|
-
i1, i2 = 2.times.map { insn_noary
|
600
|
+
i1, i2 = 2.times.map { insn_noary }
|
649
601
|
@basic_block.append i1
|
650
602
|
@basic_block.should.include i1
|
651
603
|
@basic_block.should.not.include i2
|
652
604
|
end
|
653
605
|
|
654
606
|
it 'can remove instructions' do
|
655
|
-
i1, i2 = 2.times.map { insn_noary
|
607
|
+
i1, i2 = 2.times.map { insn_noary }
|
656
608
|
@basic_block.append i1
|
657
609
|
@basic_block.append i2
|
658
610
|
@basic_block.remove i1
|
@@ -660,7 +612,7 @@ describe SSA do
|
|
660
612
|
end
|
661
613
|
|
662
614
|
it 'can replace instructions' do
|
663
|
-
i1, i2, i3, i4 = 4.times.map { insn_noary
|
615
|
+
i1, i2, i3, i4 = 4.times.map { insn_noary }
|
664
616
|
@basic_block.append i1
|
665
617
|
@basic_block.append i2
|
666
618
|
@basic_block.append i3
|
@@ -673,28 +625,26 @@ describe SSA do
|
|
673
625
|
@branch_bb = @basic_block
|
674
626
|
@branch_bb.name = 'branch'
|
675
627
|
|
676
|
-
@body_bb = SSA::BasicBlock.new(
|
628
|
+
@body_bb = SSA::BasicBlock.new([], 'body')
|
677
629
|
@function.add @body_bb
|
678
630
|
|
679
|
-
@ret_bb = SSA::BasicBlock.new(
|
631
|
+
@ret_bb = SSA::BasicBlock.new([], 'ret')
|
680
632
|
@function.add @ret_bb
|
681
633
|
|
682
|
-
@cond = DupInsn.new(
|
683
|
-
[ SSA::Constant.new(TrueClass, true) ])
|
634
|
+
@cond = DupInsn.new([ SSA::Constant.new(TrueClass, true) ])
|
684
635
|
@branch_bb.append @cond
|
685
636
|
|
686
|
-
@cond_br = CondBranchInsn.new(
|
637
|
+
@cond_br = CondBranchInsn.new(
|
687
638
|
[ @cond,
|
688
639
|
@body_bb,
|
689
640
|
@ret_bb ])
|
690
641
|
@branch_bb.append @cond_br
|
691
642
|
|
692
|
-
@uncond_br = SSA::BranchInsn.new(
|
643
|
+
@uncond_br = SSA::BranchInsn.new(
|
693
644
|
[ @ret_bb ])
|
694
645
|
@body_bb.append @uncond_br
|
695
646
|
|
696
|
-
@ret = SSA::ReturnInsn.new
|
697
|
-
[ SSA.void_value ])
|
647
|
+
@ret = SSA::ReturnInsn.new
|
698
648
|
@ret_bb.append @ret
|
699
649
|
end
|
700
650
|
|
@@ -734,12 +684,12 @@ describe SSA do
|
|
734
684
|
SSA::Constant.new(SSA::Function, @function.name)
|
735
685
|
|
736
686
|
@function.name = 'foo'
|
737
|
-
@function.to_value.
|
687
|
+
@function.to_value.awesome_print_as_value.should ==
|
738
688
|
'function "foo"'
|
739
689
|
end
|
740
690
|
|
741
691
|
it 'converts to type' do
|
742
|
-
SSA::Function.to_type.should == SSA::FunctionType.
|
692
|
+
SSA::Function.to_type.should == SSA::FunctionType.new
|
743
693
|
end
|
744
694
|
|
745
695
|
it 'generates numeric names in #make_name(nil)' do
|
@@ -762,39 +712,47 @@ describe SSA do
|
|
762
712
|
@function.should.not.include 'foobar'
|
763
713
|
end
|
764
714
|
|
715
|
+
it 'reports #size' do
|
716
|
+
@function.size.should == 1
|
717
|
+
end
|
718
|
+
|
765
719
|
it 'removes blocks' do
|
766
720
|
@function.remove @basic_block
|
767
721
|
@function.should.not.include '1'
|
768
722
|
end
|
769
723
|
|
770
724
|
it 'iterates each instruction in each block' do
|
771
|
-
bb2 = SSA::BasicBlock.new
|
725
|
+
bb2 = SSA::BasicBlock.new
|
772
726
|
@function.add bb2
|
773
727
|
|
774
|
-
i1 = insn_noary
|
728
|
+
i1 = insn_noary
|
775
729
|
@basic_block.append i1
|
776
730
|
|
777
|
-
i2 = insn_unary(
|
731
|
+
i2 = insn_unary(i1)
|
778
732
|
bb2.append i2
|
779
733
|
|
780
734
|
@function.should.enumerate :each_instruction, [i1, i2]
|
781
735
|
end
|
782
736
|
|
737
|
+
it 'sanitizes arguments' do
|
738
|
+
-> { @function.arguments = [1] }.should.raise(ArgumentError)
|
739
|
+
end
|
740
|
+
|
783
741
|
it 'pretty prints' do
|
784
742
|
@function.name = 'foo'
|
785
743
|
@function.arguments = [
|
786
|
-
SSA::Argument.new(
|
787
|
-
SSA::Argument.new(
|
744
|
+
SSA::Argument.new(Integer, 'count'),
|
745
|
+
SSA::Argument.new(Binding, 'outer')
|
788
746
|
]
|
789
747
|
|
790
|
-
@basic_block.append insn_binary(
|
748
|
+
@basic_block.append insn_binary(*@function.arguments)
|
791
749
|
|
792
|
-
bb2 = SSA::BasicBlock.new(
|
750
|
+
bb2 = SSA::BasicBlock.new([], 'foo')
|
793
751
|
@function.add bb2
|
794
|
-
bb2.append insn_unary(
|
752
|
+
bb2.append insn_unary(SSA::Constant.new(Integer, 1))
|
795
753
|
|
796
|
-
@function.
|
797
|
-
function
|
754
|
+
@function.awesome_print.should == <<-END
|
755
|
+
function bottom foo (^Integer %count, ^Binding %outer) {
|
798
756
|
1:
|
799
757
|
^Array %2 = tuple_concat %count, %outer
|
800
758
|
|
@@ -808,7 +766,7 @@ foo:
|
|
808
766
|
it 'duplicates all its content' do
|
809
767
|
@function.name = 'foo;1'
|
810
768
|
@function.arguments = [
|
811
|
-
SSA::Argument.new(
|
769
|
+
SSA::Argument.new(Integer, 'count'),
|
812
770
|
]
|
813
771
|
|
814
772
|
f1a1 = @function.arguments.first
|
@@ -816,20 +774,20 @@ foo:
|
|
816
774
|
f1bb1 = @function.entry
|
817
775
|
f1bb1.name = 'bb1'
|
818
776
|
|
819
|
-
f1bb2 = SSA::BasicBlock.new(
|
777
|
+
f1bb2 = SSA::BasicBlock.new([], 'bb2')
|
820
778
|
@function.add f1bb2
|
821
779
|
|
822
|
-
f1i1 = insn_unary(
|
780
|
+
f1i1 = insn_unary(f1a1)
|
823
781
|
@basic_block.append f1i1
|
824
782
|
|
825
783
|
f1c1 = SSA::Constant.new(Array, [1])
|
826
|
-
f1i2 = insn_binary(
|
784
|
+
f1i2 = insn_binary(f1i1, f1c1)
|
827
785
|
f1bb2.append f1i2
|
828
786
|
|
829
|
-
f1bb3 = SSA::BasicBlock.new(
|
787
|
+
f1bb3 = SSA::BasicBlock.new([], 'bb3')
|
830
788
|
@function.add f1bb3
|
831
789
|
|
832
|
-
f1phi = SSA::PhiInsn.new(
|
790
|
+
f1phi = SSA::PhiInsn.new(Integer,
|
833
791
|
{ f1bb1 => f1i1, f1bb2 => f1i2 })
|
834
792
|
f1bb3.append f1phi
|
835
793
|
|
@@ -872,7 +830,7 @@ foo:
|
|
872
830
|
end
|
873
831
|
|
874
832
|
f2.name = f1.name
|
875
|
-
f2.
|
833
|
+
f2.awesome_print.to_s.should == f1.awesome_print.to_s
|
876
834
|
end
|
877
835
|
end
|
878
836
|
|
@@ -896,7 +854,7 @@ foo:
|
|
896
854
|
bar, = @f.arguments
|
897
855
|
bar.type.should == Integer.to_type
|
898
856
|
bar.name.should == 'bar'
|
899
|
-
@f.return_type.should == Float
|
857
|
+
@f.return_type.should == Float.to_type
|
900
858
|
|
901
859
|
bb = @f.find('1')
|
902
860
|
@f.entry.should == bb
|
@@ -917,10 +875,19 @@ foo:
|
|
917
875
|
end
|
918
876
|
|
919
877
|
it 'builds ReturnInsn' do
|
920
|
-
@b.return
|
878
|
+
@b.return
|
921
879
|
i, = @b.block.to_a
|
922
880
|
i.should.be.instance_of SSA::ReturnInsn
|
923
|
-
i.operands.should == [
|
881
|
+
i.operands.should == []
|
882
|
+
end
|
883
|
+
|
884
|
+
it 'builds ReturnValueInsn' do
|
885
|
+
v1 = SSA::Constant.new(Integer, 1)
|
886
|
+
|
887
|
+
@b.return_value v1
|
888
|
+
i, = @b.block.to_a
|
889
|
+
i.should.be.instance_of SSA::ReturnValueInsn
|
890
|
+
i.operands.should == [v1]
|
924
891
|
end
|
925
892
|
end
|
926
893
|
|
@@ -932,12 +899,6 @@ foo:
|
|
932
899
|
end
|
933
900
|
end
|
934
901
|
|
935
|
-
class SyntaxTypedInsn < SSA::Instruction
|
936
|
-
syntax do |s|
|
937
|
-
s.operand :foo, Integer
|
938
|
-
end
|
939
|
-
end
|
940
|
-
|
941
902
|
class SyntaxSplatInsn < SSA::Instruction
|
942
903
|
syntax do |s|
|
943
904
|
s.operand :foo
|
@@ -948,45 +909,36 @@ foo:
|
|
948
909
|
before do
|
949
910
|
@iconst = SSA::Constant.new(Integer, 1)
|
950
911
|
@fconst = SSA::Constant.new(Float, 1.0)
|
951
|
-
@iinsn = DupInsn.new(
|
912
|
+
@iinsn = DupInsn.new([ @iconst ])
|
952
913
|
end
|
953
914
|
|
954
915
|
it 'accepts operands and decomposes them' do
|
955
|
-
i = SyntaxUntypedInsn.new(
|
916
|
+
i = SyntaxUntypedInsn.new([ @iconst, @fconst ])
|
956
917
|
i.foo.should == @iconst
|
957
918
|
i.bar.should == @fconst
|
958
919
|
end
|
959
920
|
|
960
921
|
it 'allows to change operands through accessors' do
|
961
|
-
i = SyntaxUntypedInsn.new(
|
922
|
+
i = SyntaxUntypedInsn.new([ @iconst, @fconst ])
|
962
923
|
i.foo = @iinsn
|
963
924
|
i.operands.should == [@iinsn, @fconst]
|
964
925
|
end
|
965
926
|
|
966
927
|
it 'does not accept wrong amount of operands' do
|
967
|
-
-> { SyntaxUntypedInsn.new(
|
928
|
+
-> { SyntaxUntypedInsn.new([ @iconst ]) }.
|
968
929
|
should.raise ArgumentError
|
969
|
-
-> { SyntaxUntypedInsn.new(
|
930
|
+
-> { SyntaxUntypedInsn.new([ @iconst, @iconst, @iconst ]) }.
|
970
931
|
should.raise ArgumentError
|
971
932
|
end
|
972
933
|
|
973
|
-
it 'accepts only correct typed operands' do
|
974
|
-
-> { SyntaxTypedInsn.new(@basic_block, [ @fconst ]) }.
|
975
|
-
should.raise TypeError
|
976
|
-
-> { SyntaxTypedInsn.new(@basic_block, [ @iconst ]) }.
|
977
|
-
should.not.raise
|
978
|
-
-> { SyntaxTypedInsn.new(@basic_block, [ @iinsn ]) }.
|
979
|
-
should.not.raise
|
980
|
-
end
|
981
|
-
|
982
934
|
it 'accepts splat' do
|
983
|
-
i = SyntaxSplatInsn.new(
|
935
|
+
i = SyntaxSplatInsn.new([ @iconst, @fconst, @iinsn ])
|
984
936
|
i.foo.should == @iconst
|
985
937
|
i.bars.should == [@fconst, @iinsn]
|
986
938
|
end
|
987
939
|
|
988
940
|
it 'does not accept wrong amount of operands with splat' do
|
989
|
-
-> { SyntaxSplatInsn.new(
|
941
|
+
-> { SyntaxSplatInsn.new([]) }.
|
990
942
|
should.raise ArgumentError
|
991
943
|
end
|
992
944
|
|
@@ -1011,41 +963,14 @@ foo:
|
|
1011
963
|
end
|
1012
964
|
|
1013
965
|
it 'allows to update splat' do
|
1014
|
-
i = SyntaxSplatInsn.new(
|
966
|
+
i = SyntaxSplatInsn.new([ @iconst, @fconst, @iinsn ])
|
1015
967
|
i.bars = [@iinsn, @fconst]
|
1016
968
|
i.bars.should == [@iinsn, @fconst]
|
1017
969
|
i.operands.should == [@iconst, @iinsn, @fconst]
|
1018
970
|
end
|
1019
971
|
|
1020
|
-
it 'allows to inquire status' do
|
1021
|
-
i = SyntaxTypedInsn.new(@basic_block, [ @iconst ])
|
1022
|
-
i.should.be.valid
|
1023
|
-
i.foo = @fconst
|
1024
|
-
i.should.not.be.valid
|
1025
|
-
end
|
1026
|
-
|
1027
|
-
it 'highlights invalid insns when pretty printing' do
|
1028
|
-
i = SyntaxTypedInsn.new(@basic_block, [ @iconst ])
|
1029
|
-
i.foo = @fconst
|
1030
|
-
i.pretty_print.should =~ /!syntax_typed/
|
1031
|
-
end
|
1032
|
-
|
1033
|
-
it 'allows to treat nil type as error' do
|
1034
|
-
phi = SSA::PhiInsn.new(@basic_block, nil)
|
1035
|
-
|
1036
|
-
i = SyntaxTypedInsn.new(@basic_block, [ @iconst ])
|
1037
|
-
i.should.be.valid(true)
|
1038
|
-
i.should.be.valid(false)
|
1039
|
-
-> { i.verify!(false) }.should.not.raise
|
1040
|
-
|
1041
|
-
i.foo = phi
|
1042
|
-
i.should.be.valid(true)
|
1043
|
-
i.should.not.be.valid(false)
|
1044
|
-
-> { i.verify!(false) }.should.raise TypeError, %r|<?>|
|
1045
|
-
end
|
1046
|
-
|
1047
972
|
it 'does not interfere with def-use tracking' do
|
1048
|
-
i = SyntaxUntypedInsn.new(
|
973
|
+
i = SyntaxUntypedInsn.new([ @iconst, @fconst ])
|
1049
974
|
@fconst.should.enumerate :each_use, [ i ]
|
1050
975
|
|
1051
976
|
i.bar = @iinsn
|
@@ -1054,12 +979,48 @@ foo:
|
|
1054
979
|
end
|
1055
980
|
|
1056
981
|
it 'does not break on replace_uses_of' do
|
1057
|
-
i = SyntaxUntypedInsn.new(
|
982
|
+
i = SyntaxUntypedInsn.new([ @iconst, @fconst ])
|
1058
983
|
i.replace_uses_of @iconst, @fconst
|
1059
984
|
i.foo.should == @fconst
|
1060
985
|
end
|
1061
986
|
end
|
1062
987
|
|
988
|
+
describe SSA::EventStream do
|
989
|
+
before do
|
990
|
+
@b = TestBuilder.new('foo',
|
991
|
+
[ [Integer, 'bar'], [Binding, 'baz'] ],
|
992
|
+
Float)
|
993
|
+
@fun = @b.function
|
994
|
+
|
995
|
+
SSA.start_instrumentation
|
996
|
+
|
997
|
+
@mod = SSA::Module.new
|
998
|
+
@mod.add @fun
|
999
|
+
end
|
1000
|
+
|
1001
|
+
it 'instruments functions' do
|
1002
|
+
iconst = SSA::Constant.new(Integer, 1)
|
1003
|
+
iconst2 = @b.append :dup, [ iconst ]
|
1004
|
+
|
1005
|
+
iconst2.name = 'dupped'
|
1006
|
+
|
1007
|
+
@b.add_block do
|
1008
|
+
@b.return_value iconst2
|
1009
|
+
end
|
1010
|
+
|
1011
|
+
SSA.instrumentation.mark_transform(@fun, "footrans")
|
1012
|
+
@fun.remove @b.block
|
1013
|
+
|
1014
|
+
# It's not possible to verify the instrumentation data,
|
1015
|
+
# as it includes object_ids, so we just check that no exception
|
1016
|
+
# is raised and it's not nil.
|
1017
|
+
SSA.instrumentation.data.should.is_a? Array
|
1018
|
+
|
1019
|
+
# Disable instrumentation
|
1020
|
+
SSA.instrumentation = nil
|
1021
|
+
end
|
1022
|
+
end
|
1023
|
+
|
1063
1024
|
describe SSA::Module do
|
1064
1025
|
before do
|
1065
1026
|
@module = SSA::Module.new
|
@@ -1126,4 +1087,4 @@ foo:
|
|
1126
1087
|
@module.should.not.include 'foo'
|
1127
1088
|
end
|
1128
1089
|
end
|
1129
|
-
end
|
1090
|
+
end
|