| /* Generate built-in function initialization and recognition for Power. |
| Copyright (C) 2020-2022 Free Software Foundation, Inc. |
| Contributed by Bill Schmidt, IBM <wschmidt@linux.ibm.com> |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it under |
| the terms of the GNU General Public License as published by the Free |
| Software Foundation; either version 3, or (at your option) any later |
| version. |
| |
| GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| /* This program generates built-in function initialization and |
| recognition code for Power targets, based on text files that |
| describe the built-in functions and vector overloads: |
| |
| rs6000-builtins.def Table of built-in functions |
| rs6000-overload.def Table of overload functions |
| |
| Both files group similar functions together in "stanzas," as |
| described below. |
| |
| Each stanza in the built-in function file starts with a line |
| identifying the circumstances in which the group of functions is |
| permitted, with the gating predicate in square brackets. For |
| example, this could be |
| |
| [altivec] |
| |
| or it could be |
| |
| [power9] |
| |
| The bracketed gating predicate is the only information allowed on |
| the stanza header line, other than whitespace. |
| |
| Following the stanza header are two lines for each function: the |
| prototype line and the attributes line. The prototype line has |
| this format, where the square brackets indicate optional |
| information and angle brackets indicate required information: |
| |
| [kind] <return-type> <bif-name> (<argument-list>); |
| |
| Here [kind] can be one of "const", "pure", or "fpmath"; |
| <return-type> is a legal type for a built-in function result; |
| <bif-name> is the name by which the function can be called; |
| and <argument-list> is a comma-separated list of legal types |
| for built-in function arguments. The argument list may be |
| empty, but the parentheses and semicolon are required. |
| |
| The attributes line looks like this: |
| |
| <bif-id> <bif-pattern> {<attribute-list>} |
| |
| Here <bif-id> is a unique internal identifier for the built-in |
| function that will be used as part of an enumeration of all |
| built-in functions; <bif-pattern> is the define_expand or |
| define_insn that will be invoked when the call is expanded; |
| and <attribute-list> is a comma-separated list of special |
| conditions that apply to the built-in function. The attribute |
| list may be empty, but the braces are required. |
| |
| Attributes are strings, such as these: |
| |
| init Process as a vec_init function |
| set Process as a vec_set function |
| extract Process as a vec_extract function |
| nosoft Not valid with -msoft-float |
| ldvec Needs special handling for vec_ld semantics |
| stvec Needs special handling for vec_st semantics |
| reve Needs special handling for element reversal |
| pred Needs special handling for comparison predicates |
| htm Needs special handling for transactional memory |
| htmspr HTM function using an SPR |
| htmcr HTM function using a CR |
| mma Needs special handling for MMA instructions |
| quad MMA instruction using a register quad as an input operand |
| pair MMA instruction using a register pair as an input operand |
| mmaint MMA instruction expanding to internal call at GIMPLE time |
| no32bit Not valid for TARGET_32BIT |
| 32bit Requires different handling for TARGET_32BIT |
| cpu This is a "cpu_is" or "cpu_supports" builtin |
| ldstmask Altivec mask for load or store |
| lxvrse Needs special handling for load-rightmost, sign-extended |
| lxvrze Needs special handling for load-rightmost, zero-extended |
| endian Needs special handling for endianness |
| ibmld Restrict usage to the case when TFmode is IBM-128 |
| ibm128 Restrict usage to the case where __ibm128 is supported or |
| if ibmld |
| |
| An example stanza might look like this: |
| |
| [altivec] |
| const vsc __builtin_altivec_abs_v16qi (vsc); |
| ABS_V16QI absv16qi2 {} |
| const vss __builtin_altivec_abs_v8hi (vss); |
| ABS_V8HI absv8hi2 {} |
| |
| Here "vsc" and "vss" are shorthand for "vector signed char" and |
| "vector signed short" to shorten line lengths and improve readability. |
| Note the use of indentation, which is recommended but not required. |
| |
| The overload file has more complex stanza headers. Here the stanza |
| represents all functions with the same overloaded function name: |
| |
| [<overload-id>, <abi-name>, <builtin-name>[[, <ifdef>]] ] |
| |
| Here the single square brackets are part of the syntax, <overload-id> |
| is a unique internal identifier for the overload that will be used as |
| part of an enumeration of all overloaded functions; <abi-name> is the |
| name that will appear as a #define in rs6000-vecdefines.h; |
| <builtin-name> is the name that is overloaded in the back end; and |
| <ifdef> is an optional token used to guard the #define with an #ifdef |
| in rs6000-vecdefines.h. |
| |
| Each function entry again has two lines. The first line is again a |
| prototype line (this time without [kind]): |
| |
| <return-type> <internal-name> (<argument-list>); |
| |
| The second line contains the <bif-id> that this particular instance of |
| the overloaded function maps to. It must match a token that appears in |
| rs6000-builtins.def. Optionally, a second token may appear. If only |
| one token is on the line, it is also used to build the unique identifier |
| for the overloaded function. If a second token is present, the second |
| token is used instead for this purpose. This is necessary in cases |
| where a built-in function accepts more than one type signature. It is |
| common to have a built-in function that, for example, specifies a |
| "vector signed char" argument, but accepts "vector unsigned char" and |
| "vector bool char" as well because only the mode matters. Note that |
| the overload resolution mechanism has always handled these cases by |
| performing fold_convert on vector arguments to hide type mismatches, |
| and it will continue to do so. |
| |
| As a concrete example, __builtin_altivec_mtvscr uses an opaque argument |
| type for the source operand. Its built-in function id is MTVSCR. The |
| overloaded function __builtin_vec_mtvscr takes a variety of specific |
| types, but not all vector types. Each of these maps to the same |
| __builtin_altivec_mtvscr built-in function, but the overload ID must |
| be unique, so we must specify the second token as shown here. |
| |
| [VEC_MTVSCR, vec_mtvscr, __builtin_vec_mtvscr] |
| void __builtin_vec_mtvscr (vbc); |
| MTVSCR MTVSCR_VBC |
| void __builtin_vec_mtvscr (vsc); |
| MTVSCR MTVSCR_VSC |
| ... |
| |
| Blank lines may be used as desired in these files between the lines as |
| defined above; that is, you can introduce as many extra newlines as you |
| like after a required newline, but nowhere else. Lines beginning with |
| a semicolon are also treated as blank lines. */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include <stdint.h> |
| #include <ctype.h> |
| #include <string.h> |
| #include <assert.h> |
| #include <unistd.h> |
| #include "rbtree.h" |
| |
| /* Input and output file descriptors and pathnames. */ |
| static FILE *bif_file; |
| static FILE *ovld_file; |
| static FILE *header_file; |
| static FILE *init_file; |
| static FILE *defines_file; |
| |
| static const char *pgm_path; |
| static const char *bif_path; |
| static const char *ovld_path; |
| static const char *header_path; |
| static const char *init_path; |
| static const char *defines_path; |
| |
| /* Position information. Note that "pos" is zero-indexed, but users |
| expect one-indexed column information, so representations of "pos" |
| as columns in diagnostic messages must be adjusted. */ |
| #define MAXLINES 4 |
| #define LINELEN 1024 |
| static char linebuf[LINELEN * MAXLINES]; |
| static int line; |
| static int pos; |
| |
| /* Escape-newline support. For readability, we prefer to allow developers |
| to use escape-newline to continue long lines to the next one. We |
| maintain a buffer of "original" lines here, which are concatenated into |
| linebuf, above, and which can be used to convert the virtual line |
| position "line / pos" into actual line and position information. */ |
| static char *lines[MAXLINES]; |
| static int lastline; |
| |
| /* Used to determine whether a type can be void (only return types). */ |
| enum void_status |
| { |
| VOID_NOTOK, |
| VOID_OK |
| }; |
| |
| /* Stanzas are groupings of built-in functions and overloads by some |
| common feature/attribute. These definitions are for built-in function |
| stanzas. */ |
| enum bif_stanza |
| { |
| BSTZ_ALWAYS, |
| BSTZ_P5, |
| BSTZ_P6, |
| BSTZ_P6_64, |
| BSTZ_ALTIVEC, |
| BSTZ_CELL, |
| BSTZ_VSX, |
| BSTZ_P7, |
| BSTZ_P7_64, |
| BSTZ_P8, |
| BSTZ_P8V, |
| BSTZ_P9, |
| BSTZ_P9_64, |
| BSTZ_P9V, |
| BSTZ_IEEE128_HW, |
| BSTZ_DFP, |
| BSTZ_CRYPTO, |
| BSTZ_HTM, |
| BSTZ_P10, |
| BSTZ_P10_64, |
| BSTZ_MMA, |
| NUMBIFSTANZAS |
| }; |
| |
| static bif_stanza curr_bif_stanza; |
| |
| struct stanza_entry |
| { |
| const char *stanza_name; |
| bif_stanza stanza; |
| }; |
| |
| static stanza_entry stanza_map[NUMBIFSTANZAS] = |
| { |
| { "always", BSTZ_ALWAYS }, |
| { "power5", BSTZ_P5 }, |
| { "power6", BSTZ_P6 }, |
| { "power6-64", BSTZ_P6_64 }, |
| { "altivec", BSTZ_ALTIVEC }, |
| { "cell", BSTZ_CELL }, |
| { "vsx", BSTZ_VSX }, |
| { "power7", BSTZ_P7 }, |
| { "power7-64", BSTZ_P7_64 }, |
| { "power8", BSTZ_P8 }, |
| { "power8-vector", BSTZ_P8V }, |
| { "power9", BSTZ_P9 }, |
| { "power9-64", BSTZ_P9_64 }, |
| { "power9-vector", BSTZ_P9V }, |
| { "ieee128-hw", BSTZ_IEEE128_HW }, |
| { "dfp", BSTZ_DFP }, |
| { "crypto", BSTZ_CRYPTO }, |
| { "htm", BSTZ_HTM }, |
| { "power10", BSTZ_P10 }, |
| { "power10-64", BSTZ_P10_64 }, |
| { "mma", BSTZ_MMA } |
| }; |
| |
| static const char *enable_string[NUMBIFSTANZAS] = |
| { |
| "ENB_ALWAYS", |
| "ENB_P5", |
| "ENB_P6", |
| "ENB_P6_64", |
| "ENB_ALTIVEC", |
| "ENB_CELL", |
| "ENB_VSX", |
| "ENB_P7", |
| "ENB_P7_64", |
| "ENB_P8", |
| "ENB_P8V", |
| "ENB_P9", |
| "ENB_P9_64", |
| "ENB_P9V", |
| "ENB_IEEE128_HW", |
| "ENB_DFP", |
| "ENB_CRYPTO", |
| "ENB_HTM", |
| "ENB_P10", |
| "ENB_P10_64", |
| "ENB_MMA" |
| }; |
| |
| /* Function modifiers provide special handling for const, pure, and fpmath |
| functions. These are mutually exclusive, and therefore kept separate |
| from other bif attributes. */ |
| enum fnkinds |
| { |
| FNK_NONE, |
| FNK_CONST, |
| FNK_PURE, |
| FNK_FPMATH |
| }; |
| |
| /* Legal base types for an argument or return type. */ |
| enum basetype |
| { |
| BT_CHAR, |
| BT_SHORT, |
| BT_INT, |
| BT_LONG, |
| BT_LONGLONG, |
| BT_FLOAT, |
| BT_DOUBLE, |
| BT_LONGDOUBLE, |
| BT_INT128, |
| BT_FLOAT128, |
| BT_BOOL, |
| BT_STRING, |
| BT_DECIMAL32, |
| BT_DECIMAL64, |
| BT_DECIMAL128, |
| BT_IBM128, |
| BT_VPAIR, |
| BT_VQUAD |
| }; |
| |
| /* Ways in which a const int value can be restricted. RES_BITS indicates |
| that the integer is restricted to val1 bits, interpreted as an unsigned |
| number. RES_RANGE indicates that the integer is restricted to values |
| between val1 and val2, inclusive. RES_VAR_RANGE is like RES_RANGE, but |
| the argument may be variable, so it can only be checked if it is constant. |
| RES_VALUES indicates that the integer must have one of the values val1 |
| or val2. */ |
| enum restriction |
| { |
| RES_NONE, |
| RES_BITS, |
| RES_RANGE, |
| RES_VAR_RANGE, |
| RES_VALUES |
| }; |
| |
| /* Type modifiers for an argument or return type. */ |
| struct typeinfo |
| { |
| char isvoid; |
| char isconst; |
| char isvector; |
| char issigned; |
| char isunsigned; |
| char isbool; |
| char ispixel; |
| char ispointer; |
| basetype base; |
| restriction restr; |
| char *val1; |
| char *val2; |
| }; |
| |
| /* A list of argument types. */ |
| struct typelist |
| { |
| typeinfo info; |
| typelist *next; |
| }; |
| |
| /* Attributes of a builtin function. */ |
| struct attrinfo |
| { |
| bool isinit; |
| bool isset; |
| bool isextract; |
| bool isnosoft; |
| bool isldvec; |
| bool isstvec; |
| bool isreve; |
| bool ispred; |
| bool ishtm; |
| bool ishtmspr; |
| bool ishtmcr; |
| bool ismma; |
| bool isquad; |
| bool ispair; |
| bool ismmaint; |
| bool isno32bit; |
| bool is32bit; |
| bool iscpu; |
| bool isldstmask; |
| bool islxvrse; |
| bool islxvrze; |
| bool isendian; |
| bool isibmld; |
| bool isibm128; |
| }; |
| |
| /* Fields associated with a function prototype (bif or overload). */ |
| #define MAXRESTROPNDS 3 |
| struct prototype |
| { |
| typeinfo rettype; |
| char *bifname; |
| int nargs; |
| typelist *args; |
| int restr_opnd[MAXRESTROPNDS]; |
| restriction restr[MAXRESTROPNDS]; |
| char *restr_val1[MAXRESTROPNDS]; |
| char *restr_val2[MAXRESTROPNDS]; |
| }; |
| |
| /* Data associated with a builtin function, and a table of such data. */ |
| #define MAXBIFS 16384 |
| struct bifdata |
| { |
| int stanza; |
| fnkinds kind; |
| prototype proto; |
| char *idname; |
| char *patname; |
| attrinfo attrs; |
| char *fndecl; |
| }; |
| |
| static bifdata bifs[MAXBIFS]; |
| static int num_bifs; |
| static int curr_bif; |
| |
| /* Array used to track the order in which built-ins appeared in the |
| built-in file. We reorder them alphabetically but sometimes need |
| this information. */ |
| static int *bif_order; |
| static int bif_index = 0; |
| |
| /* Stanzas are groupings of built-in functions and overloads by some |
| common feature/attribute. These definitions are for overload stanzas. */ |
| struct ovld_stanza |
| { |
| char *stanza_id; |
| char *extern_name; |
| char *intern_name; |
| char *ifdef; |
| }; |
| |
| #define MAXOVLDSTANZAS 512 |
| static ovld_stanza ovld_stanzas[MAXOVLDSTANZAS]; |
| static int num_ovld_stanzas; |
| static int curr_ovld_stanza; |
| |
| #define MAXOVLDS 16384 |
| struct ovlddata |
| { |
| int stanza; |
| prototype proto; |
| char *bif_id_name; |
| char *ovld_id_name; |
| char *fndecl; |
| }; |
| |
| static ovlddata ovlds[MAXOVLDS]; |
| static int num_ovlds; |
| static int curr_ovld; |
| static int max_ovld_args = 0; |
| |
| /* Return codes for parsing routines. */ |
| enum parse_codes |
| { |
| PC_OK, |
| PC_EOFILE, |
| PC_EOSTANZA, |
| PC_PARSEFAIL |
| }; |
| |
| /* The red-black trees for built-in function identifiers, built-in |
| overload identifiers, and function type descriptors. */ |
| static rbt_strings bif_rbt; |
| static rbt_strings ovld_rbt; |
| static rbt_strings fntype_rbt; |
| |
| /* Another red-black tree containing a mapping from built-in function |
| identifiers to the order in which they were encountered. */ |
| static rbt_strings bifo_rbt; |
| |
| /* Mapping from type tokens to type node names. */ |
| struct typemap |
| { |
| const char *key; |
| const char *value; |
| }; |
| |
| /* This table must be kept in alphabetical order, as we use binary |
| search for table lookups in map_token_to_type_node. The table |
| maps tokens from a fntype string to a tree type. For example, |
| in "si_ftype_hi" we would map "si" to "intSI_type_node" and |
| map "hi" to "intHI_type_node". */ |
| static typemap type_map[] = |
| { |
| { "bi", "bool_int" }, |
| { "bv16qi", "bool_V16QI" }, |
| { "bv1ti", "bool_V1TI" }, |
| { "bv2di", "bool_V2DI" }, |
| { "bv4si", "bool_V4SI" }, |
| { "bv8hi", "bool_V8HI" }, |
| { "ci", "integer" }, |
| { "dd", "dfloat64" }, |
| { "df", "double" }, |
| { "di", "long_long_integer" }, |
| { "hi", "intHI" }, |
| { "if", "ibm128_float_type_node " |
| "? ibm128_float_type_node " |
| ": long_double" }, |
| { "ld", "long_double" }, |
| { "lg", "long_integer" }, |
| { "pbv16qi", "ptr_bool_V16QI" }, |
| { "pbv1ti", "ptr_bool_V1TI" }, |
| { "pbv2di", "ptr_bool_V2DI" }, |
| { "pbv4si", "ptr_bool_V4SI" }, |
| { "pbv8hi", "ptr_bool_V8HI" }, |
| { "pcvoid", "pcvoid" }, |
| { "pdd", "ptr_dfloat64" }, |
| { "pdf", "ptr_double" }, |
| { "pdi", "ptr_long_long_integer" }, |
| { "phi", "ptr_intHI" }, |
| { "pld", "ptr_long_double" }, |
| { "plg", "ptr_long_integer" }, |
| { "pqi", "ptr_intQI" }, |
| { "psf", "ptr_float" }, |
| { "psi", "ptr_intSI" }, |
| { "ptd", "ptr_dfloat128" }, |
| { "ptf", "ptr_float128" }, |
| { "pti", "ptr_intTI" }, |
| { "pudi", "ptr_long_long_unsigned" }, |
| { "puhi", "ptr_uintHI" }, |
| { "pulg", "ptr_long_unsigned" }, |
| { "puqi", "ptr_uintQI" }, |
| { "pusi", "ptr_uintSI" }, |
| { "puti", "ptr_uintTI" }, |
| { "puv16qi", "ptr_unsigned_V16QI" }, |
| { "puv1ti", "ptr_unsigned_V1TI" }, |
| { "puv2di", "ptr_unsigned_V2DI" }, |
| { "puv4si", "ptr_unsigned_V4SI" }, |
| { "puv8hi", "ptr_unsigned_V8HI" }, |
| { "pv", "ptr" }, |
| { "pv16qi", "ptr_V16QI" }, |
| { "pv1poi", "ptr_vector_pair" }, |
| { "pv1pxi", "ptr_vector_quad" }, |
| { "pv1ti", "ptr_V1TI" }, |
| { "pv2df", "ptr_V2DF" }, |
| { "pv2di", "ptr_V2DI" }, |
| { "pv4sf", "ptr_V4SF" }, |
| { "pv4si", "ptr_V4SI" }, |
| { "pv8hi", "ptr_V8HI" }, |
| { "pvp8hi", "ptr_pixel_V8HI" }, |
| { "qi", "intQI" }, |
| { "sd", "dfloat32" }, |
| { "sf", "float" }, |
| { "si", "intSI" }, |
| { "st", "const_str" }, |
| { "td", "dfloat128" }, |
| { "tf", "float128" }, |
| { "ti", "intTI" }, |
| { "udi", "long_long_unsigned" }, |
| { "uhi", "unsigned_intHI" }, |
| { "ulg", "long_unsigned" }, |
| { "uqi", "unsigned_intQI" }, |
| { "usi", "unsigned_intSI" }, |
| { "uti", "unsigned_intTI" }, |
| { "uv16qi", "unsigned_V16QI" }, |
| { "uv1ti", "unsigned_V1TI" }, |
| { "uv2di", "unsigned_V2DI" }, |
| { "uv4si", "unsigned_V4SI" }, |
| { "uv8hi", "unsigned_V8HI" }, |
| { "v", "void" }, |
| { "v16qi", "V16QI" }, |
| { "v1poi", "vector_pair" }, |
| { "v1pxi", "vector_quad" }, |
| { "v1ti", "V1TI" }, |
| { "v2df", "V2DF" }, |
| { "v2di", "V2DI" }, |
| { "v4sf", "V4SF" }, |
| { "v4si", "V4SI" }, |
| { "v8hi", "V8HI" }, |
| { "vp8hi", "pixel_V8HI" }, |
| }; |
| |
| /* From a possibly extended line with a virtual position, calculate |
| the current line and character position. */ |
| static void |
| real_line_pos (int diagpos, int *real_line, int *real_pos) |
| { |
| *real_line = line - lastline; |
| *real_pos = diagpos; |
| |
| for (int i = 0; i < MAXLINES; i++) |
| { |
| int len = strlen(lines[i]); |
| if (*real_pos <= len) |
| break; |
| |
| (*real_line)++; |
| *real_pos -= len - 2; |
| } |
| |
| /* Convert from zero-base to one-base for printing. */ |
| (*real_pos)++; |
| } |
| |
| /* Pointer to a diagnostic function. */ |
| static void (*diag) (int, const char *, ...) |
| __attribute__ ((format (printf, 2, 3))); |
| |
| /* Custom diagnostics. */ |
| static void __attribute__ ((format (printf, 2, 3))) |
| bif_diag (int diagpos, const char * fmt, ...) |
| { |
| va_list args; |
| int real_line, real_pos; |
| real_line_pos (diagpos, &real_line, &real_pos); |
| fprintf (stderr, "%s:%d:%d: ", bif_path, real_line, real_pos); |
| va_start (args, fmt); |
| vfprintf (stderr, fmt, args); |
| va_end (args); |
| } |
| |
| static void __attribute__ ((format (printf, 2, 3))) |
| ovld_diag (int diagpos, const char * fmt, ...) |
| { |
| va_list args; |
| int real_line, real_pos; |
| real_line_pos (diagpos, &real_line, &real_pos); |
| fprintf (stderr, "%s:%d:%d: ", ovld_path, real_line, real_pos); |
| va_start (args, fmt); |
| vfprintf (stderr, fmt, args); |
| va_end (args); |
| } |
| |
| /* Produce a fatal error message. */ |
| static void |
| fatal (const char *msg) |
| { |
| fprintf (stderr, "FATAL: %s\n", msg); |
| abort (); |
| } |
| |
| /* Pass over whitespace (other than a newline, which terminates the scan). */ |
| static void |
| consume_whitespace (void) |
| { |
| while (pos < LINELEN && isspace(linebuf[pos]) && linebuf[pos] != '\n') |
| pos++; |
| |
| if (pos >= LINELEN) |
| { |
| diag (pos, "line length overrun.\n"); |
| exit (1); |
| } |
| |
| return; |
| } |
| |
| /* Get the next nonblank, noncomment line, returning 0 on EOF, 1 otherwise. */ |
| static int |
| advance_line (FILE *file) |
| { |
| while (1) |
| { |
| /* Read ahead one line and check for EOF. */ |
| if (!fgets (linebuf, sizeof linebuf, file)) |
| return 0; |
| line++; |
| size_t len = strlen (linebuf); |
| |
| /* Escape-newline processing. */ |
| lastline = 0; |
| if (len > 1) |
| { |
| strcpy (lines[0], linebuf); |
| while (linebuf[len - 2] == '\\' |
| && linebuf[len - 1] == '\n') |
| { |
| lastline++; |
| if (lastline == MAXLINES) |
| fatal ("number of supported overflow lines exceeded"); |
| line++; |
| if (!fgets (lines[lastline], LINELEN, file)) |
| fatal ("unexpected end of file"); |
| strcpy (&linebuf[len - 2], lines[lastline]); |
| len += strlen (lines[lastline]) - 2; |
| } |
| } |
| |
| if (linebuf[len - 1] != '\n') |
| fatal ("line doesn't terminate with newline"); |
| pos = 0; |
| consume_whitespace (); |
| if (linebuf[pos] != '\n' && linebuf[pos] != ';') |
| return 1; |
| } |
| } |
| |
| static inline void |
| safe_inc_pos (void) |
| { |
| if (++pos >= LINELEN) |
| { |
| diag (pos, "line length overrun.\n"); |
| exit (1); |
| } |
| } |
| |
| /* Match an identifier, returning NULL on failure, else a pointer to a |
| buffer containing the identifier. */ |
| static char * |
| match_identifier (void) |
| { |
| int lastpos = pos - 1; |
| while (lastpos < LINELEN - 1 |
| && (isalnum (linebuf[lastpos + 1]) || linebuf[lastpos + 1] == '_')) |
| ++lastpos; |
| |
| if (lastpos >= LINELEN - 1) |
| { |
| diag (lastpos, "line length overrun.\n"); |
| exit (1); |
| } |
| |
| if (lastpos < pos) |
| return 0; |
| |
| char *buf = (char *) malloc (lastpos - pos + 2); |
| memcpy (buf, &linebuf[pos], lastpos - pos + 1); |
| buf[lastpos - pos + 1] = '\0'; |
| |
| pos = lastpos + 1; |
| return buf; |
| } |
| |
| /* Match an integer and return the string representing its value, |
| or a null string on failure. */ |
| static char * |
| match_integer (void) |
| { |
| int startpos = pos; |
| if (linebuf[pos] == '-') |
| safe_inc_pos (); |
| |
| int lastpos = pos - 1; |
| while (lastpos < LINELEN - 1 && isdigit (linebuf[lastpos + 1])) |
| ++lastpos; |
| |
| if (lastpos >= LINELEN - 1) |
| { |
| diag (lastpos, "line length overrun.\n"); |
| exit (1); |
| } |
| |
| if (lastpos < pos) |
| return NULL; |
| |
| pos = lastpos + 1; |
| char *buf = (char *) malloc (lastpos - startpos + 2); |
| memcpy (buf, &linebuf[startpos], lastpos - startpos + 1); |
| buf[lastpos - startpos + 1] = '\0'; |
| return buf; |
| } |
| |
| /* Match a string up to but not including a ']', and return its value, |
| or zero if there is nothing before the ']'. Error if we don't find |
| such a character. */ |
| static const char * |
| match_to_right_bracket (void) |
| { |
| int lastpos = pos - 1; |
| while (lastpos < LINELEN - 1 && linebuf[lastpos + 1] != ']') |
| { |
| if (linebuf[lastpos + 1] == '\n') |
| fatal ("no ']' found before end of line.\n"); |
| ++lastpos; |
| } |
| |
| if (lastpos >= LINELEN - 1) |
| { |
| diag (lastpos, "line length overrun.\n"); |
| exit (1); |
| } |
| |
| if (lastpos < pos) |
| return 0; |
| |
| char *buf = (char *) malloc (lastpos - pos + 2); |
| memcpy (buf, &linebuf[pos], lastpos - pos + 1); |
| buf[lastpos - pos + 1] = '\0'; |
| |
| pos = lastpos + 1; |
| return buf; |
| } |
| |
| static inline void |
| handle_pointer (typeinfo *typedata) |
| { |
| consume_whitespace (); |
| if (linebuf[pos] == '*') |
| { |
| typedata->ispointer = 1; |
| safe_inc_pos (); |
| } |
| } |
| |
| static bif_stanza |
| stanza_name_to_stanza (const char *stanza_name) |
| { |
| for (int i = 0; i < NUMBIFSTANZAS; i++) |
| if (!strcmp (stanza_name, stanza_map[i].stanza_name)) |
| return stanza_map[i].stanza; |
| fatal ("Stanza mapping is inconsistent."); |
| /* Unreachable. */ |
| return BSTZ_ALWAYS; |
| } |
| |
| /* Match one of the allowable base types. Consumes one token unless the |
| token is "long", which must be paired with a second "long". Optionally |
| consumes a following '*' token for pointers. Return 1 for success, |
| 0 for failure. */ |
| static int |
| match_basetype (typeinfo *typedata) |
| { |
| consume_whitespace (); |
| int oldpos = pos; |
| char *token = match_identifier (); |
| if (!token) |
| { |
| diag (pos, "missing base type in return type\n"); |
| return 0; |
| } |
| |
| if (!strcmp (token, "char")) |
| typedata->base = BT_CHAR; |
| else if (!strcmp (token, "short")) |
| typedata->base = BT_SHORT; |
| else if (!strcmp (token, "int")) |
| typedata->base = BT_INT; |
| else if (!strcmp (token, "long")) |
| { |
| consume_whitespace (); |
| oldpos = pos; |
| char *mustbelongordbl = match_identifier (); |
| if (!mustbelongordbl) |
| typedata->base = BT_LONG; |
| else if (!strcmp (mustbelongordbl, "long")) |
| typedata->base = BT_LONGLONG; |
| else if (!strcmp (mustbelongordbl, "double")) |
| typedata->base = BT_LONGDOUBLE; |
| else |
| /* Speculatively accept "long" here and push back the token. |
| This occurs when "long" is a return type and the next token |
| is the function name. */ |
| { |
| typedata->base = BT_LONG; |
| pos = oldpos; |
| } |
| } |
| else if (!strcmp (token, "float")) |
| typedata->base = BT_FLOAT; |
| else if (!strcmp (token, "double")) |
| typedata->base = BT_DOUBLE; |
| else if (!strcmp (token, "__int128")) |
| typedata->base = BT_INT128; |
| else if (!strcmp (token, "_Float128")) |
| typedata->base = BT_FLOAT128; |
| else if (!strcmp (token, "bool")) |
| typedata->base = BT_BOOL; |
| /* A "string" is a special "const char *" -- we need it because it |
| cannot match either signed or unsigned char *. */ |
| else if (!strcmp (token, "string")) |
| typedata->base = BT_STRING; |
| else if (!strcmp (token, "_Decimal32")) |
| typedata->base = BT_DECIMAL32; |
| else if (!strcmp (token, "_Decimal64")) |
| typedata->base = BT_DECIMAL64; |
| else if (!strcmp (token, "_Decimal128")) |
| typedata->base = BT_DECIMAL128; |
| else if (!strcmp (token, "__ibm128")) |
| typedata->base = BT_IBM128; |
| else |
| { |
| diag (oldpos, "unrecognized base type\n"); |
| return 0; |
| } |
| |
| handle_pointer (typedata); |
| return 1; |
| } |
| |
| /* Helper routine for match_const_restriction. */ |
| static int |
| match_bracketed_pair (typeinfo *typedata, char open, char close, |
| restriction restr) |
| { |
| if (linebuf[pos] == open) |
| { |
| safe_inc_pos (); |
| int oldpos = pos; |
| char *x = match_integer (); |
| if (x == NULL) |
| { |
| diag (oldpos, "malformed integer.\n"); |
| return 0; |
| } |
| consume_whitespace (); |
| if (linebuf[pos] != ',') |
| { |
| diag (pos, "missing comma.\n"); |
| return 0; |
| } |
| safe_inc_pos (); |
| consume_whitespace (); |
| oldpos = pos; |
| char *y = match_integer (); |
| if (y == NULL) |
| { |
| diag (oldpos, "malformed integer.\n"); |
| return 0; |
| } |
| typedata->restr = restr; |
| typedata->val1 = x; |
| typedata->val2 = y; |
| |
| consume_whitespace (); |
| if (linebuf[pos] != close) |
| { |
| diag (pos, "malformed restriction.\n"); |
| return 0; |
| } |
| safe_inc_pos (); |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| /* A const int argument may be restricted to certain values. This is |
| indicated by one of the following occurring after the "int' token: |
| |
| <x> restricts the constant to x bits, interpreted as unsigned |
| <x,y> restricts the constant to the inclusive range [x,y] |
| [x,y] restricts the constant to the inclusive range [x,y], |
| but only applies if the argument is constant. |
| {x,y} restricts the constant to one of two values, x or y. |
| |
| Here x and y are integer tokens. Note that the "const" token is a |
| lie when the restriction is [x,y], but this simplifies the parsing |
| significantly and is hopefully forgivable. |
| |
| Return 1 for success, else 0. */ |
| static int |
| match_const_restriction (typeinfo *typedata) |
| { |
| int oldpos = pos; |
| if (linebuf[pos] == '<') |
| { |
| safe_inc_pos (); |
| oldpos = pos; |
| char *x = match_integer (); |
| if (x == NULL) |
| { |
| diag (oldpos, "malformed integer.\n"); |
| return 0; |
| } |
| consume_whitespace (); |
| if (linebuf[pos] == '>') |
| { |
| typedata->restr = RES_BITS; |
| typedata->val1 = x; |
| safe_inc_pos (); |
| return 1; |
| } |
| else if (linebuf[pos] != ',') |
| { |
| diag (pos, "malformed restriction.\n"); |
| return 0; |
| } |
| safe_inc_pos (); |
| oldpos = pos; |
| char *y = match_integer (); |
| if (y == NULL) |
| { |
| diag (oldpos, "malformed integer.\n"); |
| return 0; |
| } |
| typedata->restr = RES_RANGE; |
| typedata->val1 = x; |
| typedata->val2 = y; |
| |
| consume_whitespace (); |
| if (linebuf[pos] != '>') |
| { |
| diag (pos, "malformed restriction.\n"); |
| return 0; |
| } |
| safe_inc_pos (); |
| return 1; |
| } |
| else if (match_bracketed_pair (typedata, '{', '}', RES_VALUES) |
| || match_bracketed_pair (typedata, '[', ']', RES_VAR_RANGE)) |
| return 1; |
| |
| return 0; |
| } |
| |
| /* Look for a type, which can be terminated by a token that is not part of |
| a type, a comma, or a closing parenthesis. Place information about the |
| type in TYPEDATA. Return 1 for success, 0 for failure. */ |
| static int |
| match_type (typeinfo *typedata, int voidok) |
| { |
| /* A legal type is of the form: |
| |
| [const] [[signed|unsigned] <basetype> | <vectype>] [*] |
| |
| Legal values of <basetype> are (for now): |
| |
| char |
| short |
| int |
| long |
| long double |
| long long |
| float |
| double |
| __int128 |
| _Float128 |
| bool |
| string |
| _Decimal32 |
| _Decimal64 |
| _Decimal128 |
| __ibm128 |
| |
| Legal values of <vectype> are as follows, and are shorthand for |
| the associated meaning: |
| |
| vsc vector signed char |
| vuc vector unsigned char |
| vbc vector bool char |
| vss vector signed short |
| vus vector unsigned short |
| vbs vector bool short |
| vsi vector signed int |
| vui vector unsigned int |
| vbi vector bool int |
| vsll vector signed long long |
| vull vector unsigned long long |
| vbll vector bool long long |
| vsq vector signed __int128 |
| vuq vector unsigned __int128 |
| vbq vector bool __int128 |
| vp vector pixel |
| vf vector float |
| vd vector double |
| v256 __vector_pair |
| v512 __vector_quad |
| |
| For simplicity, We don't support "short int" and "long long int". |
| We don't currently support a <basetype> of "_Float16". "signed" |
| and "unsigned" only apply to integral base types. The optional * |
| indicates a pointer type. */ |
| |
| consume_whitespace (); |
| memset (typedata, 0, sizeof *typedata); |
| int oldpos = pos; |
| |
| char *token = match_identifier (); |
| if (!token) |
| return 0; |
| |
| if (!strcmp (token, "const")) |
| { |
| typedata->isconst = 1; |
| consume_whitespace (); |
| oldpos = pos; |
| token = match_identifier (); |
| } |
| |
| if (!strcmp (token, "void")) |
| typedata->isvoid = 1; |
| |
| if (!strcmp (token, "vsc")) |
| { |
| typedata->isvector = 1; |
| typedata->issigned = 1; |
| typedata->base = BT_CHAR; |
| handle_pointer (typedata); |
| return 1; |
| } |
| else if (!strcmp (token, "vuc")) |
| { |
| typedata->isvector = 1; |
| typedata->isunsigned = 1; |
| typedata->base = BT_CHAR; |
| handle_pointer (typedata); |
| return 1; |
| } |
| else if (!strcmp (token, "vbc")) |
| { |
| typedata->isvector = 1; |
| typedata->isbool = 1; |
| typedata->base = BT_CHAR; |
| handle_pointer (typedata); |
| return 1; |
| } |
| else if (!strcmp (token, "vss")) |
| { |
| typedata->isvector = 1; |
| typedata->issigned = 1; |
| typedata->base = BT_SHORT; |
| handle_pointer (typedata); |
| return 1; |
| } |
| else if (!strcmp (token, "vus")) |
| { |
| typedata->isvector = 1; |
| typedata->isunsigned = 1; |
| typedata->base = BT_SHORT; |
| handle_pointer (typedata); |
| return 1; |
| } |
| else if (!strcmp (token, "vbs")) |
| { |
| typedata->isvector = 1; |
| typedata->isbool = 1; |
| typedata->base = BT_SHORT; |
| handle_pointer (typedata); |
| return 1; |
| } |
| else if (!strcmp (token, "vsi")) |
| { |
| typedata->isvector = 1; |
| typedata->issigned = 1; |
| typedata->base = BT_INT; |
| handle_pointer (typedata); |
| return 1; |
| } |
| else if (!strcmp (token, "vui")) |
| { |
| typedata->isvector = 1; |
| typedata->isunsigned = 1; |
| typedata->base = BT_INT; |
| handle_pointer (typedata); |
| return 1; |
| } |
| else if (!strcmp (token, "vbi")) |
| { |
| typedata->isvector = 1; |
| typedata->isbool = 1; |
| typedata->base = BT_INT; |
| handle_pointer (typedata); |
| return 1; |
| } |
| else if (!strcmp (token, "vsll")) |
| { |
| typedata->isvector = 1; |
| typedata->issigned = 1; |
| typedata->base = BT_LONGLONG; |
| handle_pointer (typedata); |
| return 1; |
| } |
| else if (!strcmp (token, "vull")) |
| { |
| typedata->isvector = 1; |
| typedata->isunsigned = 1; |
| typedata->base = BT_LONGLONG; |
| handle_pointer (typedata); |
| return 1; |
| } |
| else if (!strcmp (token, "vbll")) |
| { |
| typedata->isvector = 1; |
| typedata->isbool = 1; |
| typedata->base = BT_LONGLONG; |
| handle_pointer (typedata); |
| return 1; |
| } |
| else if (!strcmp (token, "vsq")) |
| { |
| typedata->isvector = 1; |
| typedata->issigned = 1; |
| typedata->base = BT_INT128; |
| handle_pointer (typedata); |
| return 1; |
| } |
| else if (!strcmp (token, "vuq")) |
| { |
| typedata->isvector = 1; |
| typedata->isunsigned = 1; |
| typedata->base = BT_INT128; |
| handle_pointer (typedata); |
| return 1; |
| } |
| else if (!strcmp (token, "vbq")) |
| { |
| typedata->isvector = 1; |
| typedata->isbool = 1; |
| typedata->base = BT_INT128; |
| handle_pointer (typedata); |
| return 1; |
| } |
| else if (!strcmp (token, "vp")) |
| { |
| typedata->isvector = 1; |
| typedata->ispixel = 1; |
| typedata->base = BT_SHORT; |
| handle_pointer (typedata); |
| return 1; |
| } |
| else if (!strcmp (token, "vf")) |
| { |
| typedata->isvector = 1; |
| typedata->base = BT_FLOAT; |
| handle_pointer (typedata); |
| return 1; |
| } |
| else if (!strcmp (token, "vd")) |
| { |
| typedata->isvector = 1; |
| typedata->base = BT_DOUBLE; |
| handle_pointer (typedata); |
| return 1; |
| } |
| else if (!strcmp (token, "v256")) |
| { |
| typedata->isvector = 1; |
| typedata->base = BT_VPAIR; |
| handle_pointer (typedata); |
| return 1; |
| } |
| else if (!strcmp (token, "v512")) |
| { |
| typedata->isvector = 1; |
| typedata->base = BT_VQUAD; |
| handle_pointer (typedata); |
| return 1; |
| } |
| else if (!strcmp (token, "signed")) |
| typedata->issigned = 1; |
| else if (!strcmp (token, "unsigned")) |
| typedata->isunsigned = 1; |
| else if (!typedata->isvoid && !typedata->isconst) |
| { |
| /* Push back token. */ |
| pos = oldpos; |
| return match_basetype (typedata); |
| } |
| |
| if (typedata->isvoid) |
| { |
| consume_whitespace (); |
| if (linebuf[pos] == '*') |
| { |
| typedata->ispointer = 1; |
| safe_inc_pos (); |
| } |
| else if (!voidok) |
| return 0; |
| return 1; |
| } |
| |
| if (!typedata->issigned && !typedata->isunsigned) |
| pos = oldpos; |
| if (!match_basetype (typedata)) |
| return 0; |
| |
| if (typedata->isconst) |
| { |
| if (typedata->ispointer) |
| return 1; |
| if (typedata->base != BT_INT) |
| { |
| diag (oldpos, "'const' requires pointer or integer type\n"); |
| return 0; |
| } |
| consume_whitespace (); |
| if (linebuf[pos] == '<' || linebuf[pos] == '{' || linebuf[pos] == '[') |
| return match_const_restriction (typedata); |
| } |
| |
| return 1; |
| } |
| |
| /* Parse the argument list. */ |
| static parse_codes |
| parse_args (prototype *protoptr) |
| { |
| typelist **argptr = &protoptr->args; |
| int *nargs = &protoptr->nargs; |
| int *restr_opnd = protoptr->restr_opnd; |
| restriction *restr = protoptr->restr; |
| char **val1 = protoptr->restr_val1; |
| char **val2 = protoptr->restr_val2; |
| int restr_cnt = 0; |
| |
| int success; |
| *nargs = 0; |
| |
| /* Start the argument list. */ |
| consume_whitespace (); |
| if (linebuf[pos] != '(') |
| { |
| diag (pos, "missing '('.\n"); |
| return PC_PARSEFAIL; |
| } |
| safe_inc_pos (); |
| |
| do { |
| consume_whitespace (); |
| int oldpos = pos; |
| typelist *argentry = (typelist *) malloc (sizeof (typelist)); |
| memset (argentry, 0, sizeof *argentry); |
| typeinfo *argtype = &argentry->info; |
| success = match_type (argtype, VOID_NOTOK); |
| if (success) |
| { |
| if (argtype->restr) |
| { |
| if (restr_cnt >= MAXRESTROPNDS) |
| { |
| diag (pos, "More than two %d operands\n", MAXRESTROPNDS); |
| return PC_PARSEFAIL; |
| } |
| restr_opnd[restr_cnt] = *nargs + 1; |
| restr[restr_cnt] = argtype->restr; |
| val1[restr_cnt] = argtype->val1; |
| val2[restr_cnt] = argtype->val2; |
| restr_cnt++; |
| } |
| (*nargs)++; |
| *argptr = argentry; |
| argptr = &argentry->next; |
| consume_whitespace (); |
| if (linebuf[pos] == ',') |
| safe_inc_pos (); |
| else if (linebuf[pos] != ')') |
| { |
| diag (pos, "arg not followed by ',' or ')'.\n"); |
| return PC_PARSEFAIL; |
| } |
| |
| #ifdef DEBUG |
| diag (0, |
| "argument type: isvoid = %d, isconst = %d, isvector = %d, " |
| "issigned = %d, isunsigned = %d, isbool = %d, ispixel = %d, " |
| "ispointer = %d, base = %d, restr = %d, val1 = \"%s\", " |
| "val2 = \"%s\", pos = %d.\n", |
| argtype->isvoid, argtype->isconst, argtype->isvector, |
| argtype->issigned, argtype->isunsigned, argtype->isbool, |
| argtype->ispixel, argtype->ispointer, argtype->base, |
| argtype->restr, argtype->val1, argtype->val2, pos + 1); |
| #endif |
| } |
| else |
| { |
| free (argentry); |
| *argptr = NULL; |
| pos = oldpos; |
| if (linebuf[pos] != ')') |
| { |
| diag (pos, "badly terminated arg list.\n"); |
| return PC_PARSEFAIL; |
| } |
| safe_inc_pos (); |
| } |
| } while (success); |
| |
| return PC_OK; |
| } |
| |
| /* Parse the attribute list. */ |
| static parse_codes |
| parse_bif_attrs (attrinfo *attrptr) |
| { |
| consume_whitespace (); |
| if (linebuf[pos] != '{') |
| { |
| diag (pos, "missing attribute set.\n"); |
| return PC_PARSEFAIL; |
| } |
| safe_inc_pos (); |
| |
| memset (attrptr, 0, sizeof *attrptr); |
| char *attrname = NULL; |
| |
| do { |
| consume_whitespace (); |
| int oldpos = pos; |
| attrname = match_identifier (); |
| if (attrname) |
| { |
| if (!strcmp (attrname, "init")) |
| attrptr->isinit = 1; |
| else if (!strcmp (attrname, "set")) |
| attrptr->isset = 1; |
| else if (!strcmp (attrname, "extract")) |
| attrptr->isextract = 1; |
| else if (!strcmp (attrname, "nosoft")) |
| attrptr->isnosoft = 1; |
| else if (!strcmp (attrname, "ldvec")) |
| attrptr->isldvec = 1; |
| else if (!strcmp (attrname, "stvec")) |
| attrptr->isstvec = 1; |
| else if (!strcmp (attrname, "reve")) |
| attrptr->isreve = 1; |
| else if (!strcmp (attrname, "pred")) |
| attrptr->ispred = 1; |
| else if (!strcmp (attrname, "htm")) |
| attrptr->ishtm = 1; |
| else if (!strcmp (attrname, "htmspr")) |
| attrptr->ishtmspr = 1; |
| else if (!strcmp (attrname, "htmcr")) |
| attrptr->ishtmcr = 1; |
| else if (!strcmp (attrname, "mma")) |
| attrptr->ismma = 1; |
| else if (!strcmp (attrname, "quad")) |
| attrptr->isquad = 1; |
| else if (!strcmp (attrname, "pair")) |
| attrptr->ispair = 1; |
| else if (!strcmp (attrname, "mmaint")) |
| attrptr->ismmaint = 1; |
| else if (!strcmp (attrname, "no32bit")) |
| attrptr->isno32bit = 1; |
| else if (!strcmp (attrname, "32bit")) |
| attrptr->is32bit = 1; |
| else if (!strcmp (attrname, "cpu")) |
| attrptr->iscpu = 1; |
| else if (!strcmp (attrname, "ldstmask")) |
| attrptr->isldstmask = 1; |
| else if (!strcmp (attrname, "lxvrse")) |
| attrptr->islxvrse = 1; |
| else if (!strcmp (attrname, "lxvrze")) |
| attrptr->islxvrze = 1; |
| else if (!strcmp (attrname, "endian")) |
| attrptr->isendian = 1; |
| else if (!strcmp (attrname, "ibmld")) |
| attrptr->isibmld = 1; |
| else if (!strcmp (attrname, "ibm128")) |
| attrptr->isibm128 = 1; |
| else |
| { |
| diag (oldpos, "unknown attribute.\n"); |
| return PC_PARSEFAIL; |
| } |
| |
| consume_whitespace (); |
| if (linebuf[pos] == ',') |
| safe_inc_pos (); |
| else if (linebuf[pos] != '}') |
| { |
| diag (pos, "arg not followed by ',' or '}'.\n"); |
| return PC_PARSEFAIL; |
| } |
| } |
| else |
| { |
| pos = oldpos; |
| if (linebuf[pos] != '}') |
| { |
| diag (pos, "badly terminated attr set.\n"); |
| return PC_PARSEFAIL; |
| } |
| safe_inc_pos (); |
| } |
| } while (attrname); |
| |
| #ifdef DEBUG |
| diag (0, |
| "attribute set: init = %d, set = %d, extract = %d, nosoft = %d, " |
| "ldvec = %d, stvec = %d, reve = %d, pred = %d, htm = %d, " |
| "htmspr = %d, htmcr = %d, mma = %d, quad = %d, pair = %d, " |
| "mmaint = %d, no32bit = %d, 32bit = %d, cpu = %d, ldstmask = %d, " |
| "lxvrse = %d, lxvrze = %d, endian = %d, ibmdld = %d, ibm128 = %d.\n", |
| attrptr->isinit, attrptr->isset, attrptr->isextract, |
| attrptr->isnosoft, attrptr->isldvec, attrptr->isstvec, |
| attrptr->isreve, attrptr->ispred, attrptr->ishtm, attrptr->ishtmspr, |
| attrptr->ishtmcr, attrptr->ismma, attrptr->isquad, attrptr->ispair, |
| attrptr->ismmaint, attrptr->isno32bit, attrptr->is32bit, |
| attrptr->iscpu, attrptr->isldstmask, attrptr->islxvrse, |
| attrptr->islxvrze, attrptr->isendian, attrptr->isibmld, |
| attrptr->isibm128); |
| #endif |
| |
| return PC_OK; |
| } |
| |
| /* Convert a vector type into a mode string. */ |
| static void |
| complete_vector_type (typeinfo *typeptr, char *buf, int *bufi) |
| { |
| if (typeptr->isbool) |
| buf[(*bufi)++] = 'b'; |
| buf[(*bufi)++] = 'v'; |
| if (typeptr->ispixel) |
| { |
| memcpy (&buf[*bufi], "p8hi", 4); |
| *bufi += 4; |
| return; |
| } |
| switch (typeptr->base) |
| { |
| case BT_CHAR: |
| memcpy (&buf[*bufi], "16qi", 4); |
| *bufi += 4; |
| break; |
| case BT_SHORT: |
| memcpy (&buf[*bufi], "8hi", 3); |
| *bufi += 3; |
| break; |
| case BT_INT: |
| memcpy (&buf[*bufi], "4si", 3); |
| *bufi += 3; |
| break; |
| case BT_LONGLONG: |
| memcpy (&buf[*bufi], "2di", 3); |
| *bufi += 3; |
| break; |
| case BT_FLOAT: |
| memcpy (&buf[*bufi], "4sf", 3); |
| *bufi += 3; |
| break; |
| case BT_DOUBLE: |
| memcpy (&buf[*bufi], "2df", 3); |
| *bufi += 3; |
| break; |
| case BT_INT128: |
| memcpy (&buf[*bufi], "1ti", 3); |
| *bufi += 3; |
| break; |
| case BT_FLOAT128: |
| memcpy (&buf[*bufi], "1tf", 3); |
| *bufi += 3; |
| break; |
| case BT_VPAIR: |
| memcpy (&buf[*bufi], "1poi", 4); |
| *bufi += 4; |
| break; |
| case BT_VQUAD: |
| memcpy (&buf[*bufi], "1pxi", 4); |
| *bufi += 4; |
| break; |
| default: |
| diag (pos, "unhandled basetype %d.\n", typeptr->base); |
| exit (1); |
| } |
| } |
| |
| /* Convert a base type into a mode string. */ |
| static void |
| complete_base_type (typeinfo *typeptr, char *buf, int *bufi) |
| { |
| switch (typeptr->base) |
| { |
| case BT_CHAR: |
| memcpy (&buf[*bufi], "qi", 2); |
| break; |
| case BT_SHORT: |
| memcpy (&buf[*bufi], "hi", 2); |
| break; |
| case BT_INT: |
| memcpy (&buf[*bufi], "si", 2); |
| break; |
| case BT_LONG: |
| memcpy (&buf[*bufi], "lg", 2); |
| break; |
| case BT_LONGLONG: |
| memcpy (&buf[*bufi], "di", 2); |
| break; |
| case BT_FLOAT: |
| memcpy (&buf[*bufi], "sf", 2); |
| break; |
| case BT_DOUBLE: |
| memcpy (&buf[*bufi], "df", 2); |
| break; |
| case BT_LONGDOUBLE: |
| memcpy (&buf[*bufi], "ld", 2); |
| break; |
| case BT_INT128: |
| memcpy (&buf[*bufi], "ti", 2); |
| break; |
| case BT_FLOAT128: |
| memcpy (&buf[*bufi], "tf", 2); |
| break; |
| case BT_BOOL: |
| memcpy (&buf[*bufi], "bi", 2); |
| break; |
| case BT_STRING: |
| memcpy (&buf[*bufi], "st", 2); |
| break; |
| case BT_DECIMAL32: |
| memcpy (&buf[*bufi], "sd", 2); |
| break; |
| case BT_DECIMAL64: |
| memcpy (&buf[*bufi], "dd", 2); |
| break; |
| case BT_DECIMAL128: |
| memcpy (&buf[*bufi], "td", 2); |
| break; |
| case BT_IBM128: |
| memcpy (&buf[*bufi], "if", 2); |
| break; |
| default: |
| diag (pos, "unhandled basetype %d.\n", typeptr->base); |
| exit (1); |
| } |
| |
| *bufi += 2; |
| } |
| |
| /* Build a function type descriptor identifier from the return type |
| and argument types described by PROTOPTR, and store it if it does |
| not already exist. Return the identifier. */ |
| static char * |
| construct_fntype_id (prototype *protoptr) |
| { |
| /* Determine the maximum space for a function type descriptor id. |
| Each type requires at most 9 characters (6 for the mode*, 1 for |
| the optional 'u' preceding the mode, 1 for the optional 'p' |
| preceding the mode, and 1 for an underscore following the mode). |
| We also need 5 characters for the string "ftype" that separates |
| the return mode from the argument modes. The last argument doesn't |
| need a trailing underscore, but we count that as the one trailing |
| "ftype" instead. For the special case of zero arguments, we need 9 |
| for the return type and 7 for "ftype_v". Finally, we need one |
| character for the terminating null. Thus for a function with N |
| arguments, we need at most 9N+15 characters for N>0, otherwise 17. |
| ---- |
| *Worst case is bv16qi for "vector bool char". */ |
| int len = protoptr->nargs ? (protoptr->nargs + 1) * 9 + 6 : 17; |
| char *buf = (char *) malloc (len); |
| int bufi = 0; |
| |
| if (protoptr->rettype.ispointer) |
| buf[bufi++] = 'p'; |
| |
| if (protoptr->rettype.isvoid) |
| buf[bufi++] = 'v'; |
| else |
| { |
| if (protoptr->rettype.isunsigned) |
| buf[bufi++] = 'u'; |
| if (protoptr->rettype.isvector) |
| complete_vector_type (&protoptr->rettype, buf, &bufi); |
| else |
| complete_base_type (&protoptr->rettype, buf, &bufi); |
| } |
| |
| memcpy (&buf[bufi], "_ftype", 6); |
| bufi += 6; |
| |
| if (!protoptr->nargs) |
| { |
| memcpy (&buf[bufi], "_v", 2); |
| bufi += 2; |
| } |
| else |
| { |
| typelist *argptr = protoptr->args; |
| for (int i = 0; i < protoptr->nargs; i++, argptr = argptr->next) |
| { |
| assert (argptr); |
| buf[bufi++] = '_'; |
| if (argptr->info.isconst |
| && argptr->info.base == BT_INT |
| && !argptr->info.ispointer) |
| { |
| buf[bufi++] = 'c'; |
| buf[bufi++] = 'i'; |
| continue; |
| } |
| if (argptr->info.ispointer) |
| { |
| if (argptr->info.isvoid) |
| { |
| if (argptr->info.isconst) |
| { |
| memcpy (&buf[bufi], "pcvoid", 6); |
| bufi += 6; |
| continue; |
| } |
| else |
| { |
| buf[bufi++] = 'p'; |
| buf[bufi++] = 'v'; |
| continue; |
| } |
| } |
| else |
| buf[bufi++] = 'p'; |
| } |
| |
| if (argptr->info.isunsigned) |
| buf[bufi++] = 'u'; |
| if (argptr->info.isvector) |
| complete_vector_type (&argptr->info, buf, &bufi); |
| else |
| complete_base_type (&argptr->info, buf, &bufi); |
| } |
| assert (!argptr); |
| } |
| |
| buf[bufi] = '\0'; |
| |
| /* Ignore return value, as duplicates are fine and expected here. */ |
| rbt_insert (&fntype_rbt, buf); |
| |
| return buf; |
| } |
| |
| /* Parse a function prototype. This code is shared by the bif and overload |
| file processing. */ |
| static parse_codes |
| parse_prototype (prototype *protoptr) |
| { |
| typeinfo *ret_type = &protoptr->rettype; |
| char **bifname = &protoptr->bifname; |
| |
| /* Get the return type. */ |
| consume_whitespace (); |
| int oldpos = pos; |
| int success = match_type (ret_type, VOID_OK); |
| if (!success) |
| { |
| diag (oldpos, "missing or badly formed return type.\n"); |
| return PC_PARSEFAIL; |
| } |
| |
| #ifdef DEBUG |
| diag (0, |
| "return type: isvoid = %d, isconst = %d, isvector = %d, " |
| "issigned = %d, isunsigned = %d, isbool = %d, ispixel = %d, " |
| "ispointer = %d, base = %d, restr = %d, val1 = \"%s\", " |
| "val2 = \"%s\", pos = %d.\n", |
| ret_type->isvoid, ret_type->isconst, ret_type->isvector, |
| ret_type->issigned, ret_type->isunsigned, ret_type->isbool, |
| ret_type->ispixel, ret_type->ispointer, ret_type->base, |
| ret_type->restr, ret_type->val1, ret_type->val2, pos + 1); |
| #endif |
| |
| /* Get the bif name. */ |
| consume_whitespace (); |
| oldpos = pos; |
| *bifname = match_identifier (); |
| if (!*bifname) |
| { |
| diag (oldpos, "missing function name.\n"); |
| return PC_PARSEFAIL; |
| } |
| |
| #ifdef DEBUG |
| diag (0, "function name is '%s'.\n", *bifname); |
| #endif |
| |
| /* Process arguments. */ |
| if (parse_args (protoptr) == PC_PARSEFAIL) |
| return PC_PARSEFAIL; |
| |
| /* Process terminating semicolon. */ |
| consume_whitespace (); |
| if (linebuf[pos] != ';') |
| { |
| diag (pos, "missing semicolon.\n"); |
| return PC_PARSEFAIL; |
| } |
| safe_inc_pos (); |
| consume_whitespace (); |
| if (linebuf[pos] != '\n') |
| { |
| diag (pos, "garbage at end of line.\n"); |
| return PC_PARSEFAIL; |
| } |
| |
| return PC_OK; |
| } |
| |
| /* Parse a two-line entry for a built-in function. */ |
| static parse_codes |
| parse_bif_entry (void) |
| { |
| /* Check for end of stanza. */ |
| pos = 0; |
| consume_whitespace (); |
| if (linebuf[pos] == '[') |
| return PC_EOSTANZA; |
| |
| /* Allocate an entry in the bif table. */ |
| if (num_bifs >= MAXBIFS - 1) |
| { |
| diag (pos, "too many built-in functions.\n"); |
| return PC_PARSEFAIL; |
| } |
| |
| curr_bif = num_bifs++; |
| bifs[curr_bif].stanza = curr_bif_stanza; |
| |
| /* Read the first token and see if it is a function modifier. */ |
| consume_whitespace (); |
| int oldpos = pos; |
| char *token = match_identifier (); |
| if (!token) |
| { |
| diag (oldpos, "malformed entry.\n"); |
| return PC_PARSEFAIL; |
| } |
| |
| if (!strcmp (token, "const")) |
| bifs[curr_bif].kind = FNK_CONST; |
| else if (!strcmp (token, "pure")) |
| bifs[curr_bif].kind = FNK_PURE; |
| else if (!strcmp (token, "fpmath")) |
| bifs[curr_bif].kind = FNK_FPMATH; |
| else |
| { |
| /* No function modifier, so push the token back. */ |
| pos = oldpos; |
| bifs[curr_bif].kind = FNK_NONE; |
| } |
| |
| if (parse_prototype (&bifs[curr_bif].proto) == PC_PARSEFAIL) |
| return PC_PARSEFAIL; |
| |
| /* Build a function type descriptor identifier from the return type |
| and argument types, and store it if it does not already exist. */ |
| bifs[curr_bif].fndecl = construct_fntype_id (&bifs[curr_bif].proto); |
| |
| /* Now process line 2. First up is the builtin id. */ |
| if (!advance_line (bif_file)) |
| { |
| diag (pos, "unexpected EOF.\n"); |
| return PC_PARSEFAIL; |
| } |
| |
| pos = 0; |
| consume_whitespace (); |
| oldpos = pos; |
| bifs[curr_bif].idname = match_identifier (); |
| if (!bifs[curr_bif].idname) |
| { |
| diag (pos, "missing builtin id.\n"); |
| return PC_PARSEFAIL; |
| } |
| |
| #ifdef DEBUG |
| diag (0, "ID name is '%s'.\n", bifs[curr_bif].idname); |
| #endif |
| |
| /* Save the ID in a lookup structure. */ |
| if (!rbt_insert (&bif_rbt, bifs[curr_bif].idname)) |
| { |
| diag (oldpos, "duplicate function ID '%s'.\n", bifs[curr_bif].idname); |
| return PC_PARSEFAIL; |
| } |
| |
| /* Append a number representing the order in which this function |
| was encountered to its name, and save in another lookup |
| structure. */ |
| int orig_len = strlen (bifs[curr_bif].idname); |
| char *buf = (char *) malloc (orig_len + 7); |
| sprintf (buf, "%s:%05d", bifs[curr_bif].idname, curr_bif); |
| |
| if (!rbt_insert (&bifo_rbt, buf)) |
| { |
| diag (pos, "internal error inserting '%s' in bifo_rbt\n", buf); |
| return PC_PARSEFAIL; |
| } |
| |
| /* Now the pattern name. */ |
| consume_whitespace (); |
| bifs[curr_bif].patname = match_identifier (); |
| if (!bifs[curr_bif].patname) |
| { |
| diag (pos, "missing pattern name.\n"); |
| return PC_PARSEFAIL; |
| } |
| |
| #ifdef DEBUG |
| diag (0, "pattern name is '%s'.\n", bifs[curr_bif].patname); |
| #endif |
| |
| /* Process attributes. */ |
| return parse_bif_attrs (&bifs[curr_bif].attrs); |
| } |
| |
| /* Parse one stanza of the input BIF file. linebuf already contains the |
| first line to parse. */ |
| static parse_codes |
| parse_bif_stanza (void) |
| { |
| /* Parse the stanza header. */ |
| pos = 0; |
| consume_whitespace (); |
| |
| if (linebuf[pos] != '[') |
| { |
| diag (pos, "ill-formed stanza header.\n"); |
| return PC_PARSEFAIL; |
| } |
| safe_inc_pos (); |
| |
| const char *stanza_name = match_to_right_bracket (); |
| if (!stanza_name) |
| { |
| diag (pos, "no expression found in stanza header.\n"); |
| return PC_PARSEFAIL; |
| } |
| |
| curr_bif_stanza = stanza_name_to_stanza (stanza_name); |
| |
| if (linebuf[pos] != ']') |
| { |
| diag (pos, "ill-formed stanza header.\n"); |
| return PC_PARSEFAIL; |
| } |
| safe_inc_pos (); |
| |
| consume_whitespace (); |
| if (linebuf[pos] != '\n' && pos != LINELEN - 1) |
| { |
| diag (pos, "garbage after stanza header.\n"); |
| return PC_PARSEFAIL; |
| } |
| |
| parse_codes result = PC_OK; |
| |
| while (result != PC_EOSTANZA) |
| { |
| if (!advance_line (bif_file)) |
| return PC_EOFILE; |
| result = parse_bif_entry (); |
| if (result == PC_PARSEFAIL) |
| return PC_PARSEFAIL; |
| } |
| |
| return PC_OK; |
| } |
| |
| /* Parse the built-in file. */ |
| static parse_codes |
| parse_bif (void) |
| { |
| parse_codes result; |
| diag = &bif_diag; |
| if (!advance_line (bif_file)) |
| return PC_OK; |
| |
| do |
| result = parse_bif_stanza (); |
| while (result == PC_OK); |
| |
| if (result == PC_EOFILE) |
| return PC_OK; |
| return result; |
| } |
| |
| /* Callback function for create_bif_order. */ |
| void set_bif_order (char *str) |
| { |
| int num = 0; |
| char *colon = strchr (str, ':'); |
| sscanf (++colon, "%d", &num); |
| bif_order[bif_index++] = num; |
| } |
| |
| /* Create a mapping from function IDs in their final order to the order |
| they appear in the built-in function file. */ |
| static void |
| create_bif_order (void) |
| { |
| bif_order = (int *) malloc ((curr_bif + 1) * sizeof (int)); |
| rbt_inorder_callback (&bifo_rbt, bifo_rbt.rbt_root, set_bif_order); |
| } |
| |
| /* Parse one two-line entry in the overload file. */ |
| static parse_codes |
| parse_ovld_entry (void) |
| { |
| /* Check for end of stanza. */ |
| pos = 0; |
| consume_whitespace (); |
| if (linebuf[pos] == '[') |
| return PC_EOSTANZA; |
| |
| /* Allocate an entry in the overload table. */ |
| if (num_ovlds >= MAXOVLDS - 1) |
| { |
| diag (pos, "too many overloads.\n"); |
| return PC_PARSEFAIL; |
| } |
| |
| curr_ovld = num_ovlds++; |
| ovlds[curr_ovld].stanza = curr_ovld_stanza; |
| |
| if (parse_prototype (&ovlds[curr_ovld].proto) == PC_PARSEFAIL) |
| return PC_PARSEFAIL; |
| |
| if (ovlds[curr_ovld].proto.nargs > max_ovld_args) |
| max_ovld_args = ovlds[curr_ovld].proto.nargs; |
| |
| /* Build a function type descriptor identifier from the return type |
| and argument types, and store it if it does not already exist. */ |
| ovlds[curr_ovld].fndecl = construct_fntype_id (&ovlds[curr_ovld].proto); |
| |
| /* Now process line 2, which just contains the builtin id and an |
| optional overload id. */ |
| if (!advance_line (ovld_file)) |
| { |
| diag (0, "unexpected EOF.\n"); |
| return PC_EOFILE; |
| } |
| |
| pos = 0; |
| consume_whitespace (); |
| int oldpos = pos; |
| char *id = match_identifier (); |
| ovlds[curr_ovld].bif_id_name = id; |
| ovlds[curr_ovld].ovld_id_name = id; |
| if (!id) |
| { |
| diag (pos, "missing overload id.\n"); |
| return PC_PARSEFAIL; |
| } |
| |
| #ifdef DEBUG |
| diag (pos, "ID name is '%s'.\n", id); |
| #endif |
| |
| /* The builtin id has to match one from the bif file. */ |
| if (!rbt_find (&bif_rbt, id)) |
| { |
| diag (pos, "builtin ID '%s' not found in bif file.\n", id); |
| return PC_PARSEFAIL; |
| } |
| |
| /* Check for an optional overload id. Usually we use the builtin |
| function id for that purpose, but sometimes we need multiple |
| overload entries for the same builtin id, and it needs to be unique. */ |
| consume_whitespace (); |
| if (linebuf[pos] != '\n') |
| { |
| id = match_identifier (); |
| ovlds[curr_ovld].ovld_id_name = id; |
| consume_whitespace (); |
| } |
| |
| /* Save the overload ID in a lookup structure. */ |
| if (!rbt_insert (&ovld_rbt, id)) |
| { |
| diag (oldpos, "duplicate overload ID '%s'.\n", id); |
| return PC_PARSEFAIL; |
| } |
| |
| if (linebuf[pos] != '\n') |
| { |
| diag (pos, "garbage at end of line.\n"); |
| return PC_PARSEFAIL; |
| } |
| return PC_OK; |
| } |
| |
| /* Parse one stanza of the input overload file. linebuf already contains the |
| first line to parse. */ |
| static parse_codes |
| parse_ovld_stanza (void) |
| { |
| /* Parse the stanza header. */ |
| pos = 0; |
| consume_whitespace (); |
| |
| if (linebuf[pos] != '[') |
| { |
| diag (pos, "ill-formed stanza header.\n"); |
| return PC_PARSEFAIL; |
| } |
| safe_inc_pos (); |
| |
| char *stanza_name = match_identifier (); |
| if (!stanza_name) |
| { |
| diag (pos, "no identifier found in stanza header.\n"); |
| return PC_PARSEFAIL; |
| } |
| |
| /* Add the identifier to a table and set the number to be recorded |
| with subsequent overload entries. */ |
| if (num_ovld_stanzas >= MAXOVLDSTANZAS) |
| { |
| diag (pos, "too many stanza headers.\n"); |
| return PC_PARSEFAIL; |
| } |
| |
| curr_ovld_stanza = num_ovld_stanzas++; |
| ovld_stanza *stanza = &ovld_stanzas[curr_ovld_stanza]; |
| stanza->stanza_id = stanza_name; |
| |
| consume_whitespace (); |
| if (linebuf[pos] != ',') |
| { |
| diag (pos, "missing comma.\n"); |
| return PC_PARSEFAIL; |
| } |
| safe_inc_pos (); |
| |
| consume_whitespace (); |
| stanza->extern_name = match_identifier (); |
| if (!stanza->extern_name) |
| { |
| diag (pos, "missing external name.\n"); |
| return PC_PARSEFAIL; |
| } |
| |
| consume_whitespace (); |
| if (linebuf[pos] != ',') |
| { |
| diag (pos, "missing comma.\n"); |
| return PC_PARSEFAIL; |
| } |
| safe_inc_pos (); |
| |
| consume_whitespace (); |
| stanza->intern_name = match_identifier (); |
| if (!stanza->intern_name) |
| { |
| diag (pos, "missing internal name.\n"); |
| return PC_PARSEFAIL; |
| } |
| |
| consume_whitespace (); |
| if (linebuf[pos] == ',') |
| { |
| safe_inc_pos (); |
| consume_whitespace (); |
| stanza->ifdef = match_identifier (); |
| if (!stanza->ifdef) |
| { |
| diag (pos, "missing ifdef token.\n"); |
| return PC_PARSEFAIL; |
| } |
| consume_whitespace (); |
| } |
| else |
| stanza->ifdef = 0; |
| |
| if (linebuf[pos] != ']') |
| { |
| diag (pos, "ill-formed stanza header.\n"); |
| return PC_PARSEFAIL; |
| } |
| safe_inc_pos (); |
| |
| consume_whitespace (); |
| if (linebuf[pos] != '\n' && pos != LINELEN - 1) |
| { |
| diag (pos, "garbage after stanza header.\n"); |
| return PC_PARSEFAIL; |
| } |
| |
| parse_codes result = PC_OK; |
| |
| while (result != PC_EOSTANZA) |
| { |
| if (!advance_line (ovld_file)) |
| return PC_EOFILE; |
| |
| result = parse_ovld_entry (); |
| if (result == PC_EOFILE || result == PC_PARSEFAIL) |
| return result; |
| } |
| |
| return PC_OK; |
| } |
| |
| /* Parse the overload file. */ |
| static parse_codes |
| parse_ovld (void) |
| { |
| parse_codes result = PC_OK; |
| diag = &ovld_diag; |
| |
| if (!advance_line (ovld_file)) |
| return PC_OK; |
| |
| while (result == PC_OK) |
| result = parse_ovld_stanza (); |
| |
| if (result == PC_EOFILE) |
| return PC_OK; |
| return result; |
| } |
| |
| /* Write a comment at the top of FILE about how the code was generated. */ |
| static void |
| write_autogenerated_header (FILE *file) |
| { |
| fprintf (file, "/* Automatically generated by the program '%s'\n", |
| pgm_path); |
| fprintf (file, " from the files '%s' and '%s'. */\n\n", |
| bif_path, ovld_path); |
| } |
| |
| /* Write declarations into the header file. */ |
| static void |
| write_decls (void) |
| { |
| fprintf (header_file, "enum rs6000_gen_builtins\n{\n RS6000_BIF_NONE,\n"); |
| for (int i = 0; i <= curr_bif; i++) |
| fprintf (header_file, " RS6000_BIF_%s,\n", bifs[bif_order[i]].idname); |
| fprintf (header_file, " RS6000_BIF_MAX,\n"); |
| fprintf (header_file, " RS6000_OVLD_NONE,\n"); |
| for (int i = 0; i < num_ovld_stanzas; i++) |
| fprintf (header_file, " RS6000_OVLD_%s,\n", ovld_stanzas[i].stanza_id); |
| fprintf (header_file, " RS6000_OVLD_MAX\n};\n\n"); |
| |
| fprintf (header_file, |
| "extern GTY(()) tree rs6000_builtin_decls[RS6000_OVLD_MAX];\n\n"); |
| |
| fprintf (header_file, |
| "enum rs6000_ovld_instances\n{\n RS6000_INST_NONE,\n"); |
| for (int i = 0; i <= curr_ovld; i++) |
| fprintf (header_file, " RS6000_INST_%s,\n", ovlds[i].ovld_id_name); |
| fprintf (header_file, " RS6000_INST_MAX\n};\n\n"); |
| |
| fprintf (header_file, "#define MAX_OVLD_ARGS %d\n", max_ovld_args); |
| |
| fprintf (header_file, "enum restriction {\n"); |
| fprintf (header_file, " RES_NONE,\n"); |
| fprintf (header_file, " RES_BITS,\n"); |
| fprintf (header_file, " RES_RANGE,\n"); |
| fprintf (header_file, " RES_VAR_RANGE,\n"); |
| fprintf (header_file, " RES_VALUES\n"); |
| fprintf (header_file, "};\n\n"); |
| |
| fprintf (header_file, "enum bif_enable {\n"); |
| fprintf (header_file, " ENB_ALWAYS,\n"); |
| fprintf (header_file, " ENB_P5,\n"); |
| fprintf (header_file, " ENB_P6,\n"); |
| fprintf (header_file, " ENB_P6_64,\n"); |
| fprintf (header_file, " ENB_ALTIVEC,\n"); |
| fprintf (header_file, " ENB_CELL,\n"); |
| fprintf (header_file, " ENB_VSX,\n"); |
| fprintf (header_file, " ENB_P7,\n"); |
| fprintf (header_file, " ENB_P7_64,\n"); |
| fprintf (header_file, " ENB_P8,\n"); |
| fprintf (header_file, " ENB_P8V,\n"); |
| fprintf (header_file, " ENB_P9,\n"); |
| fprintf (header_file, " ENB_P9_64,\n"); |
| fprintf (header_file, " ENB_P9V,\n"); |
| fprintf (header_file, " ENB_IEEE128_HW,\n"); |
| fprintf (header_file, " ENB_DFP,\n"); |
| fprintf (header_file, " ENB_CRYPTO,\n"); |
| fprintf (header_file, " ENB_HTM,\n"); |
| fprintf (header_file, " ENB_P10,\n"); |
| fprintf (header_file, " ENB_P10_64,\n"); |
| fprintf (header_file, " ENB_MMA\n"); |
| fprintf (header_file, "};\n\n"); |
| |
| fprintf (header_file, "#define PPC_MAXRESTROPNDS 3\n"); |
| fprintf (header_file, "struct GTY(()) bifdata\n"); |
| fprintf (header_file, "{\n"); |
| fprintf (header_file, " const char *GTY((skip(\"\"))) bifname;\n"); |
| fprintf (header_file, " bif_enable GTY((skip(\"\"))) enable;\n"); |
| fprintf (header_file, " tree fntype;\n"); |
| fprintf (header_file, " insn_code GTY((skip(\"\"))) icode;\n"); |
| fprintf (header_file, " int nargs;\n"); |
| fprintf (header_file, " int bifattrs;\n"); |
| fprintf (header_file, " int restr_opnd[PPC_MAXRESTROPNDS];\n"); |
| fprintf (header_file, " restriction GTY((skip(\"\"))) restr[PPC_MAXRESTROPNDS];\n"); |
| fprintf (header_file, " int restr_val1[PPC_MAXRESTROPNDS];\n"); |
| fprintf (header_file, " int restr_val2[PPC_MAXRESTROPNDS];\n"); |
| fprintf (header_file, " const char *GTY((skip(\"\"))) attr_string;\n"); |
| fprintf (header_file, " rs6000_gen_builtins GTY((skip(\"\"))) assoc_bif;\n"); |
| fprintf (header_file, "};\n\n"); |
| |
| fprintf (header_file, "#define bif_init_bit\t\t(0x00000001)\n"); |
| fprintf (header_file, "#define bif_set_bit\t\t(0x00000002)\n"); |
| fprintf (header_file, "#define bif_extract_bit\t\t(0x00000004)\n"); |
| fprintf (header_file, "#define bif_nosoft_bit\t\t(0x00000008)\n"); |
| fprintf (header_file, "#define bif_ldvec_bit\t\t(0x00000010)\n"); |
| fprintf (header_file, "#define bif_stvec_bit\t\t(0x00000020)\n"); |
| fprintf (header_file, "#define bif_reve_bit\t\t(0x00000040)\n"); |
| fprintf (header_file, "#define bif_pred_bit\t\t(0x00000080)\n"); |
| fprintf (header_file, "#define bif_htm_bit\t\t(0x00000100)\n"); |
| fprintf (header_file, "#define bif_htmspr_bit\t\t(0x00000200)\n"); |
| fprintf (header_file, "#define bif_htmcr_bit\t\t(0x00000400)\n"); |
| fprintf (header_file, "#define bif_mma_bit\t\t(0x00000800)\n"); |
| fprintf (header_file, "#define bif_quad_bit\t\t(0x00001000)\n"); |
| fprintf (header_file, "#define bif_pair_bit\t\t(0x00002000)\n"); |
| fprintf (header_file, "#define bif_mmaint_bit\t\t(0x00004000)\n"); |
| fprintf (header_file, "#define bif_no32bit_bit\t\t(0x00008000)\n"); |
| fprintf (header_file, "#define bif_32bit_bit\t\t(0x00010000)\n"); |
| fprintf (header_file, "#define bif_cpu_bit\t\t(0x00020000)\n"); |
| fprintf (header_file, "#define bif_ldstmask_bit\t(0x00040000)\n"); |
| fprintf (header_file, "#define bif_lxvrse_bit\t\t(0x00080000)\n"); |
| fprintf (header_file, "#define bif_lxvrze_bit\t\t(0x00100000)\n"); |
| fprintf (header_file, "#define bif_endian_bit\t\t(0x00200000)\n"); |
| fprintf (header_file, "#define bif_ibmld_bit\t\t(0x00400000)\n"); |
| fprintf (header_file, "#define bif_ibm128_bit\t\t(0x00800000)\n"); |
| fprintf (header_file, "\n"); |
| fprintf (header_file, |
| "#define bif_is_init(x)\t\t((x).bifattrs & bif_init_bit)\n"); |
| fprintf (header_file, |
| "#define bif_is_set(x)\t\t((x).bifattrs & bif_set_bit)\n"); |
| fprintf (header_file, |
| "#define bif_is_extract(x)\t((x).bifattrs & bif_extract_bit)\n"); |
| fprintf (header_file, |
| "#define bif_is_nosoft(x)\t((x).bifattrs & bif_nosoft_bit)\n"); |
| fprintf (header_file, |
| "#define bif_is_ldvec(x)\t\t((x).bifattrs & bif_ldvec_bit)\n"); |
| fprintf (header_file, |
| "#define bif_is_stvec(x)\t\t((x).bifattrs & bif_stvec_bit)\n"); |
| fprintf (header_file, |
| "#define bif_is_reve(x)\t\t((x).bifattrs & bif_reve_bit)\n"); |
| fprintf (header_file, |
| "#define bif_is_predicate(x)\t((x).bifattrs & bif_pred_bit)\n"); |
| fprintf (header_file, |
| "#define bif_is_htm(x)\t\t((x).bifattrs & bif_htm_bit)\n"); |
| fprintf (header_file, |
| "#define bif_is_htmspr(x)\t((x).bifattrs & bif_htmspr_bit)\n"); |
| fprintf (header_file, |
| "#define bif_is_htmcr(x)\t\t((x).bifattrs & bif_htmcr_bit)\n"); |
| fprintf (header_file, |
| "#define bif_is_mma(x)\t\t((x).bifattrs & bif_mma_bit)\n"); |
| fprintf (header_file, |
| "#define bif_is_quad(x)\t\t((x).bifattrs & bif_quad_bit)\n"); |
| fprintf (header_file, |
| "#define bif_is_pair(x)\t\t((x).bifattrs & bif_pair_bit)\n"); |
| fprintf (header_file, |
| "#define bif_is_mmaint(x)\t\t((x).bifattrs & bif_mmaint_bit)\n"); |
| fprintf (header_file, |
| "#define bif_is_no32bit(x)\t((x).bifattrs & bif_no32bit_bit)\n"); |
| fprintf (header_file, |
| "#define bif_is_32bit(x)\t((x).bifattrs & bif_32bit_bit)\n"); |
| fprintf (header_file, |
| "#define bif_is_cpu(x)\t\t((x).bifattrs & bif_cpu_bit)\n"); |
| fprintf (header_file, |
| "#define bif_is_ldstmask(x)\t((x).bifattrs & bif_ldstmask_bit)\n"); |
| fprintf (header_file, |
| "#define bif_is_lxvrse(x)\t((x).bifattrs & bif_lxvrse_bit)\n"); |
| fprintf (header_file, |
| "#define bif_is_lxvrze(x)\t((x).bifattrs & bif_lxvrze_bit)\n"); |
| fprintf (header_file, |
| "#define bif_is_endian(x)\t((x).bifattrs & bif_endian_bit)\n"); |
| fprintf (header_file, |
| "#define bif_is_ibmld(x)\t((x).bifattrs & bif_ibmld_bit)\n"); |
| fprintf (header_file, |
| "#define bif_is_ibm128(x)\t((x).bifattrs & bif_ibm128_bit)\n"); |
| fprintf (header_file, "\n"); |
| |
| fprintf (header_file, |
| "extern GTY(()) bifdata rs6000_builtin_info[RS6000_BIF_MAX];\n\n"); |
| |
| fprintf (header_file, "struct GTY(()) ovlddata\n"); |
| fprintf (header_file, "{\n"); |
| fprintf (header_file, " const char *GTY((skip(\"\"))) bifname;\n"); |
| fprintf (header_file, " rs6000_gen_builtins GTY((skip(\"\"))) bifid;\n"); |
| fprintf (header_file, " tree fntype;\n"); |
| fprintf (header_file, " ovlddata *GTY((skip(\"\"))) next;\n"); |
| fprintf (header_file, "};\n\n"); |
| |
| fprintf (header_file, "struct ovldrecord\n"); |
| fprintf (header_file, "{\n"); |
| fprintf (header_file, " const char *ovld_name;\n"); |
| fprintf (header_file, " ovlddata *first_instance;\n"); |
| fprintf (header_file, "};\n\n"); |
| |
| fprintf (header_file, |
| "extern GTY(()) ovlddata rs6000_instance_info[RS6000_INST_MAX];\n"); |
| fprintf (header_file, "extern ovldrecord rs6000_overload_info[];\n\n"); |
| |
| fprintf (header_file, "extern void rs6000_init_generated_builtins ();\n\n"); |
| fprintf (header_file, |
| "extern bool rs6000_builtin_is_supported (rs6000_gen_builtins);\n"); |
| fprintf (header_file, |
| "extern tree rs6000_builtin_decl (unsigned, " |
| "bool ATTRIBUTE_UNUSED);\n\n"); |
| } |
| |
| /* Comparator for bsearch on the type map. */ |
| int |
| typemap_cmp (const void *key, const void *entry) |
| { |
| return strcmp ((const char *)key, ((const typemap *)entry)->key); |
| } |
| |
| /* Write the type node corresponding to TOK. */ |
| static void |
| write_type_node (char *tok, bool indent) |
| { |
| if (indent) |
| fprintf (init_file, " "); |
| typemap *entry |
| = (typemap *) bsearch (tok, type_map, |
| sizeof type_map / sizeof type_map[0], |
| sizeof (typemap), typemap_cmp); |
| if (!entry) |
| fatal ("Type map is inconsistent."); |
| fprintf (init_file, "%s_type_node", entry->value); |
| } |
| |
| /* Write an initializer for a function type identified by STR. */ |
| void |
| write_fntype_init (char *str) |
| { |
| char *tok; |
| |
| /* Check whether we have a "tf" token in this string, representing |
| a float128_type_node. It's possible that float128_type_node is |
| undefined (occurs for -maltivec -mno-vsx, for example), so we |
| must guard against that. */ |
| int tf_found = strstr (str, "tf") != NULL; |
| |
| /* Similarly, look for decimal float tokens. */ |
| int dfp_found = (strstr (str, "dd") != NULL |
| || strstr (str, "td") != NULL |
| || strstr (str, "sd") != NULL); |
| |
| /* Avoid side effects of strtok on the original string by using a copy. */ |
| char *buf = strdup (str); |
| |
| if (tf_found || dfp_found) |
| fprintf (init_file, " tree %s = NULL_TREE;\n", buf); |
| else |
| fprintf (init_file, " tree "); |
| |
| if (tf_found) |
| fprintf (init_file, " if (float128_type_node)\n "); |
| else if (dfp_found) |
| fprintf (init_file, " if (dfloat64_type_node)\n "); |
| |
| fprintf (init_file, "%s\n = build_function_type_list (", buf); |
| tok = strtok (buf, "_"); |
| write_type_node (tok, tf_found || dfp_found); |
| tok = strtok (0, "_"); |
| assert (tok); |
| assert (!strcmp (tok, "ftype")); |
| |
| tok = strtok (0, "_"); |
| if (tok) |
| fprintf (init_file, ",\n\t\t\t\t"); |
| |
| /* Note: A function with no arguments ends with '_ftype_v'. */ |
| while (tok && strcmp (tok, "v")) |
| { |
| write_type_node (tok, tf_found || dfp_found); |
| tok = strtok (0, "_"); |
| fprintf (init_file, ",\n\t\t\t\t"); |
| } |
| fprintf (init_file, "NULL_TREE);\n"); |
| free (buf); |
| } |
| |
| /* Write everything to the header file (rs6000-builtins.h). Return |
| 1 if successful, 0 otherwise. */ |
| static int |
| write_header_file (void) |
| { |
| write_autogenerated_header (header_file); |
| |
| fprintf (header_file, "#ifndef _RS6000_BUILTINS_H\n"); |
| fprintf (header_file, "#define _RS6000_BUILTINS_H 1\n\n"); |
| |
| write_decls (); |
| |
| fprintf (header_file, "\n"); |
| fprintf (header_file, "\n#endif\n"); |
| |
| return 1; |
| } |
| |
| /* Write the decl and initializer for rs6000_builtin_info[]. */ |
| static void |
| write_bif_static_init (void) |
| { |
| const char *res[3]; |
| fprintf (init_file, "bifdata rs6000_builtin_info[RS6000_BIF_MAX] =\n"); |
| fprintf (init_file, " {\n"); |
| fprintf (init_file, " { /* RS6000_BIF_NONE: */\n"); |
| fprintf (init_file, " \"\", ENB_ALWAYS, 0, CODE_FOR_nothing, 0,\n"); |
| fprintf (init_file, " 0, {0, 0, 0}, {RES_NONE, RES_NONE, RES_NONE},\n"); |
| fprintf (init_file, " {0, 0, 0}, {0, 0, 0}, \"\", RS6000_BIF_NONE\n"); |
| fprintf (init_file, " },\n"); |
| for (int i = 0; i <= curr_bif; i++) |
| { |
| bifdata *bifp = &bifs[bif_order[i]]; |
| fprintf (init_file, " { /* RS6000_BIF_%s: */\n", bifp->idname); |
| fprintf (init_file, " /* bifname */\t\"%s\",\n", |
| bifp->proto.bifname); |
| fprintf (init_file, " /* enable*/\t%s,\n", |
| enable_string[bifp->stanza]); |
| /* Type must be instantiated at run time. */ |
| fprintf (init_file, " /* fntype */\t0,\n"); |
| fprintf (init_file, " /* icode */\tCODE_FOR_%s,\n", |
| bifp->patname); |
| fprintf (init_file, " /* nargs */\t%d,\n", |
| bifp->proto.nargs); |
| fprintf (init_file, " /* bifattrs */\t0"); |
| if (bifp->attrs.isinit) |
| fprintf (init_file, " | bif_init_bit"); |
| if (bifp->attrs.isset) |
| fprintf (init_file, " | bif_set_bit"); |
| if (bifp->attrs.isextract) |
| fprintf (init_file, " | bif_extract_bit"); |
| if (bifp->attrs.isnosoft) |
| fprintf (init_file, " | bif_nosoft_bit"); |
| if (bifp->attrs.isldvec) |
| fprintf (init_file, " | bif_ldvec_bit"); |
| if (bifp->attrs.isstvec) |
| fprintf (init_file, " | bif_stvec_bit"); |
| if (bifp->attrs.isreve) |
| fprintf (init_file, " | bif_reve_bit"); |
| if (bifp->attrs.ispred) |
| fprintf (init_file, " | bif_pred_bit"); |
| if (bifp->attrs.ishtm) |
| fprintf (init_file, " | bif_htm_bit"); |
| if (bifp->attrs.ishtmspr) |
| fprintf (init_file, " | bif_htmspr_bit"); |
| if (bifp->attrs.ishtmcr) |
| fprintf (init_file, " | bif_htmcr_bit"); |
| if (bifp->attrs.ismma) |
| fprintf (init_file, " | bif_mma_bit"); |
| if (bifp->attrs.isquad) |
| fprintf (init_file, " | bif_quad_bit"); |
| if (bifp->attrs.ispair) |
| fprintf (init_file, " | bif_pair_bit"); |
| if (bifp->attrs.ismmaint) |
| fprintf (init_file, " | bif_mmaint_bit"); |
| if (bifp->attrs.isno32bit) |
| fprintf (init_file, " | bif_no32bit_bit"); |
| if (bifp->attrs.is32bit) |
| fprintf (init_file, " | bif_32bit_bit"); |
| if (bifp->attrs.iscpu) |
| fprintf (init_file, " | bif_cpu_bit"); |
| if (bifp->attrs.isldstmask) |
| fprintf (init_file, " | bif_ldstmask_bit"); |
| if (bifp->attrs.islxvrse) |
| fprintf (init_file, " | bif_lxvrse_bit"); |
| if (bifp->attrs.islxvrze) |
| fprintf (init_file, " | bif_lxvrze_bit"); |
| if (bifp->attrs.isendian) |
| fprintf (init_file, " | bif_endian_bit"); |
| if (bifp->attrs.isibmld) |
| fprintf (init_file, " | bif_ibmld_bit"); |
| if (bifp->attrs.isibm128) |
| fprintf (init_file, " | bif_ibm128_bit"); |
| fprintf (init_file, ",\n"); |
| fprintf (init_file, " /* restr_opnd */\t{%d, %d, %d},\n", |
| bifp->proto.restr_opnd[0], bifp->proto.restr_opnd[1], |
| bifp->proto.restr_opnd[2]); |
| for (int j = 0; j < 3; j++) |
| if (!bifp->proto.restr_opnd[j]) |
| res[j] = "RES_NONE"; |
| else if (bifp->proto.restr[j] == RES_BITS) |
| res[j] = "RES_BITS"; |
| else if (bifp->proto.restr[j] == RES_RANGE) |
| res[j] = "RES_RANGE"; |
| else if (bifp->proto.restr[j] == RES_VALUES) |
| res[j] = "RES_VALUES"; |
| else if (bifp->proto.restr[j] == RES_VAR_RANGE) |
| res[j] = "RES_VAR_RANGE"; |
| else |
| res[j] = "ERROR"; |
| fprintf (init_file, " /* restr */\t{%s, %s, %s},\n", |
| res[0], res[1], res[2]); |
| fprintf (init_file, " /* restr_val1 */\t{%s, %s, %s},\n", |
| bifp->proto.restr_val1[0] ? bifp->proto.restr_val1[0] : "0", |
| bifp->proto.restr_val1[1] ? bifp->proto.restr_val1[1] : "0", |
| bifp->proto.restr_val1[2] ? bifp->proto.restr_val1[2] : "0"); |
| fprintf (init_file, " /* restr_val2 */\t{%s, %s, %s},\n", |
| bifp->proto.restr_val2[0] ? bifp->proto.restr_val2[0] : "0", |
| bifp->proto.restr_val2[1] ? bifp->proto.restr_val2[1] : "0", |
| bifp->proto.restr_val2[2] ? bifp->proto.restr_val2[2] : "0"); |
| fprintf (init_file, " /* attr_string */\t\"%s\",\n", |
| (bifp->kind == FNK_CONST ? "= const" |
| : (bifp->kind == FNK_PURE ? "= pure" |
| : (bifp->kind == FNK_FPMATH ? "= fp, const" |
| : "")))); |
| fprintf (init_file, " /* assoc_bif */\tRS6000_BIF_%s%s\n", |
| bifp->attrs.ismmaint ? bifp->idname : "NONE", |
| bifp->attrs.ismmaint ? "_INTERNAL" : ""); |
| fprintf (init_file, " },\n"); |
| } |
| fprintf (init_file, " };\n\n"); |
| } |
| |
| /* Write the decls and initializers for rs6000_overload_info[] and |
| rs6000_instance_info[]. */ |
| static void |
| write_ovld_static_init (void) |
| { |
| fprintf (init_file, |
| "ovldrecord rs6000_overload_info[RS6000_OVLD_MAX " |
| "- RS6000_OVLD_NONE] =\n"); |
| fprintf (init_file, " {\n"); |
| fprintf (init_file, " { /* RS6000_OVLD_NONE: */\n"); |
| fprintf (init_file, " \"\", NULL\n"); |
| fprintf (init_file, " },\n"); |
| for (int i = 0; i <= curr_ovld_stanza; i++) |
| { |
| fprintf (init_file, " { /* RS6000_OVLD_%s: */\n", |
| ovld_stanzas[i].stanza_id); |
| fprintf (init_file, " /* ovld_name */\t\"%s\",\n", |
| ovld_stanzas[i].intern_name); |
| /* First-instance must currently be instantiated at run time. */ |
| fprintf (init_file, " /* first_instance */\tNULL\n"); |
| fprintf (init_file, " },\n"); |
| } |
| fprintf (init_file, " };\n\n"); |
| |
| fprintf (init_file, "ovlddata rs6000_instance_info[RS6000_INST_MAX] =\n"); |
| fprintf (init_file, " {\n"); |
| fprintf (init_file, " { /* RS6000_INST_NONE: */\n"); |
| fprintf (init_file, " \"\", RS6000_BIF_NONE, NULL_TREE, NULL\n"); |
| fprintf (init_file, " },\n"); |
| for (int i = 0; i <= curr_ovld; i++) |
| { |
| fprintf (init_file, " { /* RS6000_INST_%s: */\n", |
| ovlds[i].ovld_id_name); |
| fprintf (init_file, " /* bifname */\t\"%s\",\n", |
| ovlds[i].proto.bifname); |
| fprintf (init_file, " /* bifid */\tRS6000_BIF_%s,\n", |
| ovlds[i].bif_id_name); |
| /* Type must be instantiated at run time. */ |
| fprintf (init_file, " /* fntype */\t0,\n"); |
| fprintf (init_file, " /* next */\t"); |
| if (i < curr_ovld |
| && !strcmp (ovlds[i+1].proto.bifname, ovlds[i].proto.bifname)) |
| fprintf (init_file, |
| "&rs6000_instance_info[RS6000_INST_%s]\n", |
| ovlds[i+1].ovld_id_name); |
| else |
| fprintf (init_file, "NULL\n"); |
| fprintf (init_file, " },\n"); |
| } |
| fprintf (init_file, " };\n\n"); |
| } |
| |
| /* Write code to initialize the built-in function table. */ |
| static void |
| write_init_bif_table (void) |
| { |
| for (int i = 0; i <= curr_bif; i++) |
| { |
| fprintf (init_file, |
| " rs6000_builtin_info[RS6000_BIF_%s].fntype" |
| "\n = %s;\n", |
| bifs[i].idname, bifs[i].fndecl); |
| |
| /* Check whether we have a "tf" token in this string, representing |
| a float128_type_node. It's possible that float128_type_node is |
| undefined (occurs for -maltivec -mno-vsx, for example), so we |
| must guard against that. */ |
| int tf_found = strstr (bifs[i].fndecl, "tf") != NULL; |
| |
| /* Similarly, look for decimal float tokens. */ |
| int dfp_found = (strstr (bifs[i].fndecl, "sd") != NULL |
| || strstr (bifs[i].fndecl, "dd") != NULL |
| || strstr (bifs[i].fndecl, "td") != NULL); |
| |
| if (tf_found) |
| { |
| fprintf (init_file, " if (float128_type_node)\n"); |
| fprintf (init_file, " {\n"); |
| } |
| else if (dfp_found) |
| { |
| fprintf (init_file, " if (dfloat64_type_node)\n"); |
| fprintf (init_file, " {\n"); |
| } |
| |
| fprintf (init_file, |
| " rs6000_builtin_decls[(int)RS6000_BIF_%s] = t\n", |
| bifs[i].idname); |
| fprintf (init_file, |
| " = add_builtin_function (\"%s\",\n", |
| bifs[i].proto.bifname); |
| fprintf (init_file, |
| " %s,\n", |
| bifs[i].fndecl); |
| fprintf (init_file, |
| " (int)RS6000_BIF_%s," |
| " BUILT_IN_MD,\n", |
| bifs[i].idname); |
| fprintf (init_file, |
| " NULL, NULL_TREE);\n"); |
| if (bifs[i].kind == FNK_CONST) |
| { |
| fprintf (init_file, " TREE_READONLY (t) = 1;\n"); |
| fprintf (init_file, " TREE_NOTHROW (t) = 1;\n"); |
| } |
| else if (bifs[i].kind == FNK_PURE) |
| { |
| fprintf (init_file, " DECL_PURE_P (t) = 1;\n"); |
| fprintf (init_file, " TREE_NOTHROW (t) = 1;\n"); |
| } |
| else if (bifs[i].kind == FNK_FPMATH) |
| { |
| fprintf (init_file, " TREE_NOTHROW (t) = 1;\n"); |
| fprintf (init_file, " if (flag_rounding_math)\n"); |
| fprintf (init_file, " {\n"); |
| fprintf (init_file, " DECL_PURE_P (t) = 1;\n"); |
| fprintf (init_file, " DECL_IS_NOVOPS (t) = 1;\n"); |
| fprintf (init_file, " }\n"); |
| fprintf (init_file, " else\n"); |
| fprintf (init_file, " TREE_READONLY (t) = 1;\n"); |
| } |
| |
| if (tf_found || dfp_found) |
| { |
| fprintf (init_file, " }\n"); |
| fprintf (init_file, " else\n"); |
| fprintf (init_file, " {\n"); |
| fprintf (init_file, " rs6000_builtin_decls" |
| "[(int)RS6000_BIF_%s] = NULL_TREE;\n", bifs[i].idname); |
| fprintf (init_file, " }\n"); |
| } |
| fprintf (init_file, "\n"); |
| } |
| } |
| |
| /* Write code to initialize the overload table. */ |
| static void |
| write_init_ovld_table (void) |
| { |
| fprintf (init_file, " int base = RS6000_OVLD_NONE;\n\n"); |
| fprintf (init_file, |
| " /* The fndecl for an overload is arbitrarily the first one\n" |
| " for the overload. We sort out the real types when\n" |
| " processing the overload in the gcc front end. */\n"); |
| |
| for (int i = 0; i <= curr_ovld; i++) |
| { |
| fprintf (init_file, |
| " rs6000_instance_info[RS6000_INST_%s].fntype" |
| "\n = %s;\n", |
| ovlds[i].ovld_id_name, ovlds[i].fndecl); |
| |
| if (i == 0 || ovlds[i].stanza != ovlds[i-1].stanza) |
| { |
| ovld_stanza *stanza = &ovld_stanzas[ovlds[i].stanza]; |
| fprintf (init_file, "\n"); |
| |
| /* Check whether we have a "tf" token in this string, representing |
| a float128_type_node. It's possible that float128_type_node is |
| undefined (occurs for -maltivec -mno-vsx, for example), so we |
| must guard against that. */ |
| int tf_found = strstr (ovlds[i].fndecl, "tf") != NULL; |
| |
| /* Similarly, look for decimal float tokens. */ |
| int dfp_found = (strstr (ovlds[i].fndecl, "sd") != NULL |
| || strstr (ovlds[i].fndecl, "dd") != NULL |
| || strstr (ovlds[i].fndecl, "td") != NULL); |
| |
| if (tf_found) |
| { |
| fprintf (init_file, " if (float128_type_node)\n"); |
| fprintf (init_file, " {\n"); |
| } |
| else if (dfp_found) |
| { |
| fprintf (init_file, " if (dfloat64_type_node)\n"); |
| fprintf (init_file, " {\n"); |
| } |
| |
| fprintf (init_file, |
| " rs6000_builtin_decls[(int)RS6000_OVLD_%s] = t\n", |
| stanza->stanza_id); |
| fprintf (init_file, |
| " = add_builtin_function (\"%s\",\n", |
| stanza->intern_name); |
| fprintf (init_file, |
| " %s,\n", |
| ovlds[i].fndecl); |
| fprintf (init_file, |
| " (int)RS6000_OVLD_%s," |
| " BUILT_IN_MD,\n", |
| stanza->stanza_id); |
| fprintf (init_file, |
| " NULL, NULL_TREE);\n"); |
| |
| if (tf_found || dfp_found) |
| fprintf (init_file, " }\n"); |
| |
| fprintf (init_file, "\n"); |
| |
| fprintf (init_file, |
| " rs6000_overload_info[RS6000_OVLD_%s - base]" |
| ".first_instance\n", |
| stanza->stanza_id); |
| fprintf (init_file, |
| " = &rs6000_instance_info[RS6000_INST_%s];\n\n", |
| ovlds[i].ovld_id_name); |
| } |
| } |
| } |
| |
| /* Write everything to the initialization file (rs6000-builtins.cc). |
| Return 1 if successful, 0 otherwise. */ |
| static int |
| write_init_file (void) |
| { |
| write_autogenerated_header (init_file); |
| |
| fprintf (init_file, "#include \"config.h\"\n"); |
| fprintf (init_file, "#include \"system.h\"\n"); |
| fprintf (init_file, "#include \"coretypes.h\"\n"); |
| fprintf (init_file, "#include \"backend.h\"\n"); |
| fprintf (init_file, "#include \"rtl.h\"\n"); |
| fprintf (init_file, "#include \"tree.h\"\n"); |
| fprintf (init_file, "#include \"langhooks.h\"\n"); |
| fprintf (init_file, "#include \"insn-codes.h\"\n"); |
| fprintf (init_file, "#include \"rs6000-builtins.h\"\n"); |
| fprintf (init_file, "\n"); |
| |
| fprintf (init_file, "tree rs6000_builtin_decls[RS6000_OVLD_MAX];\n\n"); |
| |
| write_bif_static_init (); |
| write_ovld_static_init (); |
| |
| fprintf (init_file, "void\n"); |
| fprintf (init_file, "rs6000_init_generated_builtins ()\n"); |
| fprintf (init_file, "{\n"); |
| fprintf (init_file, " tree t;\n"); |
| rbt_inorder_callback (&fntype_rbt, fntype_rbt.rbt_root, write_fntype_init); |
| fprintf (init_file, "\n"); |
| |
| fprintf (init_file, |
| " rs6000_builtin_decls[RS6000_BIF_NONE] = NULL_TREE;\n"); |
| fprintf (init_file, |
| " rs6000_builtin_decls[RS6000_BIF_MAX] = NULL_TREE;\n"); |
| fprintf (init_file, |
| " rs6000_builtin_decls[RS6000_OVLD_NONE] = NULL_TREE;\n\n"); |
| |
| write_init_bif_table (); |
| write_init_ovld_table (); |
| |
| fprintf (init_file, "}\n\n"); |
| |
| return 1; |
| } |
| |
| /* Write everything to the include file (rs6000-vecdefines.h). |
| Return 1 if successful, 0 otherwise. */ |
| static int |
| write_defines_file (void) |
| { |
| fprintf (defines_file, "#ifndef _RS6000_VECDEFINES_H\n"); |
| fprintf (defines_file, "#define _RS6000_VECDEFINES_H 1\n\n"); |
| fprintf (defines_file, "#if defined(_ARCH_PPC64) && defined (_ARCH_PWR9)\n"); |
| fprintf (defines_file, " #define _ARCH_PPC64_PWR9 1\n"); |
| fprintf (defines_file, "#endif\n\n"); |
| for (int i = 0; i < num_ovld_stanzas; i++) |
| if (strcmp (ovld_stanzas[i].extern_name, "SKIP")) |
| { |
| if (ovld_stanzas[i].ifdef) |
| fprintf (defines_file, "#ifdef %s\n", ovld_stanzas[i].ifdef); |
| fprintf (defines_file, "#define %s %s\n", |
| ovld_stanzas[i].extern_name, |
| ovld_stanzas[i].intern_name); |
| if (ovld_stanzas[i].ifdef) |
| fprintf (defines_file, "#endif\n"); |
| } |
| fprintf (defines_file, "\n#endif\n"); |
| return 1; |
| } |
| |
| /* Close and delete output files after any failure, so that subsequent |
| build dependencies will fail. */ |
| static void |
| delete_output_files (void) |
| { |
| /* Depending on whence we're called, some of these may already be |
| closed. Don't check for errors. */ |
| fclose (header_file); |
| fclose (init_file); |
| fclose (defines_file); |
| |
| remove (header_path); |
| remove (init_path); |
| remove (defines_path); |
| } |
| |
| /* Main program to convert flat files into built-in initialization code. */ |
| int |
| main (int argc, const char **argv) |
| { |
| if (argc != 6) |
| { |
| fprintf (stderr, |
| "Five arguments required: two input files and three output " |
| "files.\n"); |
| exit (1); |
| } |
| |
| pgm_path = argv[0]; |
| bif_path = argv[1]; |
| ovld_path = argv[2]; |
| header_path = argv[3]; |
| init_path = argv[4]; |
| defines_path = argv[5]; |
| |
| bif_file = fopen (bif_path, "r"); |
| if (!bif_file) |
| { |
| fprintf (stderr, "Cannot open input built-in file '%s'.\n", bif_path); |
| exit (1); |
| } |
| ovld_file = fopen (ovld_path, "r"); |
| if (!ovld_file) |
| { |
| fprintf (stderr, "Cannot open input overload file '%s'.\n", ovld_path); |
| exit (1); |
| } |
| header_file = fopen (header_path, "w"); |
| if (!header_file) |
| { |
| fprintf (stderr, "Cannot open header file '%s' for output.\n", |
| header_path); |
| exit (1); |
| } |
| init_file = fopen (init_path, "w"); |
| if (!init_file) |
| { |
| fprintf (stderr, "Cannot open init file '%s' for output.\n", init_path); |
| exit (1); |
| } |
| defines_file = fopen (defines_path, "w"); |
| if (!defines_file) |
| { |
| fprintf (stderr, "Cannot open defines file '%s' for output.\n", |
| defines_path); |
| exit (1); |
| } |
| |
| /* Allocate some buffers. */ |
| for (int i = 0; i < MAXLINES; i++) |
| lines[i] = (char *) malloc (LINELEN); |
| |
| /* Initialize the balanced trees containing built-in function ids, |
| overload function ids, and function type declaration ids. */ |
| rbt_new (&bif_rbt); |
| rbt_new (&ovld_rbt); |
| rbt_new (&fntype_rbt); |
| |
| /* Initialize another balanced tree that contains a map from built-in |
| function ids to the order in which they were encountered. */ |
| rbt_new (&bifo_rbt); |
| |
| /* Parse the built-in function file. */ |
| num_bifs = 0; |
| line = 0; |
| if (parse_bif () == PC_PARSEFAIL) |
| { |
| fprintf (stderr, "Parsing of '%s' failed, aborting.\n", bif_path); |
| delete_output_files (); |
| exit (1); |
| } |
| fclose (bif_file); |
| |
| /* Create a mapping from function IDs in their final order to |
| the order they appear in the built-in function file. */ |
| create_bif_order (); |
| |
| #ifdef DEBUG |
| fprintf (stderr, "\nFunction ID list:\n"); |
| rbt_dump (&bif_rbt, bif_rbt.rbt_root); |
| fprintf (stderr, "\n"); |
| #endif |
| |
| /* Parse the overload file. */ |
| num_ovld_stanzas = 0; |
| num_ovlds = 0; |
| line = 0; |
| if (parse_ovld () == PC_PARSEFAIL) |
| { |
| fprintf (stderr, "Parsing of '%s' failed, aborting.\n", ovld_path); |
| delete_output_files (); |
| exit (1); |
| } |
| fclose (ovld_file); |
| |
| #ifdef DEBUG |
| fprintf (stderr, "\nFunction type decl list:\n"); |
| rbt_dump (&fntype_rbt, fntype_rbt.rbt_root); |
| fprintf (stderr, "\n"); |
| #endif |
| |
| /* Write the header file and the file containing initialization code. */ |
| if (!write_header_file ()) |
| { |
| fprintf (stderr, "Output to '%s' failed, aborting.\n", header_path); |
| delete_output_files (); |
| exit (1); |
| } |
| if (!write_init_file ()) |
| { |
| fprintf (stderr, "Output to '%s' failed, aborting.\n", init_path); |
| delete_output_files (); |
| exit (1); |
| } |
| |
| /* Write the defines file to be included into altivec.h. */ |
| if (!write_defines_file ()) |
| { |
| fprintf (stderr, "Output to '%s' failed, aborting.\n", defines_path); |
| delete_output_files (); |
| exit (1); |
| } |
| |
| /* Always close init_file last. This avoids race conditions in the |
| build machinery. See comments in t-rs6000. */ |
| fclose (header_file); |
| fclose (defines_file); |
| fclose (init_file); |
| |
| return 0; |
| } |