This fork of the canonical git mirror of the LLVM subversion repository adds (e)Z80 targets. Please refer to the wiki for important build instructions.
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.
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:
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.
I've encountered a segfault in code generation; the smallest repeatable case I have (with -Oz) is:
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:
After some processing, the following instructions are in the program code (amongst, of course, many others):
Eventually the combiner spots the
COPY
chain andCombinerHelper::applyNarrowOp
kicks in. This removes theG_TRUNC
and changes theG_ADD
to pre-truncate:I think when this happens %5 is no longer set by any instruction. Eventually the
G_BRCOND
folds the comparison away and produces: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.https://github.com/jacobly0/llvm-project/blob/170be88120e3aa88c20eea5615ba76b8f1d6c647/llvm/lib/CodeGen/GlobalISel/InstructionSelect.cpp#L185
If I disable the
Z80PostLegalizerCombinerHelper
rule fornarrow_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 theG_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 theCOPY
s need to be merged to eliminate %71 and %65, or perhaps the earlier rewrites ofG_ZEXT
andG_EXTRACT
shouldn't useCOPY
s - I'm beyond my understanding of LLVM to know what's appropriate.