fixed fxch emitter
[libfirm] / ir / be / scripts / generate_new_opcodes.pl
1 #!/usr/bin/perl -w
2
3 # This script generates the C code which creates the irop's and
4 # their coresponding node constructors for all operations in a given spec
5 # so they can be used as normal firm nodes.
6 # Creation: 2005/10/19
7 # $Id$
8
9 use strict;
10 use Data::Dumper;
11
12 my $specfile   = $ARGV[0];
13 my $target_dir = $ARGV[1];
14 my $state      = 1;
15 my $cur_op     = "";
16 my $line_nr    = 0;
17
18 our $arch;
19 our $additional_opcodes;
20 our %nodes;
21
22 # include spec file
23
24 my $return;
25
26 no strict "subs";
27 unless ($return = do $specfile) {
28         warn "couldn't parse $specfile: $@" if $@;
29         warn "couldn't do $specfile: $!"    unless defined $return;
30         warn "couldn't run $specfile"       unless $return;
31 }
32 use strict "subs";
33
34 my $target_c = $target_dir."/gen_".$arch."_new_nodes.c.inl";
35 my $target_h = $target_dir."/gen_".$arch."_new_nodes.h";
36
37 #print Dumper(%nodes);
38
39 # create c code file from specs
40
41 my @obst_opvar;       # stack for the "ir_op *op_<arch>_<op-name> = NULL;" statements
42 my @obst_get_opvar;   # stack for the get_op_<arch>_<op-name>() functions
43 my @obst_constructor; # stack for node constructor functions
44 my @obst_new_irop;    # stack for the new_ir_op calls
45 my @obst_header;      # stack for function prototypes
46 my @obst_is_archirn;  # stack for the is_$arch_irn() function
47 my @obst_cmp_attr;    # stack for the compare attribute functions
48 my $orig_op;
49 my $arity;
50 my $cmp_attr_func;
51 my $temp;
52 my $n_opcodes = 2;    # we have two additional border opcodes (lowest/highest)
53
54 # for registering additional opcodes
55 $n_opcodes += $additional_opcodes if (defined($additional_opcodes));
56
57 push(@obst_header, "void ".$arch."_create_opcodes(void);\n");
58
59 foreach my $op (keys(%nodes)) {
60         my %n = %{ $nodes{"$op"} };
61
62         # determine arity from in requirements
63         $arity = 0;
64         if (exists($n{"reg_req"}) && exists($n{"reg_req"}{"in"})) {
65                 $arity = scalar(@{ $n{"reg_req"}{"in"} });
66         }
67
68         $orig_op = $op;
69         $op      = $arch."_".$op;
70         $temp    = "";
71
72         push(@obst_opvar, "ir_op *op_$op = NULL;\n");
73         push(@obst_get_opvar, "ir_op *get_op_$op(void)         { return op_$op; }\n");
74         push(@obst_get_opvar, "int    is_$op(const ir_node *n) { return get_irn_op(n) == op_$op; }\n\n");
75
76         push(@obst_is_archirn, "is_$op(node)");
77
78         push(@obst_header, "extern ir_op *op_$op;\n");
79         push(@obst_header, "ir_op *get_op_$op(void);\n");
80         push(@obst_header, "int is_$op(const ir_node *n);\n");
81
82         $cmp_attr_func = 0;
83         # create compare attribute function if needed
84         if (exists($n{"cmp_attr"})) {
85                 push(@obst_cmp_attr, "static int cmp_attr_$op(ir_node *a, ir_node *b) {\n");
86                 push(@obst_cmp_attr, "  $arch\_attr_t *attr_a = get_$arch\_attr(a);\n");
87                 push(@obst_cmp_attr, "  $arch\_attr_t *attr_b = get_$arch\_attr(b);\n");
88                 push(@obst_cmp_attr, $n{"cmp_attr"});
89                 push(@obst_cmp_attr, "}\n\n");
90
91                 $cmp_attr_func = 1;
92         }
93
94         if (exists($n{"rd_constructor"}) && $n{"rd_constructor"} =~ /^NONE$/i) {
95                 # we explicitly skip the constructor if the specification entry says NONE
96         }
97         else {
98                 $n{"comment"} = "construct $op" if(!exists($n{"comment"}));
99                 $n{"comment"} =~ s/^"|"$//g;    # remove "
100                 $n{"comment"} = "/* ".$n{"comment"}." */\n";
101                 push(@obst_constructor, $n{"comment"});
102
103                 # create constructor head
104                 my $complete_args = "";
105                 my $arg_names     = "";
106                 $temp             = "";
107
108                 $temp = "ir_node *new_rd_$op(dbg_info *db, ir_graph *irg, ir_node *block";
109                 if (!exists($n{"args"}) || $n{"args"} =~ /^DEFAULT$/i) { # default args
110                         if ($arity !~ /^\d+$/) {
111                                 print "DEFAULT args require numeric arity (0, 1, 2, ...)! Ignoring op $orig_op!\n";
112                                 next;
113                         }
114                         for (my $i = 1; $i <= $arity; $i++) {
115                                 $complete_args .= ", ir_node *op".$i;
116                                 $arg_names     .= ", op".$i;
117                         }
118                         $complete_args .= ", ir_mode *mode";
119                         $arg_names     .= ", mode";
120                 }
121                 else { # user defined args
122                         for my $href (@{ $n{"args"} }) {
123                                 $href->{"type"} .= " " if ($href->{"type"} !~ / [*]?$/); # put a space between name and type if there is none at the end
124                                 $complete_args  .= ", ".$href->{"type"}.$href->{"name"};
125                                 $arg_names      .= ", ".$href->{"name"};
126                         }
127                 }
128
129                 # we have additional attribute arguements
130                 if (exists($n{"attr"})) {
131                         $complete_args .= ", ".$n{"attr"};
132                 }
133
134                 $complete_args = substr($complete_args, 2);
135                 $temp .= ", $complete_args)";
136                 push(@obst_constructor, $temp." {\n");
137                 push(@obst_header, $temp.";\n");
138
139                 # emit constructor code
140                 if (!exists($n{"rd_constructor"}) || $n{"rd_constructor"} =~ /^DEFAULT$/i) { # default constructor
141                         if ($arity !~ /^\d+$/) {
142                                 print "DEFAULT rd_constructor requires numeric arity! Ignoring op $orig_op!\n";
143                                 next;
144                         }
145
146                         $temp  = "  ir_node *res;\n";
147                         $temp .= "  ir_node *in[$arity];\n" if ($arity > 0);
148                         $temp .= "  int flags = 0;\n";
149                         $temp .= "  $arch\_attr_t *attr;\n" if (exists($n{"init_attr"}));
150
151                         undef my $in_req_var;
152                         undef my $out_req_var;
153
154                         # set up static variables for requirements and registers
155                         if (exists($n{"reg_req"})) {
156                                 my %req = %{ $n{"reg_req"} };
157                                 my $idx;
158
159                                 undef my @in;
160                                 @in = @{ $req{"in"} } if (exists($req{"in"}));
161                                 undef my @out;
162                                 @out = @{ $req{"out"} } if exists(($req{"out"}));
163
164                                 if (@in) {
165                                         $in_req_var = "_in_req_$op";
166                                         $temp .= "  static const $arch\_register_req_t *".$in_req_var."[] =\n  {\n";
167                                         for ($idx = 0; $idx <= $#in; $idx++) {
168                                                 $temp .= "    ".$op."_reg_req_in_".$idx.",\n";
169                                         }
170                                         $temp .= "  };\n";
171                                 }
172
173                                 if (@out) {
174                                         $out_req_var = "_out_req_$op";
175
176                                         $temp .= "  static const $arch\_register_req_t *".$out_req_var."[] =\n  {\n";
177                                         for ($idx = 0; $idx <= $#out; $idx++) {
178                                                 $temp .= "    ".$op."_reg_req_out_".$idx.",\n";
179                                         }
180                                         $temp .= "  };\n";
181                                 }
182                         }
183
184                         $temp .= "\n";
185                         $temp .= "  if (!op_$op) {\n";
186                         $temp .= "    assert(0);\n";
187                         $temp .= "    return NULL;\n";
188                         $temp .= "  }\n\n";
189                         for (my $i = 1; $i <= $arity; $i++) {
190                                 $temp .= "  in[".($i - 1)."] = op".$i.";\n";
191                         }
192
193                         # set flags
194                         if (exists($n{"irn_flags"})) {
195                                 foreach my $flag (split(/\|/, $n{"irn_flags"})) {
196                                         if ($flag eq "R") {
197                                                 $temp .= "  flags |= arch_irn_flags_rematerializable;   /* op can be easily recalulated */\n";
198                                         }
199                                         elsif ($flag eq "N") {
200                                                 $temp .= "  flags |= arch_irn_flags_dont_spill;         /* op is NOT spillable */\n";
201                                         }
202                                         elsif ($flag eq "I") {
203                                                 $temp .= "  flags |= arch_irn_flags_ignore;             /* ignore op for register allocation */\n";
204                                         }
205                                 }
206                         }
207
208                         my $in_param;
209                         my $out_param;
210                         # allocate memory and set pointer to register requirements
211                         if (exists($n{"reg_req"})) {
212                                 my %req = %{ $n{"reg_req"} };
213
214                                 undef my @in;
215                                 @in = @{ $req{"in"} } if (exists($req{"in"}));
216                                 undef my @out;
217                                 @out = @{ $req{"out"} } if exists(($req{"out"}));
218
219                                 if (@in) {
220                                         $in_param = $in_req_var;
221                                 }
222                                 else {
223                                         $in_param = "NULL";
224                                 }
225
226                                 if (@out) {
227                                         $out_param = $out_req_var.", ".($#out + 1);
228                                 }
229                                 else {
230                                         $out_param = "NULL, 0";
231                                 }
232                         }
233                         $temp .= "\n  /* create node */\n";
234                         $temp .= "  res = new_ir_node(db, irg, block, op_$op, mode, $arity, ".($arity > 0 ? "in" : "NULL").");\n";
235
236                         $temp .= "\n  /* init node attributes */\n";
237                         $temp .= "  init_$arch\_attributes(res, flags, $in_param, $out_param);\n";
238
239                         if (exists($n{"init_attr"})) {
240                                 $temp .= "  attr = get_$arch\_attr(res);\n";
241                                 $temp .= $n{"init_attr"}."\n";
242                         }
243
244                         $temp .= "\n  /* optimize node */\n";
245                         $temp .= "  res = optimize_node(res);\n";
246                         $temp .= "  irn_vrfy_irg(res, irg);\n\n";
247
248                         $temp .= "\n  return res;\n";
249
250                         push(@obst_constructor, $temp);
251                 }
252                 else { # user defined constructor
253                         push(@obst_constructor, $n{"rd_constructor"});
254                 }
255
256                 # close constructor function
257                 push(@obst_constructor, "}\n\n");
258
259         } # constructor creation
260
261         # set default values for state and flags if not given
262         $n{"state"}    = "floats" if (! exists($n{"state"}));
263         $n{"op_flags"} = "N"      if (! exists($n{"op_flags"}));
264
265         push(@obst_new_irop, "\n  memset(&ops, 0, sizeof(ops));\n");
266         push(@obst_new_irop, "  ops.dump_node     = dump_node_$arch;\n");
267
268         if ($cmp_attr_func) {
269                 push(@obst_new_irop, "  ops.node_cmp_attr = cmp_attr_$op;\n");
270         }
271
272         $n_opcodes++;
273         $temp  = "  op_$op = new_ir_op(cur_opcode++, \"$op\", op_pin_state_".$n{"state"}.", ".$n{"op_flags"};
274         $temp .= "|M, ".translate_arity($arity).", 0, sizeof($arch\_attr_t), &ops);\n";
275         push(@obst_new_irop, $temp);
276 }
277
278 # emit the code
279
280 open(OUT, ">$target_c") || die("Could not open $target_c, reason: $!\n");
281
282 print OUT "#include \"gen_$arch\_regalloc_if_t.h\"\n\n";
283 print OUT @obst_cmp_attr;
284 print OUT "\n";
285 print OUT @obst_opvar;
286 print OUT "\n";
287 print OUT @obst_get_opvar;
288 print OUT "\n";
289
290 print OUT<<ENDOFISIRN;
291
292 static opcode $arch\_opcode_start = -1;
293 static opcode $arch\_opcode_end   = -1;
294
295 opcode get_$arch\_opcode_first(void) {
296   return $arch\_opcode_start + 1;
297 }
298
299 opcode get_$arch\_opcode_last(void) {
300   return $arch\_opcode_end - 1;
301 }
302
303 int is_$arch\_irn(const ir_node *node) {
304   opcode opc = get_irn_opcode(node);
305
306   assert($arch\_opcode_start > 0 && "missing opcode init");
307   assert($arch\_opcode_end > 0 && "missing opcode init");
308
309   if (opc > $arch\_opcode_start && opc < $arch\_opcode_end)
310     return 1;
311
312   return 0;
313 }
314
315 ENDOFISIRN
316
317 print OUT @obst_constructor;
318
319 print OUT<<ENDOFMAIN;
320 /**
321  * Creates the $arch specific firm operations
322  * needed for the assembler irgs.
323  */
324 void $arch\_create_opcodes(void) {
325 #define N   irop_flag_none
326 #define L   irop_flag_labeled
327 #define C   irop_flag_commutative
328 #define X   irop_flag_cfopcode
329 #define I   irop_flag_ip_cfopcode
330 #define F   irop_flag_fragile
331 #define Y   irop_flag_forking
332 #define H   irop_flag_highlevel
333 #define c   irop_flag_constlike
334 #define K   irop_flag_keep
335 #define M   irop_flag_machine
336 #define R   (irop_flag_machine<<1)
337
338   ir_op_ops ops;
339   int cur_opcode = get_next_ir_opcodes($n_opcodes);
340
341   $arch\_opcode_start = cur_opcode++;
342
343 ENDOFMAIN
344
345 print OUT @obst_new_irop;
346 print OUT "\n  $arch\_register_additional_opcodes(cur_opcode);\n";
347 print OUT "  $arch\_opcode_end = cur_opcode";
348 print OUT " + $additional_opcodes" if (defined($additional_opcodes));
349 print OUT ";\n";
350 print OUT "}\n";
351
352 close(OUT);
353
354 open(OUT, ">$target_h") || die("Could not open $target_h, reason: $!\n");
355
356 print OUT "int is_$arch\_irn(const ir_node *node);\n\n";
357 print OUT "opcode get_$arch\_opcode_first(void);\n";
358 print OUT "opcode get_$arch\_opcode_last(void);\n\n";
359 print OUT @obst_header;
360
361 close(OUT);
362
363 ###
364 # Translates numeric arity into string constant.
365 ###
366 sub translate_arity {
367         my $arity = shift;
368
369         if ($arity =~ /^\d+$/) {
370                 if    ($arity == 0) {
371                         return "oparity_zero";
372                 }
373                 elsif ($arity == 1) {
374                         return "oparity_unary";
375                 }
376                 elsif ($arity == 2) {
377                         return "oparity_binary";
378                 }
379                 elsif ($arity == 3) {
380                         return "oparity_trinary";
381                 }
382                 else {
383                         return "$arity";
384                 }
385         }
386         else {
387                 return "oparity_".$arity;
388         }
389 }