12c780bde05452e22e31aa3f3372f7d91b5331b0
[libfirm] / ir / be / scripts / generate_emitter.pl
1 #!/usr/bin/perl -w
2
3 #
4 # Copyright (C) 1995-2008 University of Karlsruhe.  All right reserved.
5 #
6 # This file is part of libFirm.
7 #
8 # This file may be distributed and/or modified under the terms of the
9 # GNU General Public License version 2 as published by the Free Software
10 # Foundation and appearing in the file LICENSE.GPL included in the
11 # packaging of this file.
12 #
13 # Licensees holding valid libFirm Professional Edition licenses may use
14 # this file in accordance with the libFirm Commercial License.
15 # Agreement provided with the Software.
16 #
17 # This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
18 # WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 # PURPOSE.
20 #
21
22 # This script generates C code which emits assembler code for the
23 # assembler ir nodes. It takes a "emit" key from the node specification
24 # and substitutes lines starting with . with a corresponding fprintf().
25 # Creation: 2005/11/07
26
27 use strict;
28 use Data::Dumper;
29 use File::Basename;
30
31 my $myname = $0;
32 our $specfile   = $ARGV[0];
33 our $target_dir = $ARGV[1];
34
35 our $arch;
36 our $comment_string = "/*";
37 our $comment_string_end = "*/" ;
38 our %nodes;
39 our $new_emit_syntax = 1;
40
41 # include spec file
42
43 my $return;
44
45 no strict "subs";
46 unless ($return = do $specfile) {
47         die "Fatal error: couldn't parse $specfile: $@" if $@;
48         die "Fatal error: couldn't do $specfile: $!"    unless defined $return;
49         die "Fatal error: couldn't run $specfile"       unless $return;
50 }
51 use strict "subs";
52
53 if ($new_emit_syntax) {
54         my $newscript = dirname($myname) . "/generate_emitter_new.pl";
55         unless ($return = do "$newscript") {
56                 die "Fatal error: couldn't parse $newscript: $@" if $@;
57                 die "Fatal error: couldn't do $newscript: $!"    unless defined $return;
58                 die "Fatal error: couldn't run $newscript"       unless $return;
59         }
60         exit;
61 }
62
63 my $comment_string_quoted = quotemeta($comment_string);
64
65 my $target_c = $target_dir."/gen_".$arch."_emitter.c";
66 my $target_h = $target_dir."/gen_".$arch."_emitter.h";
67
68 # stacks for output
69 my @obst_func;   # stack for the emit functions
70 my @obst_register;  # stack for emitter register code
71 my $line;
72
73 foreach my $op (keys(%nodes)) {
74         my %n = %{ $nodes{"$op"} };
75
76         # skip this node description if no emit information is available
77         next if (!defined($n{"emit"}));
78
79         $line = "static void emit_".$arch."_".$op."(const ir_node *n, $arch\_emit_env_t *env)";
80
81         push(@obst_register, "  BE_EMIT($op);\n");
82
83         if($n{"emit"} eq "") {
84                 push(@obst_func, $line." {\n");
85                 push(@obst_func, "}\n\n");
86                 next;
87         }
88
89         push(@obst_func, $line." {\n  FILE *F = env->out;\n");
90         push(@obst_func, "  char cmd_buf[256], cmnt_buf[256];\n");
91         push(@obst_func, "  const lc_arg_env_t *arg_env = $arch\_get_arg_env();\n\n");
92         my @emit = split(/\n/, $n{"emit"});
93
94         foreach my $template (@emit) {
95                 # substitute only lines, starting with a '.'
96                 if ($template =~ /^(\d*)\.\s*/) {
97                         my $indent = "  "; # default indent is 2 spaces
98
99                         $indent = " " x $1 if ($1 && $1 > 0);
100                         # remove indent, dot and trailing spaces
101                         $template =~ s/^\d*\.\s*//;
102                         my $fmt = $template;
103                         my $cnt = 0;
104                         my $buf = 'cmd_buf';
105
106                         push(@obst_func, $indent."cmnt_buf[0] = '\\0';\n");
107                         foreach $template (split(/$comment_string_quoted/, $fmt, 2)) {
108                                 my @params;
109                                 my $res = "";
110                                 $cnt++;
111
112                                 $template =~ s/(\\t)*$//;
113
114                                 if ($cnt == 2) {
115                                         # add the comment begin string
116                                         $res .= $comment_string;
117                                         $buf  = "cmnt_buf";
118                                 }
119
120                                 # substitute all format parameter
121                                 while ($template =~ /(\%\%)|\%([ASDX])(\d)|\%([COM])|\%(\w+)/) {
122                                         $res  .= $`;      # get everything before the match
123
124                                         if ($1) {
125                                                 $res .= "%%";
126                                         }
127                                         elsif ($2 && $2 eq "S") {
128                                                 push(@params, "n");
129                                                 $res .= "%".$3."S"; # substitute %Sx with %xS
130                                         }
131                                         elsif ($2 && $2 eq "D") {
132                                                 push(@params, "n");
133                                                 $res .= "%".$3."D"; # substitute %Dx with %xD
134                                         }
135                                         elsif ($2 && $2 eq "X") {
136                                                 push(@params, "n");
137                                                 $res .= "%".$3."X"; # substitute %Xx with %xX
138                                         }
139                                         elsif ($2 && $2 eq "A") {
140                                                 push(@params, "get_irn_n(n, ".($3 - 1).")");
141                                                 $res .= "%+F";
142                                         }
143                                         elsif ($4) {
144                                                 push(@params, "n");
145                                                 $res .= "%".$4;
146                                         }
147                                         elsif ($5) {  # backend provided function to call, has to return a string
148                                                 push(@params, $5."(n, env)");
149                                                 $res .= "\%s";
150                                         }
151
152                                         $template = $'; # scan everything after the match
153                                 }
154                                 $res .= $template; # get the remaining string
155
156                                 my $parm = "";
157                                 $parm = ", ".join(", ", @params) if (@params);
158
159                                 push(@obst_func, $indent.'lc_esnprintf(arg_env, '.$buf.', 256, "'.$res.'"'.$parm.');'."\n");
160                         }
161                         push(@obst_func, $indent.'lc_efprintf(arg_env, F, "\t%-35s %-60s '.$comment_string.' %+F (%+G) '.$comment_string_end.'\n", cmd_buf, cmnt_buf, n, n);'."\n");
162                 }
163                 else {
164                         push(@obst_func, $template,"\n");
165                 }
166         }
167
168         push(@obst_func, "}\n\n");
169 }
170
171 open(OUT, ">$target_h") || die("Fatal error: Could not open $target_h, reason: $!\n");
172
173 my $creation_time = localtime(time());
174
175 my $tmp = uc($arch);
176
177 print OUT<<EOF;
178 /**
179  * \@file
180  * \@brief Function prototypes for the emitter functions.
181  * \@note  DO NOT EDIT THIS FILE, your changes will be lost.
182  *        Edit $specfile instead.
183  *        created by: $0 $specfile $target_dir
184  * \@date  $creation_time
185  */
186 #ifndef FIRM_BE_${tmp}_GEN_${tmp}_EMITTER_H
187 #define FIRM_BE_${tmp}_GEN_${tmp}_EMITTER_H
188
189 #include "irnode.h"
190 #include "$arch\_emitter.h"
191
192 void $arch\_register_spec_emitters(void);
193
194 #endif
195
196 EOF
197
198 close(OUT);
199
200 open(OUT, ">$target_c") || die("Fatal error: Could not open $target_c, reason: $!\n");
201
202 $creation_time = localtime(time());
203
204 print OUT<<EOF;
205 /**
206  * \@file
207  * \@brief  Generated functions to emit code for assembler ir nodes.
208  * \@note   DO NOT EDIT THIS FILE, your changes will be lost.
209  *         Edit $specfile instead.
210  *         created by: $0 $specfile $target_dir
211  * \@date   $creation_time
212  */
213 #include "config.h"
214
215 #include <stdio.h>
216
217 #include "irnode.h"
218 #include "irop_t.h"
219 #include "irprog_t.h"
220
221 #include "gen_$arch\_emitter.h"
222 #include "$arch\_new_nodes.h"
223
224 EOF
225
226 print OUT @obst_func;
227
228 print OUT<<EOF;
229 /**
230  * Enters the emitter functions for handled nodes into the generic
231  * pointer of an opcode.
232  */
233 void $arch\_register_spec_emitters(void) {
234
235 #define BE_EMIT(a) op_$arch\_##a->ops.generic = (op_func)emit_$arch\_##a
236
237   /* generated emitter functions */
238 EOF
239
240 print OUT @obst_register;
241
242 print OUT<<EOF;
243
244 #undef BE_EMIT
245 }
246
247 EOF
248
249 close(OUT);