------- Comment #8 from mikpe at it dot uu dot se 2009-07-13 13:05 ------- Mystery solved. Buried in revision 146451, which should just fix enum conversions for C++ compatibility, is the following bug fix:
--- trunk/gcc/config/arm/arm.c 2009/04/20 19:30:55 146450 +++ trunk/gcc/config/arm/arm.c 2009/04/20 19:35:00 146451 @@ -7408,7 +7410,7 @@ /* Don't accept any offset that will require multiple instructions to handle, since this would cause the arith_adjacentmem pattern to output an overlong sequence. */ - if (!const_ok_for_op (PLUS, val0) || !const_ok_for_op (PLUS, val1)) + if (!const_ok_for_op (val0, PLUS) || !const_ok_for_op (val1, PLUS)) return 0; /* Don't allow an eliminable register: register elimination can make The parameters to const_ok_for_op had been swapped, causing this if statement to not reject offsets that are awkward for ARM. Combined with a non-FL_LDSCHED cpu type this enabled arith_adjacentmem for a bad offset, which forced it to split the LDM into two LDRs, and that code fails to order the LDRs to avoid clobbering the shared base register. With the above patch arith_adjacentmem will not trigger for bad offsets, avoiding the broken LDM splitting code. This patch is needed also for the 4.4 and 4.3 branches, and I've checked that it fixes this test case there too. It seems that there is a bit of redundancy between the adjacent_mem_locations test and the arith_adjacentmem pattern. Both check const_ok_for_arm on the offset and the negated offset. The first attempts to reject bad offsets, while the second attempts to handle them. I'm not sure, but I _think_ that the code in arith_adjacentmem to split an LDM into two LDRs is now dead (after the bug fix above). However, just in case it isn't, I'm attaching a patch to correct it. Unrelated to this PR, buried in revision 146451 is another bug fix: --- trunk/gcc/config/arm/arm.c 2009/04/20 19:30:55 146450 +++ trunk/gcc/config/arm/arm.c 2009/04/20 19:35:00 146451 @@ -5465,7 +5465,7 @@ return true; case ABS: - if (GET_MODE_CLASS (mode == MODE_FLOAT)) + if (GET_MODE_CLASS (mode) == MODE_FLOAT) { if (TARGET_HARD_FLOAT && (mode == SFmode || mode == DFmode)) { This one is also needed in 4.4, but not in 4.3. -- http://gcc.gnu.org/bugzilla/show_bug.cgi?id=39429