shrink-wrap optimization produce buggy code

Bug #771675 reported by Leonid Moiseichuk
12
This bug affects 1 person
Affects Status Importance Assigned to Milestone
Linaro GCC
Won't Fix
Medium
Michael Collison

Bug Description

this issue discovered in 2011.03 but exists in 2011.04 if you enable -fshrink-wrap optimization.
for functions with variable parameters it damages r3 (last possible argument) and produce non-working code.
registers r0, r1 and r2 handled OK.

the code example produces in case valid code output
  5 6 7
in case of invalid code
  5 5 7
in case strings used segfault is possible.

code example

typedef __builtin_va_list __gnuc_va_list;
typedef __gnuc_va_list va_list;
extern int vprintf(char const *fmt, va_list ap);
int c;
void fun(int x, char const *y, ...) {
 if (x <= c) {
  va_list ap;
  __builtin_va_start(ap, y);
  if (x > 4)
    return;
  vprintf(y, ap);
  __builtin_va_end(ap);
 }
}
int main(void) {
  c = 5;
  fun(3, "%d %d %d", 5, 6, 7);
  return 0;
}

correct code (with -fno-shrink-wrap)
        @ args = 4, pretend = 12, frame = 8
        @ frame_needed = 0, uses_anonymous_args = 1
        stmfd sp!, {r1, r2, r3} <- r3 saved
        .save {r1, r2, r3}
.....

incorrect code (-fshrink-wrap)
        @ args = 4, pretend = 12, frame = 8
        @ frame_needed = 0, uses_anonymous_args = 1
        movw r3, #:lower16:c <- :(
        movt r3, #:upper16:c <- :(
        ldr r3, [r3, #0] <- :(
        cmp r3, r0
        bxlt lr
        stmfd sp!, {r1, r2, r3}

Revision history for this message
Leonid Moiseichuk (leonid-moiseichuk) wrote :
Revision history for this message
Leonid Moiseichuk (leonid-moiseichuk) wrote :

Btw, mainline gcc 4.5 do not have this problem. gcc 4.4 do not have such optimization.

Revision history for this message
Barry Song (21cnbao) wrote :

 > Btw, mainline gcc 4.5 do not have this problem. gcc 4.4 do not have such optimization.

not real. i am using 2011.3 gcc 4.5, it has this problem too.

Revision history for this message
Andrew Stubbs (ams-codesourcery) wrote :

The Shrink wrapping optimization does not exist in any FSF compiler yet. We're hoping to have it in GCC 4.7, next year.

It has been added to Linaro's GCC 4.5 experimentally - it gives some very nice performance improvements. However, as we've seen, there are some bugs to work out, this is why it has been disabled, by default, in 2011.04.

Revision history for this message
Leonid Moiseichuk (leonid-moiseichuk) wrote :

Linaro gcc has, mainline gcc 4.5 from gcc.gnu.org seems not.

Revision history for this message
Michael Hope (michaelh1) wrote :

Thank you for the bug report. I've confirmed this with gcc-linaro-4.5-2011.04-0 on ARM:

michaelh@ursa1:~/linaro/bugs/shrink-wrap$ /tools/toolchains/arch/armv7l/gcc-linaro-4.5-2011.04-0-armv7l-maverick-cbuild112-ursa3-cortexa8r1/bin/gcc -S -O2 -fshrink-wrap shrink-wrap.c

As you say, the code corrupts r3:

fun:
 @ args = 4, pretend = 12, frame = 8
 @ frame_needed = 0, uses_anonymous_args = 1
 movw r3, #:lower16:c
 movt r3, #:upper16:c
 ldr r3, [r3, #0]
 cmp r3, r0
 bge .L5
 bx lr
.L5:
 push {r1, r2, r3}

The work-around is to compile at -O2. The fault is Linaro GCC 4.5 specific and does not exist in Linaro GCC 4.6, gcc-4.5.2 or gcc-4.6.0.

I've set it to medium priority as while it is bad-code, the fault only occurs if you add -fshrink-wrap. This fault will be fixed before shrink wrapping is turned back on by default.

This is probably the same fault as LP: #736081.

I've set it to low priority as it is a ftbfs, occurs at high optimisation levels, the fault exists upstream, and the code uses undefined behavior by shifting by a negative amount.

Changed in gcc-linaro:
status: New → Triaged
importance: Undecided → Medium
tags: added: bad-code shrinkwrap
Revision history for this message
Michael Hope (michaelh1) wrote :

Gah, copy and paste: please ignore the last paragraph about low priority.

Revision history for this message
Barry Song (21cnbao) wrote :

> Andrew Stubbs wrote 15 hours ago: #4

> The Shrink wrapping optimization does not exist in any FSF compiler yet. We're hoping to have it in GCC 4.7, next year.

>It has been added to Linaro's GCC 4.5 experimentally - it gives some very nice performance improvements. However, as we've seen, there are some bugs to work out, this is why it has been disabled, by default, in 2011.04.

Any document to describle what's really done by Shrink wrapping optimization? can't find more detail by google.

Revision history for this message
Linaro Toolchain Builder (cbuild) wrote :

Hi Barry. Shrink wrapping is a common optimisation that defers the prologue of a function until it's needed. This helps in functions that have an early exit by reducing the amount of data written to memory.

For example, take this code:

int log(int level, char *message, ...)
{
    if (level < log_level)
      return;

  va_list ap;
  ...
  vprintf(...)
}

Without shrink-wrapping the assembly would be:

  push registers
  set up stack

  if level < log level:
     tidy up stack
     pop registers
     return

  do vprint

With shrink-wrapping it becomes:

  if level < log level:
    return

  push registers
  set up stack
  ...

Notice how the stack is unchanged and that there are no memory accesses with shrink wrapping.

Revision history for this message
Barry Song (21cnbao) wrote :

Thanks a lot! this explains why my problem will disappear after i delete "if(unifi_debug >= level)" in the below codes:

void
unifi_trace(void* ospriv, int level, const char *fmt, ...)
{
        static char s[DEBUG_BUFFER_SIZE];
        va_list args;
        unsigned int len;

        if(unifi_debug >= level) {
                va_start(args, fmt);
                len = vsnprintf(&(s)[0],
                    (DEBUG_BUFFER_SIZE),
                         fmt, args);

                va_end(args);
                if (len >= DEBUG_BUFFER_SIZE) {
                    (s)[DEBUG_BUFFER_SIZE - 2] = '\n';
                    (s)[DEBUG_BUFFER_SIZE - 1] = 0;
                }

                printf("%s", s);
        }
}

Revision history for this message
Andrew Stubbs (ams-codesourcery) wrote :

You can read more about the Shrink wrapping feature here:

  http://old.nabble.com/Shrink-wrapping%3A-Introduction-to31220423.html

As you can see, the final form of the implementation is still in development. Hopefully the sort of problems you have seen will be ironed out shortly.

In the meantime, please either use -fno-shrink-wrap, or upgrade to 2011.04.

Revision history for this message
Michael Collison (michael-collison) wrote :

Will not fix for 4.5. Shrink wrap optimization produces correct code for linaro 4.8 and 4.9

Changed in gcc-linaro:
assignee: nobody → Michael Collison (michael-collison)
assignee: Michael Collison (michael-collison) → nobody
status: Triaged → Won't Fix
assignee: nobody → Michael Collison (michael-collison)
To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.