97 lines · plain
1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 22; RUN: opt -S -passes=gvn < %s | FileCheck %s3 4@a = external constant i325; We can value forward across the fence since we can (semantically)6; reorder the following load before the fence.7define i32 @test(ptr %addr.i) {8; CHECK-LABEL: define i32 @test9; CHECK-SAME: (ptr [[ADDR_I:%.*]]) {10; CHECK-NEXT: store i32 5, ptr [[ADDR_I]], align 411; CHECK-NEXT: fence release12; CHECK-NEXT: ret i32 513;14 store i32 5, ptr %addr.i, align 415 fence release16 %a = load i32, ptr %addr.i, align 417 ret i32 %a18}19 20; Same as above21define i32 @test2(ptr %addr.i) {22; CHECK-LABEL: define i32 @test223; CHECK-SAME: (ptr [[ADDR_I:%.*]]) {24; CHECK-NEXT: fence release25; CHECK-NEXT: ret i32 026;27 %a = load i32, ptr %addr.i, align 428 fence release29 %a2 = load i32, ptr %addr.i, align 430 %res = sub i32 %a, %a231 ret i32 %res32}33 34; We can not value forward across an acquire barrier since we might35; be syncronizing with another thread storing to the same variable36; followed by a release fence. This is not so much enforcing an37; ordering property (though it is that too), but a liveness38; property. We expect to eventually see the value of store by39; another thread when spinning on that location.40define i32 @test3(ptr noalias %addr.i, ptr noalias %otheraddr) {41; CHECK-LABEL: define i32 @test342; CHECK-SAME: (ptr noalias [[ADDR_I:%.*]], ptr noalias [[OTHERADDR:%.*]]) {43; CHECK-NEXT: fence acquire44; CHECK-NEXT: [[A:%.*]] = load i32, ptr [[ADDR_I]], align 445; CHECK-NEXT: fence acquire46; CHECK-NEXT: [[A2:%.*]] = load i32, ptr [[ADDR_I]], align 447; CHECK-NEXT: [[RES:%.*]] = sub i32 [[A]], [[A2]]48; CHECK-NEXT: ret i32 [[RES]]49;50 ; the following code is intented to model the unrolling of51 ; two iterations in a spin loop of the form:52 ; do { fence acquire: tmp = *%addr.i; ) while (!tmp);53 ; It's hopefully clear that allowing PRE to turn this into:54 ; if (!*%addr.i) while(true) {} would be unfortunate55 fence acquire56 %a = load i32, ptr %addr.i, align 457 fence acquire58 %a2 = load i32, ptr %addr.i, align 459 %res = sub i32 %a, %a260 ret i32 %res61}62 63; We can forward the value forward the load64; across both the fences, because the load is from65; a constant memory location.66define i32 @test4(ptr %addr) {67; CHECK-LABEL: define i32 @test468; CHECK-SAME: (ptr [[ADDR:%.*]]) {69; CHECK-NEXT: fence release70; CHECK-NEXT: store i32 42, ptr [[ADDR]], align 871; CHECK-NEXT: fence seq_cst72; CHECK-NEXT: ret i32 073;74 %var = load i32, ptr @a75 fence release76 store i32 42, ptr %addr, align 877 fence seq_cst78 %var2 = load i32, ptr @a79 %var3 = sub i32 %var, %var280 ret i32 %var381}82 83; Another example of why forwarding across an acquire fence is problematic84; can be seen in a normal locking operation. Say we had:85; *p = 5; unlock(l); lock(l); use(p);86; forwarding the store to p would be invalid. A reasonable implementation87; of unlock and lock might be:88; unlock() { atomicrmw sub %l, 1 unordered; fence release }89; lock() {90; do {91; %res = cmpxchg %p, 0, 1, monotonic monotonic92; } while(!%res.success)93; fence acquire;94; }95; Given we chose to forward across the release fence, we clearly can't forward96; across the acquire fence as well.97