brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.5 KiB · 990d474 Raw
135 lines · plain
1; The CGSCC pass manager includes an SCC iteration utility that tracks indirect2; calls that are turned into direct calls (devirtualization) and re-visits the3; SCC to expose those calls to the SCC-based IPO passes. We trigger4; devirtualization here with GVN which forwards a store through a load and to5; an indirect call.6;7; RUN: opt -aa-pipeline=basic-aa -passes='module(inferattrs),cgscc(function-attrs,function(gvn,instcombine))' -S < %s | FileCheck %s --check-prefix=CHECK --check-prefix=BEFORE8; RUN: opt -aa-pipeline=basic-aa -passes='module(inferattrs),cgscc(devirt<1>(function-attrs,function(gvn,instcombine)))' -S < %s | FileCheck %s --check-prefix=CHECK --check-prefix=AFTER --check-prefix=AFTER19; RUN: opt -aa-pipeline=basic-aa -passes='module(inferattrs),cgscc(devirt<2>(function-attrs,function(gvn,instcombine)))' -S < %s | FileCheck %s --check-prefix=CHECK --check-prefix=AFTER --check-prefix=AFTER210;11; RUN: not --crash opt -abort-on-max-devirt-iterations-reached -aa-pipeline=basic-aa -passes='module(inferattrs),cgscc(devirt<1>(function-attrs,function(gvn,instcombine)))' -S < %s12; RUN: opt -abort-on-max-devirt-iterations-reached -aa-pipeline=basic-aa -passes='module(inferattrs),cgscc(devirt<2>(function-attrs,function(gvn,instcombine)))' -S < %s13;14; We also verify that the real O2 pipeline catches these cases.15; RUN: opt -aa-pipeline=basic-aa -passes='default<O2>' -S < %s | FileCheck %s --check-prefix=CHECK --check-prefix=AFTER --check-prefix=AFTER216 17declare void @readnone() readnone18; CHECK: Function Attrs: nofree nosync memory(none)19; CHECK-NEXT: declare void @readnone()20 21declare void @unknown()22; CHECK-NOT: Function Attrs23; CHECK-LABEL: declare void @unknown(){{ *$}}24 25; The @test1 function checks that when we refine an indirect call to a direct26; call we revisit the SCC passes to reflect the more precise information. This27; is the basic functionality.28 29define void @test1() {30; BEFORE-NOT: Function Attrs31; AFTER: Function Attrs: nofree nosync memory(none)32; CHECK-LABEL: define void @test1()33entry:34  %fptr = alloca ptr35  store ptr @readnone, ptr %fptr36  %f = load ptr, ptr %fptr37  call void %f()38  ret void39}40 41; The @test2_* functions check that when we need multiple (in this case 2)42; repetitions to compute some state that is incrementally exposed with each43; one, the limit on repetitions is enforced. So we make progress with44; one repetition but not as much as with three.45;46; This is somewhat awkward to test because we have to contrive to have a state47; repetition triggered and observed with very few passes. The technique here48; is to have one indirect call that can only be resolved when the entire SCC is49; deduced as readonly, and mark that indirect call at the call site as readonly50; to make that possible. This forces us to first deduce readonly, then51; devirtualize again, and then deduce readnone.52 53declare void @readnone_with_arg(ptr) readnone54; CHECK: Function Attrs: nofree nosync memory(none)55; CHECK-LABEL: declare void @readnone_with_arg(ptr)56 57define void @test2_a(ptr %ignore) {58; BEFORE-NOT: Function Attrs59; AFTER1: Function Attrs: nofree memory(read)60; AFTER2: Function Attrs: nofree nosync memory(none)61; BEFORE: define void @test2_a(ptr %ignore)62; AFTER: define void @test2_a(ptr readnone captures(address) %ignore)63entry:64  %f1ptr = alloca ptr65  store ptr @readnone_with_arg, ptr %f1ptr66  %f1 = load ptr, ptr %f1ptr67  ; This indirect call is the first to be resolved, allowing us to deduce68  ; readonly but not (yet) readnone.69  call void %f1(ptr %ignore)70; CHECK: call void @readnone_with_arg(ptr %ignore)71 72  ; Bogus call to test2_b to make this a cycle.73  call void @test2_b()74 75  ret void76}77 78define void @test2_b() {79; BEFORE-NOT: Function Attrs80; AFTER1: Function Attrs: nofree memory(read)81; AFTER2: Function Attrs: nofree nosync memory(none)82; CHECK-LABEL: define void @test2_b()83entry:84  %f2ptr = alloca ptr85  store ptr @readnone, ptr %f2ptr86  ; Call the other function here to prevent forwarding until the SCC has had87  ; function attrs deduced.88  call void @test2_a(ptr %f2ptr)89 90  %f2 = load ptr, ptr %f2ptr91  ; This is the second indirect call to be resolved, and can only be resolved92  ; after we deduce 'readonly' for the rest of the SCC. Once it is93  ; devirtualized, we can deduce readnone for the SCC.94  call void %f2() readonly95; BEFORE: call void %f2()96; AFTER: call void @readnone()97 98  ret void99}100 101declare ptr @memcpy(ptr, ptr, i64)102; CHECK-LABEL: ptr @memcpy(103 104; The @test3 function checks that when we refine an indirect call to an105; intrinsic we still revisit the SCC pass. This also covers cases where the106; value handle itself doesn't persist due to the nature of how instcombine107; creates the memcpy intrinsic call, and we rely on the count of indirect calls108; decreasing and the count of direct calls increasing.109; Adding 'noinline' attribute to force attributes for improved matching.110define void @test3(ptr %src, ptr %dest, i64 %size) noinline {111; CHECK: Function Attrs112; CHECK-NOT: read113; CHECK-SAME: noinline114; BEFORE-LABEL: define void @test3(ptr %src, ptr %dest, i64 %size)115; AFTER-LABEL: define void @test3(ptr readonly captures(none) %src, ptr writeonly captures(none) %dest, i64 %size)116  %fptr = alloca ptr117  store ptr @memcpy, ptr %fptr118  %f = load ptr, ptr %fptr119  call ptr %f(ptr %dest, ptr %src, i64 %size)120; CHECK: call void @llvm.memcpy121  ret void122}123 124; A boring function that just keeps our declarations around.125define void @keep(ptr %sink) {126; CHECK-NOT: Function Attrs127; CHECK-LABEL: define void @keep(128entry:129  store volatile ptr @readnone, ptr %sink130  store volatile ptr @unknown, ptr %sink131  store volatile ptr @memcpy, ptr %sink132  call void @unknown()133  ret void134}135