/* Generate built-in function initialization and recognition for Power. Copyright (C) 2020-2022 Free Software Foundation, Inc. Contributed by Bill Schmidt, IBM 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 . */ /* 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] (); Here [kind] can be one of "const", "pure", or "fpmath"; is a legal type for a built-in function result; is the name by which the function can be called; and 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: {} Here is a unique internal identifier for the built-in function that will be used as part of an enumeration of all built-in functions; is the define_expand or define_insn that will be invoked when the call is expanded; and 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: [, , [[, ]] ] Here the single square brackets are part of the syntax, is a unique internal identifier for the overload that will be used as part of an enumeration of all overloaded functions; is the name that will appear as a #define in rs6000-vecdefines.h; is the name that is overloaded in the back end; and 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]): (); The second line contains the 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 #include #include #include #include #include #include #include #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: restricts the constant to x bits, interpreted as unsigned 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] | ] [*] Legal values of are (for now): char short int long long double long long float double __int128 _Float128 bool string _Decimal32 _Decimal64 _Decimal128 __ibm128 Legal values of 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 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; }