Is it an opt bug ?

Hi all,

I’m looking at following code snippet:

void t2(double *x)
{
long long a[2];
a[0] = 3;
a[1] = 5;
*x = * ((double *) a);
*(x+1) = * ((double *) &a[a[0]-2]);
}

I use generate LLVM code using my own front-end that looks like:

; ModuleID = ‘jb.c’
target datalayout = “e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32”
target triple = “i386-pc-linux-gnu”
define void @t2(double* %x) {
L.entry:
%a = alloca [2 x i64], align 4

%x.addr = alloca double*
store double* %x, double** %x.addr
%0 = bitcast [2 x i64]* %a to i64*
store i64 3, i64* %0
%1 = getelementptr [2 x i64]* %a, i32 0, i32 1
store i64 5, i64* %1
%2 = bitcast [2 x i64]* %a to double*
%3 = load double* %2
%4 = load double** %x.addr
store double %3, double* %4
%5 = bitcast [2 x i64]* %a to double*
%6 = bitcast double* %5 to i8*
%7 = bitcast [2 x i64]* %a to i64*
%8 = load i64* %7
%9 = sub i64 %8, 2
%10 = trunc i64 %9 to i32
%11 = mul i32 %10, 8
%12 = getelementptr i8* %6, i32 %11
%13 = bitcast i8* %12 to double*
%14 = load double* %13
%15 = load double** %x.addr
%16 = bitcast double* %15 to i8*
%17 = getelementptr i8* %16, i32 8
%18 = bitcast i8* %17 to double*
store double %14, double* %18
ret void
}

when I use LLVM opt on this .ll file I’ve got:

; ModuleID = ‘jb.ll’
target datalayout = “e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32”
target triple = “i386-pc-linux-gnu”

define void @t2(double* nocapture %x) nounwind {
L.entry:
store double 1.482197e-323, double* %x, align 4
ret void
}

Now if I use clang -O2 I’ve got following llvm file:

; ModuleID = ‘jb.clang.ll’
target datalayout = “e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32”
target triple = “i386-unknown-linux-gnu”

define void @t2(double* nocapture %x) nounwind {
store double 1.482197e-323, double* %x, align 4, !tbaa !0
%1 = getelementptr inbounds double* %x, i32 1
store double 2.470328e-323, double* %1, align 4, !tbaa !0
ret void
}

!0 = metadata !{metadata !“double”, metadata !1}
!1 = metadata !{metadata !“omnipotent char”, metadata !2}
!2 = metadata !{metadata !“Simple C/C++ TBAA”, null}

Which is what I was expecting, to me it seems that there is a bug in LLVM opt, is it the case ?

Thanks for your answer
Best Regards
Seb

Hi Seb, I think it is an opt bug. If you run opt at -O1 then you get:

define void @t2(double* nocapture %x) nounwind {
L.entry:
   %a = alloca [2 x i64], align 8
   %0 = getelementptr inbounds [2 x i64]* %a, i32 0, i32 0
   store i64 3, i64* %0, align 8
   %1 = getelementptr [2 x i64]* %a, i32 0, i32 1
   store i64 5, i64* %1, align 8
   %2 = bitcast [2 x i64]* %a to double*
   %3 = load double* %2, align 8
   store double %3, double* %x, align 4
   %4 = load i64* %0, align 8
   %5 = add i64 %4, 536870910
   %6 = trunc i64 %5 to i32
   %7 = getelementptr [2 x i64]* %a, i32 0, i32 %6
   %8 = bitcast i64* %7 to double*
   %9 = load double* %8, align 8
   %10 = getelementptr double* %x, i32 1
   store double %9, double* %10, align 4
   ret void
}

Notice "%5 = add i64 %4, 536870910". This is presumably trying to subtract 2,
but this doesn't seem to be the same as subtracting 2 even after the truncate
to 32 bits.

This then causes a load from a wild stack location later. Probably at a higher
optimization level this is recognized as an undefined operation causing code
after it to be removed, including the final store.

Ciao, Duncan.

Hi Duncan,

Indeed, I made same experiment as you and concluded that it might be a BUG.
Shall I submit it to llvm bug tracking support ?
Do you think there could be a work-around ?
Thanks for your quick answer.

Best Regards
Seb

2012/2/28 Duncan Sands <baldrick@free.fr>

void t2(double *x)
{
long long a[2];
a[0] = 3;
a[1] = 5;
*x = * ((double *) a);
*(x+1) = * ((double *) &a[a[0]-2]);
}

Doesn't this code violate the strict aliasing rules?

John

Maybe, but the LLVM IR he showed seems perfectly well defined.

Ciao, Duncan.

Hi all,

If I remove datalayout definition, code is not optimized and work as expected.
So my question is:

What attribute/value/interpretation of data-layout would cause this type of bug?
Thanks for your answers
Seb

2012/2/28 Duncan Sands <baldrick@free.fr>

Hi Seb,

Indeed, I made same experiment as you and concluded that it might be a BUG.

I don't think the issue I noticed is a bug after all, see below.

2012/2/28 Duncan Sands <baldrick@free.fr <mailto:baldrick@free.fr>>

    Hi Seb, I think it is an opt bug. If you run opt at -O1 then you get:

    define void @t2(double* nocapture %x) nounwind {
    L.entry:
       %a = alloca [2 x i64], align 8
       %0 = getelementptr inbounds [2 x i64]* %a, i32 0, i32 0
       store i64 3, i64* %0, align 8
       %1 = getelementptr [2 x i64]* %a, i32 0, i32 1
       store i64 5, i64* %1, align 8
       %2 = bitcast [2 x i64]* %a to double*
       %3 = load double* %2, align 8
       store double %3, double* %x, align 4
       %4 = load i64* %0, align 8
       %5 = add i64 %4, 536870910

This add is adding -2, but with some funny values in bits 29 and up.

       %6 = trunc i64 %5 to i32
       %7 = getelementptr [2 x i64]* %a, i32 0, i32 %6

This getelementptr results in %6 being multiplied by 8. That causes the
funny values at the top of the add to be shifted off. I.e. the final
answer should be correct.

Ciao, Duncan.

Hi Seb,

If I remove datalayout definition, code is not optimized and work as expected.
So my question is:

What attribute/value/interpretation of data-layout would cause this type of bug?

all kinds of optimizers use datalayout (and are disabled if there is none). For
example, alias analysis, anything that needs to understand getelementptr
offsets, the list is endless.

I suggest you open a bug report, describing your original problem as in your
first email.

Ciao, Duncan.

Already done here : http://llvm.org/bugs/show_bug.cgi?id=12130

Thanks for your answers
Best Regards
Seb

2012/2/29 Duncan Sands <baldrick@free.fr>

Hi Seb,

Already done here : http://llvm.org/bugs/show_bug.cgi?id=12130

that doesn't describe the original issue (second store removed), it is talking
about a different issue that appeared at -O1 (and it first seemed to explain
your original problem; but now I think the -O1 transform was correct and does
not explain your original problem).

Ciao, Duncan.

Hi Duncan,

I included original email in BUG description which described original issue with store being removed.

BR
Seb

2012/2/29 Duncan Sands <baldrick@free.fr>

Hi Seb,

I included original email in BUG description which described original issue with
store being removed.

it is generally preferred to have different bug reports for different issues,
rather than one bug report that mutates itself as time goes on. Any casual
reader will see the bug as being about the -O1 issue, and not realize that
there is another issue hidden in the accompanying email. The -O1 issue is not
an issue, thus I closed the bug report. Please open a new one with the -O2
issue.

Thanks, Duncan.