-
Notifications
You must be signed in to change notification settings - Fork 138
/
File.pm
337 lines (228 loc) · 7.85 KB
/
File.pm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
#! nqp
# Copyright (C) 2001-2009, Parrot Foundation.
# XXX Better to put this into docs/ somewhere.
=begin
=head1 NAME
Ops::File - Ops To C Code Generation
=head1 SYNOPSIS
use Ops::File;
=head1 DESCRIPTION
C<Ops::File> takes one or more files of op functions and
creates real C code for them.
This class is used by F<tools/build/ops2c.pl>.
=head2 Op Functions
For ops that have trivial bodies (such as just a call to some other
function and a C<return> statement), opcode functions are in the format:
inline op opname (args) :flags {
... body of function ...
}
Note that currently the C<inline> op type is ignored.
Alternately, for opcode functions that have more internal complexity the
format is:
op opname (args) :flags {
... body of function ...
}
There may be more than one C<return>.
In both cases the closing brace B<must> be on its own line.
When specifying multiple flags, each flag gets its own prefixing colon.
=head2 Op Arguments
Op arguments are a comma-separated list of direction and type pairs.
Argument direction is one of:
in the argument passes a value into the op
out the argument passes a value out of the op
inout the argument passes a value into and out of the op
inconst the argument passes a constant value into the op
invar the argument passes a variable value into the op
Argument direction is used to determine the life times of symbols and
their related register allocations. When an argument is passed into an
op a register is read from, when it's passed out of an op a register is
written to.
Argument type is one of:
INT the argument is an integer
NUM the argument is an numeric
STR the argument is an string
PMC the argument is an PMC
KEY the argument is an aggregate PMC key
INTKEY the argument is an aggregate PMC integer key
LABEL the argument is an integer branch offset or address
The size of the return offset is determined from the op function's
signature.
=head2 Op Flags
The flags are of two types:
=over 4
=item 1 class
The classification of ops is intended to facilitate the selection of
suitable ops for a Parrot safe mode.
=item 2 behavior
The presence (or absence) of certain flags will change how the op behaves. For
example, the lack of the C<flow> flag will cause the op to be implicitly
terminated with C<goto NEXT()>. (See next section).
The :deprecated flag will generate a diagnostic to standard error at
runtime when a deprecated opcode is invoked and
C<PARROT_WARNINGS_DEPRECATED_FLAG> has been set.
=back
=head2 Op Body (Macro Substitutions)
In the following macro descriptions, C<PC> and C<PC'> are the current
and next position within the Parrot code.
=over 4
=item C<goto OFFSET(X)>
Transforms to C<PC' = PC + X>. This is used for branches.
=item C<goto NEXT()>
Transforms to C<PC' = PC + S>, where C<S> is the size of an op.
=item C<goto ADDRESS(X)>
Transforms to C<PC' = X>. This is used for absolute jumps.
=item C<expr OFFSET(X)>
Transforms to C<PC + X>. This is used to give a relative address.
=item C<expr NEXT()>
Transforms to C<PC + S>, the position of the next op.
=item C<expr ADDRESS(X)>
Transforms to C<X>, an absolute address.
=item C<OP_SIZE>
Transforms to C<S>, the size of an op.
=item C<HALT()>
Transforms to C<PC' = 0>. Halts run loop, and resets the current
position to the start of the Parrot code, without resuming.
=item C<restart OFFSET(X)>
Transforms to C<PC' = 0> and restarts at C<PC + X>.
=item C<restart NEXT()>
Transforms to C<PC' = 0> and restarts at C<PC + S>.
=item C<$n>
Transforms to the op function's nth argument. C<$0> is the opcode itself.
=back
Note that, for ease of parsing, if the argument to one of the above
notations in a ops file contains parentheses, then double the enclosing
parentheses and add a space around the argument, like so:
goto OFFSET(( (void*)interp->happy_place ))
=head2 Class Methods
=over 4
=end
class Ops::File is Hash;
pir::load_bytecode('config.pbc');
=begin
=item C<new(@files)>
Returns a new instance initialized by calling C<read_ops()> on each of
the specified op files.
=item C<new_str($str)>
Returns a new instance initialized by compiling C<$str> as the contents of an
ops file.
=end
method new(*@files, :$oplib, :$core!, :$nolines, :$quiet? = 0) {
self<files> := @files;
self<core> := $core;
self<ops> := list(); # Ops
self<preamble>:= '';
self<compiler>:= pir::compreg__Ps('Ops');
self<op_order>:= 0;
self<quiet> := $quiet;
if $core {
self<oplib> := $oplib;
self<compiler>.set_oplib($oplib);
}
else {
self<file> := @files[0];
}
self._set_version();
for @files { self.read_ops( $_, $nolines ) }
self._calculate_op_codes();
self;
}
method new_str($str, :$oplib) {
self<ops> := list(); # Ops
self<preamble> := '';
self<compiler> := pir::compreg__Ps('Ops');
self<oplib> := $oplib;
self<compiler>.set_oplib($oplib);
self._set_version();
self._set_version();
self.compile_ops($str);
self;
}
=begin
=back
=head2 Instance Methods
=over 4
=item C<read_ops($file,$nolines)>
Reads in the specified .ops file, gathering information about the ops.
=end
method read_ops($file, $nolines) {
$Ops::Compiler::Actions::OPLIB := self<oplib>;
self<quiet> || say("# Parsing $file...");
my $start_time := pir::time__N();
my $buffer := transcode_slurp($file);
my $start_ops := +self<ops>;
self.compile_ops($buffer, :experimental( $file ~~ /experimental\.ops/));
my $end_ops := +self<ops>;
pir::sprintf(my $time, "%.3f", [pir::time__N() - $start_time] );
self<quiet> || say("# Parsed $file in $time seconds; found "~
($end_ops - $start_ops) ~" ops.");
}
method compile_ops($str, :$experimental? = 0) {
my $compiler := self<compiler>;
my $past := $compiler.compile($str, :target('past'));
for @($past<ops>) {
$_.experimental($experimental);
$_.deprecated($_.flags<deprecated> ?? 1 !! 0);
self<ops>.push($_);
#say($_.full_name ~ " is number " ~ self<op_order>);
self<op_order>++;
}
for @( $past<preamble> ) {
self<preamble> := self<preamble> ~ $_;
}
$past;
}
method get_parse_tree($str) {
my $compiler := pir::compreg__Ps('Ops');
$compiler.compile($str, :target('parse'));
}
method preamble() { self<preamble> };
method ops() { self<ops> };
method oplib() { self<oplib> };
method version() { self<version>; }
method version_major() { self<version_major> }
method version_minor() { self<version_minor> }
method version_patch() { self<version_patch> }
method _calculate_op_codes() {
my $code := 0;
for self<ops> -> $op {
$op<code> := $code++;
}
}
method _set_version() {
my $config := _config();
my $version_filename;
if $config<installed> {
$version_filename :=
$config<libdir> ~
$config<versiondir> ~
$config<slash> ~
'VERSION';
}
else {
$version_filename :=
$config<prefix> ~
$config<slash> ~
'VERSION';
}
grammar VERSION {
rule TOP { <version> }
rule version { $<major>=(\d+) '.' $<minor>=(\d+) '.' $<patch>=(\d+) }
}
my $version := slurp($version_filename);
my $version_match := VERSION.parse($version);
#say("# $version");
self<version_major> := +$version_match<version><major>;
self<version_minor> := +$version_match<version><minor>;
self<version_patch> := +$version_match<version><patch>;
self<version> := [
+self<version_major>,
+self<version_minor>,
+self<version_patch>,
];
}
# Local Variables:
# mode: cperl
# cperl-indent-level: 4
# fill-column: 100
# End:
# vim: ft=perl6 expandtab shiftwidth=4: