opt -O2 leads to incorrect operation (possibly a bug in the DSE)

Hi all,

Consider the following example:

define void @fn(i8* %buf) #0 {
entry:
%arrayidx = getelementptr i8* %buf, i64 18
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* %arrayidx, i8* %buf, i64 18, i32 1, i1 false)
%arrayidx1 = getelementptr i8* %buf, i64 18
store i8 1, i8* %arrayidx1, align 1
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* %buf, i8* %arrayidx, i64 18, i32 1, i1 false)
ret void
}

I ran opt -O2 ex.ll -S, and got:

define void @fn(i8* nocapture %buf) #0 {
entry:
%arrayidx = getelementptr i8* %buf, i64 18
store i8 1, i8* %arrayidx, align 1
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* %buf, i8* %arrayidx, i64 18, i32 1, i1 false)
ret void
}

In that case previous contents of bytes 0-17 are overwritten by bytes 18-35 (pre-opt code doesn’t do that).

Another point, if I change the code to:

define void @fn(i8* %buf) #0 {
entry:
%arrayidx = getelementptr i8* %buf, i64 18
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* %arrayidx, i8* %buf, i64 18, i32 1, i1 false)

;changing 19th byte, not 18th:

%arrayidx1 = getelementptr i8* %buf, i64 19
store i8 1, i8* %arrayidx1, align 1
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* %buf, i8* %arrayidx, i64 18, i32 1, i1 false)
ret void
}

I get correct piece of code:

define void @fn(i8* nocapture %buf) #0 {
entry:
%arrayidx = getelementptr i8* %buf, i64 18
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* %arrayidx, i8* %buf, i64 18, i32 1, i1 false)
%arrayidx1 = getelementptr i8* %buf, i64 19
store i8 1, i8* %arrayidx1, align 1
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* %buf, i8* %arrayidx, i64 18, i32 1, i1 false)
ret void
}

Adding some datalayout to module also solves the issue.

-print-after-all shows that the ‘Dead Store Elimination’ pass erases first call to memcpy.

I found that the following code in DeadStoreElimination.cpp leads to such behavior:
if (DL == nullptr && Later.Ptr->getType() == Earlier.Ptr->getType())
return OverwriteComplete;

Is this issue a bug in DSE or maybe I’m doing some wrong?

Best regards,
Alexey

From: "Aliaksei Zasenka" <listhex@gmail.com>
To: LLVMdev@cs.uiuc.edu
Sent: Friday, October 17, 2014 4:24:27 AM
Subject: [LLVMdev] opt -O2 leads to incorrect operation (possibly a bug in the DSE)

Hi all,

Consider the following example:

define void @fn(i8* %buf) #0 {
entry:
%arrayidx = getelementptr i8* %buf, i64 18
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* %arrayidx, i8* %buf,
i64 18, i32 1, i1 false)
%arrayidx1 = getelementptr i8* %buf, i64 18
store i8 1, i8* %arrayidx1, align 1
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* %buf, i8* %arrayidx,
i64 18, i32 1, i1 false)
ret void
}

I ran opt -O2 ex.ll -S, and got:

define void @fn(i8* nocapture %buf) #0 {
entry:
%arrayidx = getelementptr i8* %buf, i64 18
store i8 1, i8* %arrayidx, align 1
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* %buf, i8* %arrayidx,
i64 18, i32 1, i1 false)
ret void
}

In that case previous contents of bytes 0-17 are overwritten by bytes
18-35 (pre-opt code doesn't do that).

Another point, if I change the code to:

define void @fn(i8* %buf) #0 {
entry:
%arrayidx = getelementptr i8* %buf, i64 18
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* %arrayidx, i8* %buf,
i64 18, i32 1, i1 false)

;changing 19th byte, not 18th:

%arrayidx1 = getelementptr i8* %buf, i64 19
store i8 1, i8* %arrayidx1, align 1
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* %buf, i8* %arrayidx,
i64 18, i32 1, i1 false)
ret void
}

I get correct piece of code:

define void @fn(i8* nocapture %buf) #0 {
entry:
%arrayidx = getelementptr i8* %buf, i64 18
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* %arrayidx, i8* %buf,
i64 18, i32 1, i1 false)
%arrayidx1 = getelementptr i8* %buf, i64 19
store i8 1, i8* %arrayidx1, align 1
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* %buf, i8* %arrayidx,
i64 18, i32 1, i1 false)
ret void
}

Adding some datalayout to module also solves the issue.

-print-after-all shows that the 'Dead Store Elimination' pass erases
first call to memcpy.

I found that the following code in DeadStoreElimination.cpp leads to
such behavior:
if (DL == nullptr && Later.Ptr->getType() == Earlier.Ptr->getType())
return OverwriteComplete;

Is this issue a bug in DSE or maybe I'm doing some wrong?

Looks like a bug in DSE, you're not doing anything wrong. We don't test much without data layouts anymore. This code might pre-date DSE's handling of memcpy intrinsics (just a guess). In any case, we'll fix it.

-Hal

Hal,
Thanks for your answer. Should I report a bug?

Best regards,
Alexey

From: "Aliaksei Zasenka" <listhex@gmail.com>
To: "Hal Finkel" <hfinkel@anl.gov>
Cc: LLVMdev@cs.uiuc.edu
Sent: Friday, October 17, 2014 6:57:14 AM
Subject: Re: [LLVMdev] opt -O2 leads to incorrect operation (possibly a bug in the DSE)

Hal,
Thanks for your answer. Should I report a bug?

Generally speaking, yes. In this case, don't bother; I removed the offending logic in r220035. Thanks for isolating the problem!

-Hal