Skip to content

VReg has no regclass after selection #18

Closed
@codebje

Description

@codebje

I've encountered a segfault in code generation; the smallest repeatable case I have (with -Oz) is:

# 1 "<built-in>"
# 1 "foo.c"
static const char *g_default_sigstr[2] = { "", "" };

char *strsignal(int signum)
{
  if (signum > 31) return (char *)"no";
  switch (signum)
    {
      case 1: return (char *)"SIGUSR1";
      case 2: return (char *)"SIGUSR2";
      case 3: return (char *)"SIGALRM";
      case 6: return (char *)"SIGSTOP";
      case 7: return (char *)"SIGTSTP";
      case 8: return (char *)"SIGCONT";
      case 17: return (char *)"SIGWORK";
      default: break;
    }
  return (char *)g_default_sigstr[signum];
}

Many changes to this code will result in a successful compile: removing any of the cases, changing case 1 to case 0, changing the size of g_default_sigstr to 1, changing the argument type to unsigned char.

A debug build gives the perhaps more helpful message:

fatal error: error in backend: VReg has no regclass after selection: $uhl = COPY %5:gpr(s24) (in function: strsignal)

I've traced through in llvm a little, trying to work out what's going wrong. I'm not at all familiar with LLVM's internals, so I might be way off the mark with this, sorry.

The machine block in the generated LLVM IR is:

if.end:                                           ; preds = %entry
  %switch.tableidx = add i24 %signum, -1
  %0 = icmp ult i24 %switch.tableidx, 17
  br i1 %0, label %switch.hole_check, label %sw.epilog

After some processing, the following instructions are in the program code (amongst, of course, many others):

/* A chain of COPYs ending in a G_TRUNC is created over time */
%0:r24(s24) = G_LOAD %1:gpr(p0) :: (invariant load 3 from %fixed-stack.0, align 1) /* signum */
%4:_(s24) = G_CONSTANT i24 -1
%5:_(s24) = G_ADD %0:_, %4:_         /* %5 is %switch.tableidx, signum - 1 */
%65:_(s24) = COPY %5:_(s24)          /* part of a rewrite of %8:_(s32) = G_ZEXT %5:_(s24) */
%71:_(s24) = COPY %65:_(s24)         /* Rewrite from %71:_(s24) = G_EXTRACT %8:_(s32), 0 */
%73:_(s8) = G_TRUNC %71:_(s24)

/* these three show %73 being used as s8 */
%50:r8(s8) = COPY %73:r8(s8)
$l = COPY %50:r8(s8)
CALL24 &_lshru, ...

/* These show %5 still being used */
%7:gpr(s1) = G_ICMP intpred(ult), %5:gpr(s24), %6:gpr
G_BRCOND %7:gpr(s1), %bb.4

Eventually the combiner spots the COPY chain and CombinerHelper::applyNarrowOp kicks in. This removes the G_TRUNC and changes the G_ADD to pre-truncate:

Try combining %65:_(s24) = COPY %5:_(s24)

Try combining %71:_(s24) = COPY %65:_(s24)

Try combining %73:_(s8) = G_TRUNC %71:_(s24)
Applying legalizer ruleset to: 39, Tys={s8, s8, }, Opcode=39, MMOs={}
.. match
.. .. Legal, 0, LLT_invalid
Erasing: %73:_(s8) = G_TRUNC %71:_(s24)

Erasing: %73:_(s8) = G_TRUNC %71:_(s24)

Changing: %5:_(s24) = G_ADD %0:_, %4:_

Creating: G_TRUNC

Creating: G_TRUNC

Changed: %73:_(s8) = G_ADD %79:_, %80:_

Created: %79:_(s8) = G_TRUNC %0:_(s24)
Created: %80:_(s8) = G_TRUNC %4:_(s24)

I think when this happens %5 is no longer set by any instruction. Eventually the G_BRCOND folds the comparison away and produces:

  %81:o24(s24) = LD24ri 17
  $uhl = COPY %5:gpr(s24)
  SUB24ao %81:o24(s24), implicit-def $uhl, implicit-def $f, implicit $uhl
  JQCC %bb.4, 3, implicit $f

Because the %5:_(s24) = G_ADD %0:_, %4:_ isn't there any more, %5 has no register class. In a debug build all registers are checked for a valid register class while in a release build the compilation carries on for a short while before segfaulting.

If I disable the Z80PostLegalizerCombinerHelper rule for narrow_op with -mllvm --z80postlegalizercombinerhelper-disable-rule -mllvm narrow_op the compilation succeeds.

I think the root cause is that CombinerHelper::matchNarrowOp() checks the register of operand 1 of the G_TRUNC for multiple uses, but register %71 only has one: to be safe, registers %71, %65, and %5 all need to have only one use, or the COPYs need to be merged to eliminate %71 and %65, or perhaps the earlier rewrites of G_ZEXT and G_EXTRACT shouldn't use COPYs - I'm beyond my understanding of LLVM to know what's appropriate.

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingcrashSomething crashed

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions