gensupport.c revision 117395
190075Sobrien/* Support routines for the various generation passes. 2117395Skan Copyright (C) 2000, 2001, 2002 Free Software Foundation, Inc. 390075Sobrien 490075Sobrien This file is part of GCC. 590075Sobrien 690075Sobrien GCC is free software; you can redistribute it and/or modify it 790075Sobrien under the terms of the GNU General Public License as published by 890075Sobrien the Free Software Foundation; either version 2, or (at your option) 990075Sobrien any later version. 1090075Sobrien 1190075Sobrien GCC is distributed in the hope that it will be useful, but WITHOUT 1290075Sobrien ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 1390075Sobrien or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 1490075Sobrien License for more details. 1590075Sobrien 1690075Sobrien You should have received a copy of the GNU General Public License 1790075Sobrien along with GCC; see the file COPYING. If not, write to the Free 1890075Sobrien Software Foundation, 59 Temple Place - Suite 330, Boston, MA 1990075Sobrien 02111-1307, USA. */ 2090075Sobrien 2190075Sobrien#include "hconfig.h" 2290075Sobrien#include "system.h" 2390075Sobrien#include "rtl.h" 2490075Sobrien#include "obstack.h" 2590075Sobrien#include "errors.h" 26117395Skan#include "hashtab.h" 2790075Sobrien#include "gensupport.h" 2890075Sobrien 2990075Sobrien 3090075Sobrien/* In case some macros used by files we include need it, define this here. */ 3190075Sobrienint target_flags; 3290075Sobrien 33117395Skanint insn_elision = 1; 34117395Skan 3590075Sobrienstatic struct obstack obstack; 3690075Sobrienstruct obstack *rtl_obstack = &obstack; 3790075Sobrien 3890075Sobrienstatic int sequence_num; 3990075Sobrienstatic int errors; 4090075Sobrien 4190075Sobrienstatic int predicable_default; 4290075Sobrienstatic const char *predicable_true; 4390075Sobrienstatic const char *predicable_false; 4490075Sobrien 45117395Skanstatic htab_t condition_table; 46117395Skan 4790075Sobrienstatic char *base_dir = NULL; 4890075Sobrien 4990075Sobrien/* We initially queue all patterns, process the define_insn and 5090075Sobrien define_cond_exec patterns, then return them one at a time. */ 5190075Sobrien 5290075Sobrienstruct queue_elem 5390075Sobrien{ 5490075Sobrien rtx data; 55117395Skan const char *filename; 5690075Sobrien int lineno; 5790075Sobrien struct queue_elem *next; 5890075Sobrien}; 5990075Sobrien 6090075Sobrienstatic struct queue_elem *define_attr_queue; 6190075Sobrienstatic struct queue_elem **define_attr_tail = &define_attr_queue; 6290075Sobrienstatic struct queue_elem *define_insn_queue; 6390075Sobrienstatic struct queue_elem **define_insn_tail = &define_insn_queue; 6490075Sobrienstatic struct queue_elem *define_cond_exec_queue; 6590075Sobrienstatic struct queue_elem **define_cond_exec_tail = &define_cond_exec_queue; 6690075Sobrienstatic struct queue_elem *other_queue; 6790075Sobrienstatic struct queue_elem **other_tail = &other_queue; 6890075Sobrien 69117395Skanstatic void queue_pattern PARAMS ((rtx, struct queue_elem ***, 70117395Skan const char *, int)); 7190075Sobrien 7290075Sobrien/* Current maximum length of directory names in the search path 7390075Sobrien for include files. (Altered as we get more of them.) */ 7490075Sobrien 7590075Sobriensize_t max_include_len; 7690075Sobrien 7790075Sobrienstruct file_name_list 7890075Sobrien { 7990075Sobrien struct file_name_list *next; 8090075Sobrien const char *fname; 8190075Sobrien }; 8290075Sobrien 83117395Skanstruct file_name_list *first_dir_md_include = 0; /* First dir to search */ 8490075Sobrien /* First dir to search for <file> */ 8590075Sobrienstruct file_name_list *first_bracket_include = 0; 86117395Skanstruct file_name_list *last_dir_md_include = 0; /* Last in chain */ 8790075Sobrien 8890075Sobrienstatic void remove_constraints PARAMS ((rtx)); 8990075Sobrienstatic void process_rtx PARAMS ((rtx, int)); 9090075Sobrien 9190075Sobrienstatic int is_predicable PARAMS ((struct queue_elem *)); 9290075Sobrienstatic void identify_predicable_attribute PARAMS ((void)); 9390075Sobrienstatic int n_alternatives PARAMS ((const char *)); 9490075Sobrienstatic void collect_insn_data PARAMS ((rtx, int *, int *)); 9590075Sobrienstatic rtx alter_predicate_for_insn PARAMS ((rtx, int, int, int)); 9690075Sobrienstatic const char *alter_test_for_insn PARAMS ((struct queue_elem *, 9790075Sobrien struct queue_elem *)); 9890075Sobrienstatic char *shift_output_template PARAMS ((char *, const char *, int)); 9990075Sobrienstatic const char *alter_output_for_insn PARAMS ((struct queue_elem *, 10090075Sobrien struct queue_elem *, 10190075Sobrien int, int)); 10290075Sobrienstatic void process_one_cond_exec PARAMS ((struct queue_elem *)); 10390075Sobrienstatic void process_define_cond_exec PARAMS ((void)); 104117395Skanstatic void process_include PARAMS ((rtx, int)); 10590075Sobrienstatic char *save_string PARAMS ((const char *, int)); 10690075Sobrien 10790075Sobrienvoid 10890075Sobrienmessage_with_line VPARAMS ((int lineno, const char *msg, ...)) 10990075Sobrien{ 11090075Sobrien VA_OPEN (ap, msg); 11190075Sobrien VA_FIXEDARG (ap, int, lineno); 11290075Sobrien VA_FIXEDARG (ap, const char *, msg); 11390075Sobrien 11490075Sobrien fprintf (stderr, "%s:%d: ", read_rtx_filename, lineno); 11590075Sobrien vfprintf (stderr, msg, ap); 11690075Sobrien fputc ('\n', stderr); 11790075Sobrien 11890075Sobrien VA_CLOSE (ap); 11990075Sobrien} 12090075Sobrien 12190075Sobrien/* Make a version of gen_rtx_CONST_INT so that GEN_INT can be used in 12290075Sobrien the gensupport programs. */ 12390075Sobrien 12490075Sobrienrtx 12590075Sobriengen_rtx_CONST_INT (mode, arg) 12690075Sobrien enum machine_mode mode ATTRIBUTE_UNUSED; 12790075Sobrien HOST_WIDE_INT arg; 12890075Sobrien{ 12990075Sobrien rtx rt = rtx_alloc (CONST_INT); 13090075Sobrien 13190075Sobrien XWINT (rt, 0) = arg; 13290075Sobrien return rt; 13390075Sobrien} 13490075Sobrien 13590075Sobrien/* Queue PATTERN on LIST_TAIL. */ 13690075Sobrien 13790075Sobrienstatic void 138117395Skanqueue_pattern (pattern, list_tail, filename, lineno) 13990075Sobrien rtx pattern; 14090075Sobrien struct queue_elem ***list_tail; 141117395Skan const char *filename; 14290075Sobrien int lineno; 14390075Sobrien{ 14490075Sobrien struct queue_elem *e = (struct queue_elem *) xmalloc (sizeof (*e)); 14590075Sobrien e->data = pattern; 146117395Skan e->filename = filename; 14790075Sobrien e->lineno = lineno; 14890075Sobrien e->next = NULL; 14990075Sobrien **list_tail = e; 15090075Sobrien *list_tail = &e->next; 15190075Sobrien} 15290075Sobrien 15390075Sobrien/* Recursively remove constraints from an rtx. */ 15490075Sobrien 15590075Sobrienstatic void 15690075Sobrienremove_constraints (part) 15790075Sobrien rtx part; 15890075Sobrien{ 15990075Sobrien int i, j; 16090075Sobrien const char *format_ptr; 16190075Sobrien 16290075Sobrien if (part == 0) 16390075Sobrien return; 16490075Sobrien 16590075Sobrien if (GET_CODE (part) == MATCH_OPERAND) 16690075Sobrien XSTR (part, 2) = ""; 16790075Sobrien else if (GET_CODE (part) == MATCH_SCRATCH) 16890075Sobrien XSTR (part, 1) = ""; 16990075Sobrien 17090075Sobrien format_ptr = GET_RTX_FORMAT (GET_CODE (part)); 17190075Sobrien 17290075Sobrien for (i = 0; i < GET_RTX_LENGTH (GET_CODE (part)); i++) 17390075Sobrien switch (*format_ptr++) 17490075Sobrien { 17590075Sobrien case 'e': 17690075Sobrien case 'u': 17790075Sobrien remove_constraints (XEXP (part, i)); 17890075Sobrien break; 17990075Sobrien case 'E': 18090075Sobrien if (XVEC (part, i) != NULL) 18190075Sobrien for (j = 0; j < XVECLEN (part, i); j++) 18290075Sobrien remove_constraints (XVECEXP (part, i, j)); 18390075Sobrien break; 18490075Sobrien } 18590075Sobrien} 18690075Sobrien 187117395Skan/* Process an include file assuming that it lives in gcc/config/{target}/ 188117395Skan if the include looks like (include "file"). */ 18990075Sobrien 190117395Skanstatic void 19190075Sobrienprocess_include (desc, lineno) 19290075Sobrien rtx desc; 19390075Sobrien int lineno; 19490075Sobrien{ 19590075Sobrien const char *filename = XSTR (desc, 0); 196117395Skan const char *old_filename; 197117395Skan int old_lineno; 198117395Skan char *pathname; 19990075Sobrien FILE *input_file; 20090075Sobrien 201117395Skan /* If specified file name is absolute, skip the include stack. */ 202117395Skan if (! IS_ABSOLUTE_PATHNAME (filename)) 20390075Sobrien { 204117395Skan struct file_name_list *stackp; 20590075Sobrien 206117395Skan /* Search directory path, trying to open the file. */ 207117395Skan for (stackp = first_dir_md_include; stackp; stackp = stackp->next) 20890075Sobrien { 209117395Skan static const char sep[2] = { DIR_SEPARATOR, '\0' }; 210117395Skan 211117395Skan pathname = concat (stackp->fname, sep, filename, NULL); 212117395Skan input_file = fopen (pathname, "r"); 213117395Skan if (input_file != NULL) 214117395Skan goto success; 215117395Skan free (pathname); 21690075Sobrien } 21790075Sobrien } 218117395Skan 219117395Skan if (base_dir) 220117395Skan pathname = concat (base_dir, filename, NULL); 221117395Skan else 222117395Skan pathname = xstrdup (filename); 223117395Skan input_file = fopen (pathname, "r"); 224117395Skan if (input_file == NULL) 22590075Sobrien { 226117395Skan free (pathname); 227117395Skan message_with_line (lineno, "include file `%s' not found", filename); 228117395Skan errors = 1; 229117395Skan return; 230117395Skan } 231117395Skan success: 23290075Sobrien 233117395Skan /* Save old cursor; setup new for the new file. Note that "lineno" the 234117395Skan argument to this function is the beginning of the include statement, 235117395Skan while read_rtx_lineno has already been advanced. */ 236117395Skan old_filename = read_rtx_filename; 237117395Skan old_lineno = read_rtx_lineno; 238117395Skan read_rtx_filename = pathname; 239117395Skan read_rtx_lineno = 1; 24090075Sobrien 241117395Skan /* Read the entire file. */ 242117395Skan while (1) 243117395Skan { 244117395Skan rtx desc; 245117395Skan int c; 24690075Sobrien 247117395Skan c = read_skip_spaces (input_file); 248117395Skan if (c == EOF) 249117395Skan break; 25090075Sobrien 251117395Skan ungetc (c, input_file); 252117395Skan lineno = read_rtx_lineno; 253117395Skan desc = read_rtx (input_file); 254117395Skan process_rtx (desc, lineno); 255117395Skan } 25690075Sobrien 257117395Skan /* Do not free pathname. It is attached to the various rtx queue 258117395Skan elements. */ 25990075Sobrien 260117395Skan read_rtx_filename = old_filename; 261117395Skan read_rtx_lineno = old_lineno; 26290075Sobrien 263117395Skan fclose (input_file); 26490075Sobrien} 26590075Sobrien 26690075Sobrien/* Process a top level rtx in some way, queueing as appropriate. */ 26790075Sobrien 26890075Sobrienstatic void 26990075Sobrienprocess_rtx (desc, lineno) 27090075Sobrien rtx desc; 27190075Sobrien int lineno; 27290075Sobrien{ 27390075Sobrien switch (GET_CODE (desc)) 27490075Sobrien { 27590075Sobrien case DEFINE_INSN: 276117395Skan queue_pattern (desc, &define_insn_tail, read_rtx_filename, lineno); 27790075Sobrien break; 27890075Sobrien 27990075Sobrien case DEFINE_COND_EXEC: 280117395Skan queue_pattern (desc, &define_cond_exec_tail, read_rtx_filename, lineno); 28190075Sobrien break; 28290075Sobrien 28390075Sobrien case DEFINE_ATTR: 284117395Skan queue_pattern (desc, &define_attr_tail, read_rtx_filename, lineno); 28590075Sobrien break; 28690075Sobrien 28790075Sobrien case INCLUDE: 288117395Skan process_include (desc, lineno); 28990075Sobrien break; 29090075Sobrien 29190075Sobrien case DEFINE_INSN_AND_SPLIT: 29290075Sobrien { 29390075Sobrien const char *split_cond; 29490075Sobrien rtx split; 29590075Sobrien rtvec attr; 29690075Sobrien int i; 29790075Sobrien 29890075Sobrien /* Create a split with values from the insn_and_split. */ 29990075Sobrien split = rtx_alloc (DEFINE_SPLIT); 30090075Sobrien 30190075Sobrien i = XVECLEN (desc, 1); 30290075Sobrien XVEC (split, 0) = rtvec_alloc (i); 30390075Sobrien while (--i >= 0) 30490075Sobrien { 30590075Sobrien XVECEXP (split, 0, i) = copy_rtx (XVECEXP (desc, 1, i)); 30690075Sobrien remove_constraints (XVECEXP (split, 0, i)); 30790075Sobrien } 30890075Sobrien 30990075Sobrien /* If the split condition starts with "&&", append it to the 31090075Sobrien insn condition to create the new split condition. */ 31190075Sobrien split_cond = XSTR (desc, 4); 31290075Sobrien if (split_cond[0] == '&' && split_cond[1] == '&') 313117395Skan split_cond = concat (XSTR (desc, 2), split_cond, NULL); 31490075Sobrien XSTR (split, 1) = split_cond; 31590075Sobrien XVEC (split, 2) = XVEC (desc, 5); 31690075Sobrien XSTR (split, 3) = XSTR (desc, 6); 31790075Sobrien 31890075Sobrien /* Fix up the DEFINE_INSN. */ 31990075Sobrien attr = XVEC (desc, 7); 32090075Sobrien PUT_CODE (desc, DEFINE_INSN); 32190075Sobrien XVEC (desc, 4) = attr; 32290075Sobrien 32390075Sobrien /* Queue them. */ 324117395Skan queue_pattern (desc, &define_insn_tail, read_rtx_filename, lineno); 325117395Skan queue_pattern (split, &other_tail, read_rtx_filename, lineno); 32690075Sobrien break; 32790075Sobrien } 32890075Sobrien 32990075Sobrien default: 330117395Skan queue_pattern (desc, &other_tail, read_rtx_filename, lineno); 33190075Sobrien break; 33290075Sobrien } 33390075Sobrien} 33490075Sobrien 33590075Sobrien/* Return true if attribute PREDICABLE is true for ELEM, which holds 33690075Sobrien a DEFINE_INSN. */ 33790075Sobrien 33890075Sobrienstatic int 33990075Sobrienis_predicable (elem) 34090075Sobrien struct queue_elem *elem; 34190075Sobrien{ 34290075Sobrien rtvec vec = XVEC (elem->data, 4); 34390075Sobrien const char *value; 34490075Sobrien int i; 34590075Sobrien 34690075Sobrien if (! vec) 34790075Sobrien return predicable_default; 34890075Sobrien 34990075Sobrien for (i = GET_NUM_ELEM (vec) - 1; i >= 0; --i) 35090075Sobrien { 35190075Sobrien rtx sub = RTVEC_ELT (vec, i); 35290075Sobrien switch (GET_CODE (sub)) 35390075Sobrien { 35490075Sobrien case SET_ATTR: 35590075Sobrien if (strcmp (XSTR (sub, 0), "predicable") == 0) 35690075Sobrien { 35790075Sobrien value = XSTR (sub, 1); 35890075Sobrien goto found; 35990075Sobrien } 36090075Sobrien break; 36190075Sobrien 36290075Sobrien case SET_ATTR_ALTERNATIVE: 36390075Sobrien if (strcmp (XSTR (sub, 0), "predicable") == 0) 36490075Sobrien { 36590075Sobrien message_with_line (elem->lineno, 36690075Sobrien "multiple alternatives for `predicable'"); 36790075Sobrien errors = 1; 36890075Sobrien return 0; 36990075Sobrien } 37090075Sobrien break; 37190075Sobrien 37290075Sobrien case SET: 37390075Sobrien if (GET_CODE (SET_DEST (sub)) != ATTR 37490075Sobrien || strcmp (XSTR (SET_DEST (sub), 0), "predicable") != 0) 37590075Sobrien break; 37690075Sobrien sub = SET_SRC (sub); 37790075Sobrien if (GET_CODE (sub) == CONST_STRING) 37890075Sobrien { 37990075Sobrien value = XSTR (sub, 0); 38090075Sobrien goto found; 38190075Sobrien } 38290075Sobrien 38390075Sobrien /* ??? It would be possible to handle this if we really tried. 38490075Sobrien It's not easy though, and I'm not going to bother until it 38590075Sobrien really proves necessary. */ 38690075Sobrien message_with_line (elem->lineno, 38790075Sobrien "non-constant value for `predicable'"); 38890075Sobrien errors = 1; 38990075Sobrien return 0; 39090075Sobrien 39190075Sobrien default: 39290075Sobrien abort (); 39390075Sobrien } 39490075Sobrien } 39590075Sobrien 39690075Sobrien return predicable_default; 39790075Sobrien 39890075Sobrien found: 39990075Sobrien /* Verify that predicability does not vary on the alternative. */ 40090075Sobrien /* ??? It should be possible to handle this by simply eliminating 40190075Sobrien the non-predicable alternatives from the insn. FRV would like 40290075Sobrien to do this. Delay this until we've got the basics solid. */ 40390075Sobrien if (strchr (value, ',') != NULL) 40490075Sobrien { 40590075Sobrien message_with_line (elem->lineno, 40690075Sobrien "multiple alternatives for `predicable'"); 40790075Sobrien errors = 1; 40890075Sobrien return 0; 40990075Sobrien } 41090075Sobrien 41190075Sobrien /* Find out which value we're looking at. */ 41290075Sobrien if (strcmp (value, predicable_true) == 0) 41390075Sobrien return 1; 41490075Sobrien if (strcmp (value, predicable_false) == 0) 41590075Sobrien return 0; 41690075Sobrien 41790075Sobrien message_with_line (elem->lineno, 41890075Sobrien "unknown value `%s' for `predicable' attribute", 41990075Sobrien value); 42090075Sobrien errors = 1; 42190075Sobrien return 0; 42290075Sobrien} 42390075Sobrien 42490075Sobrien/* Examine the attribute "predicable"; discover its boolean values 42590075Sobrien and its default. */ 42690075Sobrien 42790075Sobrienstatic void 42890075Sobrienidentify_predicable_attribute () 42990075Sobrien{ 43090075Sobrien struct queue_elem *elem; 43190075Sobrien char *p_true, *p_false; 43290075Sobrien const char *value; 43390075Sobrien 43490075Sobrien /* Look for the DEFINE_ATTR for `predicable', which must exist. */ 43590075Sobrien for (elem = define_attr_queue; elem ; elem = elem->next) 43690075Sobrien if (strcmp (XSTR (elem->data, 0), "predicable") == 0) 43790075Sobrien goto found; 43890075Sobrien 43990075Sobrien message_with_line (define_cond_exec_queue->lineno, 44090075Sobrien "attribute `predicable' not defined"); 44190075Sobrien errors = 1; 44290075Sobrien return; 44390075Sobrien 44490075Sobrien found: 44590075Sobrien value = XSTR (elem->data, 1); 446117395Skan p_false = xstrdup (value); 44790075Sobrien p_true = strchr (p_false, ','); 44890075Sobrien if (p_true == NULL || strchr (++p_true, ',') != NULL) 44990075Sobrien { 45090075Sobrien message_with_line (elem->lineno, 45190075Sobrien "attribute `predicable' is not a boolean"); 45290075Sobrien errors = 1; 45390075Sobrien return; 45490075Sobrien } 45590075Sobrien p_true[-1] = '\0'; 45690075Sobrien 45790075Sobrien predicable_true = p_true; 45890075Sobrien predicable_false = p_false; 45990075Sobrien 46090075Sobrien switch (GET_CODE (XEXP (elem->data, 2))) 46190075Sobrien { 46290075Sobrien case CONST_STRING: 46390075Sobrien value = XSTR (XEXP (elem->data, 2), 0); 46490075Sobrien break; 46590075Sobrien 46690075Sobrien case CONST: 46790075Sobrien message_with_line (elem->lineno, 46890075Sobrien "attribute `predicable' cannot be const"); 46990075Sobrien errors = 1; 47090075Sobrien return; 47190075Sobrien 47290075Sobrien default: 47390075Sobrien message_with_line (elem->lineno, 47490075Sobrien "attribute `predicable' must have a constant default"); 47590075Sobrien errors = 1; 47690075Sobrien return; 47790075Sobrien } 47890075Sobrien 47990075Sobrien if (strcmp (value, p_true) == 0) 48090075Sobrien predicable_default = 1; 48190075Sobrien else if (strcmp (value, p_false) == 0) 48290075Sobrien predicable_default = 0; 48390075Sobrien else 48490075Sobrien { 48590075Sobrien message_with_line (elem->lineno, 48690075Sobrien "unknown value `%s' for `predicable' attribute", 48790075Sobrien value); 48890075Sobrien errors = 1; 48990075Sobrien } 49090075Sobrien} 49190075Sobrien 49290075Sobrien/* Return the number of alternatives in constraint S. */ 49390075Sobrien 49490075Sobrienstatic int 49590075Sobrienn_alternatives (s) 49690075Sobrien const char *s; 49790075Sobrien{ 49890075Sobrien int n = 1; 49990075Sobrien 50090075Sobrien if (s) 50190075Sobrien while (*s) 50290075Sobrien n += (*s++ == ','); 50390075Sobrien 50490075Sobrien return n; 50590075Sobrien} 50690075Sobrien 50790075Sobrien/* Determine how many alternatives there are in INSN, and how many 50890075Sobrien operands. */ 50990075Sobrien 51090075Sobrienstatic void 51190075Sobriencollect_insn_data (pattern, palt, pmax) 51290075Sobrien rtx pattern; 51390075Sobrien int *palt, *pmax; 51490075Sobrien{ 51590075Sobrien const char *fmt; 51690075Sobrien enum rtx_code code; 51790075Sobrien int i, j, len; 51890075Sobrien 51990075Sobrien code = GET_CODE (pattern); 52090075Sobrien switch (code) 52190075Sobrien { 52290075Sobrien case MATCH_OPERAND: 52390075Sobrien i = n_alternatives (XSTR (pattern, 2)); 52490075Sobrien *palt = (i > *palt ? i : *palt); 52590075Sobrien /* FALLTHRU */ 52690075Sobrien 52790075Sobrien case MATCH_OPERATOR: 52890075Sobrien case MATCH_SCRATCH: 52990075Sobrien case MATCH_PARALLEL: 53090075Sobrien case MATCH_INSN: 53190075Sobrien i = XINT (pattern, 0); 53290075Sobrien if (i > *pmax) 53390075Sobrien *pmax = i; 53490075Sobrien break; 53590075Sobrien 53690075Sobrien default: 53790075Sobrien break; 53890075Sobrien } 53990075Sobrien 54090075Sobrien fmt = GET_RTX_FORMAT (code); 54190075Sobrien len = GET_RTX_LENGTH (code); 54290075Sobrien for (i = 0; i < len; i++) 54390075Sobrien { 54490075Sobrien switch (fmt[i]) 54590075Sobrien { 54690075Sobrien case 'e': case 'u': 54790075Sobrien collect_insn_data (XEXP (pattern, i), palt, pmax); 54890075Sobrien break; 54990075Sobrien 55090075Sobrien case 'V': 55190075Sobrien if (XVEC (pattern, i) == NULL) 55290075Sobrien break; 55390075Sobrien /* FALLTHRU */ 55490075Sobrien case 'E': 55590075Sobrien for (j = XVECLEN (pattern, i) - 1; j >= 0; --j) 55690075Sobrien collect_insn_data (XVECEXP (pattern, i, j), palt, pmax); 55790075Sobrien break; 55890075Sobrien 55990075Sobrien case 'i': case 'w': case '0': case 's': case 'S': case 'T': 56090075Sobrien break; 56190075Sobrien 56290075Sobrien default: 56390075Sobrien abort (); 56490075Sobrien } 56590075Sobrien } 56690075Sobrien} 56790075Sobrien 56890075Sobrienstatic rtx 56990075Sobrienalter_predicate_for_insn (pattern, alt, max_op, lineno) 57090075Sobrien rtx pattern; 57190075Sobrien int alt, max_op, lineno; 57290075Sobrien{ 57390075Sobrien const char *fmt; 57490075Sobrien enum rtx_code code; 57590075Sobrien int i, j, len; 57690075Sobrien 57790075Sobrien code = GET_CODE (pattern); 57890075Sobrien switch (code) 57990075Sobrien { 58090075Sobrien case MATCH_OPERAND: 58190075Sobrien { 58290075Sobrien const char *c = XSTR (pattern, 2); 58390075Sobrien 58490075Sobrien if (n_alternatives (c) != 1) 58590075Sobrien { 58690075Sobrien message_with_line (lineno, 58790075Sobrien "too many alternatives for operand %d", 58890075Sobrien XINT (pattern, 0)); 58990075Sobrien errors = 1; 59090075Sobrien return NULL; 59190075Sobrien } 59290075Sobrien 59390075Sobrien /* Replicate C as needed to fill out ALT alternatives. */ 59490075Sobrien if (c && *c && alt > 1) 59590075Sobrien { 59690075Sobrien size_t c_len = strlen (c); 59790075Sobrien size_t len = alt * (c_len + 1); 59890075Sobrien char *new_c = (char *) xmalloc (len); 59990075Sobrien 60090075Sobrien memcpy (new_c, c, c_len); 60190075Sobrien for (i = 1; i < alt; ++i) 60290075Sobrien { 60390075Sobrien new_c[i * (c_len + 1) - 1] = ','; 60490075Sobrien memcpy (&new_c[i * (c_len + 1)], c, c_len); 60590075Sobrien } 60690075Sobrien new_c[len - 1] = '\0'; 60790075Sobrien XSTR (pattern, 2) = new_c; 60890075Sobrien } 60990075Sobrien } 61090075Sobrien /* FALLTHRU */ 61190075Sobrien 61290075Sobrien case MATCH_OPERATOR: 61390075Sobrien case MATCH_SCRATCH: 61490075Sobrien case MATCH_PARALLEL: 61590075Sobrien case MATCH_INSN: 61690075Sobrien XINT (pattern, 0) += max_op; 61790075Sobrien break; 61890075Sobrien 61990075Sobrien default: 62090075Sobrien break; 62190075Sobrien } 62290075Sobrien 62390075Sobrien fmt = GET_RTX_FORMAT (code); 62490075Sobrien len = GET_RTX_LENGTH (code); 62590075Sobrien for (i = 0; i < len; i++) 62690075Sobrien { 62790075Sobrien rtx r; 62890075Sobrien 62990075Sobrien switch (fmt[i]) 63090075Sobrien { 63190075Sobrien case 'e': case 'u': 63290075Sobrien r = alter_predicate_for_insn (XEXP (pattern, i), alt, 63390075Sobrien max_op, lineno); 63490075Sobrien if (r == NULL) 63590075Sobrien return r; 63690075Sobrien break; 63790075Sobrien 63890075Sobrien case 'E': 63990075Sobrien for (j = XVECLEN (pattern, i) - 1; j >= 0; --j) 64090075Sobrien { 64190075Sobrien r = alter_predicate_for_insn (XVECEXP (pattern, i, j), 64290075Sobrien alt, max_op, lineno); 64390075Sobrien if (r == NULL) 64490075Sobrien return r; 64590075Sobrien } 64690075Sobrien break; 64790075Sobrien 64890075Sobrien case 'i': case 'w': case '0': case 's': 64990075Sobrien break; 65090075Sobrien 65190075Sobrien default: 65290075Sobrien abort (); 65390075Sobrien } 65490075Sobrien } 65590075Sobrien 65690075Sobrien return pattern; 65790075Sobrien} 65890075Sobrien 65990075Sobrienstatic const char * 66090075Sobrienalter_test_for_insn (ce_elem, insn_elem) 66190075Sobrien struct queue_elem *ce_elem, *insn_elem; 66290075Sobrien{ 66390075Sobrien const char *ce_test, *insn_test; 66490075Sobrien 66590075Sobrien ce_test = XSTR (ce_elem->data, 1); 66690075Sobrien insn_test = XSTR (insn_elem->data, 2); 66790075Sobrien if (!ce_test || *ce_test == '\0') 66890075Sobrien return insn_test; 66990075Sobrien if (!insn_test || *insn_test == '\0') 67090075Sobrien return ce_test; 67190075Sobrien 672117395Skan return concat ("(", ce_test, ") && (", insn_test, ")", NULL); 67390075Sobrien} 67490075Sobrien 67590075Sobrien/* Adjust all of the operand numbers in OLD to match the shift they'll 67690075Sobrien get from an operand displacement of DISP. Return a pointer after the 67790075Sobrien adjusted string. */ 67890075Sobrien 67990075Sobrienstatic char * 68090075Sobrienshift_output_template (new, old, disp) 68190075Sobrien char *new; 68290075Sobrien const char *old; 68390075Sobrien int disp; 68490075Sobrien{ 68590075Sobrien while (*old) 68690075Sobrien { 68790075Sobrien char c = *old++; 68890075Sobrien *new++ = c; 68990075Sobrien if (c == '%') 69090075Sobrien { 69190075Sobrien c = *old++; 69290075Sobrien if (ISDIGIT ((unsigned char) c)) 69390075Sobrien c += disp; 69490075Sobrien else if (ISALPHA (c)) 69590075Sobrien { 69690075Sobrien *new++ = c; 69790075Sobrien c = *old++ + disp; 69890075Sobrien } 69990075Sobrien *new++ = c; 70090075Sobrien } 70190075Sobrien } 70290075Sobrien 70390075Sobrien return new; 70490075Sobrien} 70590075Sobrien 70690075Sobrienstatic const char * 70790075Sobrienalter_output_for_insn (ce_elem, insn_elem, alt, max_op) 70890075Sobrien struct queue_elem *ce_elem, *insn_elem; 70990075Sobrien int alt, max_op; 71090075Sobrien{ 71190075Sobrien const char *ce_out, *insn_out; 71290075Sobrien char *new, *p; 71390075Sobrien size_t len, ce_len, insn_len; 71490075Sobrien 71590075Sobrien /* ??? Could coordinate with genoutput to not duplicate code here. */ 71690075Sobrien 71790075Sobrien ce_out = XSTR (ce_elem->data, 2); 71890075Sobrien insn_out = XTMPL (insn_elem->data, 3); 71990075Sobrien if (!ce_out || *ce_out == '\0') 72090075Sobrien return insn_out; 72190075Sobrien 72290075Sobrien ce_len = strlen (ce_out); 72390075Sobrien insn_len = strlen (insn_out); 72490075Sobrien 72590075Sobrien if (*insn_out == '*') 72690075Sobrien /* You must take care of the predicate yourself. */ 72790075Sobrien return insn_out; 72890075Sobrien 72990075Sobrien if (*insn_out == '@') 73090075Sobrien { 73190075Sobrien len = (ce_len + 1) * alt + insn_len + 1; 73290075Sobrien p = new = xmalloc (len); 73390075Sobrien 73490075Sobrien do 73590075Sobrien { 73690075Sobrien do 73790075Sobrien *p++ = *insn_out++; 73890075Sobrien while (ISSPACE ((unsigned char) *insn_out)); 73990075Sobrien 74090075Sobrien if (*insn_out != '#') 74190075Sobrien { 74290075Sobrien p = shift_output_template (p, ce_out, max_op); 74390075Sobrien *p++ = ' '; 74490075Sobrien } 74590075Sobrien 74690075Sobrien do 74790075Sobrien *p++ = *insn_out++; 74890075Sobrien while (*insn_out && *insn_out != '\n'); 74990075Sobrien } 75090075Sobrien while (*insn_out); 75190075Sobrien *p = '\0'; 75290075Sobrien } 75390075Sobrien else 75490075Sobrien { 75590075Sobrien len = ce_len + 1 + insn_len + 1; 75690075Sobrien new = xmalloc (len); 75790075Sobrien 75890075Sobrien p = shift_output_template (new, ce_out, max_op); 75990075Sobrien *p++ = ' '; 76090075Sobrien memcpy (p, insn_out, insn_len + 1); 76190075Sobrien } 76290075Sobrien 76390075Sobrien return new; 76490075Sobrien} 76590075Sobrien 76690075Sobrien/* Replicate insns as appropriate for the given DEFINE_COND_EXEC. */ 76790075Sobrien 76890075Sobrienstatic void 76990075Sobrienprocess_one_cond_exec (ce_elem) 77090075Sobrien struct queue_elem *ce_elem; 77190075Sobrien{ 77290075Sobrien struct queue_elem *insn_elem; 77390075Sobrien for (insn_elem = define_insn_queue; insn_elem ; insn_elem = insn_elem->next) 77490075Sobrien { 77590075Sobrien int alternatives, max_operand; 77690075Sobrien rtx pred, insn, pattern; 77790075Sobrien 77890075Sobrien if (! is_predicable (insn_elem)) 77990075Sobrien continue; 78090075Sobrien 78190075Sobrien alternatives = 1; 78290075Sobrien max_operand = -1; 78390075Sobrien collect_insn_data (insn_elem->data, &alternatives, &max_operand); 78490075Sobrien max_operand += 1; 78590075Sobrien 78690075Sobrien if (XVECLEN (ce_elem->data, 0) != 1) 78790075Sobrien { 78890075Sobrien message_with_line (ce_elem->lineno, 78990075Sobrien "too many patterns in predicate"); 79090075Sobrien errors = 1; 79190075Sobrien return; 79290075Sobrien } 79390075Sobrien 79490075Sobrien pred = copy_rtx (XVECEXP (ce_elem->data, 0, 0)); 79590075Sobrien pred = alter_predicate_for_insn (pred, alternatives, max_operand, 79690075Sobrien ce_elem->lineno); 79790075Sobrien if (pred == NULL) 79890075Sobrien return; 79990075Sobrien 80090075Sobrien /* Construct a new pattern for the new insn. */ 80190075Sobrien insn = copy_rtx (insn_elem->data); 80290075Sobrien XSTR (insn, 0) = ""; 80390075Sobrien pattern = rtx_alloc (COND_EXEC); 80490075Sobrien XEXP (pattern, 0) = pred; 80590075Sobrien if (XVECLEN (insn, 1) == 1) 80690075Sobrien { 80790075Sobrien XEXP (pattern, 1) = XVECEXP (insn, 1, 0); 80890075Sobrien XVECEXP (insn, 1, 0) = pattern; 80990075Sobrien PUT_NUM_ELEM (XVEC (insn, 1), 1); 81090075Sobrien } 81190075Sobrien else 81290075Sobrien { 81390075Sobrien XEXP (pattern, 1) = rtx_alloc (PARALLEL); 81490075Sobrien XVEC (XEXP (pattern, 1), 0) = XVEC (insn, 1); 81590075Sobrien XVEC (insn, 1) = rtvec_alloc (1); 81690075Sobrien XVECEXP (insn, 1, 0) = pattern; 81790075Sobrien } 81890075Sobrien 81990075Sobrien XSTR (insn, 2) = alter_test_for_insn (ce_elem, insn_elem); 82090075Sobrien XTMPL (insn, 3) = alter_output_for_insn (ce_elem, insn_elem, 82190075Sobrien alternatives, max_operand); 82290075Sobrien 82390075Sobrien /* ??? Set `predicable' to false. Not crucial since it's really 82490075Sobrien only used here, and we won't reprocess this new pattern. */ 82590075Sobrien 82690075Sobrien /* Put the new pattern on the `other' list so that it 82790075Sobrien (a) is not reprocessed by other define_cond_exec patterns 82890075Sobrien (b) appears after all normal define_insn patterns. 82990075Sobrien 83090075Sobrien ??? B is debatable. If one has normal insns that match 83190075Sobrien cond_exec patterns, they will be preferred over these 83290075Sobrien generated patterns. Whether this matters in practice, or if 83390075Sobrien it's a good thing, or whether we should thread these new 83490075Sobrien patterns into the define_insn chain just after their generator 83590075Sobrien is something we'll have to experiment with. */ 83690075Sobrien 837117395Skan queue_pattern (insn, &other_tail, insn_elem->filename, 838117395Skan insn_elem->lineno); 83990075Sobrien } 84090075Sobrien} 84190075Sobrien 84290075Sobrien/* If we have any DEFINE_COND_EXEC patterns, expand the DEFINE_INSN 84390075Sobrien patterns appropriately. */ 84490075Sobrien 84590075Sobrienstatic void 84690075Sobrienprocess_define_cond_exec () 84790075Sobrien{ 84890075Sobrien struct queue_elem *elem; 84990075Sobrien 85090075Sobrien identify_predicable_attribute (); 85190075Sobrien if (errors) 85290075Sobrien return; 85390075Sobrien 85490075Sobrien for (elem = define_cond_exec_queue; elem ; elem = elem->next) 85590075Sobrien process_one_cond_exec (elem); 85690075Sobrien} 85790075Sobrien 85890075Sobrienstatic char * 85990075Sobriensave_string (s, len) 86090075Sobrien const char *s; 86190075Sobrien int len; 86290075Sobrien{ 86390075Sobrien register char *result = xmalloc (len + 1); 86490075Sobrien 86590075Sobrien memcpy (result, s, len); 86690075Sobrien result[len] = 0; 86790075Sobrien return result; 86890075Sobrien} 86990075Sobrien 87090075Sobrien 87190075Sobrien/* The entry point for initializing the reader. */ 87290075Sobrien 87390075Sobrienint 87490075Sobrieninit_md_reader_args (argc, argv) 87590075Sobrien int argc; 87690075Sobrien char **argv; 87790075Sobrien{ 87890075Sobrien int i; 87990075Sobrien const char *in_fname; 88090075Sobrien 88190075Sobrien max_include_len = 0; 88290075Sobrien in_fname = NULL; 88390075Sobrien for (i = 1; i < argc; i++) 88490075Sobrien { 88590075Sobrien if (argv[i][0] != '-') 88690075Sobrien { 88790075Sobrien if (in_fname == NULL) 88890075Sobrien in_fname = argv[i]; 88990075Sobrien } 89090075Sobrien else 89190075Sobrien { 89290075Sobrien int c = argv[i][1]; 89390075Sobrien switch (c) 89490075Sobrien { 89590075Sobrien case 'I': /* Add directory to path for includes. */ 89690075Sobrien { 89790075Sobrien struct file_name_list *dirtmp; 89890075Sobrien 89990075Sobrien dirtmp = (struct file_name_list *) 90090075Sobrien xmalloc (sizeof (struct file_name_list)); 90190075Sobrien dirtmp->next = 0; /* New one goes on the end */ 902117395Skan if (first_dir_md_include == 0) 903117395Skan first_dir_md_include = dirtmp; 90490075Sobrien else 905117395Skan last_dir_md_include->next = dirtmp; 906117395Skan last_dir_md_include = dirtmp; /* Tail follows the last one */ 90790075Sobrien if (argv[i][1] == 'I' && argv[i][2] != 0) 90890075Sobrien dirtmp->fname = argv[i] + 2; 90990075Sobrien else if (i + 1 == argc) 91090075Sobrien fatal ("directory name missing after -I option"); 91190075Sobrien else 91290075Sobrien dirtmp->fname = argv[++i]; 91390075Sobrien if (strlen (dirtmp->fname) > max_include_len) 91490075Sobrien max_include_len = strlen (dirtmp->fname); 91590075Sobrien } 91690075Sobrien break; 91790075Sobrien default: 91890075Sobrien fatal ("invalid option `%s'", argv[i]); 91990075Sobrien 92090075Sobrien } 92190075Sobrien } 92290075Sobrien } 92390075Sobrien return init_md_reader (in_fname); 92490075Sobrien} 92590075Sobrien 92690075Sobrien/* The entry point for initializing the reader. */ 92790075Sobrien 92890075Sobrienint 92990075Sobrieninit_md_reader (filename) 93090075Sobrien const char *filename; 93190075Sobrien{ 93290075Sobrien FILE *input_file; 93390075Sobrien int c; 934117395Skan size_t i; 93590075Sobrien char *lastsl; 93690075Sobrien 937117395Skan lastsl = strrchr (filename, '/'); 938117395Skan if (lastsl != NULL) 939117395Skan base_dir = save_string (filename, lastsl - filename + 1 ); 94090075Sobrien 94190075Sobrien read_rtx_filename = filename; 94290075Sobrien input_file = fopen (filename, "r"); 94390075Sobrien if (input_file == 0) 94490075Sobrien { 94590075Sobrien perror (filename); 94690075Sobrien return FATAL_EXIT_CODE; 94790075Sobrien } 94890075Sobrien 949117395Skan /* Initialize the table of insn conditions. */ 950117395Skan condition_table = htab_create (n_insn_conditions, 951117395Skan hash_c_test, cmp_c_test, NULL); 952117395Skan 953117395Skan for (i = 0; i < n_insn_conditions; i++) 954117395Skan *(htab_find_slot (condition_table, (PTR) &insn_conditions[i], INSERT)) 955117395Skan = (PTR) &insn_conditions[i]; 956117395Skan 95790075Sobrien obstack_init (rtl_obstack); 95890075Sobrien errors = 0; 95990075Sobrien sequence_num = 0; 96090075Sobrien 96190075Sobrien /* Read the entire file. */ 96290075Sobrien while (1) 96390075Sobrien { 96490075Sobrien rtx desc; 96590075Sobrien int lineno; 96690075Sobrien 96790075Sobrien c = read_skip_spaces (input_file); 96890075Sobrien if (c == EOF) 96990075Sobrien break; 97090075Sobrien 97190075Sobrien ungetc (c, input_file); 97290075Sobrien lineno = read_rtx_lineno; 97390075Sobrien desc = read_rtx (input_file); 97490075Sobrien process_rtx (desc, lineno); 97590075Sobrien } 97690075Sobrien fclose (input_file); 97790075Sobrien 97890075Sobrien /* Process define_cond_exec patterns. */ 97990075Sobrien if (define_cond_exec_queue != NULL) 98090075Sobrien process_define_cond_exec (); 98190075Sobrien 98290075Sobrien return errors ? FATAL_EXIT_CODE : SUCCESS_EXIT_CODE; 98390075Sobrien} 98490075Sobrien 98590075Sobrien/* The entry point for reading a single rtx from an md file. */ 98690075Sobrien 98790075Sobrienrtx 98890075Sobrienread_md_rtx (lineno, seqnr) 98990075Sobrien int *lineno; 99090075Sobrien int *seqnr; 99190075Sobrien{ 99290075Sobrien struct queue_elem **queue, *elem; 99390075Sobrien rtx desc; 99490075Sobrien 995117395Skan discard: 996117395Skan 99790075Sobrien /* Read all patterns from a given queue before moving on to the next. */ 99890075Sobrien if (define_attr_queue != NULL) 99990075Sobrien queue = &define_attr_queue; 100090075Sobrien else if (define_insn_queue != NULL) 100190075Sobrien queue = &define_insn_queue; 100290075Sobrien else if (other_queue != NULL) 100390075Sobrien queue = &other_queue; 100490075Sobrien else 100590075Sobrien return NULL_RTX; 100690075Sobrien 100790075Sobrien elem = *queue; 100890075Sobrien *queue = elem->next; 100990075Sobrien desc = elem->data; 1010117395Skan read_rtx_filename = elem->filename; 101190075Sobrien *lineno = elem->lineno; 101290075Sobrien *seqnr = sequence_num; 101390075Sobrien 101490075Sobrien free (elem); 101590075Sobrien 1016117395Skan /* Discard insn patterns which we know can never match (because 1017117395Skan their C test is provably always false). If insn_elision is 1018117395Skan false, our caller needs to see all the patterns. Note that the 1019117395Skan elided patterns are never counted by the sequence numbering; it 1020117395Skan it is the caller's responsibility, when insn_elision is false, not 1021117395Skan to use elided pattern numbers for anything. */ 102290075Sobrien switch (GET_CODE (desc)) 102390075Sobrien { 102490075Sobrien case DEFINE_INSN: 102590075Sobrien case DEFINE_EXPAND: 1026117395Skan if (maybe_eval_c_test (XSTR (desc, 2)) != 0) 1027117395Skan sequence_num++; 1028117395Skan else if (insn_elision) 1029117395Skan goto discard; 1030117395Skan break; 1031117395Skan 103290075Sobrien case DEFINE_SPLIT: 103390075Sobrien case DEFINE_PEEPHOLE: 103490075Sobrien case DEFINE_PEEPHOLE2: 1035117395Skan if (maybe_eval_c_test (XSTR (desc, 1)) != 0) 1036117395Skan sequence_num++; 1037117395Skan else if (insn_elision) 1038117395Skan goto discard; 103990075Sobrien break; 104090075Sobrien 104190075Sobrien default: 104290075Sobrien break; 104390075Sobrien } 104490075Sobrien 104590075Sobrien return desc; 104690075Sobrien} 1047117395Skan 1048117395Skan/* Helper functions for insn elision. */ 1049117395Skan 1050117395Skan/* Compute a hash function of a c_test structure, which is keyed 1051117395Skan by its ->expr field. */ 1052117395Skanhashval_t 1053117395Skanhash_c_test (x) 1054117395Skan const PTR x; 1055117395Skan{ 1056117395Skan const struct c_test *a = (const struct c_test *) x; 1057117395Skan const unsigned char *base, *s = (const unsigned char *) a->expr; 1058117395Skan hashval_t hash; 1059117395Skan unsigned char c; 1060117395Skan unsigned int len; 1061117395Skan 1062117395Skan base = s; 1063117395Skan hash = 0; 1064117395Skan 1065117395Skan while ((c = *s++) != '\0') 1066117395Skan { 1067117395Skan hash += c + (c << 17); 1068117395Skan hash ^= hash >> 2; 1069117395Skan } 1070117395Skan 1071117395Skan len = s - base; 1072117395Skan hash += len + (len << 17); 1073117395Skan hash ^= hash >> 2; 1074117395Skan 1075117395Skan return hash; 1076117395Skan} 1077117395Skan 1078117395Skan/* Compare two c_test expression structures. */ 1079117395Skanint 1080117395Skancmp_c_test (x, y) 1081117395Skan const PTR x; 1082117395Skan const PTR y; 1083117395Skan{ 1084117395Skan const struct c_test *a = (const struct c_test *) x; 1085117395Skan const struct c_test *b = (const struct c_test *) y; 1086117395Skan 1087117395Skan return !strcmp (a->expr, b->expr); 1088117395Skan} 1089117395Skan 1090117395Skan/* Given a string representing a C test expression, look it up in the 1091117395Skan condition_table and report whether or not its value is known 1092117395Skan at compile time. Returns a tristate: 1 for known true, 0 for 1093117395Skan known false, -1 for unknown. */ 1094117395Skanint 1095117395Skanmaybe_eval_c_test (expr) 1096117395Skan const char *expr; 1097117395Skan{ 1098117395Skan const struct c_test *test; 1099117395Skan struct c_test dummy; 1100117395Skan 1101117395Skan if (expr[0] == 0) 1102117395Skan return 1; 1103117395Skan 1104117395Skan if (insn_elision_unavailable) 1105117395Skan return -1; 1106117395Skan 1107117395Skan dummy.expr = expr; 1108117395Skan test = (const struct c_test *) htab_find (condition_table, &dummy); 1109117395Skan if (!test) 1110117395Skan abort (); 1111117395Skan 1112117395Skan return test->value; 1113117395Skan} 1114117395Skan 1115117395Skan/* Given a string, return the number of comma-separated elements in it. 1116117395Skan Return 0 for the null string. */ 1117117395Skanint 1118117395Skann_comma_elts (s) 1119117395Skan const char *s; 1120117395Skan{ 1121117395Skan int n; 1122117395Skan 1123117395Skan if (*s == '\0') 1124117395Skan return 0; 1125117395Skan 1126117395Skan for (n = 1; *s; s++) 1127117395Skan if (*s == ',') 1128117395Skan n++; 1129117395Skan 1130117395Skan return n; 1131117395Skan} 1132117395Skan 1133117395Skan/* Given a pointer to a (char *), return a pointer to the beginning of the 1134117395Skan next comma-separated element in the string. Advance the pointer given 1135117395Skan to the end of that element. Return NULL if at end of string. Caller 1136117395Skan is responsible for copying the string if necessary. White space between 1137117395Skan a comma and an element is ignored. */ 1138117395Skan 1139117395Skanconst char * 1140117395Skanscan_comma_elt (pstr) 1141117395Skan const char **pstr; 1142117395Skan{ 1143117395Skan const char *start; 1144117395Skan const char *p = *pstr; 1145117395Skan 1146117395Skan if (*p == ',') 1147117395Skan p++; 1148117395Skan while (ISSPACE(*p)) 1149117395Skan p++; 1150117395Skan 1151117395Skan if (*p == '\0') 1152117395Skan return NULL; 1153117395Skan 1154117395Skan start = p; 1155117395Skan 1156117395Skan while (*p != ',' && *p != '\0') 1157117395Skan p++; 1158117395Skan 1159117395Skan *pstr = p; 1160117395Skan return start; 1161117395Skan} 1162