| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Language::P::Intermediate::Transform; |
|
2
|
|
|
|
|
|
|
|
|
3
|
19
|
|
|
19
|
|
18666
|
use strict; |
|
|
19
|
|
|
|
|
3860
|
|
|
|
19
|
|
|
|
|
1069
|
|
|
4
|
19
|
|
|
19
|
|
122
|
use warnings; |
|
|
19
|
|
|
|
|
45
|
|
|
|
19
|
|
|
|
|
744
|
|
|
5
|
19
|
|
|
19
|
|
106
|
use base qw(Class::Accessor::Fast); |
|
|
19
|
|
|
|
|
31
|
|
|
|
19
|
|
|
|
|
2401
|
|
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
__PACKAGE__->mk_accessors( qw(_temporary_count _current_basic_block |
|
8
|
|
|
|
|
|
|
_converting _queue _stack _converted |
|
9
|
|
|
|
|
|
|
_converted_segments) ); |
|
10
|
|
|
|
|
|
|
|
|
11
|
19
|
|
|
19
|
|
112
|
use Language::P::Opcodes qw(:all); |
|
|
19
|
|
|
|
|
36
|
|
|
|
19
|
|
|
|
|
19225
|
|
|
12
|
19
|
|
|
19
|
|
124
|
use Language::P::Assembly qw(:all); |
|
|
19
|
|
|
|
|
30
|
|
|
|
19
|
|
|
|
|
74550
|
|
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
my %op_map = |
|
15
|
|
|
|
|
|
|
( OP_MAKE_LIST() => '_make_list', |
|
16
|
|
|
|
|
|
|
OP_POP() => '_pop', |
|
17
|
|
|
|
|
|
|
OP_SWAP() => '_swap', |
|
18
|
|
|
|
|
|
|
OP_DUP() => '_dup', |
|
19
|
|
|
|
|
|
|
OP_CONSTANT_SUB() => '_const_sub', |
|
20
|
|
|
|
|
|
|
OP_JUMP_IF_TRUE() => '_cond_jump', |
|
21
|
|
|
|
|
|
|
OP_JUMP_IF_FALSE() => '_cond_jump', |
|
22
|
|
|
|
|
|
|
OP_JUMP_IF_NULL() => '_cond_jump', |
|
23
|
|
|
|
|
|
|
OP_JUMP_IF_F_GT() => '_cond_jump', |
|
24
|
|
|
|
|
|
|
OP_JUMP_IF_F_GE() => '_cond_jump', |
|
25
|
|
|
|
|
|
|
OP_JUMP_IF_F_EQ() => '_cond_jump', |
|
26
|
|
|
|
|
|
|
OP_JUMP_IF_F_NE() => '_cond_jump', |
|
27
|
|
|
|
|
|
|
OP_JUMP_IF_F_LE() => '_cond_jump', |
|
28
|
|
|
|
|
|
|
OP_JUMP_IF_F_LT() => '_cond_jump', |
|
29
|
|
|
|
|
|
|
OP_JUMP_IF_S_GT() => '_cond_jump', |
|
30
|
|
|
|
|
|
|
OP_JUMP_IF_S_GE() => '_cond_jump', |
|
31
|
|
|
|
|
|
|
OP_JUMP_IF_S_EQ() => '_cond_jump', |
|
32
|
|
|
|
|
|
|
OP_JUMP_IF_S_NE() => '_cond_jump', |
|
33
|
|
|
|
|
|
|
OP_JUMP_IF_S_LE() => '_cond_jump', |
|
34
|
|
|
|
|
|
|
OP_JUMP_IF_S_LT() => '_cond_jump', |
|
35
|
|
|
|
|
|
|
OP_JUMP() => '_jump', |
|
36
|
|
|
|
|
|
|
); |
|
37
|
|
|
|
|
|
|
|
|
38
|
63
|
|
|
63
|
|
352
|
sub _local_name { sprintf "t%d", ++$_[0]->{_temporary_count} } |
|
39
|
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
sub new { |
|
41
|
27
|
|
|
27
|
1
|
479
|
my( $class, $args ) = @_; |
|
42
|
27
|
|
|
|
|
176
|
my $self = $class->SUPER::new( $args ); |
|
43
|
|
|
|
|
|
|
|
|
44
|
27
|
|
|
|
|
350
|
$self->_temporary_count( 0 ); |
|
45
|
|
|
|
|
|
|
|
|
46
|
27
|
|
|
|
|
259
|
return $self; |
|
47
|
|
|
|
|
|
|
} |
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
sub _add_bytecode { |
|
50
|
404
|
|
|
404
|
|
1266
|
my( $self, @bytecode ) = @_; |
|
51
|
|
|
|
|
|
|
|
|
52
|
404
|
|
|
|
|
479
|
push @{$self->_current_basic_block->bytecode}, @bytecode; |
|
|
404
|
|
|
|
|
994
|
|
|
53
|
|
|
|
|
|
|
} |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
sub all_to_tree { |
|
56
|
14
|
|
|
14
|
0
|
81
|
my( $self, $code_segments ) = @_; |
|
57
|
14
|
|
|
|
|
55
|
my $all_ssa = $self->all_to_ssa( $code_segments ); |
|
58
|
|
|
|
|
|
|
|
|
59
|
14
|
|
|
|
|
74
|
_ssa_to_tree( $self, $_ ) foreach @$all_ssa; |
|
60
|
|
|
|
|
|
|
|
|
61
|
14
|
|
|
|
|
67
|
return $all_ssa; |
|
62
|
|
|
|
|
|
|
} |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
sub all_to_ssa { |
|
65
|
27
|
|
|
27
|
0
|
101
|
my( $self, $code_segments ) = @_; |
|
66
|
|
|
|
|
|
|
|
|
67
|
27
|
|
|
|
|
135
|
$self->_converted_segments( {} ); |
|
68
|
27
|
|
66
|
|
|
220
|
my @converted = map $self->_converted_segments->{$_} |
|
69
|
|
|
|
|
|
|
|| $self->to_ssa( $_ ), @$code_segments; |
|
70
|
27
|
|
|
|
|
151
|
$self->_converted_segments( {} ); |
|
71
|
|
|
|
|
|
|
|
|
72
|
27
|
|
|
|
|
251
|
return \@converted; |
|
73
|
|
|
|
|
|
|
} |
|
74
|
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
sub to_ssa { |
|
76
|
32
|
|
|
32
|
0
|
347
|
my( $self, $code_segment ) = @_; |
|
77
|
|
|
|
|
|
|
|
|
78
|
32
|
|
|
|
|
100
|
$self->_temporary_count( 0 ); |
|
79
|
32
|
|
|
|
|
247
|
$self->_stack( [] ); |
|
80
|
32
|
|
|
|
|
246
|
$self->_converted( {} ); |
|
81
|
|
|
|
|
|
|
|
|
82
|
32
|
|
|
|
|
259
|
my $new_code = Language::P::Intermediate::Code->new |
|
83
|
|
|
|
|
|
|
( { type => $code_segment->type, |
|
84
|
|
|
|
|
|
|
name => $code_segment->name, |
|
85
|
|
|
|
|
|
|
basic_blocks => [], |
|
86
|
|
|
|
|
|
|
lexicals => $code_segment->lexicals, |
|
87
|
|
|
|
|
|
|
} ); |
|
88
|
32
|
|
|
|
|
149
|
$self->_converted_segments->{$code_segment} = $new_code; |
|
89
|
|
|
|
|
|
|
|
|
90
|
32
|
|
|
|
|
213
|
foreach my $inner ( @{$code_segment->inner} ) { |
|
|
32
|
|
|
|
|
121
|
|
|
91
|
5
|
|
|
|
|
82
|
my $new_inner = $self->to_ssa( $inner ); |
|
92
|
5
|
|
|
|
|
14
|
$new_inner->{outer} = $new_code; |
|
93
|
|
|
|
|
|
|
} |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
# find all non-empty blocks without predecessors and enqueue them |
|
96
|
|
|
|
|
|
|
# (there can be more than one only if there is dead code) |
|
97
|
32
|
|
|
|
|
252
|
$self->_queue( [] ); |
|
98
|
32
|
|
|
|
|
179
|
foreach my $block ( @{$code_segment->basic_blocks} ) { |
|
|
32
|
|
|
|
|
108
|
|
|
99
|
135
|
100
|
|
|
|
655
|
next unless @{$block->bytecode}; |
|
|
135
|
|
|
|
|
377
|
|
|
100
|
129
|
100
|
|
|
|
792
|
next if @{$block->predecessors}; |
|
|
129
|
|
|
|
|
300
|
|
|
101
|
34
|
|
|
|
|
201
|
push @{$self->_queue}, $block; |
|
|
34
|
|
|
|
|
108
|
|
|
102
|
|
|
|
|
|
|
} |
|
103
|
|
|
|
|
|
|
|
|
104
|
32
|
|
|
|
|
245
|
my $stack = $self->_stack; |
|
105
|
32
|
|
|
|
|
146
|
while( @{$self->_queue} ) { |
|
|
193
|
|
|
|
|
1517
|
|
|
106
|
163
|
|
|
|
|
802
|
my $block = shift @{$self->_queue}; |
|
|
163
|
|
|
|
|
450
|
|
|
107
|
|
|
|
|
|
|
|
|
108
|
163
|
100
|
|
|
|
868
|
next if $self->_converted->{$block}{converted}; |
|
109
|
|
|
|
|
|
|
# process a node with input values after all its predecessors |
|
110
|
|
|
|
|
|
|
# might not be possible if more values become temporaries, |
|
111
|
|
|
|
|
|
|
# works for now |
|
112
|
131
|
100
|
100
|
|
|
1060
|
if( ( $self->_converted->{$block}{depth} || 0 ) > 0 |
|
|
60
|
|
100
|
|
|
521
|
|
|
113
|
|
|
|
|
|
|
&& grep !$self->_converted->{$_}{converted}, @{$block->predecessors} ) { |
|
114
|
2
|
|
|
|
|
65
|
push @{$self->_queue}, $block; |
|
|
2
|
|
|
|
|
8
|
|
|
115
|
2
|
|
|
|
|
13
|
redo; |
|
116
|
|
|
|
|
|
|
} |
|
117
|
129
|
|
|
|
|
323
|
$self->_converted->{$block} = |
|
118
|
129
|
|
|
|
|
1609
|
{ %{$self->_converted->{$block}}, |
|
119
|
|
|
|
|
|
|
converted => 1, |
|
120
|
|
|
|
|
|
|
created => 0, |
|
121
|
|
|
|
|
|
|
}; |
|
122
|
129
|
|
|
|
|
1817
|
$self->_converting( $self->_converted->{$block} ); |
|
123
|
129
|
|
66
|
|
|
1300
|
my $cblock = $self->_converting->{block} ||= |
|
124
|
|
|
|
|
|
|
Language::P::Intermediate::BasicBlock |
|
125
|
|
|
|
|
|
|
->new_from_label( $block->start_label ); |
|
126
|
|
|
|
|
|
|
|
|
127
|
129
|
|
|
|
|
576
|
push @{$new_code->basic_blocks}, $cblock; |
|
|
129
|
|
|
|
|
337
|
|
|
128
|
129
|
|
|
|
|
783
|
$self->_current_basic_block( $cblock ); |
|
129
|
129
|
100
|
|
|
|
654
|
@$stack = @{$self->_converting->{in_stack} || []}; |
|
|
129
|
|
|
|
|
298
|
|
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
# remove dummy phi values that all get the same value |
|
132
|
129
|
|
|
|
|
1142
|
foreach my $value ( @$stack ) { |
|
133
|
63
|
100
|
|
|
|
256
|
next unless $value->{opcode_n} == OP_PHI; |
|
134
|
23
|
|
|
|
|
51
|
my $t = $value->{parameters}[1]; |
|
135
|
23
|
100
|
|
|
|
42
|
if( !grep $value->{parameters}[$_] ne $t, |
|
|
23
|
|
|
|
|
175
|
|
|
136
|
|
|
|
|
|
|
grep $_ & 1, |
|
137
|
|
|
|
|
|
|
1 .. $#{$value->{parameters}} ) { |
|
138
|
4
|
|
|
|
|
16
|
$value = opcode_n( OP_GET, $t ); |
|
139
|
|
|
|
|
|
|
} |
|
140
|
|
|
|
|
|
|
} |
|
141
|
|
|
|
|
|
|
|
|
142
|
129
|
|
|
|
|
336
|
foreach my $bc ( @{$block->bytecode} ) { |
|
|
129
|
|
|
|
|
349
|
|
|
143
|
586
|
100
|
|
|
|
2295
|
next if $bc->{label}; |
|
144
|
457
|
|
100
|
|
|
1655
|
my $meth = $op_map{$bc->{opcode_n}} || '_generic'; |
|
145
|
|
|
|
|
|
|
|
|
146
|
457
|
|
|
|
|
1256
|
$self->$meth( $bc ); |
|
147
|
|
|
|
|
|
|
} |
|
148
|
|
|
|
|
|
|
|
|
149
|
129
|
|
33
|
|
|
1614
|
_add_bytecode $self, |
|
150
|
|
|
|
|
|
|
grep $_->{opcode_n} != OP_PHI && $_->{opcode_n} != OP_GET, @$stack; |
|
151
|
|
|
|
|
|
|
} |
|
152
|
|
|
|
|
|
|
|
|
153
|
32
|
|
|
|
|
312
|
return $new_code; |
|
154
|
|
|
|
|
|
|
} |
|
155
|
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
sub to_tree { |
|
157
|
0
|
|
|
0
|
0
|
0
|
my( $self, $code_segment ) = @_; |
|
158
|
0
|
|
|
|
|
0
|
my $ssa = $self->to_ssa( $code_segment ); |
|
159
|
|
|
|
|
|
|
|
|
160
|
0
|
|
|
|
|
0
|
return _ssa_to_tree( $self, $ssa ); |
|
161
|
|
|
|
|
|
|
} |
|
162
|
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
sub _ssa_to_tree { |
|
164
|
15
|
|
|
15
|
|
27
|
my( $self, $ssa ) = @_; |
|
165
|
|
|
|
|
|
|
|
|
166
|
15
|
|
|
|
|
64
|
$self->_temporary_count( 0 ); |
|
167
|
|
|
|
|
|
|
|
|
168
|
15
|
|
|
|
|
82
|
foreach my $block ( @{$ssa->basic_blocks} ) { |
|
|
15
|
|
|
|
|
53
|
|
|
169
|
56
|
|
|
|
|
296
|
my $op_off = 0; |
|
170
|
56
|
|
|
|
|
83
|
while( $op_off <= $#{$block->bytecode} ) { |
|
|
230
|
|
|
|
|
590
|
|
|
171
|
174
|
|
|
|
|
1050
|
my $op = $block->bytecode->[$op_off]; |
|
172
|
174
|
|
|
|
|
664
|
++$op_off; |
|
173
|
174
|
100
|
100
|
|
|
926
|
next if $op->{label} |
|
|
|
|
100
|
|
|
|
|
|
174
|
|
|
|
|
|
|
|| $op->{opcode_n} != OP_SET |
|
175
|
|
|
|
|
|
|
|| $op->{parameters}[1]->{opcode_n} != OP_PHI; |
|
176
|
|
|
|
|
|
|
|
|
177
|
8
|
|
|
|
|
15
|
my %block_variable = @{$op->{parameters}[1]->{parameters}}; |
|
|
8
|
|
|
|
|
58
|
|
|
178
|
|
|
|
|
|
|
|
|
179
|
8
|
|
|
|
|
34
|
while( my( $label, $variable ) = each %block_variable ) { |
|
180
|
17
|
|
|
|
|
250
|
my( $block_from ) = grep $_ eq $label, |
|
181
|
17
|
|
|
|
|
176
|
@{$ssa->basic_blocks}; |
|
182
|
17
|
|
|
|
|
162
|
my $op_from_off = $#{$block_from->bytecode}; |
|
|
17
|
|
|
|
|
42
|
|
|
183
|
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
# find the jump coming to this block |
|
185
|
17
|
|
|
|
|
99
|
while( $op_from_off >= 0 ) { |
|
186
|
17
|
|
|
|
|
44
|
my $op_from = $block_from->bytecode->[$op_from_off]; |
|
187
|
17
|
|
|
|
|
145
|
last if $op_from->{parameters} |
|
188
|
17
|
50
|
33
|
|
|
91
|
&& @{$op_from->{parameters}} |
|
|
|
|
33
|
|
|
|
|
|
189
|
|
|
|
|
|
|
&& $op_from->{parameters}[-1] eq $block; |
|
190
|
0
|
|
|
|
|
0
|
--$op_from_off; |
|
191
|
|
|
|
|
|
|
} |
|
192
|
|
|
|
|
|
|
|
|
193
|
17
|
50
|
|
|
|
47
|
die "Can't find jump: ", $block_from->start_label, |
|
194
|
|
|
|
|
|
|
" => ", $block->start_label |
|
195
|
|
|
|
|
|
|
if $op_from_off < 0; |
|
196
|
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
# add SET nodes to rename the variables |
|
198
|
17
|
|
|
|
|
27
|
splice @{$block_from->bytecode}, $op_from_off, 0, |
|
|
17
|
|
|
|
|
51
|
|
|
199
|
|
|
|
|
|
|
opcode_n( OP_SET, $op->{parameters}[0], |
|
200
|
|
|
|
|
|
|
opcode_n( OP_GET, $variable ) ); |
|
201
|
|
|
|
|
|
|
} |
|
202
|
|
|
|
|
|
|
|
|
203
|
8
|
|
|
|
|
138
|
--$op_off; |
|
204
|
8
|
|
|
|
|
14
|
splice @{$block->bytecode}, $op_off, 1; |
|
|
8
|
|
|
|
|
23
|
|
|
205
|
|
|
|
|
|
|
} |
|
206
|
|
|
|
|
|
|
} |
|
207
|
|
|
|
|
|
|
|
|
208
|
15
|
|
|
|
|
124
|
return $ssa; |
|
209
|
|
|
|
|
|
|
} |
|
210
|
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
sub _get_stack { |
|
212
|
141
|
|
|
141
|
|
309
|
my( $self, $count, $force_get ) = @_; |
|
213
|
141
|
100
|
|
|
|
310
|
return unless $count; |
|
214
|
139
|
|
|
|
|
179
|
my @values = splice @{$self->_stack}, -$count; |
|
|
139
|
|
|
|
|
356
|
|
|
215
|
139
|
|
|
|
|
998
|
_created( $self, -$count ); |
|
216
|
|
|
|
|
|
|
|
|
217
|
139
|
|
|
|
|
908
|
foreach my $value ( @values ) { |
|
218
|
196
|
100
|
100
|
|
|
986
|
next if $value->{opcode_n} != OP_PHI && !$force_get; |
|
219
|
33
|
|
|
|
|
79
|
my $name = _local_name( $self ); |
|
220
|
33
|
|
|
|
|
115
|
_add_bytecode $self, opcode_n( OP_SET, $name, $value ); |
|
221
|
33
|
|
|
|
|
341
|
$value = opcode_n( OP_GET, $name ); |
|
222
|
|
|
|
|
|
|
} |
|
223
|
|
|
|
|
|
|
|
|
224
|
139
|
|
|
|
|
839
|
return @values; |
|
225
|
|
|
|
|
|
|
} |
|
226
|
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
sub _jump_to { |
|
228
|
127
|
|
|
127
|
|
212
|
my( $self, $op, $to, $out_names ) = @_; |
|
229
|
|
|
|
|
|
|
|
|
230
|
127
|
|
|
|
|
364
|
my $stack = $self->_stack; |
|
231
|
127
|
|
|
|
|
721
|
my $converted_blocks = $self->_converted; |
|
232
|
127
|
|
100
|
|
|
1170
|
my $converted = $converted_blocks->{$to} ||= {}; |
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
# check that input stack height is the same on all in branches |
|
235
|
127
|
100
|
|
|
|
373
|
if( defined $converted->{depth} ) { |
|
236
|
32
|
50
|
|
|
|
136
|
die sprintf "Inconsistent depth %d != %d in %s => %s", |
|
237
|
|
|
|
|
|
|
$converted->{depth}, scalar @$stack, |
|
238
|
|
|
|
|
|
|
$self->_current_basic_block->start_label, $to->start_label |
|
239
|
|
|
|
|
|
|
if $converted->{depth} != scalar @$stack; |
|
240
|
|
|
|
|
|
|
} |
|
241
|
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
# emit as OP_SET all stack elements created in the basic block |
|
243
|
|
|
|
|
|
|
# and construct the input stack of the next basic block |
|
244
|
127
|
100
|
|
|
|
376
|
if( @$stack ) { |
|
245
|
80
|
100
|
|
|
|
251
|
@$out_names = _emit_out_stack( $self ) unless @$out_names; |
|
246
|
|
|
|
|
|
|
|
|
247
|
80
|
|
|
|
|
216
|
my $created_elements = $self->_converting->{created}; |
|
248
|
80
|
|
|
|
|
398
|
my $inherited_elements = @$stack - $created_elements; |
|
249
|
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
# copy inherited elements, generated GET or PHI for created elements |
|
251
|
80
|
100
|
|
|
|
196
|
if( !$converted->{in_stack} ) { |
|
252
|
58
|
100
|
|
|
|
83
|
if( @{$to->predecessors} > 1 ) { |
|
|
58
|
|
|
|
|
159
|
|
|
253
|
20
|
|
|
|
|
164
|
$converted->{in_stack} = [ map opcode_n( OP_PHI ), @$stack ]; |
|
254
|
|
|
|
|
|
|
} else { |
|
255
|
38
|
|
|
|
|
274
|
$converted->{in_stack} = [ map opcode_n( OP_GET, $_ ), |
|
256
|
|
|
|
|
|
|
@$out_names ]; |
|
257
|
|
|
|
|
|
|
} |
|
258
|
|
|
|
|
|
|
} |
|
259
|
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
# update PHI nodes with the (block, value) pair |
|
261
|
80
|
100
|
|
|
|
827
|
if( @{$to->predecessors} > 1 ) { |
|
|
80
|
|
|
|
|
212
|
|
|
262
|
42
|
|
|
|
|
227
|
my $i = 0; |
|
263
|
42
|
|
|
|
|
91
|
foreach my $out ( @$out_names ) { |
|
264
|
48
|
50
|
|
|
|
148
|
die "Node with multiple predecessors has no phi ($i)" |
|
265
|
|
|
|
|
|
|
unless $converted->{in_stack}[$i]->{opcode_n} == OP_PHI; |
|
266
|
48
|
|
|
|
|
67
|
push @{$converted->{in_stack}[$i]{parameters}}, |
|
|
48
|
|
|
|
|
213
|
|
|
267
|
|
|
|
|
|
|
$self->_current_basic_block, $out; |
|
268
|
48
|
|
|
|
|
427
|
++$i; |
|
269
|
|
|
|
|
|
|
} |
|
270
|
|
|
|
|
|
|
} |
|
271
|
|
|
|
|
|
|
} |
|
272
|
|
|
|
|
|
|
|
|
273
|
127
|
|
|
|
|
478
|
$converted->{depth} = @$stack; |
|
274
|
127
|
|
66
|
|
|
530
|
$converted->{block} ||= Language::P::Intermediate::BasicBlock |
|
275
|
|
|
|
|
|
|
->new_from_label( $to->start_label ); |
|
276
|
127
|
|
|
|
|
280
|
push @{$op->{parameters}}, $converted->{block}; |
|
|
127
|
|
|
|
|
352
|
|
|
277
|
127
|
|
|
|
|
170
|
push @{$self->_queue}, $to; |
|
|
127
|
|
|
|
|
357
|
|
|
278
|
|
|
|
|
|
|
|
|
279
|
127
|
|
|
|
|
663
|
return $out_names; |
|
280
|
|
|
|
|
|
|
} |
|
281
|
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
sub _emit_out_stack { |
|
283
|
162
|
|
|
162
|
|
214
|
my( $self ) = @_; |
|
284
|
162
|
|
|
|
|
410
|
my $stack = $self->_stack; |
|
285
|
162
|
100
|
|
|
|
899
|
return unless @$stack; |
|
286
|
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
# add named targets for all trees in stack, emit |
|
288
|
|
|
|
|
|
|
# them and replace stack with the targets |
|
289
|
64
|
|
|
|
|
78
|
my( @out_names, @out_stack ); |
|
290
|
64
|
|
|
|
|
183
|
my $i = @$stack - $self->_converting->{created}; |
|
291
|
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
# copy inherited stack elements and all created GET opcodes add a |
|
293
|
|
|
|
|
|
|
# SET in the block and a GET in the out stack for all other |
|
294
|
|
|
|
|
|
|
# created ops |
|
295
|
64
|
|
|
|
|
392
|
@out_stack = @{$stack}[0 .. $i - 1]; |
|
|
64
|
|
|
|
|
166
|
|
|
296
|
64
|
|
|
|
|
167
|
@out_names = map $_->{parameters}[0], @out_stack; |
|
297
|
64
|
|
|
|
|
208
|
for( my $j = $i; $i < @$stack; ++$i, ++$j ) { |
|
298
|
48
|
|
|
|
|
90
|
my $op = $stack->[$i]; |
|
299
|
48
|
100
|
|
|
|
135
|
if( $op->{opcode_n} == OP_GET ) { |
|
300
|
18
|
|
|
|
|
54
|
$out_names[$j] = $op->{parameters}[0]; |
|
301
|
18
|
|
|
|
|
65
|
$out_stack[$i] = $op; |
|
302
|
|
|
|
|
|
|
} else { |
|
303
|
30
|
|
|
|
|
69
|
$out_names[$j] = _local_name( $self ); |
|
304
|
30
|
|
|
|
|
94
|
$out_stack[$i] = opcode_n( OP_GET, $out_names[$j] ); |
|
305
|
30
|
|
|
|
|
414
|
_add_bytecode $self, opcode_n( OP_SET, $out_names[$j], $op ); |
|
306
|
|
|
|
|
|
|
} |
|
307
|
|
|
|
|
|
|
} |
|
308
|
64
|
|
|
|
|
394
|
@$stack = @out_stack; |
|
309
|
|
|
|
|
|
|
|
|
310
|
64
|
|
|
|
|
223
|
return @out_names; |
|
311
|
|
|
|
|
|
|
} |
|
312
|
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
sub _generic { |
|
314
|
244
|
|
|
244
|
|
331
|
my( $self, $op ) = @_; |
|
315
|
244
|
|
|
|
|
610
|
my $attrs = $OP_ATTRIBUTES{$op->{opcode_n}}; |
|
316
|
244
|
100
|
|
|
|
808
|
my @in = $attrs->{in_args} ? _get_stack( $self, $attrs->{in_args} ) : (); |
|
317
|
244
|
|
|
|
|
280
|
my $new_op; |
|
318
|
|
|
|
|
|
|
|
|
319
|
244
|
100
|
|
|
|
642
|
if( $op->{attributes} ) { |
|
|
|
100
|
|
|
|
|
|
|
320
|
110
|
|
|
|
|
182
|
$new_op = opcode_nm( $op->{opcode_n}, %{$op->{attributes}} ); |
|
|
110
|
|
|
|
|
478
|
|
|
321
|
110
|
100
|
|
|
|
1606
|
$new_op->{parameters} = \@in if @in; |
|
322
|
|
|
|
|
|
|
} elsif( $op->{parameters} ) { |
|
323
|
43
|
50
|
|
|
|
115
|
die "Can't handle fixed and dynamic parameters" if @in; |
|
324
|
43
|
|
|
|
|
104
|
$new_op = opcode_n( $op->{opcode_n}, @{$op->{parameters}} ); |
|
|
43
|
|
|
|
|
143
|
|
|
325
|
|
|
|
|
|
|
} else { |
|
326
|
91
|
|
|
|
|
272
|
$new_op = opcode_n( $op->{opcode_n}, @in ); |
|
327
|
|
|
|
|
|
|
} |
|
328
|
|
|
|
|
|
|
|
|
329
|
244
|
100
|
|
|
|
2159
|
if( !$attrs->{out_args} ) { |
|
|
|
50
|
|
|
|
|
|
|
330
|
49
|
|
|
|
|
122
|
_emit_out_stack( $self ); |
|
331
|
49
|
|
|
|
|
151
|
_add_bytecode $self, $new_op; |
|
332
|
|
|
|
|
|
|
} elsif( $attrs->{out_args} == 1 ) { |
|
333
|
195
|
|
|
|
|
230
|
push @{$self->_stack}, $new_op; |
|
|
195
|
|
|
|
|
532
|
|
|
334
|
195
|
|
|
|
|
1025
|
_created( $self, 1 ); |
|
335
|
|
|
|
|
|
|
} else { |
|
336
|
0
|
|
|
|
|
0
|
die "Unhandled out_args value: ", $attrs->{out_args}; |
|
337
|
|
|
|
|
|
|
} |
|
338
|
|
|
|
|
|
|
} |
|
339
|
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
sub _const_sub { |
|
341
|
2
|
|
|
2
|
|
3
|
my( $self, $op ) = @_; |
|
342
|
2
|
|
|
|
|
8
|
my $new_seg = $self->_converted_segments->{$op->{parameters}[0]}; |
|
343
|
2
|
|
|
|
|
16
|
my $new_op = opcode_n( OP_CONSTANT_SUB(), $new_seg ); |
|
344
|
|
|
|
|
|
|
|
|
345
|
2
|
|
|
|
|
23
|
push @{$self->_stack}, $new_op; |
|
|
2
|
|
|
|
|
6
|
|
|
346
|
2
|
|
|
|
|
11
|
_created( $self, 1 ); |
|
347
|
|
|
|
|
|
|
} |
|
348
|
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
sub _pop { |
|
350
|
52
|
|
|
52
|
|
121
|
my( $self, $op ) = @_; |
|
351
|
|
|
|
|
|
|
|
|
352
|
52
|
50
|
|
|
|
75
|
die 'Empty stack in pop' unless @{$self->_stack} >= 1; |
|
|
52
|
|
|
|
|
141
|
|
|
353
|
52
|
|
|
|
|
313
|
my $top = pop @{$self->_stack}; |
|
|
52
|
|
|
|
|
157
|
|
|
354
|
52
|
100
|
66
|
|
|
509
|
_add_bytecode $self, $top if $top->{opcode_n} != OP_PHI |
|
355
|
|
|
|
|
|
|
&& $top->{opcode_n} != OP_GET; |
|
356
|
52
|
|
|
|
|
346
|
_emit_out_stack( $self ); |
|
357
|
52
|
|
|
|
|
145
|
_created( $self, -1 ); |
|
358
|
|
|
|
|
|
|
} |
|
359
|
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
sub _dup { |
|
361
|
18
|
|
|
18
|
|
28
|
my( $self, $op ) = @_; |
|
362
|
|
|
|
|
|
|
|
|
363
|
18
|
50
|
|
|
|
31
|
die 'Empty stack in dup' unless @{$self->_stack} >= 1; |
|
|
18
|
|
|
|
|
52
|
|
|
364
|
18
|
|
|
|
|
143
|
my( $v ) = _get_stack( $self, 1, 1 ); |
|
365
|
18
|
|
|
|
|
28
|
push @{$self->_stack}, $v, $v; |
|
|
18
|
|
|
|
|
43
|
|
|
366
|
18
|
|
|
|
|
100
|
_created( $self, 2 ); |
|
367
|
|
|
|
|
|
|
} |
|
368
|
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
sub _swap { |
|
370
|
24
|
|
|
24
|
|
55
|
my( $self, $op ) = @_; |
|
371
|
24
|
|
|
|
|
79
|
my $stack = $self->_stack; |
|
372
|
24
|
|
|
|
|
114
|
my $t = $stack->[-1]; |
|
373
|
|
|
|
|
|
|
|
|
374
|
24
|
50
|
|
|
|
33
|
die 'Empty stack in swap' unless @{$self->_stack} >= 2; |
|
|
24
|
|
|
|
|
68
|
|
|
375
|
24
|
|
|
|
|
172
|
$stack->[-1] = $stack->[-2]; |
|
376
|
24
|
|
|
|
|
64
|
$stack->[-2] = $t; |
|
377
|
|
|
|
|
|
|
} |
|
378
|
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
sub _make_list { |
|
380
|
20
|
|
|
20
|
|
36
|
my( $self, $op ) = @_; |
|
381
|
|
|
|
|
|
|
|
|
382
|
20
|
|
|
|
|
28
|
push @{$self->_stack}, |
|
|
20
|
|
|
|
|
70
|
|
|
383
|
|
|
|
|
|
|
opcode_n( OP_MAKE_LIST, _get_stack( $self, $op->{attributes}{count} ) ); |
|
384
|
20
|
|
|
|
|
261
|
_created( $self, 1 ); |
|
385
|
|
|
|
|
|
|
} |
|
386
|
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
sub _cond_jump { |
|
388
|
30
|
|
|
30
|
|
75
|
my( $self, $op ) = @_; |
|
389
|
30
|
|
|
|
|
89
|
my $attrs = $OP_ATTRIBUTES{$op->{opcode_n}}; |
|
390
|
30
|
|
|
|
|
109
|
my @in = _get_stack( $self, $attrs->{in_args} ); |
|
391
|
30
|
|
|
|
|
123
|
my $new_cond = opcode_n( $op->{opcode_n}, @in ); |
|
392
|
30
|
|
|
|
|
424
|
my $new_jump = opcode_n( OP_JUMP ); |
|
393
|
|
|
|
|
|
|
|
|
394
|
30
|
|
|
|
|
342
|
my @out_names; |
|
395
|
30
|
|
|
|
|
117
|
_jump_to( $self, $new_cond, $op->{attributes}{true}, \@out_names ); |
|
396
|
30
|
|
|
|
|
78
|
_add_bytecode $self, $new_cond; |
|
397
|
30
|
|
|
|
|
297
|
_jump_to( $self,$new_jump, $op->{attributes}{false}, \@out_names ); |
|
398
|
30
|
|
|
|
|
81
|
_add_bytecode $self, $new_jump; |
|
399
|
|
|
|
|
|
|
} |
|
400
|
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
sub _jump { |
|
402
|
67
|
|
|
67
|
|
102
|
my( $self, $op ) = @_; |
|
403
|
67
|
|
|
|
|
254
|
my $new_jump = opcode_nm( $op->{opcode_n} ); |
|
404
|
|
|
|
|
|
|
|
|
405
|
67
|
|
|
|
|
987
|
_jump_to( $self, $new_jump, $op->{attributes}{to}, [] ); |
|
406
|
67
|
|
|
|
|
161
|
_add_bytecode $self, $new_jump; |
|
407
|
|
|
|
|
|
|
} |
|
408
|
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
sub _created { |
|
410
|
426
|
|
|
426
|
|
626
|
my( $self, $count ) = @_; |
|
411
|
|
|
|
|
|
|
|
|
412
|
426
|
|
|
|
|
1050
|
$self->_converting->{created} += $count; |
|
413
|
426
|
100
|
100
|
|
|
2973
|
if( $count < 0 && $self->_converting->{created} < 0 ) { |
|
414
|
38
|
|
|
|
|
287
|
$self->_converting->{created} = 0; |
|
415
|
|
|
|
|
|
|
} |
|
416
|
|
|
|
|
|
|
} |
|
417
|
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
1; |