#!/bin/sh
# APPLE LOCAL file AltiVec
# ops-to-gp -gcc vec.ops builtin.ops
# Creates vec.h used by rs6000.c

arg0=`basename $0`
err() {
    echo "$arg0: $*" 1>&2
    exit 2
}

if [ $# -eq 0 ] ; then
    echo "Usage: $arg0 [ -mcc | -gcc ] builtin-ops ..." 1>&2
    exit 1
fi

MCC=1
GCC=0
suffix="gp"
if [ "$1" = "-mcc" ] ; then
    shift;
elif [ "$1" = "-gcc" ] ; then
    GCC=1
    MCC=0
    suffix="h"
    shift;
fi

output=`basename $1 .ops`
gperf="gperf -G -a -o -k1-15 -p -t -D -T -N Is_Builtin_Function $output.gp";

# Lines in the ops file have the form
# @ @ betype betype-code type-spelling
# @ fetype betype [code]
# @ @ @ instruction type
# generic op1 op2 ... opn = result specific when configure [addressible
#    [instruction [const_ptr_ok [volatile_ptr_ok [transform [predicate]]]]]]

# Sort the ops file to put it in a canonical order.
sort -u $* | \

# Add specific function uid's, make generic functions from specific
# functions, validate the types used, compute default parameters, and
# compute parts of the default transform and predicate functions.
awk 'BEGIN {
	i = 0
        EQ = i++
        RESULT = i++
        SPECIFIC = i++
        WHEN = i++
        CONFIGURED = i++
        ADDRESSIBLE = i++
        INSTRUCTION = i++
        CONST_PTR_OK = i++
        VOLATILE_PTR_OK = i++
        TRANSFORM = i++
        PREDICATE = i++
        n_lines = 1;
        tree[3] = "Make_Folded_4tree";
        tree[2] = "Make_Folded_3tree";
        tree[1] = "Make_Folded_Btree";
        tree[0] = "Make_Utree";
	optimize["vec_sub"] = 1;
	optimize["vec_subs"] = 1;
	optimize["vec_xor"] = 1;
	optimize["vec_andc"] = 1;
	optimize["vec_avg"] = 2;
	optimize["vec_or"] = 2;
	optimize["vec_and"] = 2;
	optimize["vec_max"] = 2;
	optimize["vec_min"] = 2;
	optimize["vec_sld"] = 3;
	optimize["vec_splat_s8"] = 4;
	optimize["vec_splat_s16"] = 5;
	optimize["vec_splat_s32"] = 6;
	optimize["vec_splat_u8"] = 4;
	optimize["vec_splat_u16"] = 5;
	optimize["vec_splat_u32"] = 6;
	optimize["vec_cmpeq"] = 7;
	optimize["vec_lvsl"] = 8;
	optimize["vec_lvsr"] = 9;
	# These operations need additional transformation.  Key off the
	# optimize attribute to identify them.
	optimize["vec_cmplt"] = 10;
	optimize["vec_cmple"] = 10;
	optimize["vec_abs"] = 11;
	optimize["vec_abss"] = 11;
    }
    function no_type(t) {
        printf "%% Error: type %s not declared.\n", t;
        status = 1;
        exit;
    }
    # Record the type.
    $1 == "@" {
        if ($2 == "@") {
          if ($3 == "@") {
            # Definition of an instruction.
            insn_type[$4] = $5; # type
          } else {
            # Definition of a betype.
            becode[$3] = $4; # betype-code
            bespell[$3] = $5; # type-spelling
            gsub(/\=/, " ", bespell[$3]);
          }
        } else {
          # Definition of a fetype.
          print $0;
          if (!becode[$3]) no_type($3); # Must have defined the betype.
          betype[$2] = $3; # betype;
          if (NF == 3)
            code[$2] = "";      
          else
            code[$2] = $4; # code
        }
    }
    function no_equal(i,l) {
        printf "%% Syntax error %d: %s\n", i, l;
        status = 1;
        exit;
    }
    function error(f,a) {
        printf( ("%% error: " f), a);
        status = 1;
        exit;
    }
    # Ignore comment lines.
    $1 != "#" && $1 != "@" {
        # Generate the signature of the specific function, the predicate,
        # the transform, the arguments to the transform function, the
        # arguments to the predicate function, and the spelling of the
        # function type.
        signature = "";
        predicate = "";
        transform = "";
        insn_code = "";
        transform_args = "";
        predicate_args = "";
        function_type = "";
        # First, consider the parameter types.
        for (i = 2; $i != "=" && i < NF; i++) {
          if ($i != "...") {
            if (!betype[$i]) no_type($i);
            signature = (signature " " $i);
            predicate = (predicate "_" betype[$i]);
            transform = (transform code[$i]);
            transform_args = (transform_args ", ND_kid(t," i-1 ")");
            predicate_args = (predicate_args " " becode[betype[$i]]);
            if (function_type)
              function_type = (function_type ", " bespell[betype[$i]]);
            else
              function_type = bespell[betype[$i]];
          }
        }
	constraints = (transform "@");
        # Check the syntax of the ops file.
        if ($i != "=" || NF > i+PREDICATE || NF < i+CONFIGURE) no_equal(i,$0);
        if (!betype[$(i+RESULT)]) no_type($(i+RESULT));
        # Incorporate the result type.
        if (i == 2) {
          predicate = "_void";
          function_type = "void";
        }
        signature = ($(i+SPECIFIC) signature);
        predicate = sprintf("is_%s_func%s", betype[$(i+RESULT)], predicate);
        predicate_args = (becode[betype[$(i+RESULT)]] predicate_args);
        function_type = sprintf("(%s (*)(%s))", bespell[betype[$(i+RESULT)]], \
                                function_type);
        if (substr(code[$(i+RESULT)], 1, 1) == "j") {
          # Handle a jump asm.  The code is expedted to be
          # j={cc-bit-num}={cc-bit-value}[={r|d}].  The operation must have
          # one operand if the code d is used and two operands otherwise.
          # The transform function can implement the r code by reversing the
          # two operands.  In all cases, the first operand is a computed
          # constant encoding both the bit number and the test.
          n = split(code[$(i+RESULT)], jmp, "=");
          if (jmp[n] == "d" && i != 3) error("%d operands", i-2);
          if (jmp[n] != "d" && i != 4) error("%d operands", i-2);
          if (jmp[n] == "r")
            transform_args = ", ND_kid(t,2), ND_kid(t,1)";
          transform_args = sprintf("%s(OP_VCMP%s%s", tree[i-2], \
                                   toupper(jmp[3]), transform_args);
          if (jmp[n] == "r")
            transform = ("r" transform);
          insn_code = sprintf("CODE_FOR_j_%d_%s_f%s", jmp[2], jmp[3], \
                              transform);
          transform = sprintf("transform_j_%d_%s_f%s", jmp[2], jmp[3], \
                              transform);
        } else {
          transform_args = sprintf("%s(OP_%sASM%s%s", tree[i-2], \
                                   toupper(code[$(i+RESULT)]), \
				   toupper(transform), transform_args);
          insn_code = sprintf("CODE_FOR_%sf%s", code[$(i+RESULT)], transform);
          transform = sprintf("transform_%sf%s", code[$(i+RESULT)], transform);
        }       
        # Give a unique id to the signature
        if (count[signature] == 0)
          count[signature] = ++uid[$(i+SPECIFIC)];

        # Compute the default instruction name
        nf = split($(i+SPECIFIC), part, "_");
        instruction = ("MOP_" part[nf]);

	# Compute the insn_code, but use the instruction override if given.
        if (NF >= i+INSTRUCTION)
          instruction = $(i+INSTRUCTION);
        if (insn_type[instruction])
          insn_code = (insn_code "_" insn_type[instruction]);

        # Allow the user to override the addressibility, instruction,
        # const_ptr_ok, volatile_ptr_ok, transform, and predicate.
        if (NF >= i+ADDRESSIBLE)
          addressible = "";
        else
          addressible = "FALSE";

        if (NF >= i+INSTRUCTION)
          instruction = "";
        else if (substr($1, 1, 4) == "vec_")
          print "@ @3", instruction;

        if (NF >= i+CONST_PTR_OK)
          const_ptr_ok = "";
        else
          const_ptr_ok = "FALSE";

        if (NF >= i+VOLATILE_PTR_OK)
          volatile_ptr_ok = "";
        else
          volatile_ptr_ok = "FALSE";

        if (NF >= i+TRANSFORM)
          transform = "";
        else
          print "@ @1", transform, transform_args;

        if (NF >= i+PREDICATE)
          predicate = "";
        else
          print "@ @2", i-2, predicate, predicate_args, function_type;

	if (optimize[$1])
	  optimize_method = optimize[$1];
	else
	  optimize_method = "0";

        # Record the line, addressibility, instruction, transform,
        # predicate, and unique id.
        line[n_lines++] = ($0 " " addressible " " instruction " " \
                           const_ptr_ok " " volatile_ptr_ok " " transform " " \
                           predicate " " insn_code " " constraints " " \
			   optimize_method " " count[signature]);
    }
    END {
        if (status) exit;
        # generic op1 op2 ... opn = result specific when configured
        #         addressable instruction const_ptr_ok volatile_ptr_ok
        #         transform predicate insn_code constraints optimize uid
        SPECIFIC = 12
        for (i = 1; i < n_lines; i++) {
          nf = split(line[i], part);
          specific = part[nf-SPECIFIC];

          # Print the generic form.
          printf "%s", part[1];
          for (j = 2; j <= nf-SPECIFIC; j++) printf " %s", part[j];
          if (uid[specific] > 1) printf ":%d", part[nf];
          while (j < nf) printf " %s", part[j++];
          printf "\n";

          # Print the specific form.
          printf "%s", specific;
          for (j = 2; j <= nf-SPECIFIC; j++) printf " %s", part[j];
          if (uid[specific] > 1) printf ":%d", part[nf];
          while (j < nf) printf " %s", part[j++];
          printf "\n";
        }
    }' | \

# Strip out load and store qualifiers.
sed -e 's/_load_op//g' -e 's/_store_op//g' | \

# Sort the processed file and eliminate duplicates.
sort -u | \

# Append the count of each generic function to each line.
awk 'function push() {
        if (num)
          for (i = 0; i < num; i++)
            print line[i], num;
        num = 0;
    }
    $1 == "@" {
        print $0;
    }
    $1 != "@" {
        if (last != $1)
          push();
        last = $1;
        line[num++] = $0;
    }
    END {
        push();
    }' | \

# Now compute the gperf input file.
# Lines now have a fixed format
# generic op1 ... opn = result specific instruction when configured
#         addressible const_ptr_ok volatile_ptr_ok transform predicate
#         insn_code constraints optimize count
awk 'BEGIN {
	MCC = '$MCC'
	GCC = '$GCC'
        i = 0;
        COUNT = i++
	OPTIMIZE = i++
	CONSTRAINTS = i++
        INSN_CODE = i++
        PREDICATE = i++
        TRANSFORM = i++
        VOLATILE_PTR_OK = i++
        CONST_PTR_OK = i++
        INSTRUCTION = i++
        ADDRESSIBLE = i++
        CONFIGURED = i++
        WHEN = i++
        SPECIFIC = i++
        RESULT = i++
        EQ = i++
        OPN = i++
        NARGS = i++
	if (MCC) {
          print "%{";
          print "/* Command-line: '"$gperf"'  */";
	  MAXARGS = 5
	}
	if (GCC)
	  MAXARGS = 3
    }
    function write_test(tree, type, num) {
        if (type == "PTR") {
          printf "\n      && TY_kind(%s) == KIND_POINTER", tree;
        } else if (type == "I5") {
          printf "\n      && is_integer_type(%s)", tree;
          printf "\n      && Is_Const(ND_kid0(ND_kid(t,%d)), &tc)", num;
          printf "\n      && ((UINT32)Targ_To_Host(tc) + 16) < 32";
        } else if (type == "U5") {
          printf "\n      && is_integer_type(%s)", tree;
          printf "\n      && Is_Const(ND_kid0(ND_kid(t,%d)), &tc)", num;
          printf "\n      && (UINT32)Targ_To_Host(tc) < 32";
        } else if (type == "U4") {
          printf "\n      && is_integer_type(%s)", tree;
          printf "\n      && Is_Const(ND_kid0(ND_kid(t,%d)), &tc)", num;
          printf "\n      && (UINT32)Targ_To_Host(tc) < 16";
        } else if (type == "U2") {
          printf "\n      && is_integer_type(%s)", tree;
          printf "\n      && Is_Const(ND_kid0(ND_kid(t,%d)), &tc)", num;
          printf "\n      && (UINT32)Targ_To_Host(tc) < 4";
        } else if (type == "BETYPE_U4" || type == "BETYPE_I4") {
          printf "\n      && is_integer_type(%s)", tree;
        } else {
          printf "\n      && Similar_Types(%s,", tree;
          printf "\n\t\t       Be_Type_Tbl(%s), IGNORE_QUALIFIERS)", type;
        }
    }
    $1 == "@" {
      if (MCC) {
        if ($2 == "@1") {
          # Write the predicate function from the given parameters.
          # The format is:
          # @ @1 transform_ifii Make_3tree(OP_IASMII, ND_kid(t,1), ND_kid(t,2)
          print "";
          print "/*ARGSUSED*/";
          print "static void";
          print $3 "(ND *func, ND *parent, ND *t, struct builtin *self)";
          print "{";
          printf "  *t = *%s", $4;
          for (i = 5; i <= NF; i++) printf " %s", $i;
          print ",";
          if (split($3,jmp,"_") == 5 && jmp[2] == "j")
            printf "\t\t   MK_I4CONST_ND((self->data << 5) + %d));\n", \
                   jmp[3];
          else
            print "\t\t   MK_I4CONST_ND(self->data));";

          print "  Is_True(self->data > 0, (\"No implementation for %s\", self->name));";
          print "}";
        } else if ($2 == "@2") {
          # Write the transform function from the given parameters.
          # The format is:
          # @ @2 2 is_int_func_int_int BETYPE_I4 BETYPE_I4 BETYPE_I4 
          #          (int (*)(int, int))
          print "";
          print "/*ARGSUSED*/";
          print "static BOOL";
          print $4 "(ND *func, ND *parent, ND *t, struct builtin *self)";
          print "{";
          print "  TCON tc;";
          printf "  if (ND_nkids(t) == %d", $3+1;
          write_test("ST_type(ND_dec(func))", $5, "");
          for (i = 1; i <= $3; i++) {
            printf "\n      && ND_name(ND_kid(t,%d)) == TO_VAL", i;
            write_test(sprintf("The_Tree_Type(ND_kid(t,%d))", i), $(i+5), i);
          }       
          print ")";
          print "    return TRUE;";
          print "  Error_Prt_Line (ND_linenum(t), ec_builtin_function_type, self->name,";
          i = $3+6;
          printf "\t\t  \"%s", $i;
          while (++i <= NF) printf " %s", $i;
          print "\");";
          print "  return FALSE;";
          print "}";
        } else if ($2 == "@3") {
          if (once++ == 0) printf "\n#ifndef HAVE_ALTIVEC\n";
          printf "#define %s -1\n", $3;
        } else {
          if (once && twice++ == 0) printf "#endif /* HAVE_ALTIVEC */\n\n";
          printf "extern struct a_type *T_%s;\n", $2;
        }
      }
      next;
    }
    $1 == "%" {
        print $0;
        status = 1;
        exit;
    }
    {
        # Compute the signature of the generic function.
        signature=$1;
        for (i = 2; i <= NF-OPN; i++) {
          if ($i != "...")
            signature=(signature " " $i);
        }

        # Ensure that the signature is unique.
        if (signature_line[signature]) {
          print "Ambiguous signatures:";
          print $0;
          print line[signature_line[signature]];
        }
        signature_line[signature] = n_lines;

        # Require that overloaded functions have the same attributes:
        # number of arguments, when, configured, and addressible.
        if (same_arg_count[$1] && same_arg_count[$1] != NF)
          printf "%% number of arguments for %s varies: %d and %d\n", \
                 $1, NF-NARGS, same_arg_count[$1]-NARGS;
        same_arg_count[$1] = NF;

        if (same_when[$1] && same_when[$1] != $(NF-WHEN))
          printf "%% when for %s varies: %s and %s\n", \
                 $1, $(NF-WHEN), same_when[$1];
        same_when[$1] = $(NF-WHEN);

        if (same_configured[$1] && same_configured[$1] != $(NF-CONFIGURED))
          printf "%% configured for %s varies: %s and %s\n", \
                 $1, $(NF-CONFIGURED), same_configured[$1];
        same_configured[$1] = $(NF-CONFIGURED);

        if (same_addressible[$1] && same_addressible[$1] != $(NF-ADDRESSIBLE))
          printf "%% addressible for %s varies: %s and %s\n", \
                 $1, $(NF-ADDRESSIBLE), same_addressible[$1];
        else if (same_addressible[$1] && same_addressible[$1] != "FALSE")
          printf "%% Overloaded function %s is addressible\n", $1
        same_addressible[$1] = $(NF-ADDRESSIBLE);

        # Record the line.
        line[n_lines++] = $0;
    }
    function push(fcn, n) {
        if (last) printf "};\n";
        # Gcc3: declare as arrays of const pointers
        if (fcn) printf "static const struct builtin *const O_%s[%d] = {\n", fcn, n;
        last = fcn;
    }
    function mangle(name) {
        if (split(name, names, ":") == 1)
          return ("B_" names[1]);
        return ("B" names[2] "_" names[1]);
    }
    END { 
        if (status) exit;
        
        # Gcc3: Mark file as Apple local
        printf "/* APPLE LOCAL file AltiVec */\n";
        printf "/* This file is generated by ops-to-gp.  Do not edit.   */\n\n";
        printf "/* To regenerate execute:\n";
        printf "     ops-to-gp -gcc vec.ops builtin.ops\n";
        printf "   with the current directory being gcc/config/rs6000.  */\n\n";
        
        # Output the description of each specific function.
        uid = 0;
        if (MCC) print "";
        for (i = 0; i < n_lines; i++) {
          nf = split(line[i], part);
          fcn = part[nf-SPECIFIC];
          if (!done[fcn]) {
            printf "static const struct builtin %s = {", mangle(fcn);
	    if (GCC) printf " {";
            ellipsis = 1;
            for (j = 2; j <= nf-OPN; j++)
              if (part[j] != "...") {
                printf " &T_%s,", part[j];
              } else {
                ellipsis = -1;
                printf " NULL,";
              }
            while (j++ <= MAXARGS+1)
              printf " NULL,";
            instruction = part[nf-INSTRUCTION];
            if (substr(instruction, 1, 4) == "MOP_")
              instruction = substr(instruction, 5);
            if (substr(instruction, length(instruction)) == "D")
              instruction = (substr(instruction, 1, length(instruction) - 1) ".");
	    # Gcc3: Prefix each specific instruction with a "*"
	    if (match (instruction, "^[a-zA-Z]") > 0)
	      instruction = "*" instruction;
	    if (GCC) printf " },";
	    if (GCC) printf " \"%s\",", substr(part[nf-CONSTRAINTS], 1, length(part[nf-CONSTRAINTS]) - 1);
            printf " &T_%s,", part[nf-RESULT];
            if (MCC) printf " \"%s\",", part[nf-SPECIFIC];
            printf " %d,", ellipsis * (nf - NARGS);
	    if (MCC) {
	      printf " %s,", part[nf-WHEN];
	      printf " %s,", part[nf-ADDRESSIBLE];
	      printf " %s,", part[nf-CONST_PTR_OK];
	      printf " %s,", part[nf-VOLATILE_PTR_OK];
	      printf " %s,", part[nf-CONFIGURED];
	      printf " %s,", part[nf-INSTRUCTION];
	      printf " %s,", part[nf-TRANSFORM];
	      printf " %s", part[nf-PREDICATE];
            } else if (GCC) {
	      printf " %s,", part[nf-CONST_PTR_OK];
	      printf " %s,", part[nf-VOLATILE_PTR_OK];
	      printf " %s,", part[nf-OPTIMIZE];
	      printf " \"%s\",", part[nf-SPECIFIC];
	      printf " \"%s\",", instruction;
	      printf " %s,", part[nf-INSN_CODE];
	      printf " B_UID(%d)", uid++;
            }
            printf " };\n";
          }
          done[fcn] = 1;
        }

        if (GCC) printf "#define LAST_B_UID B_UID(%d)\n", uid;

	if (GCC) {
	  # Output the description of each specific function.
	  print "";
	  uid = 0;
	  for (i in done)
	    done[i] = "";
	  print "const struct builtin * const Builtin[] = {"
	  for (i = 0; i < n_lines; i++) {
	    nf = split(line[i], part);
	    fcn = part[nf-SPECIFIC];
	    if (!done[fcn]) {
	      printf "  &%s,\n", mangle(fcn);
	    }
	    done[fcn] = 1;
	  }
	  print "};"
	}

        # Output the overload tables for each generic function.
        print "";
        for (i = 0; i < n_lines; i++) {
          nf = split(line[i], part);
          fcn = part[1];
          if (last != fcn) 
            push(fcn, part[nf]);
          printf "  &%s,\n", mangle(part[nf-SPECIFIC]);
        }
        push("", 0);

        # Output the builtin function structure.
        print "";
	if (MCC) {
	  print "%}";
	  print "struct overloadx {";
	  print "  char *name;";
	  print "  int fcns;";
	  print "  int args;";
	  print "  struct builtin **functions;";
	  print "};";
	  print "%%";
	} else if (GCC) {
	  print "const struct overloadx Overload[] = {";
	}

        # Output the builtin function list and data.
        uid = 0;
        for (i = 0; i < n_lines; i++) {
          nf = split(line[i], part);
          fcn = part[1];
          args = nf - NARGS;
          if (part[nf-OPN] == "...") args = -args;
          if (last != fcn) {
	    if (MCC) printf "%s, %d, %d, O_%s\n", fcn, part[nf], args, fcn;
            if (GCC) printf "  { \"%s\", %d, %d, O_%s, O_UID(%d) },\n", \
                            fcn, part[nf], args, fcn, uid++;
	  }
          last = fcn;
        }

	if (GCC) {
          print "  { NULL, 0, 0, NULL, 0 }"
          print "};";

          printf "#define LAST_O_UID O_UID(%d)\n", uid;
	}

    }' > $output.$suffix

if [ "$MCC" = "1" ] ; then
    $gperf > $output.h
fi
