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