llvm / llvm/llvm-project

Copy avoidance broken on laundering

Open
#184,148 1 comment 0 reactions 0 assignees View on GitHub
llvm:optimizations
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

Consider the following LLVM IR:
```llvm
; launder-invgrp.ll
; Test interaction between llvm.launder.invariant.group and !invariant.group metadata.

declare ptr @llvm.launder.invariant.group.p0(ptr)
declare void @use_i64(i64)

!0 = !{} ; an invariant.group marker node

; ---------------------------
; Case A: load/store through same pointer
; ---------------------------
define void @caseA(ptr %p) {
entry:
%x = load i64, ptr %p, align 8, !invariant.group !0
%p8 = getelementptr i8, ptr %p, i64 8
%y = load i64, ptr %p8, align 8, !invariant.group !0

; prevent %x/%y from becoming unused and disappearing too early
call void @use_i64(i64 %x)
call void @use_i64(i64 %y)

; “store back what we loaded”
store i64 %x, ptr %p, align 8, !invariant.group !0
store i64 %y, ptr %p8, align 8, !invariant.group !0
ret void
}

; ---------------------------
; Case B: launder exists but stores are still through %p
; ---------------------------
define void @caseB_store_through_p(ptr %p) {
entry:
%x = load i64, ptr %p, align 8, !invariant.group !0
%p8 = getelementptr i8, ptr %p, i64 8
%y = load i64, ptr %p8, align 8, !invariant.group !0

%q = call ptr @llvm.launder.invariant.group.p0(ptr %p)

call void @use_i64(i64 %x)
call void @use_i64(i64 %y)

store i64 %x, ptr %p, align 8, !invariant.group !0
store i64 %y, ptr %p8, align 8, !invariant.group !0

; keep %q live (so launder isn't trivially DCE'd)
%qq = ptrtoint ptr %q to i64
call void @use_i64(i64 %qq)

ret void
}

; ---------------------------
; Case C: laundered pointer is used as store base
; ---------------------------
define void @caseC_store_through_launder(ptr %p) {
entry:
%x = load i64, ptr %p, align 8, !invariant.group !0
%p8 = getelementptr i8, ptr %p, i64 8
%y = load i64, ptr %p8, align 8, !invariant.group !0

%q = call ptr @llvm.launder.invariant.group.p0(ptr %p)
%q8 = getelementptr i8, ptr %q, i64 8

call void @use_i64(i64 %x)
call void @use_i64(i64 %y)

store i64 %x, ptr %q, align 8, !invariant.group !0
store i64 %y, ptr %q8, align 8, !invariant.group !0
ret void
}
```

Under `-O3`, it seems that the laundering break the analysis that avoid the redundant copying:

```llvm
; ModuleID = 'more2.ll'
source_filename = "more2.ll"

; Function Attrs: mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(inaccessiblemem: readwrite)
declare ptr @llvm.launder.invariant.group.p0(ptr) #0

declare void @use_i64(i64) local_unnamed_addr

define void @caseA(ptr readonly captures(none) %p) local_unnamed_addr {
entry:
%x = load i64, ptr %p, align 8, !invariant.group !0
%p8 = getelementptr i8, ptr %p, i64 8
%y = load i64, ptr %p8, align 8, !invariant.group !0
tail call void @use_i64(i64 %x)
tail call void @use_i64(i64 %y)
ret void
}

define void @caseB_store_through_p(ptr %p) local_unnamed_addr {
entry:
%x = load i64, ptr %p, align 8, !invariant.group !0
%p8 = getelementptr i8, ptr %p, i64 8
%y = load i64, ptr %p8, align 8, !invariant.group !0
%q = tail call ptr @llvm.launder.invariant.group.p0(ptr nonnull %p)
tail call void @use_i64(i64 %x)
tail call void @use_i64(i64 %y)
%qq = ptrtoint ptr %q to i64
tail call void @use_i64(i64 %qq)
ret void
}

define void @caseC_store_through_launder(ptr captures(none) %p) local_unnamed_addr {
entry:
%x = load i64, ptr %p, align 8, !invariant.group !0
%p8 = getelementptr i8, ptr %p, i64 8
%y = load i64, ptr %p8, align 8, !invariant.group !0
%q = tail call ptr @llvm.launder.invariant.group.p0(ptr nonnull %p)
%q8 = getelementptr i8, ptr %q, i64 8
tail call void @use_i64(i64 %x)
tail call void @use_i64(i64 %y)
store i64 %x, ptr %q, align 8, !invariant.group !0
store i64 %y, ptr %q8, align 8, !invariant.group !0
ret void
}

attributes #0 = { mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(inaccessiblemem: readwrite) }

!0 = !{}
```

Contributor guide

Open the contributing guide

Research direction

Start with the provided launder-invgrp.ll reproducer and run the three cases under -O3, comparing the redundant-store behavior. Trace the optimization handling of llvm.launder.invariant.group and !invariant.group metadata; done means identifying the cause and capturing the intended copy-elimination behavior in a regression test.

Written by the indexing model from the issue text.

Assessment

Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.