furnace 0.4.0.beta.1 → 0.4.0.beta.2
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.
- 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
|