Files
clang-p2996/llvm/test/Transforms/FunctionAttrs/writeonly.ll
Philip Reames 7b54de5fef [funcattrs] Fix a bug in recently introduced writeonly argument inference
This fixes a bug in 740057d.  There's two ways to describe the issue:
* One caller hasn't yet proven nocapture on the argument.  Given that, the inference routine is responsible for bailing out on a potential capture.
* Even if we know the argument is nocapture, the access inference needs to traverse the exact set of users the capture tracking would (or exit conservatively).  Even if capture tracking can prove a store is non-capturing (e.g. to a local alloc which doesn't escape), we still need to track the copy of the pointer to see if it's later reloaded and accessed again.

Note that all the test changes except the newly added ones appear to be false negatives.  That is, cases where we could prove writeonly, but the current code isn't strong enough.  That's why I didn't spot this originally.
2021-12-03 08:57:15 -08:00

116 lines
3.0 KiB
LLVM

; RUN: opt < %s -function-attrs -S | FileCheck %s
; RUN: opt < %s -passes=function-attrs -S | FileCheck %s
; CHECK: define void @nouses-argworn-funrn(i32* nocapture readnone %.aaa) #0 {
define void @nouses-argworn-funrn(i32* writeonly %.aaa) {
nouses-argworn-funrn_entry:
ret void
}
; CHECK: define void @nouses-argworn-funro(i32* nocapture readnone %.aaa, i32* nocapture readonly %.bbb) #1 {
define void @nouses-argworn-funro(i32* writeonly %.aaa, i32* %.bbb) {
nouses-argworn-funro_entry:
%val = load i32 , i32* %.bbb
ret void
}
%_type_of_d-ccc = type <{ i8*, i8, i8, i8, i8 }>
@d-ccc = internal global %_type_of_d-ccc <{ i8* null, i8 1, i8 13, i8 0, i8 -127 }>, align 8
; CHECK: define void @nouses-argworn-funwo(i32* nocapture readnone %.aaa) #2 {
define void @nouses-argworn-funwo(i32* writeonly %.aaa) {
nouses-argworn-funwo_entry:
store i8 0, i8* getelementptr inbounds (%_type_of_d-ccc, %_type_of_d-ccc* @d-ccc, i32 0, i32 3)
ret void
}
; CHECK: define void @test_store(i8* nocapture writeonly %p)
define void @test_store(i8* %p) {
store i8 0, i8* %p
ret void
}
@G = external global i8*
; CHECK: define i8 @test_store_capture(i8* %p)
define i8 @test_store_capture(i8* %p) {
store i8* %p, i8** @G
%p2 = load i8*, i8** @G
%v = load i8, i8* %p2
ret i8 %v
}
; CHECK: define void @test_addressing(i8* nocapture writeonly %p)
define void @test_addressing(i8* %p) {
%gep = getelementptr i8, i8* %p, i64 8
%bitcast = bitcast i8* %gep to i32*
store i32 0, i32* %bitcast
ret void
}
; CHECK: define void @test_readwrite(i8* nocapture %p)
define void @test_readwrite(i8* %p) {
%v = load i8, i8* %p
store i8 %v, i8* %p
ret void
}
; CHECK: define void @test_volatile(i8* %p)
define void @test_volatile(i8* %p) {
store volatile i8 0, i8* %p
ret void
}
; CHECK: define void @test_atomicrmw(i8* nocapture %p)
define void @test_atomicrmw(i8* %p) {
atomicrmw add i8* %p, i8 0 seq_cst
ret void
}
declare void @direct1_callee(i8* %p)
; CHECK: define void @direct1(i8* %p)
define void @direct1(i8* %p) {
call void @direct1_callee(i8* %p)
ret void
}
declare void @direct2_callee(i8* %p) writeonly
; CHECK: define void @direct2(i8* %p)
define void @direct2(i8* %p) {
call void @direct2_callee(i8* %p)
ret void
}
declare void @direct3_callee(i8* writeonly %p)
; CHECK: define void @direct3(i8* %p)
define void @direct3(i8* %p) {
call void @direct3_callee(i8* %p)
ret void
}
; CHECK: define void @fptr_test1(i8* %p, void (i8*)* nocapture %f)
define void @fptr_test1(i8* %p, void (i8*)* %f) {
call void %f(i8* %p)
ret void
}
; CHECK: define void @fptr_test2(i8* %p, void (i8*)* nocapture %f)
define void @fptr_test2(i8* %p, void (i8*)* %f) {
call void %f(i8* writeonly %p)
ret void
}
; CHECK: define void @fptr_test3(i8* %p, void (i8*)* nocapture %f)
define void @fptr_test3(i8* %p, void (i8*)* %f) {
call void %f(i8* %p) writeonly
ret void
}
; CHECK: attributes #0 = { {{.*}}readnone{{.*}} }
; CHECK: attributes #1 = { {{.*}}readonly{{.*}} }
; CHECK: attributes #2 = { {{.*}}writeonly{{.*}} }