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