Home | History | Annotate | Download | only in EarlyCSE
      1 ; RUN: opt -S -early-cse < %s | FileCheck %s
      2 ; RUN: opt -S -basicaa -early-cse-memssa < %s | FileCheck %s
      3 
      4 declare void @clobber_and_use(i32)
      5 
      6 define void @f_0(i32* %ptr) {
      7 ; CHECK-LABEL: @f_0(
      8 ; CHECK:   %val0 = load i32, i32* %ptr, !invariant.load !0
      9 ; CHECK:   call void @clobber_and_use(i32 %val0)
     10 ; CHECK:   call void @clobber_and_use(i32 %val0)
     11 ; CHECK:   call void @clobber_and_use(i32 %val0)
     12 ; CHECK:   ret void
     13 
     14   %val0 = load i32, i32* %ptr, !invariant.load !{}
     15   call void @clobber_and_use(i32 %val0)
     16   %val1 = load i32, i32* %ptr, !invariant.load !{}
     17   call void @clobber_and_use(i32 %val1)
     18   %val2 = load i32, i32* %ptr, !invariant.load !{}
     19   call void @clobber_and_use(i32 %val2)
     20   ret void
     21 }
     22 
     23 define void @f_1(i32* %ptr) {
     24 ; We can forward invariant loads to non-invariant loads.
     25 
     26 ; CHECK-LABEL: @f_1(
     27 ; CHECK:   %val0 = load i32, i32* %ptr, !invariant.load !0
     28 ; CHECK:   call void @clobber_and_use(i32 %val0)
     29 ; CHECK:   call void @clobber_and_use(i32 %val0)
     30 
     31   %val0 = load i32, i32* %ptr, !invariant.load !{}
     32   call void @clobber_and_use(i32 %val0)
     33   %val1 = load i32, i32* %ptr
     34   call void @clobber_and_use(i32 %val1)
     35   ret void
     36 }
     37 
     38 define void @f_2(i32* %ptr) {
     39 ; We can forward a non-invariant load into an invariant load.
     40 
     41 ; CHECK-LABEL: @f_2(
     42 ; CHECK:   %val0 = load i32, i32* %ptr
     43 ; CHECK:   call void @clobber_and_use(i32 %val0)
     44 ; CHECK:   call void @clobber_and_use(i32 %val0)
     45 
     46   %val0 = load i32, i32* %ptr
     47   call void @clobber_and_use(i32 %val0)
     48   %val1 = load i32, i32* %ptr, !invariant.load !{}
     49   call void @clobber_and_use(i32 %val1)
     50   ret void
     51 }
     52 
     53 define void @f_3(i1 %cond, i32* %ptr) {
     54 ; CHECK-LABEL: @f_3(
     55   %val0 = load i32, i32* %ptr, !invariant.load !{}
     56   call void @clobber_and_use(i32 %val0)
     57   br i1 %cond, label %left, label %right
     58 
     59 ; CHECK:  %val0 = load i32, i32* %ptr, !invariant.load !0
     60 ; CHECK: left:
     61 ; CHECK-NEXT:  call void @clobber_and_use(i32 %val0)
     62 
     63 left:
     64   %val1 = load i32, i32* %ptr
     65   call void @clobber_and_use(i32 %val1)
     66   ret void
     67 
     68 right:
     69   ret void
     70 }
     71 
     72 define void @f_4(i1 %cond, i32* %ptr) {
     73 ; Negative test -- can't forward %val0 to %va1 because that'll break
     74 ; def-dominates-use.
     75 
     76 ; CHECK-LABEL: @f_4(
     77   br i1 %cond, label %left, label %merge
     78 
     79 left:
     80 ; CHECK: left:
     81 ; CHECK-NEXT:  %val0 = load i32, i32* %ptr, !invariant.load !
     82 ; CHECK-NEXT:  call void @clobber_and_use(i32 %val0)
     83 
     84   %val0 = load i32, i32* %ptr, !invariant.load !{}
     85   call void @clobber_and_use(i32 %val0)
     86   br label %merge
     87 
     88 merge:
     89 ; CHECK: merge:
     90 ; CHECK-NEXT:   %val1 = load i32, i32* %ptr
     91 ; CHECK-NEXT:   call void @clobber_and_use(i32 %val1)
     92 
     93   %val1 = load i32, i32* %ptr
     94   call void @clobber_and_use(i32 %val1)
     95   ret void
     96 }
     97 
     98 ; By assumption, the call can't change contents of p
     99 ; LangRef is a bit unclear about whether the store is reachable, so
    100 ; for the moment we chose to be conservative and just assume it's valid
    101 ; to restore the same unchanging value.
    102 define void @test_dse1(i32* %p) {
    103 ; CHECK-LABEL: @test_dse1
    104 ; CHECK-NOT: store
    105   %v1 = load i32, i32* %p, !invariant.load !{}
    106   call void @clobber_and_use(i32 %v1)
    107   store i32 %v1, i32* %p
    108   ret void
    109 }
    110 
    111 ; By assumption, v1 must equal v2 (TODO)
    112 define void @test_false_negative_dse2(i32* %p, i32 %v2) {
    113 ; CHECK-LABEL: @test_false_negative_dse2
    114 ; CHECK: store
    115   %v1 = load i32, i32* %p, !invariant.load !{}
    116   call void @clobber_and_use(i32 %v1)
    117   store i32 %v2, i32* %p
    118   ret void
    119 }
    120 
    121 ; If we remove the load, we still start an invariant scope since
    122 ; it lets us remove later loads not explicitly marked invariant
    123 define void @test_scope_start_without_load(i32* %p) {
    124 ; CHECK-LABEL: @test_scope_start_without_load
    125 ; CHECK:   %v1 = load i32, i32* %p
    126 ; CHECK:   %add = add i32 %v1, %v1
    127 ; CHECK:   call void @clobber_and_use(i32 %add)
    128 ; CHECK:   call void @clobber_and_use(i32 %v1)
    129 ; CHECK:   ret void
    130   %v1 = load i32, i32* %p
    131   %v2 = load i32, i32* %p, !invariant.load !{}
    132   %add = add i32 %v1, %v2
    133   call void @clobber_and_use(i32 %add)
    134   %v3 = load i32, i32* %p
    135   call void @clobber_and_use(i32 %v3)
    136   ret void
    137 }
    138 
    139 ; If we already have an invariant scope, don't want to start a new one
    140 ; with a potentially greater generation.  This hides the earlier invariant
    141 ; load
    142 define void @test_scope_restart(i32* %p) {
    143 ; CHECK-LABEL: @test_scope_restart
    144 ; CHECK:   %v1 = load i32, i32* %p
    145 ; CHECK:   call void @clobber_and_use(i32 %v1)
    146 ; CHECK:   %add = add i32 %v1, %v1
    147 ; CHECK:   call void @clobber_and_use(i32 %add)
    148 ; CHECK:   call void @clobber_and_use(i32 %v1)
    149 ; CHECK:   ret void
    150   %v1 = load i32, i32* %p, !invariant.load !{}
    151   call void @clobber_and_use(i32 %v1)
    152   %v2 = load i32, i32* %p, !invariant.load !{}
    153   %add = add i32 %v1, %v2
    154   call void @clobber_and_use(i32 %add)
    155   %v3 = load i32, i32* %p
    156   call void @clobber_and_use(i32 %v3)
    157   ret void
    158 }
    159