| Issue |
107996
|
| Summary |
GVN wrong optimization with skipped out-of-bounds accesses
|
| Labels |
miscompilation,
llvm:GVN
|
| Assignees |
|
| Reporter |
hvdijk
|
Please consider this function:
```llvm
define i32 @f(i1 %false, i64 %zero) {
entry:
%a = alloca i32
store i32 1, ptr %a
br i1 %false, label %bb.1, label %bb.2
bb.1:
%x = load i64, ptr %a
store i64 %x, ptr %a
br label %bb.2
bb.2:
br i1 %false, label %bb.4, label %bb.3
bb.3:
%j = getelementptr i32, ptr %a, i64 %zero
store i32 3, ptr %j
br label %bb.4
bb.4:
%z = load i32, ptr %a
ret i32 %z
}
```
This is a valid function to call with two zero arguments, in which case `%bb.1` is skipped, `%bb.3` is entered, `3` is stored to `%a`, and in `%bb.4`, `3` is then returned.
If this function is called with any other argument, the behavior is undefined. If `%false` is non-zero, `%bb.1` is entered and `%a` is accessed past its end. If `%false` is zero but `%zero` is non-zero, `%bb.3` stores past `%a`'s end.
GVN transforms this (`opt -passes=gvn`, LLVM commit 09c00b6f0463f6936be5d2100f9d47c0077700f8) to:
```llvm
define i32 @f(i1 %false, i64 %zero) {
entry:
%a = alloca i32, align 4
store i32 1, ptr %a, align 4
br i1 %false, label %bb.1, label %bb.2
bb.1:
%x = load i64, ptr %a, align 4
store i64 %x, ptr %a, align 4
%0 = trunc i64 %x to i32
br label %bb.2
bb.2:
%z = phi i32 [ %0, %bb.1 ], [ 1, %entry ]
br i1 %false, label %bb.4, label %bb.3
bb.3:
%j = getelementptr i32, ptr %a, i64 %zero
store i32 3, ptr %j, align 4
br label %bb.4
bb.4:
ret i32 %z
}
```
If called with two zero arguments, `%bb.3` is still entered and still stores `3` to `%a`, but the load in `%bb.4` has been mis-optimized to its earlier value.
Alive agrees that this transformation is invalid: https://alive2.llvm.org/ce/z/EhdxXU
I have not fully understood yet what is happening, but at first glance, it appears that this bit in [`MemoryDependenceResults::getNonLocalPointerDepFromBB`](https://github.com/llvm/llvm-project/blob/433ca3ebbef50002bec716ef2c6d6a82db71048d/llvm/lib/Analysis/MemoryDependenceAnalysis.cpp#L1097) is wrong:
```c++
// This query's Size is less than the cached one. Conservatively restart
// the query using the greater size.
return getNonLocalPointerDepFromBB(
QueryInst, Pointer, Loc.getWithNewSize(CacheInfo->Size), isLoad,
StartBB, Result, Visited, SkipFirstBlock, IsIncomplete);
```
This assumes that querying with a greater size can only find additional possible dependencies, but it ignores that a greater size may cause dependencies to be left out if that size is greater than the memory region.
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs