Home | History | Annotate | Download | only in Other
      1 ; The CGSCC pass manager includes an SCC iteration utility that tracks indirect
      2 ; calls that are turned into direct calls (devirtualization) and re-visits the
      3 ; SCC to expose those calls to the SCC-based IPO passes. We trigger
      4 ; devirtualization here with GVN which forwards a store through a load and to
      5 ; an indirect call.
      6 ;
      7 ; RUN: opt -aa-pipeline=basic-aa -passes='cgscc(function-attrs,function(gvn,instcombine))' -S < %s | FileCheck %s --check-prefix=CHECK --check-prefix=BEFORE
      8 ; RUN: opt -aa-pipeline=basic-aa -passes='cgscc(devirt<1>(function-attrs,function(gvn,instcombine)))' -S < %s | FileCheck %s --check-prefix=CHECK --check-prefix=AFTER --check-prefix=AFTER1
      9 ; RUN: opt -aa-pipeline=basic-aa -passes='cgscc(devirt<2>(function-attrs,function(gvn,instcombine)))' -S < %s | FileCheck %s --check-prefix=CHECK --check-prefix=AFTER --check-prefix=AFTER2
     10 ;
     11 ; We also verify that the real O2 pipeline catches these cases.
     12 ; RUN: opt -aa-pipeline=basic-aa -passes='default<O2>' -S < %s | FileCheck %s --check-prefix=CHECK --check-prefix=AFTER --check-prefix=AFTER2
     13 
     14 declare void @readnone() readnone
     15 ; CHECK: Function Attrs: readnone
     16 ; CHECK-NEXT: declare void @readnone()
     17 
     18 declare void @unknown()
     19 ; CHECK-NOT: Function Attrs
     20 ; CHECK-LABEL: declare void @unknown(){{ *$}}
     21 
     22 ; The @test1 function checks that when we refine an indirect call to a direct
     23 ; call we revisit the SCC passes to reflect the more precise information. This
     24 ; is the basic functionality.
     25 
     26 define void @test1() {
     27 ; BEFORE-NOT: Function Attrs
     28 ; AFTER: Function Attrs: readnone
     29 ; CHECK-LABEL: define void @test1()
     30 entry:
     31   %fptr = alloca void ()*
     32   store void ()* @readnone, void ()** %fptr
     33   %f = load void ()*, void ()** %fptr
     34   call void %f()
     35   ret void
     36 }
     37 
     38 ; The @test2_* functions check that when we need multiple (in this case 2)
     39 ; repetitions to compute some state that is incrementally exposed with each
     40 ; one, the limit on repetitions is enforced. So we make progress with
     41 ; one repetition but not as much as with three.
     42 ;
     43 ; This is somewhat awkward to test because we have to contrive to have a state
     44 ; repetition triggered and observed with very few passes. The technique here
     45 ; is to have one indirect call that can only be resolved when the entire SCC is
     46 ; deduced as readonly, and mark that indirect call at the call site as readonly
     47 ; to make that possible. This forces us to first deduce readonly, then
     48 ; devirtualize again, and then deduce readnone.
     49 
     50 declare void @readnone_with_arg(void ()**) readnone
     51 ; CHECK: Function Attrs: readnone
     52 ; CHECK-LABEL: declare void @readnone_with_arg(void ()**)
     53 
     54 define void @test2_a(void ()** %ignore) {
     55 ; BEFORE-NOT: Function Attrs
     56 ; AFTER1: Function Attrs: readonly
     57 ; AFTER2: Function Attrs: readnone
     58 ; BEFORE: define void @test2_a(void ()** %ignore)
     59 ; AFTER: define void @test2_a(void ()** readnone %ignore)
     60 entry:
     61   %f1ptr = alloca void (void ()**)*
     62   store void (void ()**)* @readnone_with_arg, void (void ()**)** %f1ptr
     63   %f1 = load void (void ()**)*, void (void ()**)** %f1ptr
     64   ; This indirect call is the first to be resolved, allowing us to deduce
     65   ; readonly but not (yet) readnone.
     66   call void %f1(void ()** %ignore)
     67 ; CHECK: call void @readnone_with_arg(void ()** %ignore)
     68 
     69   ; Bogus call to test2_b to make this a cycle.
     70   call void @test2_b()
     71 
     72   ret void
     73 }
     74 
     75 define void @test2_b() {
     76 ; BEFORE-NOT: Function Attrs
     77 ; AFTER1: Function Attrs: readonly
     78 ; AFTER2: Function Attrs: readnone
     79 ; CHECK-LABEL: define void @test2_b()
     80 entry:
     81   %f2ptr = alloca void ()*
     82   store void ()* @readnone, void ()** %f2ptr
     83   ; Call the other function here to prevent forwarding until the SCC has had
     84   ; function attrs deduced.
     85   call void @test2_a(void ()** %f2ptr)
     86 
     87   %f2 = load void ()*, void ()** %f2ptr
     88   ; This is the second indirect call to be resolved, and can only be resolved
     89   ; after we deduce 'readonly' for the rest of the SCC. Once it is
     90   ; devirtualized, we can deduce readnone for the SCC.
     91   call void %f2() readonly
     92 ; BEFORE: call void %f2()
     93 ; AFTER: call void @readnone()
     94 
     95   ret void
     96 }
     97 
     98 declare i8* @memcpy(i8*, i8*, i64)
     99 ; CHECK-LABEL: declare i8* @memcpy(
    100 
    101 ; The @test3 function checks that when we refine an indirect call to an
    102 ; intrinsic we still revisit the SCC pass. This also covers cases where the
    103 ; value handle itself doesn't persist due to the nature of how instcombine
    104 ; creates the memcpy intrinsic call, and we rely on the count of indirect calls
    105 ; decreasing and the count of direct calls increasing.
    106 ; Adding 'noinline' attribute to force attributes for improved matching.
    107 define void @test3(i8* %src, i8* %dest, i64 %size) noinline {
    108 ; CHECK: Function Attrs
    109 ; CHECK-NOT: read
    110 ; CHECK-SAME: noinline
    111 ; BEFORE-LABEL: define void @test3(i8* %src, i8* %dest, i64 %size)
    112 ; AFTER-LABEL: define void @test3(i8* nocapture readonly %src, i8* nocapture %dest, i64 %size)
    113   %fptr = alloca i8* (i8*, i8*, i64)*
    114   store i8* (i8*, i8*, i64)* @memcpy, i8* (i8*, i8*, i64)** %fptr
    115   %f = load i8* (i8*, i8*, i64)*, i8* (i8*, i8*, i64)** %fptr
    116   call i8* %f(i8* %dest, i8* %src, i64 %size)
    117 ; CHECK: call void @llvm.memcpy
    118   ret void
    119 }
    120 
    121 ; A boring function that just keeps our declarations around.
    122 define void @keep(i8** %sink) {
    123 ; CHECK-NOT: Function Attrs
    124 ; CHECK-LABEL: define void @keep(
    125 entry:
    126   store volatile i8* bitcast (void ()* @readnone to i8*), i8** %sink
    127   store volatile i8* bitcast (void ()* @unknown to i8*), i8** %sink
    128   store volatile i8* bitcast (i8* (i8*, i8*, i64)* @memcpy to i8*), i8** %sink
    129   call void @unknown()
    130   ret void
    131 }
    132