1 /* Cost model implementation for RISC-V 'V' Extension for GNU compiler. 2 Copyright (C) 2023-2024 Free Software Foundation, Inc. 3 Contributed by Juzhe Zhong (juzhe.zhong (at) rivai.ai), RiVAI Technologies Ltd. 4 5 This file is part of GCC. 6 7 GCC 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 3, or (at your option) 10 any later version. 11 12 GCC 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 GCC; see the file COPYING3. If not see 19 <http://www.gnu.org/licenses/>. */ 20 21 #define IN_TARGET_CODE 1 22 23 #define INCLUDE_STRING 24 #include "config.h" 25 #include "system.h" 26 #include "coretypes.h" 27 #include "tm.h" 28 #include "target.h" 29 #include "function.h" 30 #include "tree.h" 31 #include "basic-block.h" 32 #include "rtl.h" 33 #include "gimple.h" 34 #include "targhooks.h" 35 #include "cfgloop.h" 36 #include "fold-const.h" 37 #include "tm_p.h" 38 #include "tree-vectorizer.h" 39 #include "gimple-iterator.h" 40 #include "bitmap.h" 41 #include "ssa.h" 42 #include "backend.h" 43 #include "tree-data-ref.h" 44 #include "tree-ssa-loop-niter.h" 45 #include "tree-hash-traits.h" 46 47 /* This file should be included last. */ 48 #include "riscv-vector-costs.h" 49 50 namespace riscv_vector { 51 52 /* Dynamic LMUL philosophy - Local linear-scan SSA live range based analysis 53 determine LMUL 54 55 - Collect all vectorize STMTs locally for each loop block. 56 - Build program point based graph, ignore non-vectorize STMTs: 57 58 vectorize STMT 0 - point 0 59 scalar STMT 0 - ignore. 60 vectorize STMT 1 - point 1 61 ... 62 - Compute the number of live V_REGs live at each program point 63 - Determine LMUL in VECTOR COST model according to the program point 64 which has maximum live V_REGs. 65 66 Note: 67 68 - BIGGEST_MODE is the biggest LMUL auto-vectorization element mode. 69 It's important for mixed size auto-vectorization (Conversions, ... etc). 70 E.g. For a loop that is vectorizing conversion of INT32 -> INT64. 71 The biggest mode is DImode and LMUL = 8, LMUL = 4 for SImode. 72 We compute the number live V_REGs at each program point according to 73 this information. 74 - We only compute program points and live ranges locally (within a block) 75 since we just need to compute the number of live V_REGs at each program 76 point and we are not really allocating the registers for each SSA. 77 We can make the variable has another local live range in another block 78 if it live out/live in to another block. Such approach doesn't affect 79 out accurate live range analysis. 80 - Current analysis didn't consider any instruction scheduling which 81 may improve the register pressure. So we are conservatively doing the 82 analysis which may end up with smaller LMUL. 83 TODO: Maybe we could support a reasonable live range shrink algorithm 84 which take advantage of instruction scheduling. 85 - We may have these following possible autovec modes analysis: 86 87 1. M8 -> M4 -> M2 -> M1 (stop analysis here) -> MF2 -> MF4 -> MF8 88 2. M8 -> M1(M4) -> MF2(M2) -> MF4(M1) (stop analysis here) -> MF8(MF2) 89 3. M1(M8) -> MF2(M4) -> MF4(M2) -> MF8(M1) 90 */ 91 92 static bool 93 is_gimple_assign_or_call (gimple *stmt) 94 { 95 return is_gimple_assign (stmt) || is_gimple_call (stmt); 96 } 97 98 /* Return the program point of 1st vectorized lanes statement. */ 99 static unsigned int 100 get_first_lane_point (const vec<stmt_point> program_points, 101 stmt_vec_info stmt_info) 102 { 103 for (const auto program_point : program_points) 104 if (program_point.stmt_info == DR_GROUP_FIRST_ELEMENT (stmt_info)) 105 return program_point.point; 106 return 0; 107 } 108 109 /* Return the program point of last vectorized lanes statement. */ 110 static unsigned int 111 get_last_lane_point (const vec<stmt_point> program_points, 112 stmt_vec_info stmt_info) 113 { 114 unsigned int max_point = 0; 115 for (auto s = DR_GROUP_FIRST_ELEMENT (stmt_info); s != NULL; 116 s = DR_GROUP_NEXT_ELEMENT (s)) 117 { 118 for (const auto program_point : program_points) 119 if (program_point.stmt_info == s && program_point.point > max_point) 120 max_point = program_point.point; 121 } 122 return max_point; 123 } 124 125 /* Return the last variable that is in the live range list. */ 126 static pair * 127 get_live_range (hash_map<tree, pair> *live_ranges, tree arg) 128 { 129 auto *r = live_ranges->get (arg); 130 if (r) 131 return r; 132 else 133 { 134 tree t = arg; 135 gimple *def_stmt = NULL; 136 while (t && TREE_CODE (t) == SSA_NAME && !r 137 && (def_stmt = SSA_NAME_DEF_STMT (t))) 138 { 139 if (gimple_assign_cast_p (def_stmt)) 140 { 141 t = gimple_assign_rhs1 (def_stmt); 142 r = live_ranges->get (t); 143 def_stmt = NULL; 144 } 145 else 146 /* FIXME: Currently we don't see any fold for 147 non-conversion statements. */ 148 t = NULL_TREE; 149 } 150 if (r) 151 return r; 152 else 153 { 154 bool insert_p = live_ranges->put (arg, pair (0, 0)); 155 gcc_assert (!insert_p); 156 return live_ranges->get (arg); 157 } 158 } 159 } 160 161 /* Collect all STMTs that are vectorized and compute their program points. 162 Note that we don't care about the STMTs that are not vectorized and 163 we only build the local graph (within a block) of program points. 164 165 Loop: 166 bb 2: 167 STMT 1 (be vectorized) -- point 0 168 STMT 2 (not be vectorized) -- ignored 169 STMT 3 (be vectorized) -- point 1 170 STMT 4 (be vectorized) -- point 2 171 STMT 5 (be vectorized) -- point 3 172 ... 173 bb 3: 174 STMT 1 (be vectorized) -- point 0 175 STMT 2 (be vectorized) -- point 1 176 STMT 3 (not be vectorized) -- ignored 177 STMT 4 (not be vectorized) -- ignored 178 STMT 5 (be vectorized) -- point 2 179 ... 180 */ 181 static void 182 compute_local_program_points ( 183 vec_info *vinfo, 184 hash_map<basic_block, vec<stmt_point>> &program_points_per_bb) 185 { 186 if (loop_vec_info loop_vinfo = dyn_cast<loop_vec_info> (vinfo)) 187 { 188 class loop *loop = LOOP_VINFO_LOOP (loop_vinfo); 189 basic_block *bbs = LOOP_VINFO_BBS (loop_vinfo); 190 unsigned int nbbs = loop->num_nodes; 191 gimple_stmt_iterator si; 192 unsigned int i; 193 /* Collect the stmts that is vectorized and mark their program point. */ 194 for (i = 0; i < nbbs; i++) 195 { 196 int point = 1; 197 basic_block bb = bbs[i]; 198 vec<stmt_point> program_points = vNULL; 199 if (dump_enabled_p ()) 200 dump_printf_loc (MSG_NOTE, vect_location, 201 "Compute local program points for bb %d:\n", 202 bb->index); 203 for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si)) 204 { 205 if (!is_gimple_assign_or_call (gsi_stmt (si))) 206 continue; 207 stmt_vec_info stmt_info = vinfo->lookup_stmt (gsi_stmt (si)); 208 enum stmt_vec_info_type type 209 = STMT_VINFO_TYPE (vect_stmt_to_vectorize (stmt_info)); 210 if (type != undef_vec_info_type) 211 { 212 stmt_point info = {point, gsi_stmt (si), stmt_info}; 213 program_points.safe_push (info); 214 point++; 215 if (dump_enabled_p ()) 216 dump_printf_loc (MSG_NOTE, vect_location, 217 "program point %d: %G", info.point, 218 gsi_stmt (si)); 219 } 220 } 221 program_points_per_bb.put (bb, program_points); 222 } 223 } 224 } 225 226 static machine_mode 227 get_biggest_mode (machine_mode mode1, machine_mode mode2) 228 { 229 unsigned int mode1_size = GET_MODE_BITSIZE (mode1).to_constant (); 230 unsigned int mode2_size = GET_MODE_BITSIZE (mode2).to_constant (); 231 return mode1_size >= mode2_size ? mode1 : mode2; 232 } 233 234 /* Return true if OP is invariant. */ 235 236 static bool 237 loop_invariant_op_p (class loop *loop, 238 tree op) 239 { 240 if (is_gimple_constant (op)) 241 return true; 242 if (SSA_NAME_IS_DEFAULT_DEF (op) 243 || !flow_bb_inside_loop_p (loop, gimple_bb (SSA_NAME_DEF_STMT (op)))) 244 return true; 245 return false; 246 } 247 248 /* Return true if the variable should be counted into liveness. */ 249 static bool 250 variable_vectorized_p (class loop *loop, stmt_vec_info stmt_info, tree var, 251 bool lhs_p) 252 { 253 if (!var) 254 return false; 255 gimple *stmt = STMT_VINFO_STMT (stmt_info); 256 enum stmt_vec_info_type type 257 = STMT_VINFO_TYPE (vect_stmt_to_vectorize (stmt_info)); 258 if (is_gimple_call (stmt) && gimple_call_internal_p (stmt)) 259 { 260 if (gimple_call_internal_fn (stmt) == IFN_MASK_STORE 261 || gimple_call_internal_fn (stmt) == IFN_MASK_LOAD) 262 { 263 /* .MASK_LOAD (_5, 32B, _33) 264 ^ ^ ^ 265 Only the 3rd argument will be vectorized and consume 266 a vector register. */ 267 if (TREE_CODE (TREE_TYPE (var)) == BOOLEAN_TYPE 268 || (is_gimple_reg (var) && !POINTER_TYPE_P (TREE_TYPE (var)))) 269 return true; 270 else 271 return false; 272 } 273 } 274 else if (is_gimple_assign (stmt)) 275 { 276 tree_code tcode = gimple_assign_rhs_code (stmt); 277 /* vi variant doesn't need to allocate such statement. 278 E.g. tmp_15 = _4 + 1; will be transformed into vadd.vi 279 so the INTEGER_CST '1' doesn't need a vector register. */ 280 switch (tcode) 281 { 282 case PLUS_EXPR: 283 case BIT_IOR_EXPR: 284 case BIT_XOR_EXPR: 285 case BIT_AND_EXPR: 286 return TREE_CODE (var) != INTEGER_CST 287 || !tree_fits_shwi_p (var) 288 || !IN_RANGE (tree_to_shwi (var), -16, 15); 289 case MINUS_EXPR: 290 return TREE_CODE (var) != INTEGER_CST 291 || !tree_fits_shwi_p (var) 292 || !IN_RANGE (tree_to_shwi (var), -16, 15) 293 || gimple_assign_rhs1 (stmt) != var; 294 case LSHIFT_EXPR: 295 case RSHIFT_EXPR: 296 return gimple_assign_rhs2 (stmt) != var 297 || !loop_invariant_op_p (loop, var); 298 default: 299 break; 300 } 301 } 302 303 if (lhs_p) 304 return is_gimple_reg (var) 305 && (!POINTER_TYPE_P (TREE_TYPE (var)) 306 || type != store_vec_info_type); 307 else 308 return poly_int_tree_p (var) 309 || (is_gimple_val (var) 310 && (!POINTER_TYPE_P (TREE_TYPE (var)) 311 || type != load_vec_info_type)); 312 } 313 314 /* Compute local live ranges of each vectorized variable. 315 Note that we only compute local live ranges (within a block) since 316 local live ranges information is accurate enough for us to determine 317 the LMUL/vectorization factor of the loop. 318 319 Loop: 320 bb 2: 321 STMT 1 -- point 0 322 STMT 2 (def SSA 1) -- point 1 323 STMT 3 (use SSA 1) -- point 2 324 STMT 4 -- point 3 325 bb 3: 326 STMT 1 -- point 0 327 STMT 2 -- point 1 328 STMT 3 -- point 2 329 STMT 4 (use SSA 2) -- point 3 330 331 The live range of SSA 1 is [1, 3] in bb 2. 332 The live range of SSA 2 is [0, 4] in bb 3. */ 333 static machine_mode 334 compute_local_live_ranges ( 335 loop_vec_info loop_vinfo, 336 const hash_map<basic_block, vec<stmt_point>> &program_points_per_bb, 337 hash_map<basic_block, hash_map<tree, pair>> &live_ranges_per_bb) 338 { 339 machine_mode biggest_mode = QImode; 340 class loop *loop = LOOP_VINFO_LOOP (loop_vinfo); 341 if (!program_points_per_bb.is_empty ()) 342 { 343 auto_vec<tree> visited_vars; 344 unsigned int i; 345 for (hash_map<basic_block, vec<stmt_point>>::iterator iter 346 = program_points_per_bb.begin (); 347 iter != program_points_per_bb.end (); ++iter) 348 { 349 basic_block bb = (*iter).first; 350 vec<stmt_point> program_points = (*iter).second; 351 bool existed_p = false; 352 hash_map<tree, pair> *live_ranges 353 = &live_ranges_per_bb.get_or_insert (bb, &existed_p); 354 gcc_assert (!existed_p); 355 if (dump_enabled_p ()) 356 dump_printf_loc (MSG_NOTE, vect_location, 357 "Compute local live ranges for bb %d:\n", 358 bb->index); 359 for (const auto program_point : program_points) 360 { 361 unsigned int point = program_point.point; 362 gimple *stmt = program_point.stmt; 363 tree lhs = gimple_get_lhs (stmt); 364 if (variable_vectorized_p (loop, program_point.stmt_info, lhs, 365 true)) 366 { 367 biggest_mode = get_biggest_mode (biggest_mode, 368 TYPE_MODE (TREE_TYPE (lhs))); 369 bool existed_p = false; 370 pair &live_range 371 = live_ranges->get_or_insert (lhs, &existed_p); 372 gcc_assert (!existed_p); 373 if (STMT_VINFO_MEMORY_ACCESS_TYPE (program_point.stmt_info) 374 == VMAT_LOAD_STORE_LANES) 375 point = get_first_lane_point (program_points, 376 program_point.stmt_info); 377 live_range = pair (point, point); 378 } 379 for (i = 0; i < gimple_num_args (stmt); i++) 380 { 381 tree var = gimple_arg (stmt, i); 382 if (variable_vectorized_p (loop, program_point.stmt_info, var, 383 false)) 384 { 385 biggest_mode 386 = get_biggest_mode (biggest_mode, 387 TYPE_MODE (TREE_TYPE (var))); 388 bool existed_p = false; 389 pair &live_range 390 = live_ranges->get_or_insert (var, &existed_p); 391 if (STMT_VINFO_MEMORY_ACCESS_TYPE ( 392 program_point.stmt_info) 393 == VMAT_LOAD_STORE_LANES) 394 point = get_last_lane_point (program_points, 395 program_point.stmt_info); 396 else if (existed_p) 397 point = MAX (live_range.second, point); 398 if (existed_p) 399 /* We will grow the live range for each use. */ 400 live_range = pair (live_range.first, point); 401 else 402 { 403 gimple *def_stmt; 404 if (TREE_CODE (var) == SSA_NAME 405 && (def_stmt = SSA_NAME_DEF_STMT (var)) 406 && gimple_bb (def_stmt) == bb 407 && is_gimple_assign_or_call (def_stmt)) 408 { 409 live_ranges->remove (var); 410 for (unsigned int j = 0; 411 j < gimple_num_args (def_stmt); j++) 412 { 413 tree arg = gimple_arg (def_stmt, j); 414 auto *r = get_live_range (live_ranges, arg); 415 gcc_assert (r); 416 (*r).second = MAX (point, (*r).second); 417 biggest_mode = get_biggest_mode ( 418 biggest_mode, TYPE_MODE (TREE_TYPE (arg))); 419 } 420 } 421 else 422 /* The splat vector lives the whole block. */ 423 live_range = pair (0, program_points.length ()); 424 } 425 } 426 } 427 } 428 if (dump_enabled_p ()) 429 for (hash_map<tree, pair>::iterator iter = live_ranges->begin (); 430 iter != live_ranges->end (); ++iter) 431 dump_printf_loc (MSG_NOTE, vect_location, 432 "%T: type = %T, start = %d, end = %d\n", 433 (*iter).first, TREE_TYPE ((*iter).first), 434 (*iter).second.first, (*iter).second.second); 435 } 436 } 437 if (dump_enabled_p ()) 438 dump_printf_loc (MSG_NOTE, vect_location, "Biggest mode = %s\n", 439 GET_MODE_NAME (biggest_mode)); 440 return biggest_mode; 441 } 442 443 /* Compute the mode for MODE, BIGGEST_MODE and LMUL. 444 445 E.g. If mode = SImode, biggest_mode = DImode, LMUL = M4. 446 Then return RVVM4SImode (LMUL = 4, element mode = SImode). */ 447 static unsigned int 448 compute_nregs_for_mode (loop_vec_info loop_vinfo, machine_mode mode, 449 machine_mode biggest_mode, int lmul) 450 { 451 unsigned int rgroup_size = LOOP_VINFO_LENS (loop_vinfo).is_empty () 452 ? 1 453 : LOOP_VINFO_LENS (loop_vinfo).length (); 454 unsigned int mode_size = GET_MODE_SIZE (mode).to_constant (); 455 unsigned int biggest_size = GET_MODE_SIZE (biggest_mode).to_constant (); 456 gcc_assert (biggest_size >= mode_size); 457 unsigned int ratio = biggest_size / mode_size; 458 /* RVV mask bool modes always consume 1 vector register regardless LMUL. */ 459 unsigned int nregs = mode == BImode ? 1 : lmul / ratio; 460 return MAX (nregs, 1) * rgroup_size; 461 } 462 463 /* This function helps to determine whether current LMUL will cause 464 potential vector register (V_REG) spillings according to live range 465 information. 466 467 - First, compute how many variable are alive of each program point 468 in each bb of the loop. 469 - Second, compute how many V_REGs are alive of each program point 470 in each bb of the loop according the BIGGEST_MODE and the variable 471 mode. 472 - Third, Return the maximum V_REGs are alive of the loop. */ 473 static unsigned int 474 max_number_of_live_regs (loop_vec_info loop_vinfo, const basic_block bb, 475 const hash_map<tree, pair> &live_ranges, 476 unsigned int max_point, machine_mode biggest_mode, 477 int lmul) 478 { 479 unsigned int max_nregs = 0; 480 unsigned int i; 481 unsigned int live_point = 0; 482 auto_vec<unsigned int> live_vars_vec; 483 live_vars_vec.safe_grow_cleared (max_point, true); 484 for (hash_map<tree, pair>::iterator iter = live_ranges.begin (); 485 iter != live_ranges.end (); ++iter) 486 { 487 tree var = (*iter).first; 488 pair live_range = (*iter).second; 489 for (i = live_range.first + 1; i <= live_range.second; i++) 490 { 491 machine_mode mode = TREE_CODE (TREE_TYPE (var)) == BOOLEAN_TYPE 492 ? BImode 493 : TYPE_MODE (TREE_TYPE (var)); 494 unsigned int nregs 495 = compute_nregs_for_mode (loop_vinfo, mode, biggest_mode, lmul); 496 live_vars_vec[i] += nregs; 497 if (live_vars_vec[i] > max_nregs) 498 { 499 max_nregs = live_vars_vec[i]; 500 live_point = i; 501 } 502 } 503 } 504 505 /* Collect user explicit RVV type. */ 506 auto_vec<basic_block> all_preds 507 = get_all_dominated_blocks (CDI_POST_DOMINATORS, bb); 508 tree t; 509 FOR_EACH_SSA_NAME (i, t, cfun) 510 { 511 machine_mode mode = TYPE_MODE (TREE_TYPE (t)); 512 if (!lookup_vector_type_attribute (TREE_TYPE (t)) 513 && !riscv_v_ext_vls_mode_p (mode)) 514 continue; 515 516 gimple *def = SSA_NAME_DEF_STMT (t); 517 if (gimple_bb (def) && !all_preds.contains (gimple_bb (def))) 518 continue; 519 use_operand_p use_p; 520 imm_use_iterator iterator; 521 522 FOR_EACH_IMM_USE_FAST (use_p, iterator, t) 523 { 524 if (!USE_STMT (use_p) || is_gimple_debug (USE_STMT (use_p)) 525 || !dominated_by_p (CDI_POST_DOMINATORS, bb, 526 gimple_bb (USE_STMT (use_p)))) 527 continue; 528 529 int regno_alignment = riscv_get_v_regno_alignment (mode); 530 max_nregs += regno_alignment; 531 if (dump_enabled_p ()) 532 dump_printf_loc ( 533 MSG_NOTE, vect_location, 534 "Explicit used SSA %T, vectype = %T, mode = %s, cause %d " 535 "V_REG live in bb %d at program point %d\n", 536 t, TREE_TYPE (t), GET_MODE_NAME (mode), regno_alignment, 537 bb->index, live_point); 538 break; 539 } 540 } 541 542 if (dump_enabled_p ()) 543 dump_printf_loc ( 544 MSG_NOTE, vect_location, 545 "Maximum lmul = %d, At most %d number of live V_REG at program " 546 "point %d for bb %d\n", 547 lmul, max_nregs, live_point, bb->index); 548 return max_nregs; 549 } 550 551 /* Get STORE value. */ 552 static tree 553 get_store_value (gimple *stmt) 554 { 555 if (is_gimple_call (stmt) && gimple_call_internal_p (stmt)) 556 { 557 if (gimple_call_internal_fn (stmt) == IFN_MASK_STORE) 558 return gimple_call_arg (stmt, 3); 559 else 560 gcc_unreachable (); 561 } 562 else 563 return gimple_assign_rhs1 (stmt); 564 } 565 566 /* Return true if it is non-contiguous load/store. */ 567 static bool 568 non_contiguous_memory_access_p (stmt_vec_info stmt_info) 569 { 570 enum stmt_vec_info_type type 571 = STMT_VINFO_TYPE (vect_stmt_to_vectorize (stmt_info)); 572 return ((type == load_vec_info_type || type == store_vec_info_type) 573 && !adjacent_dr_p (STMT_VINFO_DATA_REF (stmt_info))); 574 } 575 576 /* Return the LMUL of the current analysis. */ 577 static int 578 compute_estimated_lmul (loop_vec_info loop_vinfo, machine_mode mode) 579 { 580 gcc_assert (GET_MODE_BITSIZE (mode).is_constant ()); 581 int regno_alignment = riscv_get_v_regno_alignment (loop_vinfo->vector_mode); 582 if (riscv_v_ext_vls_mode_p (loop_vinfo->vector_mode)) 583 return regno_alignment; 584 else if (known_eq (LOOP_VINFO_SLP_UNROLLING_FACTOR (loop_vinfo), 1U)) 585 { 586 int estimated_vf = vect_vf_for_cost (loop_vinfo); 587 int estimated_lmul = estimated_vf * GET_MODE_BITSIZE (mode).to_constant () 588 / TARGET_MIN_VLEN; 589 if (estimated_lmul > RVV_M8) 590 return regno_alignment; 591 else 592 return estimated_lmul; 593 } 594 else 595 { 596 /* Estimate the VLA SLP LMUL. */ 597 if (regno_alignment > RVV_M1) 598 return regno_alignment; 599 else if (mode != QImode 600 || LOOP_VINFO_SLP_UNROLLING_FACTOR (loop_vinfo).is_constant ()) 601 { 602 int ratio; 603 if (can_div_trunc_p (BYTES_PER_RISCV_VECTOR, 604 GET_MODE_SIZE (loop_vinfo->vector_mode), &ratio)) 605 { 606 if (ratio == 1) 607 return RVV_M4; 608 else if (ratio == 2) 609 return RVV_M2; 610 } 611 } 612 } 613 return 0; 614 } 615 616 /* Update the live ranges according PHI. 617 618 Loop: 619 bb 2: 620 STMT 1 -- point 0 621 STMT 2 (def SSA 1) -- point 1 622 STMT 3 (use SSA 1) -- point 2 623 STMT 4 -- point 3 624 bb 3: 625 SSA 2 = PHI<SSA 1> 626 STMT 1 -- point 0 627 STMT 2 -- point 1 628 STMT 3 (use SSA 2) -- point 2 629 STMT 4 -- point 3 630 631 Before this function, the SSA 1 live range is [2, 3] in bb 2 632 and SSA 2 is [0, 3] in bb 3. 633 634 Then, after this function, we update SSA 1 live range in bb 2 635 into [2, 4] since SSA 1 is live out into bb 3. */ 636 static void 637 update_local_live_ranges ( 638 vec_info *vinfo, 639 hash_map<basic_block, vec<stmt_point>> &program_points_per_bb, 640 hash_map<basic_block, hash_map<tree, pair>> &live_ranges_per_bb, 641 machine_mode *biggest_mode) 642 { 643 loop_vec_info loop_vinfo = dyn_cast<loop_vec_info> (vinfo); 644 if (!loop_vinfo) 645 return; 646 647 class loop *loop = LOOP_VINFO_LOOP (loop_vinfo); 648 basic_block *bbs = LOOP_VINFO_BBS (loop_vinfo); 649 unsigned int nbbs = loop->num_nodes; 650 unsigned int i, j; 651 gphi_iterator psi; 652 gimple_stmt_iterator si; 653 for (i = 0; i < nbbs; i++) 654 { 655 basic_block bb = bbs[i]; 656 if (dump_enabled_p ()) 657 dump_printf_loc (MSG_NOTE, vect_location, 658 "Update local program points for bb %d:\n", 659 bbs[i]->index); 660 for (psi = gsi_start_phis (bb); !gsi_end_p (psi); gsi_next (&psi)) 661 { 662 gphi *phi = psi.phi (); 663 stmt_vec_info stmt_info = vinfo->lookup_stmt (phi); 664 if (STMT_VINFO_TYPE (vect_stmt_to_vectorize (stmt_info)) 665 == undef_vec_info_type) 666 continue; 667 668 for (j = 0; j < gimple_phi_num_args (phi); j++) 669 { 670 edge e = gimple_phi_arg_edge (phi, j); 671 tree def = gimple_phi_arg_def (phi, j); 672 auto *live_ranges = live_ranges_per_bb.get (bb); 673 auto *live_range = live_ranges->get (def); 674 if (poly_int_tree_p (def)) 675 { 676 /* Insert live range of INTEGER_CST or POLY_CST since we will 677 need to allocate a vector register for it. 678 679 E.g. # j_17 = PHI <j_11(9), 0(5)> will be transformed 680 into # vect_vec_iv_.8_24 = PHI <_25(9), { 0, ... }(5)> 681 682 The live range for such value is short which only lives 683 from program point 0 to 1. */ 684 if (live_range) 685 { 686 unsigned int start = (*live_range).first; 687 (*live_range).first = 0; 688 if (dump_enabled_p ()) 689 dump_printf_loc ( 690 MSG_NOTE, vect_location, 691 "Update %T start point from %d to 0:\n", def, start); 692 } 693 else 694 { 695 live_ranges->put (def, pair (0, 1)); 696 auto &program_points = (*program_points_per_bb.get (bb)); 697 if (program_points.is_empty ()) 698 { 699 stmt_point info = {1, phi, stmt_info}; 700 program_points.safe_push (info); 701 } 702 if (dump_enabled_p ()) 703 dump_printf_loc (MSG_NOTE, vect_location, 704 "Add %T start point from 0 to 1:\n", 705 def); 706 } 707 continue; 708 } 709 if (live_range && flow_bb_inside_loop_p (loop, e->src)) 710 { 711 unsigned int start = (*live_range).first; 712 (*live_range).first = 0; 713 if (dump_enabled_p ()) 714 dump_printf_loc (MSG_NOTE, vect_location, 715 "Update %T start point from %d to %d:\n", 716 def, start, (*live_range).first); 717 } 718 live_ranges = live_ranges_per_bb.get (e->src); 719 if (!program_points_per_bb.get (e->src)) 720 continue; 721 unsigned int max_point 722 = (*program_points_per_bb.get (e->src)).length (); 723 live_range = live_ranges->get (def); 724 if (!live_range) 725 continue; 726 727 unsigned int end = (*live_range).second; 728 (*live_range).second = max_point; 729 if (dump_enabled_p ()) 730 dump_printf_loc (MSG_NOTE, vect_location, 731 "Update %T end point from %d to %d:\n", def, 732 end, (*live_range).second); 733 } 734 } 735 for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si)) 736 { 737 if (!is_gimple_assign_or_call (gsi_stmt (si))) 738 continue; 739 stmt_vec_info stmt_info = vinfo->lookup_stmt (gsi_stmt (si)); 740 enum stmt_vec_info_type type 741 = STMT_VINFO_TYPE (vect_stmt_to_vectorize (stmt_info)); 742 if (non_contiguous_memory_access_p (stmt_info) 743 /* LOAD_LANES/STORE_LANES doesn't need a perm indice. */ 744 && STMT_VINFO_MEMORY_ACCESS_TYPE (stmt_info) 745 != VMAT_LOAD_STORE_LANES) 746 { 747 /* For non-adjacent load/store STMT, we will potentially 748 convert it into: 749 750 1. MASK_LEN_GATHER_LOAD (..., perm indice). 751 2. Continguous load/store + VEC_PERM (..., perm indice) 752 753 We will be likely using one more vector variable. */ 754 unsigned int max_point 755 = (*program_points_per_bb.get (bb)).length (); 756 auto *live_ranges = live_ranges_per_bb.get (bb); 757 bool existed_p = false; 758 tree var = type == load_vec_info_type 759 ? gimple_get_lhs (gsi_stmt (si)) 760 : get_store_value (gsi_stmt (si)); 761 tree sel_type = build_nonstandard_integer_type ( 762 TYPE_PRECISION (TREE_TYPE (var)), 1); 763 *biggest_mode 764 = get_biggest_mode (*biggest_mode, TYPE_MODE (sel_type)); 765 tree sel = build_decl (UNKNOWN_LOCATION, VAR_DECL, 766 get_identifier ("vect_perm"), sel_type); 767 pair &live_range = live_ranges->get_or_insert (sel, &existed_p); 768 gcc_assert (!existed_p); 769 live_range = pair (0, max_point); 770 if (dump_enabled_p ()) 771 dump_printf_loc (MSG_NOTE, vect_location, 772 "Add perm indice %T, start = 0, end = %d\n", 773 sel, max_point); 774 if (!LOOP_VINFO_LENS (loop_vinfo).is_empty () 775 && LOOP_VINFO_LENS (loop_vinfo).length () > 1) 776 { 777 /* If we are vectorizing a permutation when the rgroup number 778 > 1, we will need additional mask to shuffle the second 779 vector. */ 780 tree mask = build_decl (UNKNOWN_LOCATION, VAR_DECL, 781 get_identifier ("vect_perm_mask"), 782 boolean_type_node); 783 pair &live_range 784 = live_ranges->get_or_insert (mask, &existed_p); 785 gcc_assert (!existed_p); 786 live_range = pair (0, max_point); 787 if (dump_enabled_p ()) 788 dump_printf_loc (MSG_NOTE, vect_location, 789 "Add perm mask %T, start = 0, end = %d\n", 790 mask, max_point); 791 } 792 } 793 } 794 } 795 } 796 797 /* Compute the maximum live V_REGS. */ 798 static bool 799 has_unexpected_spills_p (loop_vec_info loop_vinfo) 800 { 801 /* Compute local program points. 802 It's a fast and effective computation. */ 803 hash_map<basic_block, vec<stmt_point>> program_points_per_bb; 804 compute_local_program_points (loop_vinfo, program_points_per_bb); 805 806 /* Compute local live ranges. */ 807 hash_map<basic_block, hash_map<tree, pair>> live_ranges_per_bb; 808 machine_mode biggest_mode 809 = compute_local_live_ranges (loop_vinfo, program_points_per_bb, 810 live_ranges_per_bb); 811 812 /* Update live ranges according to PHI. */ 813 update_local_live_ranges (loop_vinfo, program_points_per_bb, 814 live_ranges_per_bb, &biggest_mode); 815 816 int lmul = compute_estimated_lmul (loop_vinfo, biggest_mode); 817 gcc_assert (lmul <= RVV_M8); 818 /* TODO: We calculate the maximum live vars base on current STMTS 819 sequence. We can support live range shrink if it can give us 820 big improvement in the future. */ 821 if (lmul > RVV_M1) 822 { 823 if (!live_ranges_per_bb.is_empty ()) 824 { 825 unsigned int max_nregs = 0; 826 for (hash_map<basic_block, hash_map<tree, pair>>::iterator iter 827 = live_ranges_per_bb.begin (); 828 iter != live_ranges_per_bb.end (); ++iter) 829 { 830 basic_block bb = (*iter).first; 831 unsigned int max_point 832 = (*program_points_per_bb.get (bb)).length () + 1; 833 if ((*iter).second.is_empty ()) 834 continue; 835 /* We prefer larger LMUL unless it causes register spillings. */ 836 unsigned int nregs 837 = max_number_of_live_regs (loop_vinfo, bb, (*iter).second, 838 max_point, biggest_mode, lmul); 839 if (nregs > max_nregs) 840 max_nregs = nregs; 841 } 842 live_ranges_per_bb.empty (); 843 if (max_nregs > V_REG_NUM) 844 return true; 845 } 846 } 847 if (!program_points_per_bb.is_empty ()) 848 { 849 for (hash_map<basic_block, vec<stmt_point>>::iterator iter 850 = program_points_per_bb.begin (); 851 iter != program_points_per_bb.end (); ++iter) 852 { 853 vec<stmt_point> program_points = (*iter).second; 854 if (!program_points.is_empty ()) 855 program_points.release (); 856 } 857 program_points_per_bb.empty (); 858 } 859 return false; 860 } 861 862 costs::costs (vec_info *vinfo, bool costing_for_scalar) 863 : vector_costs (vinfo, costing_for_scalar) 864 { 865 if (costing_for_scalar) 866 m_cost_type = SCALAR_COST; 867 else if (riscv_v_ext_vector_mode_p (vinfo->vector_mode)) 868 m_cost_type = VLA_VECTOR_COST; 869 else 870 m_cost_type = VLS_VECTOR_COST; 871 } 872 873 /* Do one-time initialization of the costs given that we're 874 costing the loop vectorization described by LOOP_VINFO. */ 875 void 876 costs::analyze_loop_vinfo (loop_vec_info loop_vinfo) 877 { 878 /* Detect whether we're vectorizing for VLA and should apply the unrolling 879 heuristic described above m_unrolled_vls_niters. */ 880 record_potential_vls_unrolling (loop_vinfo); 881 882 /* Detect whether the LOOP has unexpected spills. */ 883 record_potential_unexpected_spills (loop_vinfo); 884 } 885 886 /* Analyze the vectorized program stataments and use dynamic LMUL 887 heuristic to detect whether the loop has unexpected spills. */ 888 void 889 costs::record_potential_unexpected_spills (loop_vec_info loop_vinfo) 890 { 891 /* We only want to apply the heuristic if LOOP_VINFO is being 892 vectorized for VLA and known NITERS VLS loop. */ 893 if (rvv_max_lmul == RVV_DYNAMIC 894 && (m_cost_type == VLA_VECTOR_COST 895 || (m_cost_type == VLS_VECTOR_COST 896 && LOOP_VINFO_NITERS_KNOWN_P (loop_vinfo)))) 897 { 898 bool post_dom_available_p = dom_info_available_p (CDI_POST_DOMINATORS); 899 if (!post_dom_available_p) 900 calculate_dominance_info (CDI_POST_DOMINATORS); 901 m_has_unexpected_spills_p = has_unexpected_spills_p (loop_vinfo); 902 if (!post_dom_available_p) 903 free_dominance_info (CDI_POST_DOMINATORS); 904 } 905 } 906 907 /* Decide whether to use the unrolling heuristic described above 908 m_unrolled_vls_niters, updating that field if so. LOOP_VINFO 909 describes the loop that we're vectorizing. */ 910 void 911 costs::record_potential_vls_unrolling (loop_vec_info loop_vinfo) 912 { 913 /* We only want to apply the heuristic if LOOP_VINFO is being 914 vectorized for VLA. */ 915 if (m_cost_type != VLA_VECTOR_COST) 916 return; 917 918 /* We don't want to apply the heuristic to outer loops, since it's 919 harder to track two levels of unrolling. */ 920 if (LOOP_VINFO_LOOP (loop_vinfo)->inner) 921 return; 922 923 /* Only handle cases in which the number of VLS iterations 924 would be known at compile time but the number of SVE iterations 925 would not. */ 926 if (!LOOP_VINFO_NITERS_KNOWN_P (loop_vinfo) 927 || BYTES_PER_RISCV_VECTOR.is_constant ()) 928 return; 929 930 /* Guess how many times the VLS loop would iterate and make 931 sure that it is within the complete unrolling limit. Even if the 932 number of iterations is small enough, the number of statements might 933 not be, which is why we need to estimate the number of statements too. */ 934 unsigned int vls_vf = vect_vf_for_cost (loop_vinfo); 935 unsigned HOST_WIDE_INT unrolled_vls_niters 936 = LOOP_VINFO_INT_NITERS (loop_vinfo) / vls_vf; 937 if (unrolled_vls_niters > (unsigned int) param_max_completely_peel_times) 938 return; 939 940 /* Record that we're applying the heuristic and should try to estimate 941 the number of statements in the VLS loop. */ 942 m_unrolled_vls_niters = unrolled_vls_niters; 943 } 944 945 /* Return true if (a) we're applying the VLS vs. VLA unrolling 946 heuristic described above m_unrolled_vls_niters and (b) the heuristic 947 says that we should prefer the VLS loop. */ 948 bool 949 costs::prefer_unrolled_loop () const 950 { 951 if (!m_unrolled_vls_stmts) 952 return false; 953 954 if (dump_enabled_p ()) 955 dump_printf_loc (MSG_NOTE, vect_location, 956 "Number of insns in" 957 " unrolled VLS loop = " HOST_WIDE_INT_PRINT_UNSIGNED "\n", 958 m_unrolled_vls_stmts); 959 960 /* The balance here is tricky. On the one hand, we can't be sure whether 961 the code is vectorizable with VLS or not. However, even if 962 it isn't vectorizable with VLS, there's a possibility that 963 the scalar code could also be unrolled. Some of the code might then 964 benefit from SLP, or from using LDP and STP. We therefore apply 965 the heuristic regardless of can_use_vls_p. */ 966 return (m_unrolled_vls_stmts 967 && (m_unrolled_vls_stmts 968 <= (unsigned int) param_max_completely_peeled_insns)); 969 } 970 971 bool 972 costs::better_main_loop_than_p (const vector_costs *uncast_other) const 973 { 974 auto other = static_cast<const costs *> (uncast_other); 975 auto this_loop_vinfo = as_a<loop_vec_info> (this->m_vinfo); 976 auto other_loop_vinfo = as_a<loop_vec_info> (other->m_vinfo); 977 978 if (dump_enabled_p ()) 979 dump_printf_loc (MSG_NOTE, vect_location, 980 "Comparing two main loops (%s at VF %d vs %s at VF %d)\n", 981 GET_MODE_NAME (this_loop_vinfo->vector_mode), 982 vect_vf_for_cost (this_loop_vinfo), 983 GET_MODE_NAME (other_loop_vinfo->vector_mode), 984 vect_vf_for_cost (other_loop_vinfo)); 985 986 /* Apply the unrolling heuristic described above m_unrolled_vls_niters. */ 987 if (bool (m_unrolled_vls_stmts) != bool (other->m_unrolled_vls_stmts) 988 && m_cost_type != other->m_cost_type) 989 { 990 bool this_prefer_unrolled = this->prefer_unrolled_loop (); 991 bool other_prefer_unrolled = other->prefer_unrolled_loop (); 992 if (this_prefer_unrolled != other_prefer_unrolled) 993 { 994 if (dump_enabled_p ()) 995 dump_printf_loc (MSG_NOTE, vect_location, 996 "Preferring VLS loop because" 997 " it can be unrolled\n"); 998 return other_prefer_unrolled; 999 } 1000 } 1001 else if (rvv_max_lmul == RVV_DYNAMIC) 1002 { 1003 if (other->m_has_unexpected_spills_p) 1004 { 1005 if (dump_enabled_p ()) 1006 dump_printf_loc (MSG_NOTE, vect_location, 1007 "Preferring smaller LMUL loop because" 1008 " it has unexpected spills\n"); 1009 return true; 1010 } 1011 else if (riscv_v_ext_vector_mode_p (other_loop_vinfo->vector_mode)) 1012 { 1013 if (LOOP_VINFO_NITERS_KNOWN_P (other_loop_vinfo)) 1014 { 1015 if (maybe_gt (LOOP_VINFO_INT_NITERS (this_loop_vinfo), 1016 LOOP_VINFO_VECT_FACTOR (this_loop_vinfo))) 1017 { 1018 if (dump_enabled_p ()) 1019 dump_printf_loc (MSG_NOTE, vect_location, 1020 "Keep current LMUL loop because" 1021 " known NITERS exceed the new VF\n"); 1022 return false; 1023 } 1024 } 1025 else 1026 { 1027 if (dump_enabled_p ()) 1028 dump_printf_loc (MSG_NOTE, vect_location, 1029 "Keep current LMUL loop because" 1030 " it is unknown NITERS\n"); 1031 return false; 1032 } 1033 } 1034 } 1035 /* If NITERS is unknown, we should not use VLS modes to vectorize 1036 the loop since we don't support partial vectors for VLS modes, 1037 that is, we will have full vectors (VLSmodes) on loop body 1038 and partial vectors (VLAmodes) on loop epilogue which is very 1039 inefficient. Instead, we should apply partial vectors (VLAmodes) 1040 on loop body without an epilogue on unknown NITERS loop. */ 1041 else if (!LOOP_VINFO_NITERS_KNOWN_P (this_loop_vinfo) 1042 && m_cost_type == VLS_VECTOR_COST) 1043 return false; 1044 1045 return vector_costs::better_main_loop_than_p (other); 1046 } 1047 1048 /* Adjust vectorization cost after calling riscv_builtin_vectorization_cost. 1049 For some statement, we would like to further fine-grain tweak the cost on 1050 top of riscv_builtin_vectorization_cost handling which doesn't have any 1051 information on statement operation codes etc. */ 1052 1053 unsigned 1054 costs::adjust_stmt_cost (enum vect_cost_for_stmt kind, loop_vec_info loop, 1055 stmt_vec_info stmt_info, 1056 slp_tree, tree vectype, int stmt_cost) 1057 { 1058 const cpu_vector_cost *costs = get_vector_costs (); 1059 switch (kind) 1060 { 1061 case scalar_to_vec: 1062 stmt_cost += (FLOAT_TYPE_P (vectype) ? costs->regmove->FR2VR 1063 : costs->regmove->GR2VR); 1064 break; 1065 case vec_to_scalar: 1066 stmt_cost += (FLOAT_TYPE_P (vectype) ? costs->regmove->VR2FR 1067 : costs->regmove->VR2GR); 1068 break; 1069 case vector_load: 1070 case vector_store: 1071 { 1072 /* Unit-stride vector loads and stores do not have offset addressing 1073 as opposed to scalar loads and stores. 1074 If the address depends on a variable we need an additional 1075 add/sub for each load/store in the worst case. */ 1076 if (stmt_info && stmt_info->stmt) 1077 { 1078 data_reference *dr = STMT_VINFO_DATA_REF (stmt_info); 1079 class loop *father = stmt_info->stmt->bb->loop_father; 1080 if (!loop && father && !father->inner && father->superloops) 1081 { 1082 tree ref; 1083 if (TREE_CODE (dr->ref) != MEM_REF 1084 || !(ref = TREE_OPERAND (dr->ref, 0)) 1085 || TREE_CODE (ref) != SSA_NAME) 1086 break; 1087 1088 if (SSA_NAME_IS_DEFAULT_DEF (ref)) 1089 break; 1090 1091 if (memrefs.contains ({ref, cst0})) 1092 break; 1093 1094 memrefs.add ({ref, cst0}); 1095 1096 /* In case we have not seen REF before and the base address 1097 is a pointer operation try a bit harder. */ 1098 tree base = DR_BASE_ADDRESS (dr); 1099 if (TREE_CODE (base) == POINTER_PLUS_EXPR 1100 || TREE_CODE (base) == POINTER_DIFF_EXPR) 1101 { 1102 /* Deconstruct BASE's first operand. If it is a binary 1103 operation, i.e. a base and an "offset" store this 1104 pair. Only increase the stmt_cost if we haven't seen 1105 it before. */ 1106 tree argp = TREE_OPERAND (base, 1); 1107 typedef std::pair<tree, tree> addr_pair; 1108 addr_pair pair; 1109 if (TREE_CODE_CLASS (TREE_CODE (argp)) == tcc_binary) 1110 { 1111 tree argp0 = tree_strip_nop_conversions 1112 (TREE_OPERAND (argp, 0)); 1113 tree argp1 = TREE_OPERAND (argp, 1); 1114 pair = addr_pair (argp0, argp1); 1115 if (memrefs.contains (pair)) 1116 break; 1117 1118 memrefs.add (pair); 1119 stmt_cost += builtin_vectorization_cost (scalar_stmt, 1120 NULL_TREE, 0); 1121 } 1122 } 1123 } 1124 } 1125 break; 1126 } 1127 1128 default: 1129 break; 1130 } 1131 return stmt_cost; 1132 } 1133 1134 unsigned 1135 costs::add_stmt_cost (int count, vect_cost_for_stmt kind, 1136 stmt_vec_info stmt_info, slp_tree node, tree vectype, 1137 int misalign, vect_cost_model_location where) 1138 { 1139 int stmt_cost 1140 = targetm.vectorize.builtin_vectorization_cost (kind, vectype, misalign); 1141 1142 /* Do one-time initialization based on the vinfo. */ 1143 loop_vec_info loop_vinfo = dyn_cast<loop_vec_info> (m_vinfo); 1144 if (!m_analyzed_vinfo) 1145 { 1146 if (loop_vinfo) 1147 analyze_loop_vinfo (loop_vinfo); 1148 1149 memrefs.empty (); 1150 m_analyzed_vinfo = true; 1151 } 1152 1153 if (stmt_info) 1154 { 1155 /* If we're applying the VLA vs. VLS unrolling heuristic, 1156 estimate the number of statements in the unrolled VLS 1157 loop. For simplicitly, we assume that one iteration of the 1158 VLS loop would need the same number of statements 1159 as one iteration of the VLA loop. */ 1160 if (where == vect_body && m_unrolled_vls_niters) 1161 m_unrolled_vls_stmts += count * m_unrolled_vls_niters; 1162 } 1163 1164 if (vectype) 1165 stmt_cost = adjust_stmt_cost (kind, loop_vinfo, stmt_info, node, vectype, 1166 stmt_cost); 1167 1168 return record_stmt_cost (stmt_info, where, count * stmt_cost); 1169 } 1170 1171 /* For some target specific vectorization cost which can't be handled per stmt, 1172 we check the requisite conditions and adjust the vectorization cost 1173 accordingly if satisfied. One typical example is to model and adjust 1174 loop_len cost for known_lt (NITERS, VF). */ 1175 1176 void 1177 costs::adjust_vect_cost_per_loop (loop_vec_info loop_vinfo) 1178 { 1179 if (LOOP_VINFO_FULLY_WITH_LENGTH_P (loop_vinfo) 1180 && !LOOP_VINFO_USING_DECREMENTING_IV_P (loop_vinfo)) 1181 { 1182 /* In middle-end loop vectorizer, we don't count the loop_len cost in 1183 vect_estimate_min_profitable_iters when NITERS < VF, that is, we only 1184 count cost of len that we need to iterate loop more than once with VF. 1185 It's correct for most of the cases: 1186 1187 E.g. VF = [4, 4] 1188 for (int i = 0; i < 3; i ++) 1189 a[i] += b[i]; 1190 1191 We don't need to cost MIN_EXPR or SELECT_VL for the case above. 1192 1193 However, for some inefficient vectorized cases, it does use MIN_EXPR 1194 to generate len. 1195 1196 E.g. VF = [256, 256] 1197 1198 Loop body: 1199 # loop_len_110 = PHI <18(2), _119(11)> 1200 ... 1201 _117 = MIN_EXPR <ivtmp_114, 18>; 1202 _118 = 18 - _117; 1203 _119 = MIN_EXPR <_118, POLY_INT_CST [256, 256]>; 1204 ... 1205 1206 Epilogue: 1207 ... 1208 _112 = .VEC_EXTRACT (vect_patt_27.14_109, _111); 1209 1210 We cost 1 unconditionally for this situation like other targets which 1211 apply mask as the loop control. */ 1212 rgroup_controls *rgc; 1213 unsigned int num_vectors_m1; 1214 unsigned int body_stmts = 0; 1215 FOR_EACH_VEC_ELT (LOOP_VINFO_LENS (loop_vinfo), num_vectors_m1, rgc) 1216 if (rgc->type) 1217 body_stmts += num_vectors_m1 + 1; 1218 1219 add_stmt_cost (body_stmts, scalar_stmt, NULL, NULL, NULL_TREE, 0, 1220 vect_body); 1221 } 1222 } 1223 1224 void 1225 costs::finish_cost (const vector_costs *scalar_costs) 1226 { 1227 if (loop_vec_info loop_vinfo = dyn_cast<loop_vec_info> (m_vinfo)) 1228 { 1229 adjust_vect_cost_per_loop (loop_vinfo); 1230 } 1231 vector_costs::finish_cost (scalar_costs); 1232 } 1233 1234 } // namespace riscv_vector 1235