~ubuntu-branches/ubuntu/trusty/clc-intercal/trusty-proposed

« back to all changes in this revision

Viewing changes to Language/INTERCAL/Optimiser.pm

  • Committer: Bazaar Package Importer
  • Author(s): Mark Brown
  • Date: 2006-10-08 13:30:54 UTC
  • mfrom: (1.1.1 upstream) (3.1.1 dapper)
  • Revision ID: james.westby@ubuntu.com-20061008133054-fto70u71yoyltr3m
Tags: 1:1.0~2pre1.-94.-4.1-1
* New upstream release.
* Change to dh_installman.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
package Language::INTERCAL::Optimiser;
2
 
 
3
 
# Oprimiser for CLC-INTERCAL
4
 
 
5
 
# This file is part of CLC-INTERCAL.
6
 
 
7
 
# Copyright (C) 1999 Claudio Calvelli <lunatic@assurdo.com>, all rights reserved
8
 
 
9
 
# WARNING - do not operate heavy machinery while using CLC-INTERCAL
10
 
 
11
 
# This program is free software; you can redistribute it and/or modify
12
 
# it under the terms of the GNU General Public License as published by
13
 
# the Free Software Foundation; either 2 of the License, or
14
 
# (at your option) any later version.
15
 
 
16
 
# This program is distributed in the hope that it will be useful,
17
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
 
# GNU General Public License for more details.
20
 
 
21
 
# You should have received a copy of the GNU General Public License
22
 
# along with this program; if not, write to the Free Software
23
 
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24
 
 
25
 
use vars qw($VERSION);
26
 
$VERSION = '0.05';
27
 
 
28
 
use Language::INTERCAL::Opcodes;
29
 
use Language::INTERCAL::Runtime::Library;
30
 
 
31
 
sub _is_plainreg {
32
 
    $_[0][0] == E_REGISTER;
33
 
}
34
 
 
35
 
sub _is_const {
36
 
    $_[0][0] == E_CONSTANT;
37
 
}
38
 
 
39
 
sub first {
40
 
    my ($ptree, $filename, $line) = @_;
41
 
    # I've temporarily moved everything to the second optimiser until I
42
 
    # convince myself of what is safe to run before seeing the whole program
43
 
    $ptree;
44
 
}
45
 
 
46
 
sub second {
47
 
    my ($ptree, $filename, $line) = @_;
48
 
    my $quantum = exists $ptree->{'flags'}{'quantum'};
49
 
    my $ignore = exists $ptree->{'flags'}{'ignore'};
50
 
    my $postprocess = exists $ptree->{'flags'}{'postprocess'};
51
 
    my $overload = exists $ptree->{'flags'}{'overload'};
52
 
    my $qip = $quantum || $postprocess || $ignore;
53
 
    my $abstain = $ptree->{'abstain'};
54
 
    my $reinstate = $ptree->{'reinstate'};
55
 
    my $gabstain = $ptree->{'gabstain'};
56
 
    my $greinstate = $ptree->{'greinstate'};
57
 
    my $labels = $ptree->{'labels'};
58
 
    my $abstain_calculate = 0;
59
 
 
60
 
    $ptree->iterate(sub {
61
 
        my ($p, $fid, $bid, $sid, $stmt) = @_;
62
 
        if ($stmt->[3][0] == S_GABSTAIN || $stmt->[3][0] == S_REINSTATE) {
63
 
            $abstain_calculate = 1 if grep {$_ == S_ASSIGN} @{$stmt->[3]};
64
 
        }
65
 
    });
66
 
 
67
 
    my %regcon = ();
68
 
    my $code;
69
 
    $code = sub {
70
 
        my ($p, $fid, $bid, $sid, $stmt) = @_;
71
 
        my $bc = $stmt->[3][0];
72
 
        if ($bc == S_ABSTAIN ||
73
 
            $bc == S_COME ||
74
 
            $bc == S_REINSTATE ||
75
 
            $bc == S_FORGET ||
76
 
            $bc == S_NEXT ||
77
 
            $bc == S_RESUME)
78
 
        {
79
 
            do_expression($stmt->[3], 1, \%regcon, $quantum);
80
 
        }
81
 
        if ($bc == S_WHILE_E) {
82
 
            do_expression($stmt->[3], 2, \%regcon, $quantum);
83
 
        }
84
 
        if ($bc == S_WHILE_BC || $bc == S_WHILE_CB) {
85
 
            %regcon = ();
86
 
            &$code($p, -1, 0, 0, [1, 100, 0, $stmt->[3][1]]);
87
 
            %regcon = ();
88
 
            &$code($p, -1, 0, 0, [1, 100, 0, $stmt->[3][2]]);
89
 
            %regcon = ();
90
 
        }
91
 
        if ($bc == S_WRITE) {
92
 
            my $rname = $stmt->[3][1][1] . $stmt->[3][1][2];
93
 
            delete $regcon{$rname};
94
 
        }
95
 
        if ($bc == S_READ) {
96
 
            my $rname = $stmt->[3][1][1] . $stmt->[3][1][2];
97
 
            $regcon{$rname}[3] = 0 if exists $regcon{$rname};
98
 
        }
99
 
        if ($bc == S_ASSIGN) {
100
 
            my $i;
101
 
            my $s = $stmt->[3];
102
 
            for $i (2..$#{$s}) {
103
 
                do_expression($s, $i, \%regcon, $quantum);
104
 
            }
105
 
            # the next condition errs on the side of correctness, it
106
 
            # would be OK to say "$s->[1] cannot be overloaded" instead
107
 
            # of "the program does not contain overloading". Same for
108
 
            # ignore. Perhaps building a list of registers which appear
109
 
            # in overload/ignore... make a note for another release.
110
 
            # we also need to check for ABSTAIN FROM CALCULATING
111
 
            # (ABSTAIN FROM (label) does not matter because we never join
112
 
            # together statements if one has a label)
113
 
            if (! $qip && ! $overload && _is_plainreg($s->[1])) {
114
 
                my $rname = $s->[1][1] . $s->[1][2];
115
 
                if (ref $stmt->[2] || $stmt->[2] < 100 || $abstain_calculate) {
116
 
                    delete $regcon{$rname};
117
 
                } else {
118
 
                    if (exists $regcon{$rname} && $regcon{$rname}[3]) {
119
 
                        _remove($ptree, $regcon{$rname}[1], $regcon{$rname}[2]);
120
 
                    }
121
 
                    if (@$s == 3 && _is_const($s->[2])) {
122
 
                        my $v = $s->[2][1];
123
 
                        $v = $s->[1][1] eq ':' ? ato32($v) : ato16($v);
124
 
                        $regcon{$rname} = [$v, $fid, $stmt, 1];
125
 
                    } else {
126
 
                        delete $regcon{$rname};
127
 
                    }
128
 
                }
129
 
            }
130
 
        }
131
 
        if ($bc == S_RETRIEVE || $bc == S_STASH) {
132
 
            my $i;
133
 
            my $s = $stmt->[3];
134
 
            for $i (1..$#{$s}) {
135
 
                if (_is_plainreg($s->[$i])) {
136
 
                    # this errs on the side of correctness - we could
137
 
                    # easily build a stack within $regcon{$rname} and
138
 
                    # push it at stash, pop at retrieve. Maybe in a
139
 
                    # future version.
140
 
                    my $rname = $s->[$i][1] . $s->[$i][2];
141
 
                    delete $regcon{$rname};
142
 
                }
143
 
            }
144
 
        }
145
 
        if ($bc == S_LEARN) {
146
 
            do_expression($stmt->[3], 2, \%regcon, $quantum);
147
 
        }
148
 
        if ($bc == S_STUDY) {
149
 
            do_expression($stmt->[3], 1, \%regcon, $quantum);
150
 
            do_expression($stmt->[3], 2, \%regcon, $quantum);
151
 
            do_expression($stmt->[3], 3, \%regcon, $quantum);
152
 
        }
153
 
        if ($bc == S_ENROL) {
154
 
            my $i;
155
 
            for $i (2..$#{$stmt->[3]}) {
156
 
                do_expression($stmt->[3], $i, \%regcon, $quantum);
157
 
            }
158
 
        }
159
 
    };
160
 
    $ptree->iterate($code, sub { %regcon = (); });
161
 
 
162
 
    my $stop = 0;
163
 
    my $first = 1;
164
 
    $ptree->iterate(sub {
165
 
        my ($p, $fid, $bid, $sid, $stmt) = @_;
166
 
        my $bc = $stmt->[3][0];
167
 
 
168
 
        # anything following a stop can be removed
169
 
        _remove($ptree, $fid, $stmt) if $stop;
170
 
        $stop = 1 if $stmt->[1] &&
171
 
                     (! ref $stmt->[2]) && $stmt->[2] == 100 &&
172
 
                     $bc == S_STOP &&
173
 
                     ! $postprocess;
174
 
 
175
 
        # if we are in the first block, we can execute any ABSTAINs etc
176
 
        if ($first && $bid == 1) {
177
 
            if (($bc == S_ABSTAIN || $bc == S_REINSTATE) &&
178
 
                (! ref $stmt->[2]) && $stmt->[2] == 100 &&
179
 
                $stmt->[1] &&
180
 
                _is_const($stmt->[3][1]) && exists $labels->{$stmt->[3][1][1]})
181
 
            {
182
 
                my $l = $labels->{$stmt->[3][1][1]};
183
 
                $ptree->{'files'}[$l->[0]][$l->[1]][$l->[2]][1] =
184
 
                    $bc == S_ABSTAIN ? 0 : 1;
185
 
                _remove($ptree, $fid, $stmt);
186
 
            }
187
 
 
188
 
            if (($bc == S_GABSTAIN || $bc == S_GREINSTATE) &&
189
 
                (! ref $stmt->[2]) && $stmt->[2] == 100 &&
190
 
                $stmt->[1])
191
 
            {
192
 
                my $ab = $bc == S_GABSTAIN ? 0 : 1;
193
 
                my @s = @{$stmt->[3]};
194
 
                shift @s;
195
 
                $ptree->iterate(sub {
196
 
                    my ($p1, $fid1, $bid1, $sid1, $stmt1) = @_;
197
 
                    if (grep {$_ == $stmt1->[3][0]} @s) {
198
 
                        $stmt1->[1] = $ab;
199
 
                    }
200
 
                });
201
 
                _remove($ptree, $fid, $stmt);
202
 
            }
203
 
 
204
 
            if ($bc == S_CONVERT || $bc == S_SWAP) {
205
 
                if ((! ref $stmt->[2]) && $stmt->[2] == 100 && $stmt->[1]) {
206
 
                    my $code1 = $stmt->[3][1][0];
207
 
                    my $code2 = $stmt->[3][2][0];
208
 
 
209
 
                    if ($code1 != $code2) {
210
 
                        $ptree->iterate(sub {
211
 
                            my ($p1, $fid1, $bid1, $sid1, $stmt1) = @_;
212
 
                            if ($fid1 > $fid ||
213
 
                                ($fid1 == $fid && $bid1 > $bid) ||
214
 
                                ($fid1 == $fid &&
215
 
                                 $bid1 == $bid &&
216
 
                                 $sid1 > $sid))
217
 
                            {
218
 
                                my $code = $stmt1->[3][0];
219
 
                                if ($code == S_SWAP || $code == S_CONVERT) {
220
 
                                    for (my $i = 1; $i < @{$stmt1->[3]}; $i++) {
221
 
                                        my $c = $stmt1->[3][$i][0];
222
 
                                        if ($c == $code1) {
223
 
                                            $stmt1->[3][$i][0] = $code2;
224
 
                                        }
225
 
                                        if ($c == $code2 && $bc == S_SWAP) {
226
 
                                            $stmt1->[3][$i][0] = $code1;
227
 
                                        }
228
 
                                    }
229
 
                                }
230
 
                                if ($code == $code1) {
231
 
                                    _convert($ptree, $fid1, $bid1, $sid1,
232
 
                                             $stmt1, $code2);
233
 
                                }
234
 
                                if ($code == $code2 && $bc == S_SWAP) {
235
 
                                    _convert($ptree, $fid1, $bid1, $sid1,
236
 
                                             $stmt1, $code1);
237
 
                                }
238
 
                            }
239
 
                        });
240
 
                    }
241
 
                    _remove($ptree, $fid, $stmt);
242
 
                } else {
243
 
                    $first = 0;
244
 
                }
245
 
            }
246
 
        }
247
 
 
248
 
        # a statement initially abstained from -- if there is no
249
 
        # way to reinstate it, away it goes...
250
 
        if (! $stmt->[1] && ($bc == S_STOP || ! $quantum && ! $postprocess)) {
251
 
            my $ok = 1;
252
 
            if ($bc != S_STOP) {
253
 
                if ($stmt->[0]) {
254
 
                    my $s;
255
 
                    for $s (@$reinstate) {
256
 
                        if (_is_const($s->[0])) {
257
 
                            $ok = 0 if $stmt->[0] == atoi($s->[0][1]);
258
 
                        } else {
259
 
                            $ok = 0;
260
 
                        }
261
 
                    }
262
 
                }
263
 
                if ($ok) {
264
 
                    my $s;
265
 
                    for $s (@$greinstate) {
266
 
                        my @s = @{$s->[0]};
267
 
                        shift @s;
268
 
                        $ok = 0 if grep {$_ == $bc} @s;
269
 
                    }
270
 
                }
271
 
            }
272
 
            _remove($ptree, $fid, $stmt) if $ok;
273
 
        }
274
 
    }, sub {
275
 
        $stop = 0;
276
 
    }, sub {
277
 
        my ($p, $fid, $bid, $sid, $stmt) = @_;
278
 
        $first = $first && $stop if $bid == 1;
279
 
    });
280
 
    $ptree;
281
 
}
282
 
 
283
 
sub do_expression {
284
 
    my ($stmt, $place, $regcon, $quantum) = @_;
285
 
    my $e = $stmt->[$place];
286
 
    my $ec = $e->[0];
287
 
    if ($ec == E_AND || $ec == E_OR || $ec == E_XOR) {
288
 
        do_expression($e, 1, $regcon, $quantum);
289
 
        if (_is_const($e->[1])) {
290
 
            my $val = $e->[1][1];
291
 
            $val = i_and($val) if $ec == E_AND;
292
 
            $val = i_or($val) if $ec == E_OR;
293
 
            $val = i_xor($val) if $ec == E_XOR;
294
 
            $stmt->[$place] = [E_CONSTANT, $val];
295
 
        }
296
 
        return;
297
 
    }
298
 
    if ($ec == E_INTERLEAVE || $ec == E_SELECT || $ec == E_OVERLOAD_RANGE) {
299
 
        do_expression($e, 1, $regcon, $quantum);
300
 
        do_expression($e, 2, $regcon, $quantum);
301
 
        if (_is_const($e->[1]) && _is_const($e->[2])) {
302
 
            my $val;
303
 
            if ($ec == E_INTERLEAVE) {
304
 
                $val = i_interleave($e->[1][1], $e->[2][1]);
305
 
            } elsif ($ec == E_SELECT) {
306
 
                $val = i_select($e->[1][1], $e->[2][1]);
307
 
            }
308
 
            $stmt->[$place] = [E_CONSTANT, $val];
309
 
        } elsif ($ec == E_SELECT &&
310
 
                 ($e->[1][0] == E_AND ||
311
 
                  $e->[1][0] == E_OR ||
312
 
                  $e->[1][0] == E_XOR) &&
313
 
                 $e->[1][1][0] == E_INTERLEAVE &&
314
 
                 $e->[2][0] == E_CONSTANT &&
315
 
                 atoi($e->[2][1]) == 0x55555555)
316
 
        {
317
 
            # This is a "binary" logical operator in disguise
318
 
            my $op = $e->[1][0] == E_AND ? E_BAND
319
 
                                         : $e->[1][0] == E_OR ? E_BOR
320
 
                                                              : E_BXOR;
321
 
            $stmt->[$place] = [$op, $e->[1][1][1], $e->[1][1][2]];
322
 
        }
323
 
        return;
324
 
    }
325
 
    if ($ec == E_SUBSCRIPT) {
326
 
        my $i;
327
 
        for $i (2..$#$e) {
328
 
            do_expression($e, $i, $regcon, $quantum);
329
 
        }
330
 
        return;
331
 
    }
332
 
    if ($ec == E_OVERLOAD_REGISTER) {
333
 
        do_expression($e, 2, {}, $quantum);
334
 
        return;
335
 
    }
336
 
#    if ($ec == E_OWNER) {
337
 
#       do_expression($e, 2, $regcon, $quantum);
338
 
#       return;
339
 
#    }
340
 
    if ($ec == E_REGISTER && exists $regcon->{$e->[1] . $e->[2]}) {
341
 
        $stmt->[$place] = [E_CONSTANT, $regcon->{$e->[1] . $e->[2]}[0]];
342
 
    }
343
 
}
344
 
 
345
 
sub _remove {
346
 
    my ($ptree, $fid, $stmt) = @_;
347
 
    my $code = $stmt->[3][0];
348
 
    $stmt->[3][0] = S_NOP;
349
 
    my $id = '';
350
 
    $id = 'come_froms' if $code == S_COME;
351
 
    $id = 'abstain' if $code == S_ABSTAIN;
352
 
    $id = 'gabstain' if $code == S_GABSTAIN;
353
 
    $id = 'reinstate' if $code == S_REINSTATE;
354
 
    $id = 'greinstate' if $code == S_GREINSTATE;
355
 
    return $ptree if $id eq '';
356
 
    my $cid;
357
 
    my $c = $ptree->{$id};
358
 
    for ($cid = 0; $cid < @$c; $cid++) {
359
 
        if ($c->[$cid][1] == $fid && $c->[$cid][4] == $stmt->[4]) {
360
 
            splice(@$c, $cid, 1);
361
 
            last;
362
 
        }
363
 
    }
364
 
    $ptree;
365
 
}
366
 
 
367
 
sub _add {
368
 
    my ($ptree, $fid, $bid, $sid, $stmt, $code) = @_;
369
 
    $stmt->[3][0] = $code;
370
 
    my $id = '';
371
 
    $id = 'come_froms' if $code == S_COME;
372
 
    $id = 'abstain' if $code == S_ABSTAIN;
373
 
    $id = 'gabstain' if $code == S_GABSTAIN;
374
 
    $id = 'reinstate' if $code == S_REINSTATE;
375
 
    $id = 'greinstate' if $code == S_GREINSTATE;
376
 
    return $ptree if $id eq '';
377
 
    my $cid;
378
 
    my $c = $ptree->{$id};
379
 
    my $l = $stmt->[3][1];
380
 
    $l = $l->[1] if $l->[0] == E_CONSTANT;
381
 
    push @$c, [$stmt->[3][1], $fid, $bid, $sid, $stmt->[4]];
382
 
    $ptree;
383
 
}
384
 
 
385
 
sub _convert {
386
 
    my ($ptree, $fid, $bid, $sid, $stmt, $newcode) = @_;
387
 
    _remove($ptree, $fid, $stmt);
388
 
    _add($ptree, $fid, $bid, $sid, $stmt, $newcode);
389
 
}
390
 
 
391
 
1;