# skip this node description if no emit information is available
next if (!$n{"emit"} || length($n{"emit"}) < 1);
- $line = "void emit_".$arch."_".$op."(FILE *F, ir_node *n)";
+ $line = "void emit_".$arch."_".$op."(ir_node *n, emit_env_t *env)";
push(@obst_header, $line.";\n");
- push(@obst_func, $line." {\n");
+ push(@obst_func, $line." {\n FILE *F = env->out;\n");
+ my $cio = 0;
# check in/out register if needed
if (exists($n{"check_inout"}) && $n{"check_inout"} == 1) {
push(@obst_func, " equalize_dest_src(F, n);\n\n");
+ $cio = 1;
}
my @emit = split(/\n/, $n{"emit"});
- foreach(@emit) {
+ foreach my $template (@emit) {
# substitute only lines, starting with a '.'
- if (/^(\d*)\.\s*/) {
+ if ($template =~ /^(\d*)\.\s*/) {
my @params;
- my $regkind;
+ my $res = "";
+ my $res2 = "";
my $indent = " "; # default indent is 2 spaces
$indent = " " x $1 if ($1 && $1 > 0);
# remove indent, dot and trailing spaces
- s/^\d*\.\s*//;
+ $template =~ s/^\d*\.\s*//;
# substitute all format parameter
- while (/%(([sd])(\d)|([co]))/) {
+ while ($template =~ /\%(([asd])(\d)|([com]))/) {
+ $res .= $`; # get everything before the match
+ $res2 .= $`;
+
if ($4 && $4 eq "c") {
- push(@params, "node_const_to_str(n)");
+ push(@params, "n");
+ $res .= "\%c";
+ $res2 .= "\%c";
}
elsif ($4 && $4 eq "o") {
- push(@params, "node_offset_to_str(n)");
+ push(@params, "n");
+ $res .= "\%o";
+ $res2 .= "\%o";
+ }
+ elsif ($4 && $4 eq "m") {
+ push(@params, "n");
+ $res .= "\%m";
+ $res2 .= "\%m";
+ }
+ elsif ($2 && $2 eq "s") {
+ push(@params, "n");
+ if ($cio && $3 == 2) {
+ # check_in_out was set: if (s1 != d1) we
+ # need to exchange s2 by s1
+ $res2 .= "%1s"; # get name for first register
+ }
+ else {
+ $res2 .= "%".$3."s"; # substitute %sx with %xs
+ }
+ $res .= "%".$3."s"; # substitute %sx with %xs
}
- else {
- $regkind = ($2 eq "s" ? "source" : "dest");
- push(@params, "get_".$regkind."_reg_name(n, $3)");
+ elsif ($2 && $2 eq "d") {
+ push(@params, "n");
+ $res .= "%".$3."d"; # substitute %sx with %xs
+ $res2 .= "%".$3."d"; # substitute %sx with %xs
}
- s/%$1/%%\%s/;
+ elsif ($2 && $2 eq "a") {
+ push(@params, "get_irn_n(n, ".($3 - 1).")");
+ $res .= "%+F";
+ $res2 .= "%+F";
+ }
+
+ $template = $'; # scan everything after the match
}
+ $res .= $template; # get the remaining string
+ $res2 .= $template; # get the remaining string
+
my $parm = "";
$parm = ", ".join(", ", @params) if (@params);
- push(@obst_func, $indent.'fprintf(F, "\t'.$_.'\n"'.$parm.');'."\n");
+
+ if ($cio) {
+ push(@obst_func, $indent."if (get_irn_arity(n) > 1 && get_$arch\_in_regnr(n, 1) == get_$arch\_out_regnr(n, 0)) {\n");
+ push(@obst_func, $indent.' lc_efprintf(ia32_get_arg_env(), F, "\t'.$res2.'\n"'.$parm.');'."\n");
+ push(@obst_func, $indent."}\n");
+ push(@obst_func, $indent."else {\n");
+ push(@obst_func, $indent.' lc_efprintf(ia32_get_arg_env(), F, "\t'.$res.'\n"'.$parm.');'."\n");
+ push(@obst_func, $indent."}\n");
+ }
+ else {
+ push(@obst_func, $indent.'lc_efprintf(ia32_get_arg_env(), F, "\t'.$res.'\n"'.$parm.');'."\n");
+ }
}
else {
- push(@obst_func, $_,"\n");
+ push(@obst_func, $template,"\n");
}
}
push(@obst_func, "}\n\n");
*/
#include "irnode.h"
+#include "$arch\_emitter.h"
EOF
#include "irnode.h"
#include "gen_$arch\_emitter.h"
-#include "$arch\_emitter.h"
#include "$arch\_new_nodes.h"
EOF
use strict "subs";
my $target_c = $target_dir."/gen_".$arch."_new_nodes.c.inl";
-my $target_h = $target_dir."/gen_".$arch."_new_nodes.h.inl";
+my $target_h = $target_dir."/gen_".$arch."_new_nodes.h";
#print Dumper(%nodes);
my @obst_new_irop; # stack for the new_ir_op calls
my @obst_header; # stack for function prototypes
my @obst_is_archirn; # stack for the is_$arch_irn() function
+my @obst_cmp_attr; # stack for the compare attribute functions
my $orig_op;
my $arity;
+my $cmp_attr_func;
push(@obst_header, "void ".$arch."_create_opcodes(void);\n");
push(@obst_header, "int is_$op(const ir_node *n);\n");
- $n{"comment"} =~ s/^"|"$//g;
+ $n{"comment"} = "construct $op" if(!exists($n{"comment"}));
+ $n{"comment"} =~ s/^"|"$//g; # remove "
$n{"comment"} = "/* ".$n{"comment"}." */\n";
push(@obst_constructor, $n{"comment"});
+ $cmp_attr_func = 0;
+ # create compare attribute function if needed
+ if (exists($n{"cmp_attr"})) {
+ push(@obst_cmp_attr, "static int cmp_attr_$op(ir_node *a, ir_node *b) {\n");
+ push(@obst_cmp_attr, " asmop_attr *attr_a = get_ia32_attr(a);\n");
+ push(@obst_cmp_attr, " asmop_attr *attr_b = get_ia32_attr(b);\n");
+ push(@obst_cmp_attr, $n{"cmp_attr"});
+ push(@obst_cmp_attr, "}\n\n");
+
+ $cmp_attr_func = 1;
+ }
+
# create constructor head
my $complete_args = "";
my $arg_names = "";
$temp .= " in[".($i - 1)."] = op".$i.";\n";
}
$temp .= " res = new_ir_node(db, irg, block, op_$op, mode, $arity, ".($arity > 0 ? "in" : "NULL").");\n";
+ $temp .= " set_ia32_pncode(res, -1);\n";
+ $temp .= " res = optimize_node(res);\n";
+ $temp .= " irn_vrfy_irg(res, irg);\n\n";
# set register flags
$temp .= " attr = get_ia32_attr(res);\n\n";
if (@in) {
$temp .= "\n /* allocate memory for IN register requirements and assigned registers */\n";
- $temp .= " attr->in_req = malloc(".($#in + 1)." * sizeof(arch_register_req_t *)); /* space for in requirements */\n";
+ $temp .= " attr->in_req = calloc(".($#in + 1).", sizeof(arch_register_req_t *)); /* space for in requirements */\n";
for ($idx = 0; $idx <= $#in; $idx++) {
- $temp .= " attr->in_req[$idx] = &".$op."_reg_req_in_".$idx.";\n";
+ $temp .= " attr->in_req[$idx] = ".$op."_reg_req_in_".$idx.";\n";
}
}
if (@out) {
$temp .= "\n /* allocate memory for OUT register requirements and assigned registers */\n";
- $temp .= " attr->out_req = malloc(".($#out + 1)." * sizeof(arch_register_req_t *)); /* space for out requirements */\n";
- $temp .= " attr->slots = calloc(sizeof(arch_register_t *), ".($#out + 1)."); /* space for assigned registers */\n";
+ $temp .= " attr->out_req = calloc(".($#out + 1).", sizeof(arch_register_req_t *)); /* space for out requirements */\n";
+ $temp .= " attr->slots = calloc(".($#out + 1).", sizeof(arch_register_t *)); /* space for assigned registers */\n";
for ($idx = 0; $idx <= $#out; $idx++) {
- $temp .= " attr->out_req[$idx] = &".$op."_reg_req_out_".$idx.";\n";
+ $temp .= " attr->out_req[$idx] = ".$op."_reg_req_out_".$idx.";\n";
}
$temp .= " attr->n_res = ".($#out + 1).";\n";
}
$n{"state"} = "pinned" if (! exists($n{"state"}));
$n{"op_flags"} = "N" if (! exists($n{"op_flags"}));
+ push(@obst_new_irop, "\n memset(&ops, 0, sizeof(ops));\n");
+ push(@obst_new_irop, " ops.dump_node = dump_node_$arch;\n");
+
+ if ($cmp_attr_func) {
+ push(@obst_new_irop, " ops.node_cmp_attr = cmp_attr_$op;\n");
+ }
+
$temp = " op_$op = new_ir_op(get_next_ir_opcode(), \"$op\", op_pin_state_".$n{"state"}.", ".$n{"op_flags"};
$temp .= ", ".translate_arity($arity).", 0, sizeof(asmop_attr), &ops);\n";
push(@obst_new_irop, $temp);
open(OUT, ">$target_c") || die("Could not open $target_c, reason: $!\n");
+print OUT @obst_cmp_attr;
+print OUT "\n";
print OUT @obst_opvar;
print OUT "\n";
print OUT @obst_get_opvar;
ir_op_ops ops;
- memset(&ops, 0, sizeof(ops));
- ops.dump_node = dump_node_$arch;
-
ENDOFMAIN
print OUT @obst_new_irop;
sub translate_arity {
my $arity = shift;
- if ($arity == 0) {
- return "oparity_zero";
- }
- elsif ($arity == 1) {
- return "oparity_unary";
- }
- elsif ($arity == 1) {
- return "oparity_binary";
- }
- elsif ($arity == 1) {
- return "oparity_trinary";
+ if ($arity =~ /^\d+$/) {
+ if ($arity == 0) {
+ return "oparity_zero";
+ }
+ elsif ($arity == 1) {
+ return "oparity_unary";
+ }
+ elsif ($arity == 2) {
+ return "oparity_binary";
+ }
+ elsif ($arity == 3) {
+ return "oparity_trinary";
+ }
+ else {
+ return "$arity";
+ }
}
else {
- return "$arity";
+ return "oparity_".$arity;
}
}
}
use strict "subs";
-my $target_c = $target_dir."/gen_".$arch."_regalloc_if.c.inl";
-my $target_h = $target_dir."/gen_".$arch."_regalloc_if.h";
+my $target_c = $target_dir."/gen_".$arch."_regalloc_if.c";
+my $target_h = $target_dir."/gen_".$arch."_regalloc_if.h";
+my $target_h_t = $target_dir."/gen_".$arch."_regalloc_if_t.h";
# helper function
my @rt = ("arch_register_type_none",
# stacks for output
my @obst_regtypes; # stack for the register type variables
my @obst_regclasses; # stack for the register class variables
-my @obst_classdef; # stack to assign a number to a certain class
+my @obst_classdef; # stack to define a name for a class index
+my @obst_regdef; # stack to define a name for a register index
my @obst_reginit; # stack for the register type inits
my @obst_req; # stack for the register requirements
my @obst_limit_func; # stack for functions to return a subset of a register class
+my @obst_defreq_head; # stack for prototypes of default requirement function
+my @obst_header_all; # stack for some extern struct defs needed for bearch_$arch include
my $numregs;
+my $class_ptr;
my $class_idx = 0;
+my $tmp;
+
my %reg2class;
-# generate register type and class variable and init function
+# there is a default NONE requirement
+$tmp = "/* Default NONE register requirements */\n";
+$tmp .= "const arch_register_req_t ia32_default_req_none = {\n";
+$tmp .= " arch_register_req_type_none,\n";
+$tmp .= " NULL,\n";
+$tmp .= " { NULL }\n";
+$tmp .= "};\n\n";
+push(@obst_req, $tmp);
+
+push(@obst_header_all, "extern arch_register_class_t $arch\_reg_classes[N_CLASSES];\n\n");
+push(@obst_header_all, "extern const arch_register_req_t ia32_default_req_none;\n");
+
+push(@obst_classdef, "#define N_CLASSES ".scalar(keys(%reg_classes))."\n");
+
+# generate register type and class variable, init function and default requirements
foreach my $class_name (keys(%reg_classes)) {
my @class = @{ $reg_classes{"$class_name"} };
my $old_classname = $class_name;
$class_name = $arch."_".$class_name;
$numregs = "N_".$class_name."_REGS";
+ $class_ptr = "&".$arch."_reg_classes[CLASS_".$class_name."]";
push(@obst_regtypes, "#define $numregs ".($#class + 1)."\n");
push(@obst_regtypes, "arch_register_t ".$class_name."_regs[$numregs];\n\n");
push(@obst_classdef, "#define CLASS_$class_name $class_idx\n");
push(@obst_regclasses, "{ \"$class_name\", $numregs, ".$class_name."_regs }");
+ # there is a default NORMAL requirement for each class
+ $tmp = "/* Default NORMAL register requirements for class $class_name */\n";
+ $tmp .= "const arch_register_req_t ia32_default_req_$class_name = {\n";
+ $tmp .= " arch_register_req_type_normal,\n";
+ $tmp .= " $class_ptr,\n";
+ $tmp .= " { NULL }\n";
+ $tmp .= "};\n\n";
+ push(@obst_req, $tmp);
+
+ push(@obst_header_all, "\nextern const arch_register_req_t ia32_default_req_$class_name;\n");
+
my $idx = 0;
push(@obst_reginit, " /* Init of all registers in class '$class_name' */\n\n");
foreach (@class) {
+ # For each class we build for each of it's member registers a limit function
+ # which limits the class to this particular register. We also build the
+ # corresponding requirement structs.
+ # We need those functions to set register requirements on demand in transformation
+ # esp. for Call and RegParams where we can mix int and float parameters.
+
+ my $limit_func_name = $arch."_limit_".$class_name."_".$_->{"name"};
+
+ # push the function prototype
+ $tmp = "int $limit_func_name(const ir_node *irn, int pos, bitset_t *bs)";
+ push(@obst_defreq_head, $tmp.";\n");
+
+ # push the function definition
+ $tmp .= " {\n";
+ $tmp .= " bs = bitset_clear_all(bs);\n";
+ $tmp .= " bitset_set(bs, REG_".uc($_->{"name"}).");\n"; # REGISTER to index assignment is done some lines down
+ $tmp .= " return 1;\n";
+ $tmp .= "}\n\n";
+ push(@obst_limit_func, $tmp);
+
+ # push the default requirement struct
+ $tmp = "const arch_register_req_t ia32_default_req_$class_name\_".$_->{"name"}." = {\n";
+ $tmp .= " arch_register_req_type_limited,\n";
+ $tmp .= " $class_ptr,\n";
+ $tmp .= " { $limit_func_name }\n";
+ $tmp .= "};\n\n";
+ push(@obst_req, $tmp);
+
+ push(@obst_header_all, "extern const arch_register_req_t ia32_default_req_$class_name\_".$_->{"name"}.";\n");
+
$reg2class{$_->{"name"}} = { "class" => $old_classname, "index" => $idx }; # remember reg to class for later use
+ push(@obst_regdef, "#define REG_".uc($_->{"name"})." $idx\n");
push(@obst_reginit, " ".$class_name."_regs[$idx].name = \"".$_->{"name"}."\";\n");
- push(@obst_reginit, " ".$class_name."_regs[$idx].reg_class = &$arch\_reg_classes[CLASS_$class_name];\n");
+ push(@obst_reginit, " ".$class_name."_regs[$idx].reg_class = $class_ptr;\n");
push(@obst_reginit, " ".$class_name."_regs[$idx].index = $idx;\n");
push(@obst_reginit, " ".$class_name."_regs[$idx].type = ".$rt[$_->{"type"}].";\n\n");
$idx++;
$class_idx++;
}
+push(@obst_header_all, "\n/* node specific requirements */\n");
+
# generate node-register constraints
foreach my $op (keys(%nodes)) {
my %n = %{ $nodes{"$op"} };
push(@obst_req, "/* IN requirements for '$op' */\n");
+ # we need to remember the classes of the IN constraints for
+ # OUT constraints like "in_s1"
my @inidx_class;
# check for argument requirements
for (my $idx = 0; $idx <= $#in; $idx++) {
my $class = undef;
- push(@obst_req, "static const arch_register_req_t ".$op."_reg_req_in_$idx = {\n");
+ my $tmp2 = "const arch_register_req_t _".$op."_reg_req_in_$idx = ";
+
+ $tmp = "const arch_register_req_t *".$op."_reg_req_in_$idx = ";
+
+ push(@obst_header_all, "extern const arch_register_req_t *".$op."_reg_req_in_$idx;\n");
if ($in[$idx] eq "none") {
push(@inidx_class, "none");
- push(@obst_req, " arch_register_req_type_none,\n NULL,\n { NULL }");
+ $tmp .= "&ia32_default_req_none;\n";
}
elsif (is_reg_class($in[$idx])) {
push(@inidx_class, $in[$idx]);
- push(@obst_req, " arch_register_req_type_normal,\n &$arch\_reg_classes[CLASS_$arch\_".$in[$idx]."],\n { NULL }");
+ $tmp .= "&ia32_default_req_".$arch."_".$in[$idx].";\n";
}
else {
$class = build_subset_class_func($op, $idx, 1, $in[$idx]);
die("Could not build subset for IN requirements '$op' pos $idx ... exiting.\n");
}
push(@inidx_class, $class);
- push(@obst_req, " arch_register_req_type_limited,\n &$arch\_reg_classes[CLASS_$arch\_".$class."],\n { limit_reg_".$op."_in_".$idx." }");
+ $tmp .= "&_".$op."_reg_req_in_$idx;\n";
+ $tmp2 .= " {\n arch_register_req_type_limited,\n &$arch\_reg_classes[CLASS_$arch\_".$class."],\n { limit_reg_".$op."_in_".$idx." }\n};\n";
+
+ $tmp = $tmp2.$tmp;
}
- push(@obst_req, "\n};\n\n");
+ push(@obst_req, $tmp."\n");
}
}
for (my $idx = 0; $idx <= $#out; $idx++) {
my $class = undef;
- push(@obst_req, "static const arch_register_req_t ".$op."_reg_req_out_$idx = {\n");
+ my $tmp2 = "const arch_register_req_t _".$op."_reg_req_out_$idx = ";
+
+ $tmp = "const arch_register_req_t *".$op."_reg_req_out_$idx = ";
+
+ push(@obst_header_all, "extern const arch_register_req_t *".$op."_reg_req_out_$idx;\n");
if ($out[$idx] eq "none") {
- push(@obst_req, " arch_register_req_type_none,\n NULL,\n { NULL }");
+ $tmp .= "&ia32_default_req_none;\n";
}
elsif (is_reg_class($out[$idx])) {
- push(@obst_req, " arch_register_req_type_normal,\n &$arch\_reg_classes[CLASS_$arch\_".$out[$idx]."],\n { NULL }");
+ $tmp .= "&ia32_default_req_".$arch."_".$out[$idx].";\n";
}
elsif ($out[$idx] =~ /^(!)?in_s(\d+)/) { # this is a "should be (un)equal to register at in_X"
- push(@obst_req, " arch_register_req_type_".($1 ? "un" : "")."equal,\n");
- push(@obst_req, " &$arch\_reg_classes[CLASS_$arch\_".$inidx_class[$2 - 1]."],\n");
- push(@obst_req, " { ".($2 - 1)." }");
+ $tmp .= "&_".$op."_reg_req_out_$idx;\n";
+ $tmp2 .= " {\n";
+ $tmp2 .= " arch_register_req_type_".($1 ? "un" : "")."equal,\n";
+ $tmp2 .= " &$arch\_reg_classes[CLASS_$arch\_".$inidx_class[$2 - 1]."],\n";
+ $tmp2 .= " { ".($2 - 1)." }\n};\n";
+
+ $tmp = $tmp2.$tmp
}
else {
$class = build_subset_class_func($op, $idx, 0, $out[$idx]);
if (!defined $class) {
die("Could not build subset for OUT requirements '$op' pos $idx ... exiting.\n");
}
- push(@obst_req, " arch_register_req_type_limited,\n &$arch\_reg_classes[CLASS_$arch\_".$class."],\n { limit_reg_".$op."_out_".$idx." }");
+ $tmp .= "&_".$op."_reg_req_out_$idx;\n";
+ $tmp2 .= " {\n arch_register_req_type_limited,\n &$arch\_reg_classes[CLASS_$arch\_".$class."],\n { limit_reg_".$op."_out_".$idx." }\n};\n";
+
+ $tmp = $tmp2.$tmp
}
- push(@obst_req, "\n};\n\n");
+ push(@obst_req, $tmp."\n");
}
}
}
-# generate header file
-open(OUT, ">$target_h") || die("Could not open $target_h, reason: $!\n");
+# generate header _t (internal usage) file
+open(OUT, ">$target_h_t") || die("Could not open $target_h_t, reason: $!\n");
my $creation_time = localtime(time());
-my $tmp = uc($arch);
+$tmp = uc($arch);
print OUT<<EOF;
-#ifndef _GEN_$tmp\_REGALLOC_IF_H_
-#define _GEN_$tmp\_REGALLOC_IF_H_
+#ifndef _GEN_$tmp\_REGALLOC_IF_T_H_
+#define _GEN_$tmp\_REGALLOC_IF_T_H_
/**
- * Generated register classes from spec
+ * Generated register classes from spec.
*
* DO NOT EDIT THIS FILE, your changes will be lost.
* Edit $specfile instead.
* date: $creation_time
*/
+#include "../bearch.h"
+
EOF
-print OUT @obst_regtypes;
+print OUT @obst_regdef, "\n";
+
+print OUT @obst_classdef, "\n";
+
+print OUT @obst_regtypes, "\n";
+
+print OUT @obst_defreq_head, "\n";
print OUT "void ".$arch."_register_init(void);\n\n";
+print OUT "\n#endif /* _GEN_$tmp\_REGALLOC_IF_T_H_ */\n";
+
+
+
+# generate header (external usage) file
+open(OUT, ">$target_h") || die("Could not open $target_h, reason: $!\n");
+
+$creation_time = localtime(time());
+
+print OUT<<EOF;
+#ifndef _GEN_$tmp\_REGALLOC_IF_H_
+#define _GEN_$tmp\_REGALLOC_IF_H_
+
+/**
+ * Contains additional external requirements defs for external includes.
+ *
+ * DO NOT EDIT THIS FILE, your changes will be lost.
+ * Edit $specfile instead.
+ * created by: $0 $specfile $target_dir
+ * date: $creation_time
+ */
+
+#include "gen_$arch\_regalloc_if_t.h"
+
+EOF
+
+print OUT @obst_header_all;
+
print OUT "\n#endif /* _GEN_$tmp\_REGALLOC_IF_H_ */\n";
+close(OUT);
+
# generate c inline file
* date: $creation_time
*/
-#include "gen_$arch\_regalloc_if.h"
+#include "gen_$arch\_regalloc_if_t.h"
EOF
-print OUT @obst_classdef, "\n";
-
print OUT "arch_register_class_t $arch\_reg_classes[] = {\n ".join(",\n ", @obst_regclasses)."\n};\n\n";
print OUT "void ".$arch."_register_init(void) {\n";