98beec1d2364113f756bd2ecb24d84fc148d393b
[libfirm] / ir / be / scripts / generate_emitter.pl
1 #!/usr/bin/perl -w
2
3 # This script generates C code which emits assembler code for the
4 # assembler ir nodes. It takes a "emit" key from the node specification
5 # and substitutes lines starting with . with a corresponding fprintf().
6 # Creation: 2005/11/07
7 # $Id$
8
9 use strict;
10 use Data::Dumper;
11
12 my $specfile   = $ARGV[0];
13 my $target_dir = $ARGV[1];
14
15 our $arch;
16 our %nodes;
17
18 # include spec file
19
20 my $return;
21
22 no strict "subs";
23 unless ($return = do $specfile) {
24   warn "couldn't parse $specfile: $@" if $@;
25   warn "couldn't do $specfile: $!"    unless defined $return;
26   warn "couldn't run $specfile"       unless $return;
27 }
28 use strict "subs";
29
30 my $target_c = $target_dir."/gen_".$arch."_emitter.c";
31 my $target_h = $target_dir."/gen_".$arch."_emitter.h";
32
33 # stacks for output
34 my @obst_func;   # stack for the emit functions
35 my @obst_header;  # stack for the function prototypes
36
37 my $line;
38
39 foreach my $op (keys(%nodes)) {
40   my %n = %{ $nodes{"$op"} };
41
42   # skip this node description if no emit information is available
43   next if (!$n{"emit"} || length($n{"emit"}) < 1);
44
45   $line = "void emit_".$arch."_".$op."(FILE *F, ir_node *n)";
46   push(@obst_header, $line.";\n");
47   push(@obst_func, $line." {\n");
48
49   # check in/out register if needed
50   if (exists($n{"check_inout"}) && $n{"check_inout"} == 1) {
51     push(@obst_func, "  equalize_dest_src(F, n);\n\n");
52   }
53
54   my @emit = split(/\n/, $n{"emit"});
55
56   foreach(@emit) {
57     # substitute only lines, starting with a '.'
58     if (/^(\d*)\.\s*/) {
59       my @params;
60       my $regkind;
61       my $indent = "  "; # default indent is 2 spaces
62
63       $indent = " " x $1 if ($1 && $1 > 0);
64       # remove indent, dot and trailing spaces
65       s/^\d*\.\s*//;
66       # substitute all format parameter
67       while (/%(([sd])(\d)|([co]))/) {
68         if ($4 && $4 eq "c") {
69           push(@params, "node_const_to_str(n)");
70         }
71         elsif ($4 && $4 eq "o") {
72           push(@params, "node_offset_to_str(n)");
73         }
74         else {
75           $regkind = ($2 eq "s" ? "source" : "dest");
76           push(@params, "get_".$regkind."_reg_name(n, $3)");
77         }
78         s/%$1/%%\%s/;
79       }
80       my $parm = "";
81       $parm = ", ".join(", ", @params) if (@params);
82       push(@obst_func, $indent.'fprintf(F, "\t'.$_.'\n"'.$parm.');'."\n");
83     }
84     else {
85       push(@obst_func, $_,"\n");
86     }
87   }
88   push(@obst_func, "}\n\n");
89 }
90
91 open(OUT, ">$target_h") || die("Could not open $target_h, reason: $!\n");
92
93 my $creation_time = localtime(time());
94
95 my $tmp = uc($arch);
96
97 print OUT<<EOF;
98 #ifndef _GEN_$tmp\_EMITTER_H_
99 #define _GEN_$tmp\_EMITTER_H_
100
101 /**
102  * Function prototypes for the emitter functions.
103  * DO NOT EDIT THIS FILE, your changes will be lost.
104  * Edit $specfile instead.
105  * created by: $0 $specfile $target_dir
106  * date:       $creation_time
107  */
108
109 #include "irnode.h"
110
111 EOF
112
113 print OUT @obst_header;
114
115 print OUT "#endif /* _GEN_$tmp\_EMITTER_H_ */\n";
116
117 close(OUT);
118
119 open(OUT, ">$target_c") || die("Could not open $target_c, reason: $!\n");
120
121 $creation_time = localtime(time());
122
123 print OUT<<EOF;
124 /**
125  * Generated functions to emit code for assembler ir nodes.
126  * DO NOT EDIT THIS FILE, your changes will be lost.
127  * Edit $specfile instead.
128  * created by: $0 $specfile $target_dir
129  * date:       $creation_time
130  */
131
132 #include <stdio.h>
133
134 #include "irnode.h"
135 #include "gen_$arch\_emitter.h"
136 #include "$arch\_emitter.h"
137 #include "$arch\_new_nodes.h"
138
139 EOF
140
141 print OUT @obst_func;
142
143 close(OUT);