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