1 /* Dependency checks for instruction scheduling, shared between ARM and 2 AARCH64. 3 4 Copyright (C) 1991-2024 Free Software Foundation, Inc. 5 Contributed by ARM Ltd. 6 7 This file is part of GCC. 8 9 GCC is free software; you can redistribute it and/or modify it 10 under the terms of the GNU General Public License as published 11 by the Free Software Foundation; either version 3, or (at your 12 option) any later version. 13 14 GCC is distributed in the hope that it will be useful, but WITHOUT 15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 17 License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with GCC; see the file COPYING3. If not see 21 <http://www.gnu.org/licenses/>. */ 22 23 24 #define IN_TARGET_CODE 1 25 26 #include "config.h" 27 #include "system.h" 28 #include "coretypes.h" 29 #include "insn-modes.h" 30 #include "tm.h" 31 #include "rtl.h" 32 #include "rtl-iter.h" 33 #include "memmodel.h" 34 #include "diagnostic.h" 35 #include "tree.h" 36 #include "expr.h" 37 #include "function.h" 38 #include "emit-rtl.h" 39 #include "aarch-common.h" 40 #include "aarch-common-protos.h" 41 42 /* Return TRUE if X is either an arithmetic shift left, or 43 is a multiplication by a power of two. */ 44 bool 45 arm_rtx_shift_left_p (rtx x) 46 { 47 enum rtx_code code = GET_CODE (x); 48 49 if (code == MULT && CONST_INT_P (XEXP (x, 1)) 50 && exact_log2 (INTVAL (XEXP (x, 1))) > 0) 51 return true; 52 53 if (code == ASHIFT) 54 return true; 55 56 return false; 57 } 58 59 static rtx_code shift_rtx_codes[] = 60 { ASHIFT, ROTATE, ASHIFTRT, LSHIFTRT, 61 ROTATERT, ZERO_EXTEND, SIGN_EXTEND }; 62 63 /* Traverse PATTERN looking for a sub-rtx with RTX_CODE CODE. 64 If FIND_ANY_SHIFT then we are interested in anything which can 65 reasonably be described as a SHIFT RTX. */ 66 static rtx 67 arm_find_sub_rtx_with_code (rtx pattern, rtx_code code, bool find_any_shift) 68 { 69 subrtx_var_iterator::array_type array; 70 FOR_EACH_SUBRTX_VAR (iter, array, pattern, NONCONST) 71 { 72 rtx x = *iter; 73 if (find_any_shift) 74 { 75 /* Left shifts might have been canonicalized to a MULT of some 76 power of two. Make sure we catch them. */ 77 if (arm_rtx_shift_left_p (x)) 78 return x; 79 else 80 for (unsigned int i = 0; i < ARRAY_SIZE (shift_rtx_codes); i++) 81 if (GET_CODE (x) == shift_rtx_codes[i]) 82 return x; 83 } 84 85 if (GET_CODE (x) == code) 86 return x; 87 } 88 return NULL_RTX; 89 } 90 91 /* Traverse PATTERN looking for any sub-rtx which looks like a shift. */ 92 static rtx 93 arm_find_shift_sub_rtx (rtx pattern) 94 { 95 return arm_find_sub_rtx_with_code (pattern, ASHIFT, true); 96 } 97 98 /* PRODUCER and CONSUMER are two potentially dependant RTX. PRODUCER 99 (possibly) contains a SET which will provide a result we can access 100 using the SET_DEST macro. We will place the RTX which would be 101 written by PRODUCER in SET_SOURCE. 102 Similarly, CONSUMER (possibly) contains a SET which has an operand 103 we can access using SET_SRC. We place this operand in 104 SET_DESTINATION. 105 106 Return nonzero if we found the SET RTX we expected. */ 107 static int 108 arm_get_set_operands (rtx producer, rtx consumer, 109 rtx *set_source, rtx *set_destination) 110 { 111 rtx set_producer = arm_find_sub_rtx_with_code (PATTERN (producer), 112 SET, false); 113 rtx set_consumer = arm_find_sub_rtx_with_code (PATTERN (consumer), 114 SET, false); 115 116 if (set_producer && set_consumer) 117 { 118 *set_source = SET_DEST (set_producer); 119 *set_destination = SET_SRC (set_consumer); 120 return 1; 121 } 122 return 0; 123 } 124 125 bool 126 aarch_rev16_shright_mask_imm_p (rtx val, machine_mode mode) 127 { 128 return CONST_INT_P (val) 129 && INTVAL (val) 130 == trunc_int_for_mode (HOST_WIDE_INT_C (0xff00ff00ff00ff), 131 mode); 132 } 133 134 bool 135 aarch_rev16_shleft_mask_imm_p (rtx val, machine_mode mode) 136 { 137 return CONST_INT_P (val) 138 && INTVAL (val) 139 == trunc_int_for_mode (HOST_WIDE_INT_C (0xff00ff00ff00ff00), 140 mode); 141 } 142 143 144 static bool 145 aarch_rev16_p_1 (rtx lhs, rtx rhs, machine_mode mode) 146 { 147 if (GET_CODE (lhs) == AND 148 && GET_CODE (XEXP (lhs, 0)) == ASHIFT 149 && CONST_INT_P (XEXP (XEXP (lhs, 0), 1)) 150 && INTVAL (XEXP (XEXP (lhs, 0), 1)) == 8 151 && REG_P (XEXP (XEXP (lhs, 0), 0)) 152 && CONST_INT_P (XEXP (lhs, 1)) 153 && GET_CODE (rhs) == AND 154 && GET_CODE (XEXP (rhs, 0)) == LSHIFTRT 155 && REG_P (XEXP (XEXP (rhs, 0), 0)) 156 && CONST_INT_P (XEXP (XEXP (rhs, 0), 1)) 157 && INTVAL (XEXP (XEXP (rhs, 0), 1)) == 8 158 && CONST_INT_P (XEXP (rhs, 1)) 159 && REGNO (XEXP (XEXP (rhs, 0), 0)) == REGNO (XEXP (XEXP (lhs, 0), 0))) 160 161 { 162 rtx lhs_mask = XEXP (lhs, 1); 163 rtx rhs_mask = XEXP (rhs, 1); 164 165 return aarch_rev16_shright_mask_imm_p (rhs_mask, mode) 166 && aarch_rev16_shleft_mask_imm_p (lhs_mask, mode); 167 } 168 169 return false; 170 } 171 172 /* Recognise a sequence of bitwise operations corresponding to a rev16 operation. 173 These will be of the form: 174 ((x >> 8) & 0x00ff00ff) 175 | ((x << 8) & 0xff00ff00) 176 for SImode and with similar but wider bitmasks for DImode. 177 The two sub-expressions of the IOR can appear on either side so check both 178 permutations with the help of aarch_rev16_p_1 above. */ 179 180 bool 181 aarch_rev16_p (rtx x) 182 { 183 rtx left_sub_rtx, right_sub_rtx; 184 bool is_rev = false; 185 186 if (GET_CODE (x) != IOR) 187 return false; 188 189 left_sub_rtx = XEXP (x, 0); 190 right_sub_rtx = XEXP (x, 1); 191 192 /* There are no canonicalisation rules for the position of the two shifts 193 involved in a rev, so try both permutations. */ 194 is_rev = aarch_rev16_p_1 (left_sub_rtx, right_sub_rtx, GET_MODE (x)); 195 196 if (!is_rev) 197 is_rev = aarch_rev16_p_1 (right_sub_rtx, left_sub_rtx, GET_MODE (x)); 198 199 return is_rev; 200 } 201 202 /* Return non-zero if the RTX representing a memory model is a memory model 203 that needs acquire semantics. */ 204 bool 205 aarch_mm_needs_acquire (rtx const_int) 206 { 207 enum memmodel model = memmodel_from_int (INTVAL (const_int)); 208 return !(is_mm_relaxed (model) 209 || is_mm_consume (model) 210 || is_mm_release (model)); 211 } 212 213 /* Return non-zero if the RTX representing a memory model is a memory model 214 that needs release semantics. */ 215 bool 216 aarch_mm_needs_release (rtx const_int) 217 { 218 enum memmodel model = memmodel_from_int (INTVAL (const_int)); 219 return !(is_mm_relaxed (model) 220 || is_mm_consume (model) 221 || is_mm_acquire (model)); 222 } 223 224 /* Return nonzero if the CONSUMER instruction (a load) does need 225 PRODUCER's value to calculate the address. */ 226 int 227 arm_early_load_addr_dep (rtx producer, rtx consumer) 228 { 229 rtx value, addr; 230 231 if (!arm_get_set_operands (producer, consumer, &value, &addr)) 232 return 0; 233 234 return reg_overlap_mentioned_p (value, addr); 235 } 236 237 /* Return nonzero if the CONSUMER instruction (a load) does need 238 a Pmode PRODUCER's value to calculate the address. */ 239 240 int 241 arm_early_load_addr_dep_ptr (rtx producer, rtx consumer) 242 { 243 rtx value = arm_find_sub_rtx_with_code (PATTERN (producer), SET, false); 244 rtx addr = arm_find_sub_rtx_with_code (PATTERN (consumer), SET, false); 245 246 if (!value || !addr || !MEM_P (SET_SRC (value))) 247 return 0; 248 249 value = SET_DEST (value); 250 addr = SET_SRC (addr); 251 252 return GET_MODE (value) == Pmode && reg_overlap_mentioned_p (value, addr); 253 } 254 255 /* Return nonzero if the CONSUMER instruction (an ALU op) does not 256 have an early register shift value or amount dependency on the 257 result of PRODUCER. */ 258 int 259 arm_no_early_alu_shift_dep (rtx producer, rtx consumer) 260 { 261 rtx value, op; 262 rtx early_op; 263 264 if (!arm_get_set_operands (producer, consumer, &value, &op)) 265 return 0; 266 267 if ((early_op = arm_find_shift_sub_rtx (op))) 268 return !reg_overlap_mentioned_p (value, early_op); 269 270 return 0; 271 } 272 273 /* Return nonzero if the CONSUMER instruction (an ALU op) does not 274 have an early register shift value dependency on the result of 275 PRODUCER. */ 276 int 277 arm_no_early_alu_shift_value_dep (rtx producer, rtx consumer) 278 { 279 rtx value, op; 280 rtx early_op; 281 282 if (!arm_get_set_operands (producer, consumer, &value, &op)) 283 return 0; 284 285 if ((early_op = arm_find_shift_sub_rtx (op))) 286 /* We want to check the value being shifted. */ 287 if (!reg_overlap_mentioned_p (value, XEXP (early_op, 0))) 288 return 1; 289 290 return 0; 291 } 292 293 /* Return nonzero if the CONSUMER (a mul or mac op) does not 294 have an early register mult dependency on the result of 295 PRODUCER. */ 296 int 297 arm_no_early_mul_dep (rtx producer, rtx consumer) 298 { 299 rtx value, op; 300 301 if (!arm_get_set_operands (producer, consumer, &value, &op)) 302 return 0; 303 304 if (GET_CODE (op) == PLUS || GET_CODE (op) == MINUS) 305 { 306 if (GET_CODE (XEXP (op, 0)) == MULT) 307 return !reg_overlap_mentioned_p (value, XEXP (op, 0)); 308 else 309 return !reg_overlap_mentioned_p (value, XEXP (op, 1)); 310 } 311 312 return 0; 313 } 314 315 /* Return nonzero if the CONSUMER instruction (a store) does not need 316 PRODUCER's value to calculate the address. */ 317 318 int 319 arm_no_early_store_addr_dep (rtx producer, rtx consumer) 320 { 321 rtx value = arm_find_sub_rtx_with_code (PATTERN (producer), SET, false); 322 rtx addr = arm_find_sub_rtx_with_code (PATTERN (consumer), SET, false); 323 324 if (value) 325 value = SET_DEST (value); 326 327 if (addr) 328 addr = SET_DEST (addr); 329 330 if (!value || !addr) 331 return 0; 332 333 return !reg_overlap_mentioned_p (value, addr); 334 } 335 336 /* Return nonzero if the CONSUMER instruction (a store) does need 337 PRODUCER's value to calculate the address. */ 338 339 int 340 arm_early_store_addr_dep (rtx producer, rtx consumer) 341 { 342 return !arm_no_early_store_addr_dep (producer, consumer); 343 } 344 345 /* Return nonzero if the CONSUMER instruction (a store) does need 346 a Pmode PRODUCER's value to calculate the address. */ 347 348 int 349 arm_early_store_addr_dep_ptr (rtx producer, rtx consumer) 350 { 351 rtx value = arm_find_sub_rtx_with_code (PATTERN (producer), SET, false); 352 rtx addr = arm_find_sub_rtx_with_code (PATTERN (consumer), SET, false); 353 354 if (!value || !addr || !MEM_P (SET_SRC (value))) 355 return 0; 356 357 value = SET_DEST (value); 358 addr = SET_DEST (addr); 359 360 return GET_MODE (value) == Pmode && reg_overlap_mentioned_p (value, addr); 361 } 362 363 /* Return non-zero iff the consumer (a multiply-accumulate or a 364 multiple-subtract instruction) has an accumulator dependency on the 365 result of the producer and no other dependency on that result. It 366 does not check if the producer is multiply-accumulate instruction. */ 367 int 368 arm_mac_accumulator_is_result (rtx producer, rtx consumer) 369 { 370 rtx result; 371 rtx op0, op1, acc; 372 373 producer = PATTERN (producer); 374 consumer = PATTERN (consumer); 375 376 if (GET_CODE (producer) == COND_EXEC) 377 producer = COND_EXEC_CODE (producer); 378 if (GET_CODE (consumer) == COND_EXEC) 379 consumer = COND_EXEC_CODE (consumer); 380 381 if (GET_CODE (producer) != SET) 382 return 0; 383 384 result = XEXP (producer, 0); 385 386 if (GET_CODE (consumer) != SET) 387 return 0; 388 389 /* Check that the consumer is of the form 390 (set (...) (plus (mult ...) (...))) 391 or 392 (set (...) (minus (...) (mult ...))). */ 393 if (GET_CODE (XEXP (consumer, 1)) == PLUS) 394 { 395 if (GET_CODE (XEXP (XEXP (consumer, 1), 0)) != MULT) 396 return 0; 397 398 op0 = XEXP (XEXP (XEXP (consumer, 1), 0), 0); 399 op1 = XEXP (XEXP (XEXP (consumer, 1), 0), 1); 400 acc = XEXP (XEXP (consumer, 1), 1); 401 } 402 else if (GET_CODE (XEXP (consumer, 1)) == MINUS) 403 { 404 if (GET_CODE (XEXP (XEXP (consumer, 1), 1)) != MULT) 405 return 0; 406 407 op0 = XEXP (XEXP (XEXP (consumer, 1), 1), 0); 408 op1 = XEXP (XEXP (XEXP (consumer, 1), 1), 1); 409 acc = XEXP (XEXP (consumer, 1), 0); 410 } 411 else 412 return 0; 413 414 return (reg_overlap_mentioned_p (result, acc) 415 && !reg_overlap_mentioned_p (result, op0) 416 && !reg_overlap_mentioned_p (result, op1)); 417 } 418 419 /* Return non-zero if the destination of PRODUCER feeds the accumulator 420 operand of an MLA-like operation. */ 421 422 int 423 aarch_accumulator_forwarding (rtx_insn *producer, rtx_insn *consumer) 424 { 425 rtx producer_set = single_set (producer); 426 rtx consumer_set = single_set (consumer); 427 428 /* We are looking for a SET feeding a SET. */ 429 if (!producer_set || !consumer_set) 430 return 0; 431 432 rtx dest = SET_DEST (producer_set); 433 rtx mla = SET_SRC (consumer_set); 434 435 /* We're looking for a register SET. */ 436 if (!REG_P (dest)) 437 return 0; 438 439 rtx accumulator; 440 441 /* Strip a zero_extend. */ 442 if (GET_CODE (mla) == ZERO_EXTEND) 443 mla = XEXP (mla, 0); 444 445 switch (GET_CODE (mla)) 446 { 447 case PLUS: 448 /* Possibly an MADD. */ 449 if (GET_CODE (XEXP (mla, 0)) == MULT) 450 accumulator = XEXP (mla, 1); 451 else 452 return 0; 453 break; 454 case MINUS: 455 /* Possibly an MSUB. */ 456 if (GET_CODE (XEXP (mla, 1)) == MULT) 457 accumulator = XEXP (mla, 0); 458 else 459 return 0; 460 break; 461 case FMA: 462 { 463 /* Possibly an FMADD/FMSUB/FNMADD/FNMSUB. */ 464 if (REG_P (XEXP (mla, 1)) 465 && REG_P (XEXP (mla, 2)) 466 && (REG_P (XEXP (mla, 0)) 467 || GET_CODE (XEXP (mla, 0)) == NEG)) 468 469 { 470 /* FMADD/FMSUB. */ 471 accumulator = XEXP (mla, 2); 472 } 473 else if (REG_P (XEXP (mla, 1)) 474 && GET_CODE (XEXP (mla, 2)) == NEG 475 && (REG_P (XEXP (mla, 0)) 476 || GET_CODE (XEXP (mla, 0)) == NEG)) 477 { 478 /* FNMADD/FNMSUB. */ 479 accumulator = XEXP (XEXP (mla, 2), 0); 480 } 481 else 482 return 0; 483 break; 484 } 485 default: 486 /* Not an MLA-like operation. */ 487 return 0; 488 } 489 490 if (SUBREG_P (accumulator)) 491 accumulator = SUBREG_REG (accumulator); 492 493 if (!REG_P (accumulator)) 494 return 0; 495 496 return (REGNO (dest) == REGNO (accumulator)); 497 } 498 499 /* Return non-zero if the consumer (a multiply-accumulate instruction) 500 has an accumulator dependency on the result of the producer (a 501 multiplication instruction) and no other dependency on that result. */ 502 int 503 arm_mac_accumulator_is_mul_result (rtx producer, rtx consumer) 504 { 505 rtx mul = PATTERN (producer); 506 rtx mac = PATTERN (consumer); 507 rtx mul_result; 508 rtx mac_op0, mac_op1, mac_acc; 509 510 if (GET_CODE (mul) == COND_EXEC) 511 mul = COND_EXEC_CODE (mul); 512 if (GET_CODE (mac) == COND_EXEC) 513 mac = COND_EXEC_CODE (mac); 514 515 /* Check that mul is of the form (set (...) (mult ...)) 516 and mla is of the form (set (...) (plus (mult ...) (...))). */ 517 if ((GET_CODE (mul) != SET || GET_CODE (XEXP (mul, 1)) != MULT) 518 || (GET_CODE (mac) != SET || GET_CODE (XEXP (mac, 1)) != PLUS 519 || GET_CODE (XEXP (XEXP (mac, 1), 0)) != MULT)) 520 return 0; 521 522 mul_result = XEXP (mul, 0); 523 mac_op0 = XEXP (XEXP (XEXP (mac, 1), 0), 0); 524 mac_op1 = XEXP (XEXP (XEXP (mac, 1), 0), 1); 525 mac_acc = XEXP (XEXP (mac, 1), 1); 526 527 return (reg_overlap_mentioned_p (mul_result, mac_acc) 528 && !reg_overlap_mentioned_p (mul_result, mac_op0) 529 && !reg_overlap_mentioned_p (mul_result, mac_op1)); 530 } 531 532 /* Worker function for TARGET_MD_ASM_ADJUST. 533 We implement asm flag outputs. */ 534 535 rtx_insn * 536 arm_md_asm_adjust (vec<rtx> &outputs, vec<rtx> & /*inputs*/, 537 vec<machine_mode> & /*input_modes*/, 538 vec<const char *> &constraints, 539 vec<rtx> & /*uses*/, vec<rtx> & /*clobbers*/, 540 HARD_REG_SET & /*clobbered_regs*/, location_t loc) 541 { 542 bool saw_asm_flag = false; 543 544 start_sequence (); 545 for (unsigned i = 0, n = outputs.length (); i < n; ++i) 546 { 547 const char *con = constraints[i]; 548 if (!startswith (con, "=@cc")) 549 continue; 550 con += 4; 551 if (strchr (con, ',') != NULL) 552 { 553 error_at (loc, "alternatives not allowed in %<asm%> flag output"); 554 continue; 555 } 556 557 machine_mode mode; 558 rtx_code code; 559 int con01 = 0; 560 561 #define C(X, Y) (unsigned char)(X) * 256 + (unsigned char)(Y) 562 563 /* All of the condition codes are two characters. */ 564 if (con[0] != 0 && con[1] != 0 && con[2] == 0) 565 con01 = C(con[0], con[1]); 566 567 switch (con01) 568 { 569 case C('c', 'c'): 570 case C('l', 'o'): 571 mode = CC_Cmode, code = GEU; 572 break; 573 case C('c', 's'): 574 case C('h', 's'): 575 mode = CC_Cmode, code = LTU; 576 break; 577 case C('e', 'q'): 578 mode = CC_NZmode, code = EQ; 579 break; 580 case C('g', 'e'): 581 mode = CCmode, code = GE; 582 break; 583 case C('g', 't'): 584 mode = CCmode, code = GT; 585 break; 586 case C('h', 'i'): 587 mode = CCmode, code = GTU; 588 break; 589 case C('l', 'e'): 590 mode = CCmode, code = LE; 591 break; 592 case C('l', 's'): 593 mode = CCmode, code = LEU; 594 break; 595 case C('l', 't'): 596 mode = CCmode, code = LT; 597 break; 598 case C('m', 'i'): 599 mode = CC_NZmode, code = LT; 600 break; 601 case C('n', 'e'): 602 mode = CC_NZmode, code = NE; 603 break; 604 case C('p', 'l'): 605 mode = CC_NZmode, code = GE; 606 break; 607 case C('v', 'c'): 608 mode = CC_Vmode, code = EQ; 609 break; 610 case C('v', 's'): 611 mode = CC_Vmode, code = NE; 612 break; 613 default: 614 error_at (loc, "unknown %<asm%> flag output %qs", constraints[i]); 615 continue; 616 } 617 618 #undef C 619 620 rtx dest = outputs[i]; 621 machine_mode dest_mode = GET_MODE (dest); 622 if (!SCALAR_INT_MODE_P (dest_mode)) 623 { 624 error_at (loc, "invalid type for %<asm%> flag output"); 625 continue; 626 } 627 628 if (!saw_asm_flag) 629 { 630 /* This is the first asm flag output. Here we put the flags 631 register in as the real output and adjust the condition to 632 allow it. */ 633 constraints[i] = "=c"; 634 outputs[i] = gen_rtx_REG (CCmode, CC_REGNUM); 635 saw_asm_flag = true; 636 } 637 else 638 { 639 /* We don't need the flags register as output twice. */ 640 constraints[i] = "=X"; 641 outputs[i] = gen_rtx_SCRATCH (word_mode); 642 } 643 644 rtx x = gen_rtx_REG (mode, CC_REGNUM); 645 x = gen_rtx_fmt_ee (code, word_mode, x, const0_rtx); 646 647 if (dest_mode == word_mode && REG_P (dest)) 648 emit_insn (gen_rtx_SET (dest, x)); 649 else 650 { 651 rtx tmp = gen_reg_rtx (word_mode); 652 emit_insn (gen_rtx_SET (tmp, x)); 653 654 tmp = convert_modes (dest_mode, word_mode, tmp, true); 655 emit_move_insn (dest, tmp); 656 } 657 } 658 rtx_insn *seq = get_insns (); 659 end_sequence (); 660 661 return saw_asm_flag ? seq : NULL; 662 } 663 664 /* In-place split *str at delim, return *str and set *str to the tail 665 of the string or NULL if the end is reached. */ 666 667 static char * 668 next_tok (char **str, int delim) 669 { 670 char *tok = *str; 671 for (char *p = tok; p && *p != '\0'; p++) 672 { 673 if (*p == delim) 674 { 675 *p = '\0'; 676 *str = p + 1; 677 return tok; 678 } 679 } 680 *str = NULL; 681 return tok; 682 } 683 684 /* Parses CONST_STR according to branch protection features specified in 685 TYPES. The first type resets the settings, the last type is marked with 686 name == NULL. On failure an error message is printed referencing OPT as 687 the source of the options. Returns true on success. */ 688 689 bool 690 aarch_validate_mbranch_protection ( 691 const struct aarch_branch_protect_type *types, const char *const_str, 692 const char *opt) 693 { 694 char *str_root = xstrdup (const_str); 695 char *next_str = str_root; 696 char *str = next_tok (&next_str, '+'); 697 char *alone_str = NULL; 698 bool reject_alone = false; 699 bool res = true; 700 701 /* First entry is "none" and it is used to reset the state. */ 702 types->handler (); 703 704 while (str) 705 { 706 const aarch_branch_protect_type *type = types; 707 for (; type->name; type++) 708 if (strcmp (str, type->name) == 0) 709 break; 710 if (type->name == NULL) 711 { 712 res = false; 713 if (strcmp (str, "") == 0) 714 error ("missing feature or flag for %<%s%>", opt); 715 else 716 error ("invalid argument %<%s%> for %<%s%>", str, opt); 717 break; 718 } 719 720 if (type->alone && alone_str == NULL) 721 alone_str = str; 722 else 723 reject_alone = true; 724 if (reject_alone && alone_str != NULL) 725 { 726 res = false; 727 error ("argument %<%s%> can only appear alone in %<%s%>", 728 alone_str, opt); 729 break; 730 } 731 732 type->handler (); 733 str = next_tok (&next_str, '+'); 734 if (type->subtypes == NULL) 735 continue; 736 737 /* Loop through tokens until we find one that isn't a subtype. */ 738 while (str) 739 { 740 const aarch_branch_protect_type *subtype = type->subtypes; 741 for (; subtype->name; subtype++) 742 if (strcmp (str, subtype->name) == 0) 743 break; 744 if (subtype->name == NULL) 745 break; 746 747 subtype->handler (); 748 str = next_tok (&next_str, '+'); 749 } 750 } 751 752 free (str_root); 753 return res; 754 } 755