| ; 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 | |
| } |