1 /*
2 * Copyright (c) 2016-2018, NVIDIA CORPORATION. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18 /** \file
19 * \brief Argument parser implementation
20 */
21
22 #include "flang/ADT/hash.h"
23 #include "flang/ArgParser/arg_parser.h"
24 #include "flang/ArgParser/xflag.h"
25 #include "flang/Error/pgerror.h"
26 #include <stdbool.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 /** \brief Internal representation of argument parser */
31 struct arg_parser_ {
32 /** Hash map from string keys to instances of value data structure */
33 hashmap_t flags;
34 /** Registered values */
35 hashset_t values;
36 /** Values set by the parser */
37 hashset_t value_hits;
38 /** Set to true to throw an error on unknown arguments, to false to silently
39 * continue parsing */
40 bool fail_on_unknown_args;
41 /** Where to write input file name */
42 char **input_file_name_ptr;
43 };
44
45 /** \brief Link a bool * and a char ** value together */
46 typedef struct bool_string_ {
47 bool *bool_ptr;
48 char **string_ptr;
49 } bool_string_t;
50
51 /** \brief Combine input and output for action map arguments */
52 typedef struct action_map_bundle_ {
53 action_map_t *input;
54 action_map_t *output;
55 } action_map_bundle_t;
56
57 /** \brief Argument value type */
58 typedef enum value_type_ {
59 ARG_ActionMap,
60 ARG_Boolean,
61 ARG_CombinedBoolean,
62 ARG_InformLevel,
63 ARG_Integer,
64 ARG_ReverseBoolean,
65 ARG_String,
66 ARG_StringList,
67 ARG_QFlag,
68 ARG_XFlag,
69 ARG_YFlag,
70 } value_type_t;
71
72 /** \brief Argument type and location */
73 typedef struct value_ {
74 /** Type of value to write */
75 value_type_t type;
76 /** Where to write it */
77 void *location;
78 } value_t;
79
80 static void add_generic_argument(arg_parser_t *parser, const char *arg_name,
81 value_type_t value_type, void *value_ptr);
82 static void deallocate_arg_value(hash_key_t ignore, hash_data_t value_ptr,
83 void *ignore_context);
84 static void compose_and_throw(const char *first_part, const char *second_part);
85 static char *next_value(char **argv, int *arg_index);
86 static inform_level_t strtoinform(const char *string);
87
88 /** Allocate argument parser hash map */
89 void
create_arg_parser(arg_parser_t ** parser,bool fail_on_unknown_args)90 create_arg_parser(arg_parser_t **parser, bool fail_on_unknown_args)
91 {
92 *parser = (arg_parser_t*) malloc(sizeof(arg_parser_t));
93 (*parser)->flags = hashmap_alloc(hash_functions_strings);
94 (*parser)->values = hashset_alloc(hash_functions_direct);
95 (*parser)->value_hits = hashset_alloc(hash_functions_direct);
96 (*parser)->fail_on_unknown_args = fail_on_unknown_args;
97 (*parser)->input_file_name_ptr = NULL;
98 }
99
100 /** Deallocate argument parser hash map */
101 void
destroy_arg_parser(arg_parser_t ** parser)102 destroy_arg_parser(arg_parser_t **parser)
103 {
104 /* Deallocate entries */
105 hashmap_iterate((*parser)->flags, deallocate_arg_value, NULL);
106
107 /* Free flags data structure */
108 hashmap_free((*parser)->flags);
109
110 /* Deallocate the data structure itself */
111 free(*parser);
112 *parser = NULL;
113 }
114
115 /** Deallocate hashtable entry */
116 static void
deallocate_arg_value(hash_key_t key,hash_data_t value_ptr,void * key_context)117 deallocate_arg_value(hash_key_t key, hash_data_t value_ptr, void *key_context)
118 {
119 /* Some of the argument types require deallocation */
120 switch (((value_t *)value_ptr)->type) {
121 case ARG_ActionMap:
122 case ARG_CombinedBoolean:
123 free(((value_t *)value_ptr)->location);
124 break;
125 case ARG_ReverseBoolean:
126 free((char *)key);
127 break;
128 default:
129 /* Do nothing */
130 break;
131 }
132 free((value_t *)value_ptr);
133 }
134
135 /** Register a string argument */
136 void
register_string_arg(arg_parser_t * parser,const char * arg_name,char ** target,const char * default_value)137 register_string_arg(arg_parser_t *parser, const char *arg_name, char **target,
138 const char *default_value)
139 {
140 /* Set default value */
141 *target = (char *)default_value;
142
143 add_generic_argument(parser, arg_name, ARG_String, (void *)target);
144 }
145
146 /** Register a string list argument */
147 void
register_string_list_arg(arg_parser_t * parser,const char * arg_name,char ** target)148 register_string_list_arg(arg_parser_t *parser, const char *arg_name,
149 char **target)
150 {
151 /* Terminate list */
152 *target = NULL;
153
154 add_generic_argument(parser, arg_name, ARG_StringList, (void *)target);
155 }
156
157 /** Register an integer argument */
158 void
register_integer_arg(arg_parser_t * parser,const char * arg_name,int * target,const int default_value)159 register_integer_arg(arg_parser_t *parser, const char *arg_name, int *target,
160 const int default_value)
161 {
162 /* Set default value */
163 *target = (int)default_value;
164
165 add_generic_argument(parser, arg_name, ARG_Integer, (void *)target);
166 }
167
168 /** Register a boolean argument */
169 void
register_boolean_arg(arg_parser_t * parser,const char * arg_name,bool * target,const bool default_value)170 register_boolean_arg(arg_parser_t *parser, const char *arg_name, bool *target,
171 const bool default_value)
172 {
173 /* Space to write reverse argument name, deallocation is done in
174 * deallocate_arg_value */
175 char *reverse_arg_name = (char*) malloc(strlen(arg_name) + 3);
176
177 /* -no<arg> */
178 strcpy(reverse_arg_name, "no");
179 strcat(reverse_arg_name, arg_name);
180
181 /* Set default value */
182 *target = (bool)default_value;
183
184 /* -<arg> */
185 add_generic_argument(parser, arg_name, ARG_Boolean, (void *)target);
186 /* -no<arg> */
187 add_generic_argument(parser, reverse_arg_name, ARG_ReverseBoolean,
188 (void *)target);
189 }
190
191 /** Register a combines bool and string argument */
192 void
register_combined_bool_string_arg(arg_parser_t * parser,const char * arg_name,bool * bool_target,char ** string_target)193 register_combined_bool_string_arg(arg_parser_t *parser, const char *arg_name,
194 bool *bool_target, char **string_target)
195 {
196 /* Boolean target defaults to false (not set) */
197 *bool_target = false;
198 /* and string target defaults to NULL */
199 *string_target = NULL;
200
201 /* Store both pointers for argument processsing */
202 bool_string_t *target = (bool_string_t*) malloc(sizeof(bool_string_t));
203 target->bool_ptr = bool_target;
204 target->string_ptr = string_target;
205
206 add_generic_argument(parser, arg_name, ARG_CombinedBoolean, (void *)target);
207 }
208
209 /** Register "x" flag argument */
210 void
register_qflag_arg(arg_parser_t * parser,const char * arg_name,int * qflags,const int qflags_size)211 register_qflag_arg(arg_parser_t *parser, const char *arg_name, int *qflags,
212 const int qflags_size)
213 {
214 /* Fill array with zeros */
215 memset(qflags, 0, qflags_size * sizeof(int));
216
217 add_generic_argument(parser, arg_name, ARG_QFlag, (void *)qflags);
218 }
219
220 /** Register "x" flag argument */
221 void
register_xflag_arg(arg_parser_t * parser,const char * arg_name,int * xflags,const int xflags_size)222 register_xflag_arg(arg_parser_t *parser, const char *arg_name, int *xflags,
223 const int xflags_size)
224 {
225 /* Fill xflags array with zeros */
226 memset(xflags, 0, xflags_size * sizeof(int));
227
228 add_generic_argument(parser, arg_name, ARG_XFlag, (void *)xflags);
229 }
230
231 /** Register "y" flag argument */
232 void
register_yflag_arg(arg_parser_t * parser,const char * arg_name,int * xflags)233 register_yflag_arg(arg_parser_t *parser, const char *arg_name, int *xflags)
234 {
235 add_generic_argument(parser, arg_name, ARG_YFlag, (void *)xflags);
236 }
237
238 /** Register verbosity argument */
239 void
register_inform_level_arg(arg_parser_t * parser,const char * arg_name,inform_level_t * target,const inform_level_t default_value)240 register_inform_level_arg(arg_parser_t *parser, const char *arg_name,
241 inform_level_t *target,
242 const inform_level_t default_value)
243 {
244 /* Set default value */
245 *target = (inform_level_t)default_value;
246
247 add_generic_argument(parser, arg_name, ARG_InformLevel, (void *)target);
248 }
249
250 /** Register "action list" argument */
251 void
register_action_map_arg(arg_parser_t * parser,const char * arg_name,action_map_t * target,const action_map_t * input)252 register_action_map_arg(arg_parser_t *parser, const char *arg_name,
253 action_map_t *target, const action_map_t *input)
254 {
255 action_map_bundle_t *value = (action_map_bundle_t*) malloc(
256 sizeof(action_map_bundle_t));
257 value->input = (action_map_t *)input;
258 value->output = target;
259
260 add_generic_argument(parser, arg_name, ARG_ActionMap, (void *)value);
261 }
262
263 /** Register input file name */
264 void
register_filename_arg(arg_parser_t * parser,char ** target)265 register_filename_arg(arg_parser_t *parser, char **target)
266 {
267 parser->input_file_name_ptr = target;
268 }
269
270 /** \brief Add a generic argument, specifying argument type
271 *
272 * \param arg_parser Parser data structure
273 * \param arg_name Argument name (key to flags table)
274 * \param value_type Type of the value to store
275 * \param value_ptr Location to store value to
276 */
277 static void
add_generic_argument(arg_parser_t * parser,const char * arg_name,value_type_t value_type,void * value_ptr)278 add_generic_argument(arg_parser_t *parser, const char *arg_name,
279 value_type_t value_type, void *value_ptr)
280 {
281 /* Mapped value */
282 value_t *value = NULL;
283 /* Old (ignored) value */
284 hash_data_t old_value = NULL;
285
286 /* Check if this argument is already registered */
287 if (hashmap_lookup(parser->flags, arg_name, &old_value)) {
288 /* Compose & throw error */
289 compose_and_throw("Argument already registered: ", arg_name);
290 }
291
292 /* Add value to flags hashmap */
293 value = (value_t*) malloc(sizeof(value_t));
294 value->type = value_type;
295 value->location = value_ptr;
296 hashmap_insert(parser->flags, arg_name, value);
297
298 /* Put the same value into hashset tracking registered values (this time it is
299 * the key) */
300 if (value_type == ARG_ActionMap) {
301 action_map_t *target = ((action_map_bundle_t *)value_ptr)->output;
302 hashset_replace(parser->values, target);
303 } else {
304 hashset_replace(parser->values, value_ptr);
305 }
306 }
307
308 /** Parse all arguments */
309 void
parse_arguments(const arg_parser_t * parser,int argc,char ** argv)310 parse_arguments(const arg_parser_t *parser, int argc, char **argv)
311 {
312 int argindex = 1; /* Argument counter */
313 char *next_string; /* Next argument */
314 int x_index, x_value; /* Index to xflags array and value to set there */
315
316 /* Reset value hits */
317 hashset_clear(parser->value_hits);
318
319 /* Make sure we can set file name */
320 if (!parser->input_file_name_ptr) {
321 interr("Input file name is not registered", 0, ERR_Fatal);
322 }
323
324 /* First grab the source file name */
325 if (*argv[1] != '-') {
326 *parser->input_file_name_ptr = argv[1];
327 argindex = 2;
328 } else if (*argv[argc - 1] != '-') {
329 *parser->input_file_name_ptr = argv[argc - 1];
330 --argc;
331 }
332
333 /* Loop through provided arguments */
334 while (argindex < argc) {
335 value_t *value = NULL;
336 char *arg = argv[argindex];
337
338 /* All switches should start with a '-' */
339 if (*arg != '-') {
340 compose_and_throw("Unrecognized argument: ", arg);
341 }
342 ++arg; /* skip over '-' */
343
344 /* All arguments need to be in the data structure */
345 if (!hashmap_lookup(parser->flags, arg, (hash_data_t *)&value)) {
346 if (parser->fail_on_unknown_args) {
347 compose_and_throw("Unknown command line argument: ", arg);
348 } else {
349 /* Skip until next switch or end of argument list */
350 while ((argindex < argc - 1) && (*argv[++argindex] != '-'))
351 ;
352 if (argindex == argc - 1)
353 break;
354 continue;
355 }
356 }
357
358 /* Parse argument type */
359 switch (value->type) {
360 case ARG_ActionMap: {
361 action_map_t *from = ((action_map_bundle_t *)value->location)->input;
362 action_map_t *to = ((action_map_bundle_t *)value->location)->output;
363
364 /* TODO parse lists of arguments */
365 char *phase_string = next_value(argv, &argindex);
366 char *dump_string = next_value(argv, &argindex);
367
368 copy_action(from, dump_string, to, phase_string);
369 } break;
370
371 case ARG_Boolean:
372 *((bool *)value->location) = true;
373 break;
374
375 case ARG_ReverseBoolean:
376 *((bool *)value->location) = false;
377 break;
378
379 case ARG_String:
380 next_string = next_value(argv, &argindex);
381 if (!next_string)
382 compose_and_throw("Missing value for -", arg);
383 /* Change stored value to point to passed string */
384 *((char **)value->location) = next_string;
385 break;
386
387 case ARG_StringList:
388 /* Get argument value */
389 next_string = next_value(argv, &argindex);
390 if (!next_string)
391 compose_and_throw("Missing value for -", arg);
392 /* Set argument value */
393 *((char **)value->location) = next_string;
394 /* Point to the next value on the list */
395 value->location = (void *)(((char **)value->location) + 1);
396 /* Terminate the list */
397 *((char **)value->location) = NULL;
398 break;
399
400 case ARG_InformLevel:
401 next_string = next_value(argv, &argindex);
402 if (!next_string)
403 compose_and_throw("Missing value for -", arg);
404 *((inform_level_t *)value->location) = strtoinform(next_string);
405 break;
406
407 case ARG_Integer:
408 next_string = next_value(argv, &argindex);
409 if (!next_string)
410 compose_and_throw("Missing value for -", arg);
411 *((int *)value->location) = (int)strtol(next_string, NULL, 10);
412 break;
413
414 case ARG_QFlag:
415 next_string = next_value(argv, &argindex);
416
417 if (!next_string)
418 compose_and_throw("Missing value for -", arg);
419
420 x_index = (int)strtol(next_string, NULL, 10);
421
422 next_string = next_value(argv, &argindex);
423
424 if (!next_string)
425 compose_and_throw("Missing second value for -", arg);
426
427 x_value = (int)strtol(next_string, NULL, 10);
428
429 ((int *)value->location)[x_index] |= x_value;
430 break;
431
432 case ARG_XFlag:
433 next_string = next_value(argv, &argindex);
434
435 if (!next_string)
436 compose_and_throw("Missing value for -", arg);
437 x_index = (int)strtol(next_string, NULL, 10);
438
439 next_string = next_value(argv, &argindex);
440
441 if (next_string)
442 x_value = (int)strtol(next_string, NULL, 0);
443 else
444 x_value = 1;
445
446 set_xflag_value((int *)value->location, x_index, x_value);
447 break;
448
449 case ARG_YFlag:
450 next_string = next_value(argv, &argindex);
451
452 if (!next_string)
453 compose_and_throw("Missing value for -", arg);
454 x_index = (int)strtol(next_string, NULL, 10);
455
456 next_string = next_value(argv, &argindex);
457
458 if (next_string)
459 x_value = (int)strtol(next_string, NULL, 0);
460 else
461 x_value = 1;
462
463 unset_xflag_value((int *)value->location, x_index, x_value);
464 break;
465
466 case ARG_CombinedBoolean:
467 *(((bool_string_t *)value->location)->bool_ptr) = true;
468 next_string = next_value(argv, &argindex);
469 if (next_string) {
470 *(((bool_string_t *)value->location)->string_ptr) = next_string;
471 }
472 break;
473
474 default:
475 interr("Uknown command line argument value type", value->type, ERR_Fatal);
476 break;
477 }
478
479 /* Remember that the value as set */
480 if (value->type == ARG_ActionMap) {
481 action_map_t *target = ((action_map_bundle_t *)value->location)->output;
482 hashset_replace(parser->value_hits, target);
483 } else {
484 hashset_replace(parser->value_hits, value->location);
485 }
486
487 ++argindex;
488 }
489 }
490
491 /** \brief Compose and throw internal compiler error
492 *
493 * Produces internal compiler error with the message composed by concatenating
494 * the two operands
495 */
496 static void
compose_and_throw(const char * first_part,const char * second_part)497 compose_and_throw(const char *first_part, const char *second_part)
498 {
499 char *msg = (char*) malloc(strlen(first_part) + strlen(second_part) + 1);
500 strcpy(msg, first_part);
501 strcat(msg, second_part);
502 interr(msg, 0, ERR_Fatal);
503 }
504
505 /** \brief Point to next argument value
506 *
507 * If next argument in argument list does not start with a '-' return it and
508 * advance argument counter, otherwise return NULL and keep counter the same
509 *
510 * \return pointer to the next non-switch command line argument
511 * \param argv command line argument array
512 * \param arg_index current index into the argument array
513 */
514 static char *
next_value(char ** argv,int * arg_index)515 next_value(char **argv, int *arg_index)
516 {
517 if (*argv[*arg_index + 1] == '-')
518 return NULL;
519
520 *arg_index = *arg_index + 1;
521
522 return argv[*arg_index];
523 }
524
525 /** \brief Convert a inform level string to corresponding constant */
526 static inform_level_t
strtoinform(const char * string)527 strtoinform(const char *string)
528 {
529 if (!strcmp("inform", string))
530 return LV_Inform;
531 if (!strcmp("severe", string))
532 return LV_Severe;
533 if (!strcmp("warn", string))
534 return LV_Warn;
535 if (!strcmp("fatal", string))
536 return LV_Fatal;
537
538 compose_and_throw("Unrecognized inform level: ", string);
539
540 /* unreacheable */
541 return LV_Inform;
542 }
543
544 /** Check if argument was found during parse */
545 bool
was_value_set(const arg_parser_t * parser,const void * location)546 was_value_set(const arg_parser_t *parser, const void *location)
547 {
548 /* Any value passed to this function need to be registered */
549 if (!hashset_lookup(parser->values, location)) {
550 compose_and_throw(__func__, ": value location not registered");
551 }
552
553 return (hashset_lookup(parser->value_hits, location));
554 }
555