| #!/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 |