1 /* cond.c - conditional assembly pseudo-ops, and .include 2 Copyright (C) 1990-2020 Free Software Foundation, Inc. 3 4 This file is part of GAS, the GNU Assembler. 5 6 GAS is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 3, or (at your option) 9 any later version. 10 11 GAS is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with GAS; see the file COPYING. If not, write to the Free 18 Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 19 02110-1301, USA. */ 20 21 #include "as.h" 22 #include "sb.h" 23 #include "macro.h" 24 25 #include "obstack.h" 26 27 /* This is allocated to grow and shrink as .ifdef/.endif pairs are 28 scanned. */ 29 struct obstack cond_obstack; 30 31 struct file_line 32 { 33 const char *file; 34 unsigned int line; 35 }; 36 37 /* We push one of these structures for each .if, and pop it at the 38 .endif. */ 39 40 struct conditional_frame 41 { 42 /* The source file & line number of the "if". */ 43 struct file_line if_file_line; 44 /* The source file & line of the "else". */ 45 struct file_line else_file_line; 46 /* The previous conditional. */ 47 struct conditional_frame *previous_cframe; 48 /* Have we seen an else yet? */ 49 int else_seen; 50 /* Whether we are currently ignoring input. */ 51 int ignoring; 52 /* Whether a conditional at a higher level is ignoring input. 53 Set also when a branch of an "if .. elseif .." tree has matched 54 to prevent further matches. */ 55 int dead_tree; 56 /* Macro nesting level at which this conditional was created. */ 57 int macro_nest; 58 }; 59 60 static void initialize_cframe (struct conditional_frame *cframe); 61 static char *get_mri_string (int, int *); 62 63 static struct conditional_frame *current_cframe = NULL; 64 65 /* Performs the .ifdef (test_defined == 1) and 66 the .ifndef (test_defined == 0) pseudo op. */ 67 68 void 69 s_ifdef (int test_defined) 70 { 71 /* Points to name of symbol. */ 72 char *name; 73 /* Points to symbol. */ 74 symbolS *symbolP; 75 struct conditional_frame cframe; 76 char c; 77 78 /* Leading whitespace is part of operand. */ 79 SKIP_WHITESPACE (); 80 name = input_line_pointer; 81 82 if (!is_name_beginner (*name) && *name != '"') 83 { 84 as_bad (_("invalid identifier for \".ifdef\"")); 85 obstack_1grow (&cond_obstack, 0); 86 ignore_rest_of_line (); 87 return; 88 } 89 90 c = get_symbol_name (& name); 91 symbolP = symbol_find (name); 92 (void) restore_line_pointer (c); 93 94 initialize_cframe (&cframe); 95 96 if (cframe.dead_tree) 97 cframe.ignoring = 1; 98 else 99 { 100 int is_defined; 101 102 /* Use the same definition of 'defined' as .equiv so that a symbol 103 which has been referenced but not yet given a value/address is 104 considered to be undefined. */ 105 is_defined = 106 symbolP != NULL 107 && (S_IS_DEFINED (symbolP) || symbol_equated_p (symbolP)) 108 && S_GET_SEGMENT (symbolP) != reg_section; 109 110 cframe.ignoring = ! (test_defined ^ is_defined); 111 } 112 113 current_cframe = 114 (struct conditional_frame *) obstack_alloc (&cond_obstack, sizeof cframe); 115 memcpy (current_cframe, &cframe, sizeof cframe); 116 117 if (LISTING_SKIP_COND () 118 && cframe.ignoring 119 && (cframe.previous_cframe == NULL 120 || ! cframe.previous_cframe->ignoring)) 121 listing_list (2); 122 123 demand_empty_rest_of_line (); 124 } 125 126 void 127 s_if (int arg) 128 { 129 expressionS operand; 130 struct conditional_frame cframe; 131 int t; 132 char *stop = NULL; 133 char stopc = 0; 134 135 if (flag_mri) 136 stop = mri_comment_field (&stopc); 137 138 /* Leading whitespace is part of operand. */ 139 SKIP_WHITESPACE (); 140 141 if (current_cframe != NULL && current_cframe->ignoring) 142 { 143 operand.X_add_number = 0; 144 while (! is_end_of_line[(unsigned char) *input_line_pointer]) 145 ++input_line_pointer; 146 } 147 else 148 { 149 expression_and_evaluate (&operand); 150 if (operand.X_op != O_constant) 151 as_bad (_("non-constant expression in \".if\" statement")); 152 } 153 154 switch ((operatorT) arg) 155 { 156 case O_eq: t = operand.X_add_number == 0; break; 157 case O_ne: t = operand.X_add_number != 0; break; 158 case O_lt: t = operand.X_add_number < 0; break; 159 case O_le: t = operand.X_add_number <= 0; break; 160 case O_ge: t = operand.X_add_number >= 0; break; 161 case O_gt: t = operand.X_add_number > 0; break; 162 default: 163 abort (); 164 return; 165 } 166 167 /* If the above error is signaled, this will dispatch 168 using an undefined result. No big deal. */ 169 initialize_cframe (&cframe); 170 cframe.ignoring = cframe.dead_tree || ! t; 171 current_cframe = 172 (struct conditional_frame *) obstack_alloc (&cond_obstack, sizeof cframe); 173 memcpy (current_cframe, & cframe, sizeof cframe); 174 175 if (LISTING_SKIP_COND () 176 && cframe.ignoring 177 && (cframe.previous_cframe == NULL 178 || ! cframe.previous_cframe->ignoring)) 179 listing_list (2); 180 181 if (flag_mri) 182 mri_comment_end (stop, stopc); 183 184 demand_empty_rest_of_line (); 185 } 186 187 /* Performs the .ifb (test_blank == 1) and 188 the .ifnb (test_blank == 0) pseudo op. */ 189 190 void 191 s_ifb (int test_blank) 192 { 193 struct conditional_frame cframe; 194 195 initialize_cframe (&cframe); 196 197 if (cframe.dead_tree) 198 cframe.ignoring = 1; 199 else 200 { 201 int is_eol; 202 203 SKIP_WHITESPACE (); 204 is_eol = is_end_of_line[(unsigned char) *input_line_pointer]; 205 cframe.ignoring = (test_blank == !is_eol); 206 } 207 208 current_cframe = 209 (struct conditional_frame *) obstack_alloc (&cond_obstack, sizeof cframe); 210 memcpy (current_cframe, &cframe, sizeof cframe); 211 212 if (LISTING_SKIP_COND () 213 && cframe.ignoring 214 && (cframe.previous_cframe == NULL 215 || ! cframe.previous_cframe->ignoring)) 216 listing_list (2); 217 218 ignore_rest_of_line (); 219 } 220 221 /* Get a string for the MRI IFC or IFNC pseudo-ops. */ 222 223 static char * 224 get_mri_string (int terminator, int *len) 225 { 226 char *ret; 227 char *s; 228 229 SKIP_WHITESPACE (); 230 s = ret = input_line_pointer; 231 if (*input_line_pointer == '\'') 232 { 233 ++s; 234 ++input_line_pointer; 235 while (! is_end_of_line[(unsigned char) *input_line_pointer]) 236 { 237 *s++ = *input_line_pointer++; 238 if (s[-1] == '\'') 239 { 240 if (*input_line_pointer != '\'') 241 break; 242 ++input_line_pointer; 243 } 244 } 245 SKIP_WHITESPACE (); 246 } 247 else 248 { 249 while (*input_line_pointer != terminator 250 && ! is_end_of_line[(unsigned char) *input_line_pointer]) 251 ++input_line_pointer; 252 s = input_line_pointer; 253 while (s > ret && (s[-1] == ' ' || s[-1] == '\t')) 254 --s; 255 } 256 257 *len = s - ret; 258 return ret; 259 } 260 261 /* The MRI IFC and IFNC pseudo-ops. */ 262 263 void 264 s_ifc (int arg) 265 { 266 char *stop = NULL; 267 char stopc = 0; 268 char *s1, *s2; 269 int len1, len2; 270 int res; 271 struct conditional_frame cframe; 272 273 if (flag_mri) 274 stop = mri_comment_field (&stopc); 275 276 s1 = get_mri_string (',', &len1); 277 278 if (*input_line_pointer != ',') 279 as_bad (_("bad format for ifc or ifnc")); 280 else 281 ++input_line_pointer; 282 283 s2 = get_mri_string (';', &len2); 284 285 res = len1 == len2 && strncmp (s1, s2, len1) == 0; 286 287 initialize_cframe (&cframe); 288 cframe.ignoring = cframe.dead_tree || ! (res ^ arg); 289 current_cframe = 290 (struct conditional_frame *) obstack_alloc (&cond_obstack, sizeof cframe); 291 memcpy (current_cframe, &cframe, sizeof cframe); 292 293 if (LISTING_SKIP_COND () 294 && cframe.ignoring 295 && (cframe.previous_cframe == NULL 296 || ! cframe.previous_cframe->ignoring)) 297 listing_list (2); 298 299 if (flag_mri) 300 mri_comment_end (stop, stopc); 301 302 demand_empty_rest_of_line (); 303 } 304 305 void 306 s_elseif (int arg) 307 { 308 if (current_cframe == NULL) 309 { 310 as_bad (_("\".elseif\" without matching \".if\"")); 311 } 312 else if (current_cframe->else_seen) 313 { 314 as_bad (_("\".elseif\" after \".else\"")); 315 as_bad_where (current_cframe->else_file_line.file, 316 current_cframe->else_file_line.line, 317 _("here is the previous \".else\"")); 318 as_bad_where (current_cframe->if_file_line.file, 319 current_cframe->if_file_line.line, 320 _("here is the previous \".if\"")); 321 } 322 else 323 { 324 current_cframe->else_file_line.file 325 = as_where (¤t_cframe->else_file_line.line); 326 327 current_cframe->dead_tree |= !current_cframe->ignoring; 328 current_cframe->ignoring = current_cframe->dead_tree; 329 } 330 331 if (current_cframe == NULL || current_cframe->ignoring) 332 { 333 while (! is_end_of_line[(unsigned char) *input_line_pointer]) 334 ++input_line_pointer; 335 336 if (current_cframe == NULL) 337 return; 338 } 339 else 340 { 341 expressionS operand; 342 int t; 343 344 /* Leading whitespace is part of operand. */ 345 SKIP_WHITESPACE (); 346 347 expression_and_evaluate (&operand); 348 if (operand.X_op != O_constant) 349 as_bad (_("non-constant expression in \".elseif\" statement")); 350 351 switch ((operatorT) arg) 352 { 353 case O_eq: t = operand.X_add_number == 0; break; 354 case O_ne: t = operand.X_add_number != 0; break; 355 case O_lt: t = operand.X_add_number < 0; break; 356 case O_le: t = operand.X_add_number <= 0; break; 357 case O_ge: t = operand.X_add_number >= 0; break; 358 case O_gt: t = operand.X_add_number > 0; break; 359 default: 360 abort (); 361 return; 362 } 363 364 current_cframe->ignoring = current_cframe->dead_tree || ! t; 365 } 366 367 if (LISTING_SKIP_COND () 368 && (current_cframe->previous_cframe == NULL 369 || ! current_cframe->previous_cframe->ignoring)) 370 { 371 if (! current_cframe->ignoring) 372 listing_list (1); 373 else 374 listing_list (2); 375 } 376 377 demand_empty_rest_of_line (); 378 } 379 380 void 381 s_endif (int arg ATTRIBUTE_UNUSED) 382 { 383 struct conditional_frame *hold; 384 385 if (current_cframe == NULL) 386 { 387 as_bad (_("\".endif\" without \".if\"")); 388 } 389 else 390 { 391 if (LISTING_SKIP_COND () 392 && current_cframe->ignoring 393 && (current_cframe->previous_cframe == NULL 394 || ! current_cframe->previous_cframe->ignoring)) 395 listing_list (1); 396 397 hold = current_cframe; 398 current_cframe = current_cframe->previous_cframe; 399 obstack_free (&cond_obstack, hold); 400 } /* if one pop too many */ 401 402 if (flag_mri) 403 { 404 while (! is_end_of_line[(unsigned char) *input_line_pointer]) 405 ++input_line_pointer; 406 } 407 408 demand_empty_rest_of_line (); 409 } 410 411 void 412 s_else (int arg ATTRIBUTE_UNUSED) 413 { 414 if (current_cframe == NULL) 415 { 416 as_bad (_("\".else\" without matching \".if\"")); 417 } 418 else if (current_cframe->else_seen) 419 { 420 as_bad (_("duplicate \".else\"")); 421 as_bad_where (current_cframe->else_file_line.file, 422 current_cframe->else_file_line.line, 423 _("here is the previous \".else\"")); 424 as_bad_where (current_cframe->if_file_line.file, 425 current_cframe->if_file_line.line, 426 _("here is the previous \".if\"")); 427 } 428 else 429 { 430 current_cframe->else_file_line.file 431 = as_where (¤t_cframe->else_file_line.line); 432 433 current_cframe->ignoring = 434 current_cframe->dead_tree | !current_cframe->ignoring; 435 436 if (LISTING_SKIP_COND () 437 && (current_cframe->previous_cframe == NULL 438 || ! current_cframe->previous_cframe->ignoring)) 439 { 440 if (! current_cframe->ignoring) 441 listing_list (1); 442 else 443 listing_list (2); 444 } 445 446 current_cframe->else_seen = 1; 447 } 448 449 if (flag_mri) 450 { 451 while (! is_end_of_line[(unsigned char) *input_line_pointer]) 452 ++input_line_pointer; 453 } 454 455 demand_empty_rest_of_line (); 456 } 457 458 void 459 s_ifeqs (int arg) 460 { 461 char *s1, *s2; 462 int len1, len2; 463 int res; 464 struct conditional_frame cframe; 465 466 s1 = demand_copy_C_string (&len1); 467 468 SKIP_WHITESPACE (); 469 if (*input_line_pointer != ',') 470 { 471 as_bad (_(".ifeqs syntax error")); 472 ignore_rest_of_line (); 473 return; 474 } 475 476 ++input_line_pointer; 477 478 s2 = demand_copy_C_string (&len2); 479 480 res = len1 == len2 && strncmp (s1, s2, len1) == 0; 481 482 initialize_cframe (&cframe); 483 cframe.ignoring = cframe.dead_tree || ! (res ^ arg); 484 current_cframe = 485 (struct conditional_frame *) obstack_alloc (&cond_obstack, sizeof cframe); 486 memcpy (current_cframe, &cframe, sizeof cframe); 487 488 if (LISTING_SKIP_COND () 489 && cframe.ignoring 490 && (cframe.previous_cframe == NULL 491 || ! cframe.previous_cframe->ignoring)) 492 listing_list (2); 493 494 demand_empty_rest_of_line (); 495 } 496 497 int 498 ignore_input (void) 499 { 500 char *s; 501 502 s = input_line_pointer; 503 504 if (NO_PSEUDO_DOT || flag_m68k_mri) 505 { 506 if (s[-1] != '.') 507 --s; 508 } 509 else 510 { 511 if (s[-1] != '.') 512 return (current_cframe != NULL) && (current_cframe->ignoring); 513 } 514 515 /* We cannot ignore certain pseudo ops. */ 516 if (((s[0] == 'i' 517 || s[0] == 'I') 518 && (!strncasecmp (s, "if", 2) 519 || !strncasecmp (s, "ifdef", 5) 520 || !strncasecmp (s, "ifndef", 6))) 521 || ((s[0] == 'e' 522 || s[0] == 'E') 523 && (!strncasecmp (s, "else", 4) 524 || !strncasecmp (s, "endif", 5) 525 || !strncasecmp (s, "endc", 4)))) 526 return 0; 527 528 return (current_cframe != NULL) && (current_cframe->ignoring); 529 } 530 531 static void 532 initialize_cframe (struct conditional_frame *cframe) 533 { 534 memset (cframe, 0, sizeof (*cframe)); 535 cframe->if_file_line.file 536 = as_where (&cframe->if_file_line.line); 537 cframe->previous_cframe = current_cframe; 538 cframe->dead_tree = current_cframe != NULL && current_cframe->ignoring; 539 cframe->macro_nest = macro_nest; 540 } 541 542 /* Give an error if a conditional is unterminated inside a macro or 543 the assembly as a whole. If NEST is non negative, we are being 544 called because of the end of a macro expansion. If NEST is 545 negative, we are being called at the of the input files. */ 546 547 void 548 cond_finish_check (int nest) 549 { 550 if (current_cframe != NULL && current_cframe->macro_nest >= nest) 551 { 552 if (nest >= 0) 553 as_bad (_("end of macro inside conditional")); 554 else 555 as_bad (_("end of file inside conditional")); 556 557 as_bad_where (current_cframe->if_file_line.file, 558 current_cframe->if_file_line.line, 559 _("here is the start of the unterminated conditional")); 560 if (current_cframe->else_seen) 561 as_bad_where (current_cframe->else_file_line.file, 562 current_cframe->else_file_line.line, 563 _("here is the \"else\" of the unterminated conditional")); 564 } 565 } 566 567 /* This function is called when we exit out of a macro. We assume 568 that any conditionals which began within the macro are correctly 569 nested, and just pop them off the stack. */ 570 571 void 572 cond_exit_macro (int nest) 573 { 574 while (current_cframe != NULL && current_cframe->macro_nest >= nest) 575 { 576 struct conditional_frame *hold; 577 578 hold = current_cframe; 579 current_cframe = current_cframe->previous_cframe; 580 obstack_free (&cond_obstack, hold); 581 } 582 } 583