This happens on the 4.1 branch, not on 4.2/trunk, but it's also latent there:

% cat large-ofs.c
static char l_info[100];
void bug1 (unsigned long tag)
{
  char *info = l_info;
  info[tag - 0x100000000 + 1] = 1;
}
void bug2 (unsigned long tag)
{
  char *info = l_info;
  info[tag - 0x700000000 + 2] = 2;
}
extern void abort();
int main()
{
  unsigned i;
  for (i = 0; i < sizeof(l_info); i++)
    l_info[i] = 0;
  bug1(0x100000000);
  bug2(0x700000000);
  if (l_info[2] != 2 || l_info[1] != 1)
    abort();
  return 0;
}

% gcc -O2 -fPIC -march=z9-109 large-ofs.c
large-ofs.c: In function &#8216;bug2&#8217;:
large-ofs.c:13: error: unrecognizable insn:
(insn 9 7 10 1 (set (reg:DI 45)
        (const:DI (plus:DI (symbol_ref:DI ("l_info") [flags 0x2] <var_decl
0x200002d0e70 l_info>)
                (const_int -30064771038 [0xfffffff900000022])))) -1 (nil)
    (nil))
large-ofs.c:13: internal compiler error: in extract_insn, at recog.c:2084

This demonstrates two problems: 1) with such large offset to static (or
hidden) objects gcc is confused (bug2), and 2) when one inspects the asm
output for bug1 (comment out bug2 for that) we see:
bug1:
.LFB2:
        larl    %r1,l_info+34
        lhi     %r3,1
        stc     %r3,1(%r2,%r1)
        br      %r14

Note how the subtraction of 0x700000000 is missing.  Both are a problem in
legitimize_pic_address.  In this case we are asked to legitimize
  (const:DI (plus:DI (symbol_ref:DI ("l_info") [flags 0x42]
                      <var_decl 0x2b6af897d000 l_info>)
              (const_int -15032385264 [0xfffffffc80000110])))

It detects that this is a local symbols plus an int offset, and hence tries
to use larl (this is z/arch after all).  But it checks only op0 (the
symbol_ref) for larl_operand(), not the offset.  But larl can only
accept numbers which fit in 33 bit, which indeed is also tested
by larl_operand.  But it never is called with the whole plus operand.
That is because it would also reject odd integer offsets which this very code
tries to handle.

So we need to handle that on our own.  If we do that then we'll see the second
bug, resulting in a segfault when the program is run.  It is the use of 'int'
to store an INTVAL when fixing up the case for odd offsets.

Note that this situation arises in glibc, ld.so.  The index into an array
is the DT_TAG (an arbitrary large 32bit number) minus the base for the
tag in question (a large constant like 0x7000000), and because the indexed
array contains pointers the overall offset if larger than 33 bit.

That attached patch fixes this testcase.


-- 
           Summary: ICE unrecognizable insn: offset too large for larl
                    (breaks glibc)
           Product: gcc
           Version: 4.1.2
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: target
        AssignedTo: unassigned at gcc dot gnu dot org
        ReportedBy: matz at gcc dot gnu dot org
  GCC host triplet: s390x-linux


http://gcc.gnu.org/bugzilla/show_bug.cgi?id=29319

Reply via email to