tagged release 0.7.1
[parrot.git] / languages / dotnet / build / translator.pl
blob9269c4a7696a81c2b228a38a5e72cc83535f4867
1 #! perl
2 # $Id$
3 # .NET CLI => PIR Translator Generator
4 # Script to generate the instruction translator from a rules file and a
5 # stack to register mapping algorithm.
7 # Copyright (C) 2006, The Perl Foundation.
9 use strict;
10 use warnings;
11 use Getopt::Long;
12 use lib 'build';
14 # This is a list of instructions that need the magical enum fixup.
15 our @need_enum_fix = (
16 qw/2E 2F 30 31 32 33 34 35 36 37 3B 3C 3D 3E 3F 40 41 42 43 44/, # branch
17 qw/58 59 5A 5B 5C 5D 5E 5F 60 61/, # arithmetic and logical
18 'FE01', 'FE02', 'FE03', 'FE04', 'FE05' # compare
21 # Grab parameters.
22 my ( $rules_file, $output_file, $srm_module );
23 GetOptions(
24 "output=s" => \$output_file,
25 "srm=s" => \$srm_module
26 ) or usage();
27 $rules_file = shift @ARGV;
28 usage() if !$rules_file || @ARGV;
30 # Attempt to laod stack to register mapping module.
31 require "SRM/$srm_module.pm";
32 my $srm = "SRM::$srm_module"->new();
34 # Ensure rules file exists and parse it.
35 my @rules = ();
36 if ( -e $rules_file ) {
37 @rules = parse_rules($rules_file);
39 else {
40 die "Error: Cannot load rules file $rules_file: $!\n";
43 # Create metavariables table.
44 my $metavars = {};
46 # Generate initial translator code and populate metafields.
47 my $pir = generate_initial_pir( $srm, \@rules, $metavars );
49 # Emit translation dispatch table.
50 $pir .= generate_dispatch_table( $srm, \@rules, $metavars );
52 # Generate instruction translation code from rules.
53 foreach (@rules) {
54 $pir .= generate_rule_code( $srm, $_, $metavars );
57 # Generate final translator code.
58 $pir .= generate_final_pir( $srm, $metavars );
60 # Finally, we need to insert auto-magically instantiated local variables
61 # into the translator code.
62 $pir = insert_automagicals( $pir, $metavars );
64 # Append any custom subs that the SRM wants.
65 my $csubs = $srm->subs();
66 $pir .= sub_meta( $csubs, {}, 'custom subs' );
68 # Finally, write generated PIR to output file.
69 open my $fh, ">", "$output_file";
70 print $fh $pir;
71 close $fh;
73 # And display count of ops that can be translated.
74 print "Currently able to translate " . scalar(@rules) . " out of 213 instructions.\n";
76 ##############################################################################
77 # Subroutines.
78 ##############################################################################
80 # Parse rules file and build a data structure.
81 # ############################################
82 sub parse_rules {
84 # Get filename and open the file.
85 my $filename = shift;
86 open my $fh, "<", "$filename" or die "Unable to open $filename: $!\n";
88 # We'll store an array of hashes containing the data.
89 my @rules = ();
90 my $rule;
92 # Read through rules file line by line.
93 my $line = 0;
94 my $in_heredoc = 0;
95 my ( $heredoc_key, $heredoc_value, $heredoc_terminator );
96 while (<$fh>) {
97 chomp;
98 $line++;
100 # If it's a blank line or a comemnt line, skip it.
101 next if !$in_heredoc && /^\s*#|^\s*$/;
103 # Is this a new rule?
104 if ( !$in_heredoc && /^\s*\[([\w\.]+)\]\s*$/ ) {
106 # If we have a current rule...
107 my $name = $1;
108 if ($rule) {
110 # Validate it.
111 validate_rule($rule);
113 # Save it.
114 push @rules, $rule;
117 # Create new rule structure.
118 $rule = { name => $name };
121 # Is it a value within a rule with a heredoc?
122 elsif ( !$in_heredoc && $rule && /^\s*(\w+)\s*=\s*<<(\w+)\s*$/ ) {
124 # Initialize heredoc.
125 $in_heredoc = 1;
126 $heredoc_key = $1;
127 $heredoc_value = "";
128 $heredoc_terminator = $2;
131 # Or is it a value within a rule and not a herdoc?
132 elsif ( !$in_heredoc && $rule && /^\s*(\w+)\s*=\s*(.+?)\s*$/ ) {
134 # Stash key and value.
135 $rule->{$1} = $2;
138 # Are we at the end of a heredoc?
139 elsif ( $in_heredoc && /^$heredoc_terminator\s*$/ ) {
141 # Stash key/value pair away and unset heredoc flag.
142 $rule->{$heredoc_key} = $heredoc_value;
143 $in_heredoc = 0;
146 # Is this heredoc content?
147 elsif ($in_heredoc) {
148 $heredoc_value .= "$_\n";
151 # Otherwise, syntax eror.
152 else {
153 die "Syntax error on line $line (\"$_\")\n";
157 # If we're still in a heredoc, we got an error.
158 if ($in_heredoc) {
159 die "Unterminated heredoc runs to end of file\n";
162 # If we've a rule left, validate and stash it.
163 if ($rule) {
164 validate_rule($rule);
165 push @rules, $rule;
168 # Close file and return parsed rules.
169 close $fh;
170 return @rules;
173 # Rule validator.
174 # ###############
175 sub validate_rule {
176 my $rule = shift;
178 # Flags we'll set as we go through key/value pairs.
179 my ( $has_code, $has_class, $has_push, $has_pop, $has_args, $has_trans, $has_typeinfo );
181 # Iterate over keys and do validation.
182 for ( keys %$rule ) {
184 # name
185 if (/^name$/) {
187 # always fine
190 # code
191 elsif (/^code$/) {
192 if ( $rule->{$_} =~ /^([0-9A-F]{2}\s+)*[0-9A-F]{2}$/ ) {
193 if ($has_code) {
194 die "Duplicate value for code in rule $rule->{'name'}\n";
196 else {
197 $has_code = 1;
200 else {
201 die "Invalid value for code in rule $rule->{'name'}\n";
205 # class
206 elsif (/^class$/) {
207 if ( $rule->{$_} =~ /^(op|load|store|branch|calling)$/ ) {
208 if ($has_class) {
209 die "Duplicate value for class in rule $rule->{'name'}\n";
211 else {
212 $has_class = 1;
215 else {
216 die "Invalid value for class in rule $rule->{'name'}\n";
220 # push
221 elsif (/^push$/) {
222 if ( $rule->{$_} =~ /^\d+$/ ) {
223 if ($has_push) {
224 die "Duplicate value for push in rule $rule->{'name'}\n";
226 else {
227 $has_push = 1;
230 else {
231 die "Invalid value for push in rule $rule->{'name'}\n";
235 # pop
236 elsif (/^pop$/) {
237 if ( $rule->{$_} =~ /^\d+$/ ) {
238 if ($has_pop) {
239 die "Duplicate value for pop in rule $rule->{'name'}\n";
241 else {
242 $has_pop = 1;
245 else {
246 die "Invalid value for pop in rule $rule->{'name'}\n";
250 # arguments
251 elsif (/^arguments$/) {
252 if ($has_args) {
253 die "Duplicate value for arguments in rule $rule->{'name'}\n";
255 else {
256 my @args = split( /\s*,\s+/, $rule->{'arguments'} );
257 foreach (@args) {
258 die "Invalid argument type $_ in rule $rule->{'name'}\n"
259 unless /^((u?int(8|16|32|64))|float(32|64)|jumptable)$/
260 || /^t(string|standalonesig|valuetype|method|field|type)$/;
262 $has_args = 1;
266 # instruction or pir
267 elsif (/^(instruction|pir)$/) {
268 if ($has_trans) {
269 die "Only one of instruction or pir is allowed in rule " . "$rule->{'name'}\n";
271 else {
272 $has_trans = 1;
276 # typeinfo
277 elsif (/^typeinfo$/) {
278 if ($has_typeinfo) {
279 die "Duplicate value for typeinfo in rule $rule->{'name'}\n";
281 else {
282 $has_typeinfo = 1;
286 # Unknown key.
287 else {
288 die "Unknown key $_ in rule $rule->{'name'}\n";
292 # Check we had mandatory fields.
293 unless ($has_code) {
294 die "Mandatory entry code missing in rule $rule->{'name'}\n";
296 unless ($has_class) {
297 die "Mandatory entry class missing in rule $rule->{'name'}\n";
299 unless ($has_trans) {
300 die "Translation (instruction or pir) not provided in rule " . "$rule->{'name'}\n";
303 # typeinfo must be supplied with op, load and calling, but not with
304 # anything else.
305 if ( $has_typeinfo
306 && $rule->{'class'} ne 'op'
307 && $rule->{'class'} ne 'store'
308 && $rule->{'class'} ne 'load'
309 && $rule->{'class'} ne 'calling' )
311 die "typeinfo only valid for rules of class op, calling or load in " . "$rule->{'name'}\n";
313 elsif (
314 !$has_typeinfo
315 && ( $rule->{'class'} eq 'op'
316 || $rule->{'class'} eq 'load'
317 || $rule->{'class'} eq 'calling' )
320 die "typeinfo must be supplied for rule $rule->{'name'}\n";
323 # pop and push forbidden for calling
324 if ( $rule->{'class'} eq 'calling' && ( $has_pop || $has_push ) ) {
325 die "pop and push not allowed for class calling in rule $rule->{'name'}\n";
328 # Set default values.
329 $rule->{'pop'} ||= 0;
330 $rule->{'push'} ||= 0;
331 $rule->{'instruction'} ||= "";
332 $rule->{'pir'} ||= "";
333 $rule->{'arguments'} = "" unless $has_args;
336 # Generate the translator initialization code.
337 # ############################################
338 sub generate_initial_pir {
339 my $srm = shift;
340 my $rules = shift;
341 my $mv = shift;
343 # Get number of locals we need for ${STACKn} and ${DESTm} and set up
344 # their meta-variables.
345 my ( $max_pop, $max_push ) = ( 0, 0 );
346 foreach (@$rules) {
347 if ( $_->{'pop'} > $max_pop ) {
348 $max_pop = $_->{'pop'};
350 if ( $_->{'push'} > $max_push ) {
351 $max_push = $_->{'push'};
354 my $stack_locals = "";
355 for ( 0 .. $max_pop - 1 ) {
356 $stack_locals .= $stack_locals ? ', ' : '.local string ';
357 $stack_locals .= "stack$_";
358 $mv->{"STACK$_"} = "stack$_";
360 my $dest_locals = "";
361 for ( 0 .. $max_push - 1 ) {
362 $dest_locals .= $dest_locals ? ', ' : '.local string ';
363 $dest_locals .= "dest$_";
364 $mv->{"DEST$_"} = "dest$_";
367 # Emit the translator PIR.
368 my $pir = <<TRANSPIR;
369 # THIS IS A GENERATED FILE! DO NOT EDIT!
371 .HLL '_dotnet', ''
373 .sub trans_instructions
374 .param pmc assembly
375 .param pmc class
376 .param pmc meth
377 .param pmc ptypes
378 .param pmc ltypes
379 .param pmc rettype
380 .param int trace
381 .local pmc bc, ex, stypes, dtypes, loadtype, type_trans, c_params, escaper
382 .local pmc ehs, eh, ss_propogate, ss
383 .local string gen_pir, loadreg, storereg, err, str_ic, tmp
384 .local int pc, next_pc, bc_length, cur_ic, pop_count, label_num, sp_dest
385 .local int i, j, type, try_offset, try_length, try_end, handler_offset, eh_flags
386 .local int class_type, class_id
387 $stack_locals
388 $dest_locals
389 \${AUTO_MAGICALS}
391 # Type constants.
392 .const int ELEMENT_TYPE_VOID = 0x01
393 .const int ELEMENT_TYPE_BOOLEAN = 0x02
394 .const int ELEMENT_TYPE_CHAR = 0x03
395 .const int ELEMENT_TYPE_I1 = 0x04
396 .const int ELEMENT_TYPE_U1 = 0x05
397 .const int ELEMENT_TYPE_I2 = 0x06
398 .const int ELEMENT_TYPE_U2 = 0x07
399 .const int ELEMENT_TYPE_I4 = 0x08
400 .const int ELEMENT_TYPE_U4 = 0x09
401 .const int ELEMENT_TYPE_I8 = 0x0A
402 .const int ELEMENT_TYPE_U8 = 0x0B
403 .const int ELEMENT_TYPE_R4 = 0x0C
404 .const int ELEMENT_TYPE_R8 = 0x0D
405 .const int ELEMENT_TYPE_STRING = 0x0E
406 .const int ELEMENT_TYPE_PTR = 0x0F
407 .const int ELEMENT_TYPE_BYREF = 0x10
408 .const int ELEMENT_TYPE_VALUETYPE = 0x11
409 .const int ELEMENT_TYPE_CLASS = 0x12
410 .const int ELEMENT_TYPE_ARRAY= 0x14
411 .const int ELEMENT_TYPE_TYPEDBYREF = 0x16
412 .const int ELEMENT_TYPE_I = 0x18
413 .const int ELEMENT_TYPE_U = 0x19
414 .const int ELEMENT_TYPE_FNPTR = 0x1B
415 .const int ELEMENT_TYPE_OBJECT = 0x1C
416 .const int ELEMENT_TYPE_SZARRAY = 0x1D
417 .const int ELEMENT_TYPE_INTERNAL = 0x21
419 # Trace info - param and local types.
420 if trace != 2 goto NO_IN_TRACE
421 printerr " PTYPES: "
422 i = elements ptypes
423 j = 0
424 PTYPES_LOOP:
425 if j == i goto PTYPES_LOOP_END
426 type_trans = ptypes[j]
427 type = type_trans["type"]
428 printerr type
429 printerr " "
430 inc j
431 goto PTYPES_LOOP
432 PTYPES_LOOP_END:
433 printerr "\\n"
434 printerr " LTYPES: "
435 i = elements ltypes
436 j = 0
437 LTYPES_LOOP:
438 if j == i goto LTYPES_LOOP_END
439 type_trans = ltypes[j]
440 type = type_trans["type"]
441 printerr type
442 printerr " "
443 inc j
444 goto LTYPES_LOOP
445 LTYPES_LOOP_END:
446 printerr "\\n"
447 NO_IN_TRACE:
449 # Get bytecode and initialize pc.
450 bc = meth.get_bytecode()
451 bc_length = bc.get_length()
452 pc = 0
454 # Initialize stack types array.
455 stypes = new ResizablePMCArray
457 # Instantiate a bytecode escaper.
458 escaper = find_global "Data::Escape", "String"
460 # Source of generated label numbers set to zero.
461 label_num = 0
462 TRANSPIR
464 # Set up some more metavariables.
465 $mv->{'ASSEMBLY'} = 'assembly';
466 $mv->{'BC'} = 'bc';
467 $mv->{'INS'} = 'gen_pir';
468 $mv->{'PC'} = 'pc';
469 $mv->{'NEXTPC'} = 'next_pc';
470 $mv->{'PTYPES'} = 'ptypes';
471 $mv->{'LTYPES'} = 'ltypes';
472 $mv->{'STYPES'} = 'stypes';
473 $mv->{'DTYPES'} = 'dtypes';
474 $mv->{'LOADTYPE'} = 'loadtype';
475 $mv->{'RETTYPE'} = 'rettype';
476 $mv->{'LABELNUM'} = 'label_num';
477 $mv->{'EHANDLERS'} = 'ehs';
479 # SRM pre translation code.
480 $pir .= "### pre_translation\n";
481 my $srm_pt = $srm->pre_translation();
482 $pir .= sub_meta( $srm_pt, $mv, 'pre_translation' );
483 $pir .= "### end pre_translation\n\n";
485 # Emit first bit of PIR to loop over instructions. Each translation
486 # routine will jump back to TRANS_LOOP after translating the instruction.
487 $pir .= <<'PIRCODE';
488 # If we have exception handlers, need some intial stuff emitted.
489 ehs = bc.get_eh()
490 if null ehs goto NO_EH_HEADER
491 i = elements ehs
492 gen_pir = concat ".local pmc cur_exception\n"
493 gen_pir = concat ".local pmc saved_ehs\nsaved_ehs = new .FixedPMCArray\nsaved_ehs = "
494 tmp = i
495 gen_pir = concat tmp
496 gen_pir = concat "\n"
497 NO_EH_HEADER:
498 gen_pir = concat "pushmark 0\n"
500 # Translation loop.
501 ss_propogate = new .Hash
502 TRANS_LOOP:
503 pc = bc.get_pos()
504 next_pc = pc
505 if pc >= bc_length goto COMPLETE
507 # If we have a stack type state propogated here, put it in place.
508 ss = ss_propogate[pc]
509 if null ss goto NO_SS_PROP
510 stypes = ss
511 NO_SS_PROP:
513 # Generate label.
514 PIRCODE
516 # Emit label generation code.
517 $pir .= "### gen_label\n";
518 my $srm_label = $srm->gen_label();
519 $pir .= sub_meta( $srm_label, $mv, 'gen_label' );
520 $pir .= "### end gen_label\n\n";
522 # Emit code to do exception related stuff.
523 $pir .= <<'PIRCODE';
524 # Look through exception handlers.
525 if null ehs goto END_EH_LOOP
526 i = elements ehs
527 EH_LOOP:
528 dec i
529 if i < 0 goto END_EH_LOOP
530 eh = ehs[i]
532 # If this is the start of a try block, emit a push_eh instruction.
533 try_offset = eh.get_try_offset()
534 try_length = eh.get_try_length()
535 if try_offset != pc goto NOT_TRY_START
536 gen_pir = concat "push_eh LAB"
537 handler_offset = eh.get_handler_offset()
538 tmp = handler_offset
539 gen_pir = concat tmp
540 gen_pir = concat "\npushmark "
541 $I0 = i + 1
542 tmp = $I0
543 gen_pir = concat tmp
544 gen_pir = concat "\n"
545 NOT_TRY_START:
547 # If this is the start of a typed exception handler...
548 handler_offset = eh.get_handler_offset()
549 eh_flags = eh.get_flags()
550 if eh_flags != 0 goto NOT_TYPED_EH_START
551 if handler_offset != pc goto NOT_TYPED_EH_START
553 # Emit code to get the .NET exception object and check if it's of the type that
554 # is accepted.
555 gen_pir = concat ".get_results (cur_exception, $S1000000)\n"
556 gen_pir = concat "$P1000001 = cur_exception[\"obj\"]\n"
557 class_type = eh.get_class_type()
558 class_id = eh.get_class_id()
559 gen_pir = concat "$I1000000 = isa $P1000001, "
560 ($P0, $S0) = class_info_from_ehtype(assembly, class_type, class_id)
561 $S0 = namespace_to_key($S0)
562 gen_pir = concat $S0
563 gen_pir = concat "\nif $I1000000 goto TYPED_EH_FOUND_"
564 tmp = handler_offset
565 gen_pir = concat tmp
566 gen_pir = concat "\nthrow $P1000000\nTYPED_EH_FOUND_"
567 gen_pir = concat tmp
568 gen_pir = concat ":\n"
570 # Need to fix up stack type state. Create new empty array.
571 stypes = new ResizablePMCArray
572 type_trans = new Hash
573 type_trans["type"] = ELEMENT_TYPE_CLASS
574 type_trans["byref"] = 0
575 annotate_reg_type(type_trans)
576 stypes = push type_trans
577 loadtype = type_trans
579 # If the object is OK, we'll not have re-thrown and will need to put it on
580 # the stack. Emit code to deal with that.
581 PIRCODE
582 my $pop_all = $srm->pop_all();
583 $pir .= "### pop_all (typed eh)\n";
584 $pir .= sub_meta( $pop_all, $mv, "pop_all for typed exception handler" );
585 $pir .= "### end pop_all (typed eh)\n";
586 my $pre_load = $srm->pre_load(0);
587 $pir .= "### pre_load (typed eh)\n";
588 $pir .= sub_meta(
589 $pre_load,
590 { %$mv, LOADREG => 'loadreg' },
591 "pre_load for typed exception handler"
593 $pir .= "### end pre_load (typed eh)\n";
594 $pir .= <<'PIRCODE';
595 loadreg = "$P1000001"
596 PIRCODE
597 my $post_load = $srm->post_load(0);
598 $pir .= "### post_load (typed eh)\n";
599 $pir .= sub_meta(
600 $post_load,
601 { %$mv, LOADREG => 'loadreg' },
602 "post_load for typed exception handler"
604 $pir .= "### end post_load (typed eh)\n";
605 $pir .= <<'PIRCODE';
606 NOT_TYPED_EH_START:
608 # If this is the start of a finally handler...
609 if eh_flags != 2 goto NOT_FINALLY_START
610 if handler_offset != pc goto NOT_FINALLY_START
612 # Emit code to get and stash the .NET exception object, then put a jump
613 # label.
614 gen_pir = concat ".get_results (cur_exception, $S1000000)\nsaved_ehs["
615 tmp = i
616 gen_pir = concat tmp
617 gen_pir = concat "] = cur_exception\nFINALLY_"
618 tmp = handler_offset
619 gen_pir = concat tmp
620 gen_pir = concat ": "
621 NOT_FINALLY_START:
623 # Goto next handler.
624 goto EH_LOOP
625 END_EH_LOOP:
626 PIRCODE
628 # Return generated code.
629 return $pir;
632 # Generate the dispatch table.
633 # ############################
634 sub generate_dispatch_table {
635 my $srm = shift;
636 my @rules = @{ shift() };
637 my $mv = shift;
639 # Make a copy of the original instruction code before we trash it.
640 foreach (@rules) {
641 $_->{'orig_code'} = $_->{'code'};
644 # Put rules with the same startcode into a group.
645 my %groups = ();
646 foreach (@rules) {
647 $_->{'code'} =~ /^(\w{2})/;
648 $groups{$1} ||= [];
649 push @{ $groups{$1} }, $_;
652 # Go through groups, sorted by start code.
653 my @rules_grouped = ();
654 foreach ( sort keys %groups ) {
656 # If there is only a single element with a single code...
657 if ( @{ $groups{$_} } == 1 && $groups{$_}->[0]->{'code'} =~ /^\w{2}$/ ) {
659 # Just put that rule into the list, with no grouping.
660 push @rules_grouped, $groups{$_}->[0];
662 else {
664 # Strip first code from all the groups.
665 foreach ( @{ $groups{$_} } ) {
666 $_->{'code'} =~ s/^\w{2} //;
669 # Build a group entry.
670 push @rules_grouped,
672 code => $_,
673 group => $groups{$_}
678 # We'll use recursion to build up a binary search style tree to dispatch
679 # to the translation code for an instruction in something like O(log(n))
680 # rather than O(n).
681 my $pir = <<PIRCODE;
682 # Translation code dispatch table.
683 cur_ic = bc.read_uint8()
684 next_pc += 1
685 PIRCODE
686 $pir .= binary_dispatch_table( '', @rules_grouped );
688 # Emit unknown instruction code.
689 $pir .= <<PIRCODE;
690 INS_NOT_FOUND_ERROR:
691 ex = new 'Exception'
692 err = "Attempt to translate unknown instruction (code "
693 str_ic = cur_ic
694 err = concat str_ic
695 err = concat ")"
696 ex = err
697 throw ex
699 PIRCODE
701 # Restore original instruction codes, but remove spaces.
702 foreach (@rules) {
703 $_->{'code'} = $_->{'orig_code'};
704 $_->{'code'} =~ s/\s//g;
707 # Return generated PIR.
708 return $pir;
711 # Binary dispatch table builder.
712 # ##############################
713 sub binary_dispatch_table {
714 my $prefix = shift;
715 my @rules = @_;
716 my $pir = "";
718 # Ensure groups have been built properly and we only dispatch one
719 # byte at a time.
720 foreach (@rules) {
721 if ( $_->{'code'} =~ /\w\s+\w/ ) {
722 die "Currently unable to build dispatch table for instructions of "
723 . "more than 2 bytes in rule $_->{'name'}\n";
727 # If we have 3 or less rules, dispatch directly to the translator.
728 if ( @rules <= 3 ) {
729 my %byte2_todo = ();
730 foreach (@rules) {
732 # If we have a group, need to build dispatch code for that.
733 if ( $_->{'group'} ) {
734 $pir .= " if cur_ic == 0x$_->{'code'} goto B2_BRANCH_$prefix$_->{'code'}\n";
735 $byte2_todo{"$prefix$_->{'code'}"} = $_->{'group'};
737 else {
739 # Not a group; jump straight to instruction.
740 my $name = $_->{'name'};
741 $name =~ s/\./_/g;
742 $pir .= " if cur_ic == 0x$_->{'code'} goto INS_TRANS_$name\n";
746 # If we don't branch at any of them, we've got an unknown op.
747 $pir .= " goto INS_NOT_FOUND_ERROR\n";
749 # We may need to bulid second byte dispatch tables.
750 foreach ( keys %byte2_todo ) {
751 my @rules = @{ $byte2_todo{$_} };
752 @rules = sort {
753 $a->{'code'} =~ /^(\w{2})/;
754 my $x = $1;
755 $b->{'code'} =~ /^(\w{2})/;
756 hex($x) <=> hex($1)
757 } @rules;
758 $pir .= "B2_BRANCH_$_:\n";
759 $pir .= " cur_ic = bc.read_uint8()\n next_pc += 1\n";
760 $pir .= binary_dispatch_table( $_, @rules );
763 else {
765 # Otherwise, split the rules into two groups.
766 my $split_point = int( @rules / 2 );
767 my @r1 = @rules[ 0 .. $split_point - 1 ];
768 my @r2 = @rules[ $split_point .. $#rules ];
770 # Emit branch code.
771 $pir .= " if cur_ic >= 0x$r2[0]->{'code'} goto INS_BRANCH_$prefix$r2[0]->{'code'}\n";
772 $pir .= " if cur_ic < 0x$r2[0]->{'code'} goto INS_BRANCH_$prefix$r1[$#r1]->{'code'}\n";
774 # Recurse to make code for sub branches.
775 $pir .= "INS_BRANCH_$prefix$r1[$#r1]->{'code'}:\n";
776 $pir .= binary_dispatch_table( $prefix, @r1 );
777 $pir .= "INS_BRANCH_$prefix$r2[0]->{'code'}:\n";
778 $pir .= binary_dispatch_table( $prefix, @r2 );
781 # Return generated code.
782 return $pir;
785 # Generate translation code relating to a rule.
786 # #############################################
787 sub generate_rule_code {
788 my $srm = shift;
789 my $rule = shift;
790 my $mv = shift;
791 my @localmv = ();
792 my $pir = "";
794 # Make current instruction code meta-variable.
795 $mv->{'CURIC'} = $rule->{'code'};
796 push @localmv, 'CURIC';
798 # Emit dispatch label.
799 my $name = $rule->{'name'};
800 $pir .= " # Translation code for $name\n";
801 $name =~ s/\./_/g;
802 $pir .= "INS_TRANS_$name:\n";
804 # Emit trace code.
805 $pir .= <<PIR;
806 if trace != 2 goto NO_TRACE_$name
807 printerr " $name STS: "
808 i = elements stypes
809 j = 0
810 STS_LOOP_$name:
811 if j == i goto STS_LOOP_END_$name
812 type_trans = stypes[j]
813 type = type_trans["type"]
814 printerr type
815 printerr " "
816 inc j
817 goto STS_LOOP_$name
818 STS_LOOP_END_$name:
819 printerr "\\n"
820 NO_TRACE_$name:
823 # Emit code to read arguments for the op and set argument meta-variables.
824 # There is something slightly curious going on here. sub_meta will create
825 # the .local declarations for arguments, but it needs to know the argument
826 # type. Thus we assign to meta-variable I_ARG_0, I_ARG_1 etc, let sub_meta
827 # do what it needs to, then set ARG0 in the meta-variables table to whatever
828 # I_ARG_0 turns out to me. We also keep track of the fact that ARG0 should
829 # only exist inside this rule.
830 my @args = split( /,\s*/, $rule->{'arguments'} );
831 $mv->{'ARGCOUNT'} = scalar @args;
832 push @localmv, 'ARGCOUNT';
833 my $arg_num = 0;
834 my $arg_size = 0;
835 foreach (@args) {
836 my $out;
837 my $arg_name;
839 # uint8
840 if (/^uint8$/) {
841 $out = " \${I_ARG_$arg_num} = bc.read_uint8()\n next_pc += 1\n";
842 $arg_name = "I_ARG_$arg_num";
845 # int8
846 elsif (/^int8$/) {
847 $out = " \${I_ARG_$arg_num} = bc.read_int8()\n next_pc += 1\n";
848 $arg_name = "I_ARG_$arg_num";
851 # uint16
852 elsif (/^uint16$/) {
853 $out = " \${I_ARG_$arg_num} = bc.read_uint16()\n next_pc += 2\n";
854 $arg_name = "I_ARG_$arg_num";
857 # int16
858 elsif (/^int16$/) {
859 $out = " \${I_ARG_$arg_num} = bc.read_int16()\n next_pc += 2\n";
860 $arg_name = "I_ARG_$arg_num";
863 # uint32
864 elsif (/^uint32$/) {
865 $out = " \${I_ARG_$arg_num} = bc.read_uint32()\n next_pc += 4\n";
866 $arg_name = "I_ARG_$arg_num";
869 # int32
870 elsif (/^int32$/) {
871 $out = " \${I_ARG_$arg_num} = bc.read_int32()\n next_pc += 4\n";
872 $arg_name = "I_ARG_$arg_num";
875 # float32
876 elsif (/^float32$/) {
877 $out = " \${N_ARG_$arg_num} = bc.read_float32()\n next_pc += 4\n";
878 $arg_name = "N_ARG_$arg_num";
881 # float64
882 elsif (/^float64$/) {
883 $out = " \${N_ARG_$arg_num} = bc.read_float64()\n next_pc += 8\n";
884 $arg_name = "N_ARG_$arg_num";
887 # tfield
888 elsif (/^tfield$/) {
889 $out = " \${I_ARG_$arg_num} = bc.read_tfield()\n next_pc += 4\n";
890 $arg_name = "I_ARG_$arg_num";
893 # tmethod
894 elsif (/^tmethod$/) {
895 $out = " \${I_ARG_$arg_num} = bc.read_tmethod()\n next_pc += 4\n";
896 $arg_name = "I_ARG_$arg_num";
899 # ttype
900 elsif (/^ttype$/) {
901 $out = " \${I_ARG_$arg_num} = bc.read_ttype()\n next_pc += 4\n";
902 $arg_name = "I_ARG_$arg_num";
905 # tstring
906 elsif (/^tstring$/) {
907 $out = " \${I_ARG_$arg_num} = bc.read_tstring()\n next_pc += 4\n";
908 $arg_name = "I_ARG_$arg_num";
911 # jumptable
912 elsif (/^jumptable$/) {
913 $out = <<"PIR";
914 \${P_ARG_$arg_num} = new FixedPMCArray
915 i = bc.read_uint32()
916 next_pc += 4
917 \${P_ARG_$arg_num} = i
918 j = 0
919 JT_LOOP_$name:
920 if j == i goto JT_LOOP_END_$name
921 \$I0 = bc.read_int32()
922 next_pc += 4
923 \${P_ARG_$arg_num}[j] = \$I0
924 inc j
925 goto JT_LOOP_$name
926 JT_LOOP_END_$name:
928 $arg_name = "P_ARG_$arg_num";
931 # Unknown.
932 else {
933 die "Known argument type $_ not implemented yet.\n";
936 # Generate PIR.
937 $pir .= sub_meta( $out, $mv, "argument read" );
938 $mv->{"ARG$arg_num"} = $mv->{$arg_name};
939 push @localmv, "ARG$arg_num";
940 $arg_num++;
943 # Generate code that we need to insert to handle enum fixups if this is
944 # an instruction that needs it.
945 my $pre_translate_code = "";
946 if ( grep { $rule->{'code'} eq $_ } @need_enum_fix ) {
947 $pre_translate_code = <<'PIR'
948 (${STEMP0}, ${STACK0}, ${STACK1}) = fix_enum_operands(${STYPES}, ${STACK0}, ${STACK1})
949 ${INS} = concat ${STEMP0}
953 # Now we split based upon the class.
954 # Operations (op class).
955 $rule->{'pop'} ||= 0;
956 $rule->{'push'} ||= 0;
957 if ( $rule->{'class'} eq 'op' ) {
959 # Init destination types array.
960 $pir .= " dtypes = new ResizablePMCArray\n";
962 # Insert typeinfo code (sets up dtypes).
963 $pir .= "### typeinfo\n";
964 $pir .= sub_meta( $rule->{'typeinfo'}, $mv, "typeinfo for rule $rule->{'name'}" );
965 $pir .= "\n" if $pir !~ /\n$/;
966 $pir .= "### end typeinfo\n";
968 # Now call pre_op and append code that it generates.
969 my $pre_op = $srm->pre_op( $rule->{'pop'}, $rule->{'push'} );
970 $pir .= "### pre_op\n";
971 $pir .= sub_meta( $pre_op, $mv, "pre_op for rule $rule->{'name'}" );
972 $pir .= "### end pre_op\n";
974 # Add pre-translate code, if any.
975 $pir .= sub_meta( $pre_translate_code, $mv, "pre-translate for $rule->{'name'}" );
977 # If we have PIR for the instruction, just take that. If not, we need
978 # to generate it from the "to generate" instruction directive.
979 $pir .= "### translation\n";
980 if ( $rule->{'pir'} ) {
981 $pir .= sub_meta( $rule->{'pir'}, $mv, "pir for rule $rule->{'name'}" );
983 else {
984 $pir .= sub_meta( ins_to_pir( $rule->{'instruction'} ),
985 $mv, "pir for rule $rule->{'name'}" );
987 $pir .= "\n" unless $pir =~ /\n$/;
988 $pir .= "### end translation\n";
990 # Emit code to fix up the stack type array.
991 for ( 1 .. $rule->{'pop'} ) {
992 $pir .= " type_trans = pop stypes\n";
994 for ( 1 .. $rule->{'push'} ) {
995 $pir .= " type_trans = pop dtypes\n";
996 $pir .= " stypes = push type_trans\n";
999 # Finally, call post_op and append code it generates.
1000 my $post_op = $srm->post_op( $rule->{'pop'}, $rule->{'push'} );
1001 $pir .= "### post_op\n";
1002 $pir .= sub_meta( $post_op, $mv, "post_op for rule $rule->{'name'}" );
1003 $pir .= "### end post_op\n";
1006 # Loads (load class).
1007 elsif ( $rule->{'class'} eq 'load' ) {
1009 # Undef the loadtype so we can detect case where typeinfo fails to set
1010 # it.
1011 $pir .= " loadtype = null\n";
1013 # Insert typeinfo code.
1014 $pir .= "### typeinfo\n";
1015 $pir .= sub_meta( $rule->{'typeinfo'}, $mv, "typeinfo for rule $rule->{'name'}" );
1016 $pir .= "\n" if $pir !~ /\n$/;
1017 $pir .= "### end typeinfo\n";
1019 # Does the translator code actually load a value or just give back a
1020 # register name?
1021 my $need_dest;
1022 if ( $rule->{'pir'} =~ /\$\{DEST0\}/
1023 && $rule->{'pir'} =~ /\$\{LOADREG\}/ )
1025 die "pir must use one of \${DEST0} or \${LOADREG} in rule " . "$rule->{'name'}\n";
1027 elsif ($rule->{'instruction'} =~ /\$\{DEST0\}/
1028 && $rule->{'instruction'} =~ /\$\{LOADREG\}/ )
1030 die "pir must use one of \${DEST0} or \${LOADREG} in rule " . "$rule->{'name'}\n";
1032 elsif ( $rule->{'pir'} =~ /\$\{DEST0\}/ || $rule->{'instruction'} =~ /\$\{DEST0\}/ ) {
1033 $need_dest = 1;
1035 elsif ( $rule->{'pir'} =~ /\$\{LOADREG\}/ || $rule->{'instruction'} =~ /\$\{LOADREG\}/ ) {
1036 $need_dest = 0;
1037 $mv->{'LOADREG'} = 'loadreg';
1038 push @localmv, 'LOADREG';
1040 else {
1041 die "pir or instruction must use one of \${DEST0} or \${LOADREG} in rule "
1042 . "$rule->{'name'}\n";
1045 # Now call pre_load and append code that it generates.
1046 my $pre_load = $srm->pre_load($need_dest);
1047 $pir .= "### pre_load\n";
1048 $pir .= sub_meta( $pre_load, $mv, "pre_load for rule $rule->{'name'}" );
1049 $pir .= "### end pre_load\n";
1051 # If we have PIR for the instruction, just take that. If not, we need
1052 # to generate it from the "to generate" instruction directive.
1053 $pir .= "### translation\n";
1054 if ( $rule->{'pir'} ) {
1055 $pir .= sub_meta( $rule->{'pir'}, $mv, "pir for rule $rule->{'name'}" );
1057 else {
1058 $pir .= sub_meta( ins_to_pir( $rule->{'instruction'} ),
1059 $mv, "pir for rule $rule->{'name'}" );
1061 $pir .= "\n" unless $pir =~ /\n$/;
1062 $pir .= "### end translation\n";
1064 # Push load type onto the stack types array.
1065 $pir .= " stypes = push loadtype\n";
1067 # Emit code to clone value types for the need_dest set case.
1068 if ($need_dest) {
1069 my $label = "LD_VALTYPE_$name";
1070 $pir .= <<PIR
1071 \$I1000000 = loadtype["type"]
1072 if \$I1000000 != ELEMENT_TYPE_VALUETYPE goto $label
1073 gen_pir = concat dest0
1074 gen_pir = concat " = clone "
1075 gen_pir = concat dest0
1076 gen_pir = concat "\\n"
1077 $label:
1081 # Finally, call post_load and append code it generates.
1082 my $post_load = $srm->post_load($need_dest);
1083 $pir .= "### post_load\n";
1084 $pir .= sub_meta( $post_load, $mv, "post_load for rule $rule->{'name'}" );
1085 $pir .= "### end post_load\n";
1087 # Emit code to clone value types for the need_dest not set case.
1088 if ( !$need_dest ) {
1089 my $label = "LD_VALTYPE_$name";
1090 my $pre_op = $srm->pre_op( 1, 1 );
1091 $pre_op = sub_meta( $pre_op, $mv, "pre_op for rule $rule->{'name'} value type clone" );
1092 my $post_op = $srm->post_op( 1, 1 );
1093 $post_op =
1094 sub_meta( $post_op, $mv, "post_op for rule $rule->{'name'} value type clone" );
1095 $pir .= <<PIR
1096 \$I1000000 = loadtype["type"]
1097 if \$I1000000 != ELEMENT_TYPE_VALUETYPE goto $label
1098 dtypes = new ResizablePMCArray
1099 dtypes[0] = loadtype
1100 $pre_op
1101 gen_pir = concat dest0
1102 gen_pir = concat " = clone "
1103 gen_pir = concat stack0
1104 gen_pir = concat "\\n"
1105 $post_op
1106 $label:
1111 # Stores (store class).
1112 elsif ( $rule->{'class'} eq 'store' ) {
1114 # Does the translator code actually store a value or just give back a
1115 # register name where the value would be stored in?
1116 my $dest_reg;
1117 if ( $rule->{'pir'} =~ /\$\{STACK0\}/
1118 && $rule->{'pir'} =~ /\$\{STOREREG\}/ )
1120 die "pir must use one of \${STACK0} or \${STOREREG} in rule " . "$rule->{'name'}\n";
1122 elsif ($rule->{'instruction'} =~ /\$\{STACK0\}/
1123 && $rule->{'instruction'} =~ /\$\{STOREREG\}/ )
1125 die "instruction must use one of \${STACK0} or \${STOREREG} in rule "
1126 . "$rule->{'name'}\n";
1128 elsif ( "$rule->{'pir'}$rule->{'instruction'}" =~ /\$\{STACK0\}/ ) {
1129 $dest_reg = 0;
1131 elsif ( "$rule->{'pir'}$rule->{'instruction'}" =~ /\$\{STOREREG\}/ ) {
1132 $dest_reg = 1;
1133 $mv->{'STOREREG'} = 'storereg';
1134 push @localmv, 'STOREREG';
1136 else {
1137 die "pir or instruction must use one of \${STACK0} or \${STOREREG} in rule "
1138 . "$rule->{'name'}\n";
1141 # Insert typeinfo code if we have any. Note that it has no obligations.
1142 if ( $rule->{'typeinfo'} ) {
1143 $pir .= "### typeinfo\n";
1144 $pir .= sub_meta( $rule->{'typeinfo'}, $mv, "typeinfo for rule $rule->{'name'}" );
1145 $pir .= "\n" if $pir !~ /\n$/;
1146 $pir .= "### end typeinfo\n";
1149 # Now call pre_store and append code that it generates.
1150 my $pre_store = $srm->pre_store($dest_reg);
1151 $pir .= "### pre_store\n";
1152 $pir .= sub_meta( $pre_store, $mv, "pre_store for rule $rule->{'name'}" );
1153 $pir .= "### end pre_store\n";
1155 # If we have PIR for the instruction, just take that. If not, we need
1156 # to generate it from the "to generate" instruction directive.
1157 $pir .= "### translation\n";
1158 if ( $rule->{'pir'} ) {
1159 $pir .= sub_meta( $rule->{'pir'}, $mv, "pir for rule $rule->{'name'}" );
1161 else {
1162 $pir .= sub_meta( ins_to_pir( $rule->{'instruction'} ),
1163 $mv, "pir for rule $rule->{'name'}" );
1165 $pir .= "\n" unless $pir =~ /\n$/;
1166 $pir .= "### end translation\n";
1168 # Finally, call post_store and append code it generates.
1169 my $post_store = $srm->post_store($dest_reg);
1170 $pir .= "### post_store\n";
1171 $pir .= sub_meta( $post_store, $mv, "post_store for rule $rule->{'name'}" );
1172 $pir .= "### end post_store\n";
1174 # Now pop type off the stack types array.
1175 $pir .= " type_trans = pop stypes\n";
1178 # Branches (branch class).
1179 elsif ( $rule->{'class'} eq 'branch' ) {
1181 # Call pre_branch and append code that it generates.
1182 my $pre_branch = $srm->pre_branch( $rule->{'pop'} );
1183 $pir .= "### pre_branch\n";
1184 $pir .= sub_meta( $pre_branch, $mv, "pre_branch for rule $rule->{'name'}" );
1185 $pir .= "### end pre_branch\n";
1187 # Add pre-translate code, if any.
1188 $pir .= sub_meta( $pre_translate_code, $mv, "pre-translate for $rule->{'name'}" );
1190 # If we have PIR for the instruction, just take that. If not, we need
1191 # to generate it from the "to generate" instruction directive.
1192 $pir .= "### translation\n";
1193 if ( $rule->{'pir'} ) {
1194 $pir .= sub_meta( $rule->{'pir'}, $mv, "pir for rule $rule->{'name'}" );
1196 else {
1197 $pir .= sub_meta( ins_to_pir( $rule->{'instruction'} ),
1198 $mv, "pir for rule $rule->{'name'}" );
1200 $pir .= "### end translation\n";
1202 # Now emit code to fix up the stack type array and propogate it.
1203 for ( 1 .. $rule->{'pop'} ) {
1204 $pir .= " type_trans = pop stypes\n";
1206 if ( $rule->{'code'} eq '45' ) {
1207 $pir .= <<'PIRCODE';
1208 i = elements P_arg_0
1209 SS_PROP_LOOP_switch:
1210 if i == 0 goto SS_PROP_LOOP_EXIT_switch
1211 dec i
1212 j = P_arg_0[i]
1213 sp_dest = next_pc + j
1214 ss = clone stypes
1215 ss_propogate[sp_dest] = ss
1216 goto SS_PROP_LOOP_switch
1217 SS_PROP_LOOP_EXIT_switch:
1218 PIRCODE
1220 else {
1221 $pir .= <<'PIRCODE';
1222 sp_dest = next_pc + I_arg_0
1223 ss = clone stypes
1224 ss_propogate[sp_dest] = ss
1225 PIRCODE
1228 # Finally, call post_branch and append code it generates.
1229 my $post_branch = $srm->post_branch( $rule->{'pop'} );
1230 $pir .= "### post_branch\n";
1231 $pir .= sub_meta( $post_branch, $mv, "post_branch for rule $rule->{'name'}" );
1232 $pir .= "### end post_branch\n";
1235 # Calls/returns (calling class)
1236 elsif ( $rule->{'class'} eq 'calling' ) {
1238 # Init destination types array and params array and set meta-variable.
1239 $pir .= " dtypes = new ResizablePMCArray\n";
1240 $pir .= " c_params = new ResizableStringArray\n";
1241 $mv->{'PARAMS'} = 'c_params';
1242 push @localmv, 'PARAMS';
1244 # If we don't have an ARG0, we'll fake it out. This is needed as ret will
1245 # never have a method to call, thus no argument, but we still want to use
1246 # this mechanism. Ugly, huh?
1247 unless ( $mv->{'ARG0'} ) {
1248 $mv->{'ARG0'} = 'I_arg_0';
1249 push @localmv, 'ARG0';
1252 # Insert typeinfo code (sets up dtypes as needed).
1253 $pir .= "### typeinfo\n";
1254 $pir .= sub_meta( $rule->{'typeinfo'}, $mv, "typeinfo for rule $rule->{'name'}" );
1255 $pir .= "\n" if $pir !~ /\n$/;
1256 $pir .= "### end typeinfo\n";
1258 # Now call pre_call and append code that it generates.
1259 my $pre_call = $srm->pre_call();
1260 $pir .= "### pre_call\n";
1261 $pir .= sub_meta( $pre_call, $mv, "pre_call for rule $rule->{'name'}" );
1262 $pir .= "### end pre_call\n";
1264 # If we have PIR for the instruction, just take that. If not, we need
1265 # to generate it from the "to generate" instruction directive.
1266 $pir .= "### translation\n";
1267 if ( $rule->{'pir'} ) {
1268 $pir .= sub_meta( $rule->{'pir'}, $mv, "pir for rule $rule->{'name'}" );
1270 else {
1271 $pir .= sub_meta( ins_to_pir( $rule->{'instruction'} ),
1272 $mv, "pir for rule $rule->{'name'}" );
1274 $pir .= "\n" unless $pir =~ /\n$/;
1275 $pir .= "### end translation\n";
1277 # Emit code to fix up the stack type array.
1278 $pir .= <<PIR;
1279 pop_count = elements c_params
1280 goto INS_TRANS_CP_LOOP_CHK_$name
1281 INS_TRANS_CP_LOOP_$name:
1282 type_trans = pop stypes
1283 dec pop_count
1284 INS_TRANS_CP_LOOP_CHK_$name:
1285 if pop_count != 0 goto INS_TRANS_CP_LOOP_$name
1286 pop_count = elements dtypes
1287 if pop_count == 0 goto INS_TRANS_PUSH_LOOP_$name
1288 type_trans = dtypes[0]
1289 push stypes, type_trans
1290 INS_TRANS_PUSH_LOOP_$name:
1293 # Finally, call post_call and append code it generates.
1294 my $post_call = $srm->post_call();
1295 $pir .= "### post_call\n";
1296 $pir .= sub_meta( $post_call, $mv, "post_call for rule $rule->{'name'}" );
1297 $pir .= "### end post_call\n";
1300 # Unsupported class.
1301 else {
1302 die "Do not know how to handle class $rule->{'class'}\n";
1305 # Finally, emit code to go to translate next instruction.
1306 $pir .= " goto TRANS_LOOP\n\n";
1308 # Clean up meta-variables hash.
1309 foreach (@localmv) {
1310 delete $mv->{$_};
1313 # Return generated code.
1314 return $pir;
1317 # Instruction to PIR routine.
1318 # ###########################
1319 sub ins_to_pir {
1320 my $ins = shift;
1321 my $mv = shift;
1322 my $output;
1324 # Ensure we have a newline at the end.
1325 $ins .= "\n" unless $ins =~ /\n$/;
1327 # Escape some characters that will go into the output.
1328 $ins =~ s/\\/\\\\/g;
1329 $ins =~ s/\n/\\n/g;
1330 $ins =~ s/"/\\"/g;
1332 # Substitute in meta-variables. Yes, this really is the least evil way I
1333 # can think of to do it.
1334 $ins =~ s/(\$\{\w+\})/
1335 "\"\n\${INS} = concat $1\n\${INS} = concat \""
1336 /ge;
1337 $ins = "\${INS} = concat \"$ins\"\n";
1339 # Return PIR.
1340 return $ins;
1343 # Generate the translator trailer code.
1344 # #####################################
1345 sub generate_final_pir {
1346 my $srm = shift;
1347 my $mv = shift;
1349 # Emit complete label.
1350 my $pir .= "COMPLETE:\n";
1352 # SRM post translation code
1353 $pir .= "### post_translation\n";
1354 my $srm_pt = $srm->post_translation();
1355 $pir .= sub_meta( $srm_pt, $mv, 'post_translation' );
1356 $pir .= "### end post_translation\n";
1358 # Emit the end of the translator PIR.
1359 $pir .= <<TRANSPIR;
1360 .return(gen_pir)
1361 .end
1362 TRANSPIR
1364 # Return generated code.
1365 return $pir;
1368 # Inserts auto-magically instantiated meta-variable locals.
1369 # #########################################################
1370 sub insert_automagicals {
1371 my $pir = shift;
1372 my $mv = shift;
1374 # Loop over keys to look for automagicals and build up declaration list.
1375 my $decls = "";
1376 for ( keys %$mv ) {
1377 if ( /^([INSP])_ARG_(\d+)$/ || /^([INSP])TEMP(\d+)$/ ) {
1378 my $type =
1379 $1 eq 'I' ? 'int'
1380 : $1 eq 'N' ? 'num'
1381 : $1 eq 'S' ? 'string'
1382 : 'pmc';
1383 $decls .= " .local $type $mv->{$_}\n";
1387 # Insert 'em.
1388 $pir =~ s/[ \t]*\$\{AUTO_MAGICALS\}/$decls/;
1389 return $pir;
1392 # Substiture meta variables.
1393 # ##########################
1394 sub sub_meta {
1395 my $pir = shift;
1396 my $mv = shift;
1397 my $code_source = shift;
1398 $code_source ||= "(unknown)";
1400 # Substiture in known meta-variables.
1401 for ( keys %$mv ) {
1402 $pir =~ s/\${$_}/$mv->{$_}/g;
1405 # We need to automagically instantiate [INSP]_ARG_\d+ and [INSP]TEMP\d+.
1406 while ( $pir =~ /\$\{([INSP])_ARG_(\d+)\}/g ) {
1407 my $key = "${1}_ARG_$2";
1408 my $value = "${1}_arg_$2";
1409 $mv->{$key} = $value;
1410 $pir =~ s/\$\{$key\}/$value/g;
1412 while ( $pir =~ /\$\{([INSP])TEMP(\d+)\}/g ) {
1413 my $key = "${1}TEMP$2";
1414 my $value = "${1}_temp_$2";
1415 $mv->{$key} = $value;
1416 $pir =~ s/\$\{$key\}/$value/g;
1419 # If we have any unsubstituted variables, error.
1420 if ( $pir =~ /\$\{([^}]*)}/ ) {
1421 die "Unknown metavariable $1 used in $code_source\n";
1423 else {
1424 return $pir;
1428 # Usage message.
1429 # ##############
1430 sub usage {
1431 print <<USAGE;
1432 Usage:
1433 perl build/translator.pl src/translation.rules --srm Stack \
1434 --ouptput src/it.pir
1435 USAGE
1436 exit(1);
1439 # Local Variables:
1440 # mode: cperl
1441 # cperl-indent-level: 4
1442 # fill-column: 100
1443 # End:
1444 # vim: expandtab shiftwidth=4: