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 ‘bug2’: 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