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; |