https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92656
--- Comment #3 from Jim Wilson <wilson at gcc dot gnu.org> --- Looking at this, I see that the problem occurs in record_value_for_reg where it does if (!insn || (value && rsp->last_set_table_tick >= label_tick_ebb_start)) rsp->last_set_invalid = 1; last_set_table_tick is 2 and label_tick_ebb_start is 1 because this is the first block of the function. This actually causes a lot of variables set in the first block to be marked invalid if used in a successful combination two or more times, which then prevents the nonzero bits info from being used for any of them. There seems to be a problem with how label_tick is used. In the very first block in the body of the function, label_tick is 2 and label_tick_ebb_start is 1. This is because it is considered to be the second block in the ebb after the entry block. In the second block in the body of the function, label_tick is 3 and label_tick_ebb_start is 3. This means that every variable set in the first block gets treated differently than in every block after the first. If I add a little bit of code before the loop to force it to be the second block, then I get correct output from combine. I just added this before the loop static int j = 0; if (val) j++; This also explains why the problem only occurs with -mtune=sifive-7-series because this enables the conditional move support that turns the loop into a single block, and then the -funroll-loops option fully unrolls the loop, turning the entire function into one block, which prevents combine from handling many of the register sets correctly because everything is in the first block now. This also explains why the problem started when the 2->2 combination support was added, as that causes more successful combinations, and hence more registers getting invalidated in the first block. So the question is why we need label_tick > label_tick_ebb_start for the first block of the function. There is nothing set in the entry block other than hard registers, and those could always be handled specially by just marking them as invalid somehow before processing instructions. Or alternatively, in record_value_for_reg, maybe we can add a check for a pseudo reg only set once and not live in the prologue, and avoid marking it as invalid when we process it a second time. There are already a lot of checks like this scattered around the code.