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