Add support for CSE of writeonly calls, similar to the existing support for readonly calls.
164 lines
4.9 KiB
LLVM
164 lines
4.9 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
|
; RUN: opt -S -passes=early-cse -earlycse-debug-hash < %s | FileCheck %s --check-prefixes=CHECK,NO-MSSA
|
|
; RUN: opt -S -passes='early-cse<memssa>' < %s | FileCheck %s --check-prefixes=CHECK,MSSA
|
|
|
|
@var = global i32 undef
|
|
declare void @foo() nounwind
|
|
|
|
define void @test() {
|
|
; CHECK-LABEL: @test(
|
|
; CHECK-NEXT: call void @foo() #[[ATTR1:[0-9]+]]
|
|
; CHECK-NEXT: store i32 2, ptr @var, align 4
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
store i32 1, ptr @var
|
|
call void @foo() writeonly
|
|
store i32 2, ptr @var
|
|
ret void
|
|
}
|
|
|
|
declare void @writeonly_void() memory(write)
|
|
|
|
; Can CSE writeonly calls, including non-nounwind/willreturn.
|
|
define void @writeonly_cse() {
|
|
; CHECK-LABEL: @writeonly_cse(
|
|
; CHECK-NEXT: call void @writeonly_void()
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
call void @writeonly_void()
|
|
call void @writeonly_void()
|
|
ret void
|
|
}
|
|
|
|
; Can CSE, loads do not matter.
|
|
define i32 @writeonly_cse_intervening_load(ptr %p) {
|
|
; CHECK-LABEL: @writeonly_cse_intervening_load(
|
|
; CHECK-NEXT: call void @writeonly_void()
|
|
; CHECK-NEXT: [[V:%.*]] = load i32, ptr [[P:%.*]], align 4
|
|
; CHECK-NEXT: ret i32 [[V]]
|
|
;
|
|
call void @writeonly_void()
|
|
%v = load i32, ptr %p
|
|
call void @writeonly_void()
|
|
ret i32 %v
|
|
}
|
|
|
|
; Cannot CSE, the store may be to the same memory.
|
|
define void @writeonly_cse_intervening_store(ptr %p) {
|
|
; CHECK-LABEL: @writeonly_cse_intervening_store(
|
|
; CHECK-NEXT: call void @writeonly_void()
|
|
; CHECK-NEXT: store i32 0, ptr [[P:%.*]], align 4
|
|
; CHECK-NEXT: call void @writeonly_void()
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
call void @writeonly_void()
|
|
store i32 0, ptr %p
|
|
call void @writeonly_void()
|
|
ret void
|
|
}
|
|
|
|
; Can CSE, the store does not alias the writeonly call.
|
|
define void @writeonly_cse_intervening_noalias_store(ptr noalias %p) {
|
|
; NO-MSSA-LABEL: @writeonly_cse_intervening_noalias_store(
|
|
; NO-MSSA-NEXT: call void @writeonly_void()
|
|
; NO-MSSA-NEXT: store i32 0, ptr [[P:%.*]], align 4
|
|
; NO-MSSA-NEXT: call void @writeonly_void()
|
|
; NO-MSSA-NEXT: ret void
|
|
;
|
|
; MSSA-LABEL: @writeonly_cse_intervening_noalias_store(
|
|
; MSSA-NEXT: call void @writeonly_void()
|
|
; MSSA-NEXT: store i32 0, ptr [[P:%.*]], align 4
|
|
; MSSA-NEXT: ret void
|
|
;
|
|
call void @writeonly_void()
|
|
store i32 0, ptr %p
|
|
call void @writeonly_void()
|
|
ret void
|
|
}
|
|
|
|
; Cannot CSE loads across writeonly call.
|
|
define i32 @load_cse_across_writeonly(ptr %p) {
|
|
; CHECK-LABEL: @load_cse_across_writeonly(
|
|
; CHECK-NEXT: [[V1:%.*]] = load i32, ptr [[P:%.*]], align 4
|
|
; CHECK-NEXT: call void @writeonly_void()
|
|
; CHECK-NEXT: [[V2:%.*]] = load i32, ptr [[P]], align 4
|
|
; CHECK-NEXT: [[RES:%.*]] = sub i32 [[V1]], [[V2]]
|
|
; CHECK-NEXT: ret i32 [[RES]]
|
|
;
|
|
%v1 = load i32, ptr %p
|
|
call void @writeonly_void()
|
|
%v2 = load i32, ptr %p
|
|
%res = sub i32 %v1, %v2
|
|
ret i32 %res
|
|
}
|
|
|
|
; Can CSE loads across eliminated writeonly call.
|
|
define i32 @load_cse_across_csed_writeonly(ptr %p) {
|
|
; CHECK-LABEL: @load_cse_across_csed_writeonly(
|
|
; CHECK-NEXT: call void @writeonly_void()
|
|
; CHECK-NEXT: [[V2:%.*]] = load i32, ptr [[P:%.*]], align 4
|
|
; CHECK-NEXT: ret i32 0
|
|
;
|
|
call void @writeonly_void()
|
|
%v1 = load i32, ptr %p
|
|
call void @writeonly_void()
|
|
%v2 = load i32, ptr %p
|
|
%res = sub i32 %v1, %v2
|
|
ret i32 %res
|
|
}
|
|
|
|
declare i32 @writeonly(ptr %p) memory(write)
|
|
|
|
; Can CSE writeonly calls with arg and return.
|
|
define i32 @writeonly_ret_cse(ptr %p) {
|
|
; CHECK-LABEL: @writeonly_ret_cse(
|
|
; CHECK-NEXT: [[V2:%.*]] = call i32 @writeonly(ptr [[P:%.*]])
|
|
; CHECK-NEXT: ret i32 0
|
|
;
|
|
%v1 = call i32 @writeonly(ptr %p)
|
|
%v2 = call i32 @writeonly(ptr %p)
|
|
%res = sub i32 %v1, %v2
|
|
ret i32 %res
|
|
}
|
|
|
|
; Cannot CSE writeonly calls with different arguments.
|
|
define i32 @writeonly_different_args(ptr %p1, ptr %p2) {
|
|
; CHECK-LABEL: @writeonly_different_args(
|
|
; CHECK-NEXT: [[V1:%.*]] = call i32 @writeonly(ptr [[P1:%.*]])
|
|
; CHECK-NEXT: [[V2:%.*]] = call i32 @writeonly(ptr [[P2:%.*]])
|
|
; CHECK-NEXT: [[RES:%.*]] = sub i32 [[V1]], [[V2]]
|
|
; CHECK-NEXT: ret i32 [[RES]]
|
|
;
|
|
%v1 = call i32 @writeonly(ptr %p1)
|
|
%v2 = call i32 @writeonly(ptr %p2)
|
|
%res = sub i32 %v1, %v2
|
|
ret i32 %res
|
|
}
|
|
|
|
declare void @callee()
|
|
|
|
; These are weird cases where the same call is both readonly and writeonly
|
|
; based on call-site attributes. I believe this implies that both calls are
|
|
; actually readnone and safe to CSE, but leave them alone to be conservative.
|
|
define void @readonly_and_writeonly() {
|
|
; CHECK-LABEL: @readonly_and_writeonly(
|
|
; CHECK-NEXT: call void @callee() #[[ATTR2:[0-9]+]]
|
|
; CHECK-NEXT: call void @callee() #[[ATTR1]]
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
call void @callee() memory(read)
|
|
call void @callee() memory(write)
|
|
ret void
|
|
}
|
|
|
|
define void @writeonly_and_readonly() {
|
|
; CHECK-LABEL: @writeonly_and_readonly(
|
|
; CHECK-NEXT: call void @callee() #[[ATTR1]]
|
|
; CHECK-NEXT: call void @callee() #[[ATTR2]]
|
|
; CHECK-NEXT: ret void
|
|
;
|
|
call void @callee() memory(write)
|
|
call void @callee() memory(read)
|
|
ret void
|
|
}
|