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.
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
22 my ( $rules_file, $output_file, $srm_module );
24 "output=s" => \
$output_file,
25 "srm=s" => \
$srm_module
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.
36 if ( -e
$rules_file ) {
37 @rules = parse_rules
($rules_file);
40 die "Error: Cannot load rules file $rules_file: $!\n";
43 # Create metavariables table.
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.
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";
73 # And display count of ops that can be translated.
74 print "Currently able to translate " . scalar(@rules) . " out of 213 instructions.\n";
76 ##############################################################################
78 ##############################################################################
80 # Parse rules file and build a data structure.
81 # ############################################
84 # Get filename and open the file.
86 open my $fh, "<", "$filename" or die "Unable to open $filename: $!\n";
88 # We'll store an array of hashes containing the data.
92 # Read through rules file line by line.
95 my ( $heredoc_key, $heredoc_value, $heredoc_terminator );
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...
111 validate_rule
($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.
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.
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;
146 # Is this heredoc content?
147 elsif ($in_heredoc) {
148 $heredoc_value .= "$_\n";
151 # Otherwise, syntax eror.
153 die "Syntax error on line $line (\"$_\")\n";
157 # If we're still in a heredoc, we got an error.
159 die "Unterminated heredoc runs to end of file\n";
162 # If we've a rule left, validate and stash it.
164 validate_rule
($rule);
168 # Close file and return parsed rules.
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 ) {
192 if ( $rule->{$_} =~ /^([0-9A-F]{2}\s+)*[0-9A-F]{2}$/ ) {
194 die "Duplicate value for code in rule $rule->{'name'}\n";
201 die "Invalid value for code in rule $rule->{'name'}\n";
207 if ( $rule->{$_} =~ /^(op|load|store|branch|calling)$/ ) {
209 die "Duplicate value for class in rule $rule->{'name'}\n";
216 die "Invalid value for class in rule $rule->{'name'}\n";
222 if ( $rule->{$_} =~ /^\d+$/ ) {
224 die "Duplicate value for push in rule $rule->{'name'}\n";
231 die "Invalid value for push in rule $rule->{'name'}\n";
237 if ( $rule->{$_} =~ /^\d+$/ ) {
239 die "Duplicate value for pop in rule $rule->{'name'}\n";
246 die "Invalid value for pop in rule $rule->{'name'}\n";
251 elsif (/^arguments$/) {
253 die "Duplicate value for arguments in rule $rule->{'name'}\n";
256 my @args = split( /\s*,\s+/, $rule->{'arguments'} );
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)$/;
267 elsif (/^(instruction|pir)$/) {
269 die "Only one of instruction or pir is allowed in rule " . "$rule->{'name'}\n";
277 elsif (/^typeinfo$/) {
279 die "Duplicate value for typeinfo in rule $rule->{'name'}\n";
288 die "Unknown key $_ in rule $rule->{'name'}\n";
292 # Check we had mandatory fields.
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
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";
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
{
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 );
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!
373 .sub trans_instructions
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
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
425 if j == i goto PTYPES_LOOP_END
426 type_trans = ptypes[j]
427 type = type_trans["type"]
438 if j == i goto LTYPES_LOOP_END
439 type_trans = ltypes[j]
440 type = type_trans["type"]
449 # Get bytecode and initialize pc.
450 bc = meth.get_bytecode()
451 bc_length = bc.get_length()
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.
464 # Set up some more metavariables.
465 $mv->{'ASSEMBLY'} = 'assembly';
467 $mv->{'INS'} = 'gen_pir';
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.
488 # If we have exception handlers, need some intial stuff emitted.
490 if null ehs goto NO_EH_HEADER
492 gen_pir = concat ".local pmc cur_exception\n"
493 gen_pir = concat ".local pmc saved_ehs\nsaved_ehs = new .FixedPMCArray\nsaved_ehs = "
496 gen_pir = concat "\n"
498 gen_pir = concat "pushmark 0\n"
501 ss_propogate = new .Hash
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
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.
524 # Look through exception handlers.
525 if null ehs goto END_EH_LOOP
529 if i < 0 goto END_EH_LOOP
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()
540 gen_pir = concat "\npushmark "
544 gen_pir = concat "\n"
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
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)
563 gen_pir = concat "\nif $I1000000 goto TYPED_EH_FOUND_"
566 gen_pir = concat "\nthrow $P1000000\nTYPED_EH_FOUND_"
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.
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";
590 { %$mv, LOADREG
=> 'loadreg' },
591 "pre_load for typed exception handler"
593 $pir .= "### end pre_load (typed eh)\n";
595 loadreg = "$P1000001"
597 my $post_load = $srm->post_load(0);
598 $pir .= "### post_load (typed eh)\n";
601 { %$mv, LOADREG
=> 'loadreg' },
602 "post_load for typed exception handler"
604 $pir .= "### end post_load (typed eh)\n";
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
614 gen_pir = concat ".get_results (cur_exception, $S1000000)\nsaved_ehs["
617 gen_pir = concat "] = cur_exception\nFINALLY_"
620 gen_pir = concat ": "
628 # Return generated code.
632 # Generate the dispatch table.
633 # ############################
634 sub generate_dispatch_table
{
636 my @rules = @
{ shift() };
639 # Make a copy of the original instruction code before we trash it.
641 $_->{'orig_code'} = $_->{'code'};
644 # Put rules with the same startcode into a group.
647 $_->{'code'} =~ /^(\w{2})/;
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];
664 # Strip first code from all the groups.
665 foreach ( @
{ $groups{$_} } ) {
666 $_->{'code'} =~ s/^\w{2} //;
669 # Build a group entry.
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))
682 # Translation code dispatch table.
683 cur_ic = bc.read_uint8()
686 $pir .= binary_dispatch_table
( '', @rules_grouped );
688 # Emit unknown instruction code.
692 err = "Attempt to translate unknown instruction (code "
701 # Restore original instruction codes, but remove spaces.
703 $_->{'code'} = $_->{'orig_code'};
704 $_->{'code'} =~ s/\s//g;
707 # Return generated PIR.
711 # Binary dispatch table builder.
712 # ##############################
713 sub binary_dispatch_table
{
718 # Ensure groups have been built properly and we only dispatch one
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.
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'};
739 # Not a group; jump straight to instruction.
740 my $name = $_->{'name'};
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{$_} };
753 $a->{'code'} =~ /^(\w{2})/;
755 $b->{'code'} =~ /^(\w{2})/;
758 $pir .= "B2_BRANCH_$_:\n";
759 $pir .= " cur_ic = bc.read_uint8()\n next_pc += 1\n";
760 $pir .= binary_dispatch_table
( $_, @rules );
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 ];
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.
785 # Generate translation code relating to a rule.
786 # #############################################
787 sub generate_rule_code
{
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";
802 $pir .= "INS_TRANS_$name:\n";
806 if trace != 2 goto NO_TRACE_$name
807 printerr " $name STS: "
811 if j == i goto STS_LOOP_END_$name
812 type_trans = stypes[j]
813 type = type_trans["type"]
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';
841 $out = " \${I_ARG_$arg_num} = bc.read_uint8()\n next_pc += 1\n";
842 $arg_name = "I_ARG_$arg_num";
847 $out = " \${I_ARG_$arg_num} = bc.read_int8()\n next_pc += 1\n";
848 $arg_name = "I_ARG_$arg_num";
853 $out = " \${I_ARG_$arg_num} = bc.read_uint16()\n next_pc += 2\n";
854 $arg_name = "I_ARG_$arg_num";
859 $out = " \${I_ARG_$arg_num} = bc.read_int16()\n next_pc += 2\n";
860 $arg_name = "I_ARG_$arg_num";
865 $out = " \${I_ARG_$arg_num} = bc.read_uint32()\n next_pc += 4\n";
866 $arg_name = "I_ARG_$arg_num";
871 $out = " \${I_ARG_$arg_num} = bc.read_int32()\n next_pc += 4\n";
872 $arg_name = "I_ARG_$arg_num";
876 elsif (/^float32$/) {
877 $out = " \${N_ARG_$arg_num} = bc.read_float32()\n next_pc += 4\n";
878 $arg_name = "N_ARG_$arg_num";
882 elsif (/^float64$/) {
883 $out = " \${N_ARG_$arg_num} = bc.read_float64()\n next_pc += 8\n";
884 $arg_name = "N_ARG_$arg_num";
889 $out = " \${I_ARG_$arg_num} = bc.read_tfield()\n next_pc += 4\n";
890 $arg_name = "I_ARG_$arg_num";
894 elsif (/^tmethod$/) {
895 $out = " \${I_ARG_$arg_num} = bc.read_tmethod()\n next_pc += 4\n";
896 $arg_name = "I_ARG_$arg_num";
901 $out = " \${I_ARG_$arg_num} = bc.read_ttype()\n next_pc += 4\n";
902 $arg_name = "I_ARG_$arg_num";
906 elsif (/^tstring$/) {
907 $out = " \${I_ARG_$arg_num} = bc.read_tstring()\n next_pc += 4\n";
908 $arg_name = "I_ARG_$arg_num";
912 elsif (/^jumptable$/) {
914 \${P_ARG_$arg_num} = new FixedPMCArray
917 \${P_ARG_$arg_num} = i
920 if j == i goto JT_LOOP_END_$name
921 \$I0 = bc.read_int32()
923 \${P_ARG_$arg_num}[j] = \$I0
928 $arg_name = "P_ARG_$arg_num";
933 die "Known argument type $_ not implemented yet.\n";
937 $pir .= sub_meta
( $out, $mv, "argument read" );
938 $mv->{"ARG$arg_num"} = $mv->{$arg_name};
939 push @localmv, "ARG$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'}" );
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
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
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\}/ ) {
1035 elsif ( $rule->{'pir'} =~ /\$\{LOADREG\}/ || $rule->{'instruction'} =~ /\$\{LOADREG\}/ ) {
1037 $mv->{'LOADREG'} = 'loadreg';
1038 push @localmv, 'LOADREG';
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'}" );
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.
1069 my $label = "LD_VALTYPE_$name";
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"
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 );
1094 sub_meta
( $post_op, $mv, "post_op for rule $rule->{'name'} value type clone" );
1096 \$I1000000 = loadtype["type"]
1097 if \$I1000000 != ELEMENT_TYPE_VALUETYPE goto $label
1098 dtypes = new ResizablePMCArray
1099 dtypes[0] = loadtype
1101 gen_pir = concat dest0
1102 gen_pir = concat " = clone "
1103 gen_pir = concat stack0
1104 gen_pir = concat "\\n"
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?
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\}/ ) {
1131 elsif ( "$rule->{'pir'}$rule->{'instruction'}" =~ /\$\{STOREREG\}/ ) {
1133 $mv->{'STOREREG'} = 'storereg';
1134 push @localmv, 'STOREREG';
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'}" );
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'}" );
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
1213 sp_dest = next_pc + j
1215 ss_propogate[sp_dest] = ss
1216 goto SS_PROP_LOOP_switch
1217 SS_PROP_LOOP_EXIT_switch:
1221 $pir .= <<'PIRCODE';
1222 sp_dest = next_pc + I_arg_0
1224 ss_propogate[sp_dest] = ss
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'}" );
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.
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
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.
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) {
1313 # Return generated code.
1317 # Instruction to PIR routine.
1318 # ###########################
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;
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 \""
1337 $ins = "\${INS} = concat \"$ins\"\n";
1343 # Generate the translator trailer code.
1344 # #####################################
1345 sub generate_final_pir
{
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.
1364 # Return generated code.
1368 # Inserts auto-magically instantiated meta-variable locals.
1369 # #########################################################
1370 sub insert_automagicals
{
1374 # Loop over keys to look for automagicals and build up declaration list.
1377 if ( /^([INSP])_ARG_(\d+)$/ || /^([INSP])TEMP(\d+)$/ ) {
1381 : $1 eq 'S' ?
'string'
1383 $decls .= " .local $type $mv->{$_}\n";
1388 $pir =~ s/[ \t]*\$\{AUTO_MAGICALS\}/$decls/;
1392 # Substiture meta variables.
1393 # ##########################
1397 my $code_source = shift;
1398 $code_source ||= "(unknown)";
1400 # Substiture in known meta-variables.
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";
1433 perl build/translator.pl src/translation.rules --srm Stack \
1434 --ouptput src/it.pir
1441 # cperl-indent-level: 4
1444 # vim: expandtab shiftwidth=4: