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