llvm.org GIT mirror llvm / b5e0bec
Add an @llvm.sideeffect intrinsic This patch implements Chandler's idea [0] for supporting languages that require support for infinite loops with side effects, such as Rust, providing part of a solution to bug 965 [1]. Specifically, it adds an `llvm.sideeffect()` intrinsic, which has no actual effect, but which appears to optimization passes to have obscure side effects, such that they don't optimize away loops containing it. It also teaches several optimization passes to ignore this intrinsic, so that it doesn't significantly impact optimization in most cases. As discussed on llvm-dev [2], this patch is the first of two major parts. The second part, to change LLVM's semantics to have defined behavior on infinite loops by default, with a function attribute for opting into potential-undefined-behavior, will be implemented and posted for review in a separate patch. [0] http://lists.llvm.org/pipermail/llvm-dev/2015-July/088103.html [1] https://bugs.llvm.org/show_bug.cgi?id=965 [2] http://lists.llvm.org/pipermail/llvm-dev/2017-October/118632.html Differential Revision: https://reviews.llvm.org/D38336 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@317729 91177308-0d34-0410-b5e6-96231b3b80d8 Dan Gohman 1 year, 10 months ago
31 changed file(s) with 446 addition(s) and 6 deletion(s). Raw diff Collapse all Expand all
1426614266 a constant initializer folded into a function body. This intrinsic can be
1426714267 used to avoid the possibility of overflows when loading from such a constant.
1426814268
14269 '``llvm.sideeffect``' Intrinsic
14270 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
14271
14272 Syntax:
14273 """""""
14274
14275 ::
14276
14277 declare void @llvm.sideeffect() inaccessiblememonly nounwind
14278
14279 Overview:
14280 """""""""
14281
14282 The ``llvm.sideeffect`` intrinsic doesn't perform any operation. Optimizers
14283 treat it as having side effects, so it can be inserted into a loop to
14284 indicate that the loop shouldn't be assumed to terminate (which could
14285 potentially lead to the loop being optimized away entirely), even if it's
14286 an infinite loop with no other side effects.
14287
14288 Arguments:
14289 """"""""""
14290
14291 None.
14292
14293 Semantics:
14294 """"""""""
14295
14296 This intrinsic actually does nothing, but optimizers must assume that it
14297 has externally observable side effects.
14298
1426914299 Stack Map Intrinsics
1427014300 --------------------
1427114301
151151
152152 case Intrinsic::annotation:
153153 case Intrinsic::assume:
154 case Intrinsic::sideeffect:
154155 case Intrinsic::dbg_declare:
155156 case Intrinsic::dbg_value:
156157 case Intrinsic::invariant_start:
10221022 // FIXME: We should return 0 whenever getIntrinsicCost == TCC_Free.
10231023 case Intrinsic::lifetime_start:
10241024 case Intrinsic::lifetime_end:
1025 case Intrinsic::sideeffect:
10251026 return 0;
10261027 case Intrinsic::masked_store:
10271028 return static_cast(this)
808808
809809 // NOP: calls/invokes to this intrinsic are removed by codegen
810810 def int_donothing : Intrinsic<[], [], [IntrNoMem]>;
811
812 // This instruction has no actual effect, though it is treated by the optimizer
813 // has having opaque side effects. This may be inserted into loops to ensure
814 // that they are not removed even if they turn out to be empty, for languages
815 // which specify that infinite loops must be preserved.
816 def int_sideeffect : Intrinsic<[], [], [IntrInaccessibleMemOnly]>;
811817
812818 // Intrisics to support half precision floating point format
813819 let IntrProperties = [IntrNoMem] in {
435435 break;
436436 // FIXME: Add lifetime/invariant intrinsics (See: PR30807).
437437 case Intrinsic::assume:
438 case Intrinsic::sideeffect:
438439 return;
439440 }
440441 }
432432 default: break;
433433 // FIXME: This list is repeated from NoTTI::getIntrinsicCost.
434434 case Intrinsic::assume:
435 case Intrinsic::sideeffect:
435436 case Intrinsic::dbg_declare:
436437 case Intrinsic::dbg_value:
437438 case Intrinsic::invariant_start:
38563857 // FIXME: This isn't aggressive enough; a call which only writes to a global
38573858 // is guaranteed to return.
38583859 return CS.onlyReadsMemory() || CS.onlyAccessesArgMemory() ||
3859 match(I, m_Intrinsic());
3860 match(I, m_Intrinsic()) ||
3861 match(I, m_Intrinsic());
38603862 }
38613863
38623864 // Other instructions return normally.
9090 return Intrinsic::not_intrinsic;
9191
9292 if (isTriviallyVectorizable(ID) || ID == Intrinsic::lifetime_start ||
93 ID == Intrinsic::lifetime_end || ID == Intrinsic::assume)
93 ID == Intrinsic::lifetime_end || ID == Intrinsic::assume ||
94 ID == Intrinsic::sideeffect)
9495 return ID;
9596 return Intrinsic::not_intrinsic;
9697 }
11311131 case Intrinsic::lifetime_end:
11321132 // The donothing intrinsic does, well, nothing.
11331133 case Intrinsic::donothing:
1134 // Neither does the sideeffect intrinsic.
1135 case Intrinsic::sideeffect:
11341136 // Neither does the assume intrinsic; it's also OK not to codegen its operand.
11351137 case Intrinsic::assume:
11361138 return true;
57015701 return nullptr;
57025702 case Intrinsic::assume:
57035703 case Intrinsic::var_annotation:
5704 // Discard annotate attributes and assumptions
5704 case Intrinsic::sideeffect:
5705 // Discard annotate attributes, assumptions, and artificial side-effects.
57055706 return nullptr;
57065707
57075708 case Intrinsic::codeview_annotation: {
708708 continue;
709709 }
710710
711 // Skip sideeffect intrinsics, for the same reason as assume intrinsics.
712 if (match(Inst, m_Intrinsic())) {
713 DEBUG(dbgs() << "EarlyCSE skipping sideeffect: " << *Inst << '\n');
714 continue;
715 }
716
711717 // Skip invariant.start intrinsics since they only read memory, and we can
712718 // forward values across it. Also, we dont need to consume the last store
713719 // since the semantics of invariant.start allow us to perform DSE of the
11091109 else if (auto *Call = dyn_cast(&I1)) {
11101110 if (auto *Intr = dyn_cast(Call)) {
11111111 if (isa(Intr) ||
1112 Intr->getIntrinsicID() == Intrinsic::assume)
1112 Intr->getIntrinsicID() == Intrinsic::assume ||
1113 Intr->getIntrinsicID() == Intrinsic::sideeffect)
11131114 continue;
11141115 }
11151116 if (Call->mayHaveSideEffects())
432432 DEBUG(dbgs() << "Skipping assume intrinsic.\n");
433433 ++CurInst;
434434 continue;
435 } else if (II->getIntrinsicID() == Intrinsic::sideeffect) {
436 DEBUG(dbgs() << "Skipping sideeffect intrinsic.\n");
437 ++CurInst;
438 continue;
435439 }
436440
437441 DEBUG(dbgs() << "Unknown intrinsic. Can not evaluate.\n");
3333 #include "llvm/IR/InstrTypes.h"
3434 #include "llvm/IR/Instruction.h"
3535 #include "llvm/IR/Instructions.h"
36 #include "llvm/IR/IntrinsicInst.h"
3637 #include "llvm/IR/Module.h"
3738 #include "llvm/IR/Type.h"
3839 #include "llvm/IR/User.h"
499500 MemoryInstrs.push_back(&I);
500501 else
501502 ChainInstrs.push_back(&I);
503 } else if (isa(&I) &&
504 cast(&I)->getIntrinsicID() ==
505 Intrinsic::sideeffect) {
506 // Ignore llvm.sideeffect calls.
502507 } else if (IsLoadChain && (I.mayWriteToMemory() || I.mayThrow())) {
503508 DEBUG(dbgs() << "LSV: Found may-write/throw operation: " << I << '\n');
504509 break;
81168116 if (CallInst *CI = dyn_cast(I)) {
81178117 Intrinsic::ID ID = getVectorIntrinsicIDForCall(CI, TLI);
81188118 if (ID && (ID == Intrinsic::assume || ID == Intrinsic::lifetime_end ||
8119 ID == Intrinsic::lifetime_start))
8119 ID == Intrinsic::lifetime_start || ID == Intrinsic::sideeffect))
81208120 return false;
81218121 }
81228122
36113611 "new ScheduleData already in scheduling region");
36123612 SD->init(SchedulingRegionID, I);
36133613
3614 if (I->mayReadOrWriteMemory()) {
3614 if (I->mayReadOrWriteMemory() &&
3615 (!isa(I) ||
3616 cast(I)->getIntrinsicID() != Intrinsic::sideeffect)) {
36153617 // Update the linked list of memory accessing instructions.
36163618 if (CurrentLoadStore) {
36173619 CurrentLoadStore->NextLoadStore = SD;
4444 %q = call i8* @llvm.invariant.group.barrier(i8* %p)
4545 ret i8* %q
4646 }
47
48 ; sideeffect
49
50 declare void @llvm.sideeffect()
51
52 define void @test_sideeffect() {
53 call void @llvm.sideeffect()
54 ret void
55 }
0 ; RUN: opt -S < %s -instcombine | FileCheck %s
1
2 declare void @llvm.sideeffect()
3
4 ; Don't DCE llvm.sideeffect calls.
5
6 ; CHECK-LABEL: dce
7 ; CHECK: call void @llvm.sideeffect()
8 define void @dce() {
9 call void @llvm.sideeffect()
10 ret void
11 }
0 ; RUN: opt -S < %s -dse | FileCheck %s
1
2 declare void @llvm.sideeffect()
3
4 ; Dead store elimination across a @llvm.sideeffect.
5
6 ; CHECK-LABEL: dse
7 ; CHECK: store
8 ; CHECK-NOT: store
9 define void @dse(float* %p) {
10 store float 0.0, float* %p
11 call void @llvm.sideeffect()
12 store float 0.0, float* %p
13 ret void
14 }
0 ; RUN: opt -S < %s -early-cse | FileCheck %s
1
2 declare void @llvm.sideeffect()
3
4 ; Store-to-load forwarding across a @llvm.sideeffect.
5
6 ; CHECK-LABEL: s2l
7 ; CHECK-NOT: load
8 define float @s2l(float* %p) {
9 store float 0.0, float* %p
10 call void @llvm.sideeffect()
11 %t = load float, float* %p
12 ret float %t
13 }
14
15 ; Redundant load elimination across a @llvm.sideeffect.
16
17 ; CHECK-LABEL: rle
18 ; CHECK: load
19 ; CHECK-NOT: load
20 define float @rle(float* %p) {
21 %r = load float, float* %p
22 call void @llvm.sideeffect()
23 %s = load float, float* %p
24 %t = fadd float %r, %s
25 ret float %t
26 }
0 ; RUN: opt -S < %s -functionattrs | FileCheck %s
1
2 declare void @llvm.sideeffect()
3
4 ; Don't add readnone or similar attributes when an @llvm.sideeffect() intrinsic
5 ; is present.
6
7 ; CHECK: define void @test() {
8 define void @test() {
9 call void @llvm.sideeffect()
10 ret void
11 }
12
13 ; CHECK: define void @loop() {
14 define void @loop() {
15 br label %loop
16
17 loop:
18 call void @llvm.sideeffect()
19 br label %loop
20 }
0 ; RUN: opt -S < %s -gvn | FileCheck %s
1
2 declare void @llvm.sideeffect()
3
4 ; Store-to-load forwarding across a @llvm.sideeffect.
5
6 ; CHECK-LABEL: s2l
7 ; CHECK-NOT: load
8 define float @s2l(float* %p) {
9 store float 0.0, float* %p
10 call void @llvm.sideeffect()
11 %t = load float, float* %p
12 ret float %t
13 }
14
15 ; Redundant load elimination across a @llvm.sideeffect.
16
17 ; CHECK-LABEL: rle
18 ; CHECK: load
19 ; CHECK-NOT: load
20 define float @rle(float* %p) {
21 %r = load float, float* %p
22 call void @llvm.sideeffect()
23 %s = load float, float* %p
24 %t = fadd float %r, %s
25 ret float %t
26 }
27
28 ; LICM across a @llvm.sideeffect.
29
30 ; CHECK-LABEL: licm
31 ; CHECK: load
32 ; CHECK: loop:
33 ; CHECK-NOT: load
34 define float @licm(i64 %n, float* nocapture readonly %p) #0 {
35 bb0:
36 br label %loop
37
38 loop:
39 %i = phi i64 [ 0, %bb0 ], [ %t5, %loop ]
40 %sum = phi float [ 0.000000e+00, %bb0 ], [ %t4, %loop ]
41 call void @llvm.sideeffect()
42 %t3 = load float, float* %p
43 %t4 = fadd float %sum, %t3
44 %t5 = add i64 %i, 1
45 %t6 = icmp ult i64 %t5, %n
46 br i1 %t6, label %loop, label %bb2
47
48 bb2:
49 ret float %t4
50 }
0 ; RUN: opt -S < %s -gvn-hoist | FileCheck %s
1
2 declare void @llvm.sideeffect()
3
4 ; GVN hoisting across a @llvm.sideeffect.
5
6 ; CHECK-LABEL: scalarsHoisting
7 ; CHECK: = fsub
8 ; CHECK: br i1 %cmp,
9 ; CHECK-NOT: fsub
10 define float @scalarsHoisting(float %d, float %m, float %a, i1 %cmp) {
11 entry:
12 br i1 %cmp, label %if.then, label %if.else
13
14 if.then:
15 call void @llvm.sideeffect()
16 %sub0 = fsub float %m, %a
17 %mul = fmul float %sub0, %d
18 br label %if.end
19
20 if.else:
21 %sub1 = fsub float %m, %a
22 %div = fdiv float %sub1, %d
23 br label %if.end
24
25 if.end:
26 %phi = phi float [ %mul, %if.then ], [ %div, %if.else ]
27 ret float %phi
28 }
29
0 ; RUN: opt -S < %s -gvn-sink | FileCheck %s
1
2 declare void @llvm.sideeffect()
3
4 ; GVN sinking across a @llvm.sideeffect.
5
6 ; CHECK-LABEL: scalarsSinking
7 ; CHECK-NOT: fmul
8 ; CHECK: = phi
9 ; CHECK: = fmul
10 define float @scalarsSinking(float %d, float %m, float %a, i1 %cmp) {
11 entry:
12 br i1 %cmp, label %if.then, label %if.else
13
14 if.then:
15 call void @llvm.sideeffect()
16 %sub = fsub float %m, %a
17 %mul0 = fmul float %sub, %d
18 br label %if.end
19
20 if.else:
21 %add = fadd float %m, %a
22 %mul1 = fmul float %add, %d
23 br label %if.end
24
25 if.end:
26 %phi = phi float [ %mul0, %if.then ], [ %mul1, %if.else ]
27 ret float %phi
28 }
29
0 ; RUN: opt -S < %s -globalopt | FileCheck %s
1
2 ; Static evaluation across a @llvm.sideeffect.
3
4 ; CHECK-NOT: store
5
6 declare void @llvm.sideeffect()
7
8 @llvm.global_ctors = appending global [1 x { i32, void ()* }] [ { i32, void ()* } { i32 65535, void ()* @ctor } ]
9 @G = global i32 0
10
11 define internal void @ctor() {
12 store i32 1, i32* @G
13 call void @llvm.sideeffect()
14 ret void
15 }
0 ; RUN: opt -S < %s -instcombine | FileCheck %s
1
2 declare void @llvm.sideeffect()
3
4 ; Store-to-load forwarding across a @llvm.sideeffect.
5
6 ; CHECK-LABEL: s2l
7 ; CHECK-NOT: load
8 define float @s2l(float* %p) {
9 store float 0.0, float* %p
10 call void @llvm.sideeffect()
11 %t = load float, float* %p
12 ret float %t
13 }
0 ; RUN: opt -S < %s -licm | FileCheck %s
1
2 declare void @llvm.sideeffect()
3
4 ; LICM across a @llvm.sideeffect.
5
6 ; CHECK-LABEL: licm
7 ; CHECK: load
8 ; CHECK: loop:
9 ; CHECK-NOT: load
10 define float @licm(i64 %n, float* nocapture readonly %p) #0 {
11 bb0:
12 br label %loop
13
14 loop:
15 %i = phi i64 [ 0, %bb0 ], [ %t5, %loop ]
16 %sum = phi float [ 0.000000e+00, %bb0 ], [ %t4, %loop ]
17 call void @llvm.sideeffect()
18 %t3 = load float, float* %p
19 %t4 = fadd float %sum, %t3
20 %t5 = add i64 %i, 1
21 %t6 = icmp ult i64 %t5, %n
22 br i1 %t6, label %loop, label %bb2
23
24 bb2:
25 ret float %t4
26 }
0 ; RUN: opt -S < %s -load-store-vectorizer | FileCheck %s
1
2 declare void @llvm.sideeffect()
3
4 ; load-store vectorization across a @llvm.sideeffect.
5
6 ; CHECK-LABEL: test
7 ; CHECK: load <4 x float>
8 ; CHECK: store <4 x float>
9 define void @test(float* %p) {
10 %p0 = getelementptr float, float* %p, i64 0
11 %p1 = getelementptr float, float* %p, i64 1
12 %p2 = getelementptr float, float* %p, i64 2
13 %p3 = getelementptr float, float* %p, i64 3
14 %l0 = load float, float* %p0, align 16
15 %l1 = load float, float* %p1
16 %l2 = load float, float* %p2
17 call void @llvm.sideeffect()
18 %l3 = load float, float* %p3
19 store float %l0, float* %p0, align 16
20 call void @llvm.sideeffect()
21 store float %l1, float* %p1
22 store float %l2, float* %p2
23 store float %l3, float* %p3
24 ret void
25 }
0 ; RUN: opt -S < %s -loop-idiom | FileCheck %s
1
2 declare void @llvm.sideeffect()
3
4 ; Loop idiom recognition across a @llvm.sideeffect.
5
6 ; CHECK-LABEL: zero
7 ; CHECK: llvm.memset
8 define void @zero(float* %p, i64 %n) nounwind {
9 bb7.lr.ph:
10 br label %bb7
11
12 bb7:
13 %i.02 = phi i64 [ 0, %bb7.lr.ph ], [ %tmp13, %bb7 ]
14 %tmp10 = getelementptr inbounds float, float* %p, i64 %i.02
15 store float 0.000000e+00, float* %tmp10, align 4
16 %tmp13 = add i64 %i.02, 1
17 %tmp6 = icmp ult i64 %tmp13, %n
18 br i1 %tmp6, label %bb7, label %bb14
19
20 bb14:
21 ret void
22 }
0 ; RUN: opt -S < %s -loop-vectorize -force-vector-width=4 | FileCheck %s
1
2 declare void @llvm.sideeffect()
3
4 ; Vectorization across a @llvm.sideeffect.
5
6 ; CHECK-LABEL: store_ones
7 ; CHECK: store <4 x float>
8 define void @store_ones(float* %p, i64 %n) nounwind {
9 bb7.lr.ph:
10 br label %bb7
11
12 bb7:
13 %i.02 = phi i64 [ 0, %bb7.lr.ph ], [ %tmp13, %bb7 ]
14 call void @llvm.sideeffect()
15 %tmp10 = getelementptr inbounds float, float* %p, i64 %i.02
16 store float 1.0, float* %tmp10, align 4
17 %tmp13 = add i64 %i.02, 1
18 %tmp6 = icmp ult i64 %tmp13, %n
19 br i1 %tmp6, label %bb7, label %bb14
20
21 bb14:
22 ret void
23 }
0 ; RUN: opt -S < %s -newgvn | FileCheck %s
1
2 declare void @llvm.sideeffect()
3
4 ; Store-to-load forwarding across a @llvm.sideeffect.
5
6 ; CHECK-LABEL: s2l
7 ; CHECK-NOT: load
8 define float @s2l(float* %p) {
9 store float 0.0, float* %p
10 call void @llvm.sideeffect()
11 %t = load float, float* %p
12 ret float %t
13 }
14
15 ; Redundant load elimination across a @llvm.sideeffect.
16
17 ; CHECK-LABEL: rle
18 ; CHECK: load
19 ; CHECK-NOT: load
20 define float @rle(float* %p) {
21 %r = load float, float* %p
22 call void @llvm.sideeffect()
23 %s = load float, float* %p
24 %t = fadd float %r, %s
25 ret float %t
26 }
0 ; RUN: opt -S < %s -slp-vectorizer -slp-max-reg-size=128 -slp-min-reg-size=128 | FileCheck %s
1
2 declare void @llvm.sideeffect()
3
4 ; SLP vectorization across a @llvm.sideeffect.
5
6 ; CHECK-LABEL: test
7 ; CHECK: store <4 x float>
8 define void @test(float* %p) {
9 %p0 = getelementptr float, float* %p, i64 0
10 %p1 = getelementptr float, float* %p, i64 1
11 %p2 = getelementptr float, float* %p, i64 2
12 %p3 = getelementptr float, float* %p, i64 3
13 %l0 = load float, float* %p0
14 %l1 = load float, float* %p1
15 %l2 = load float, float* %p2
16 call void @llvm.sideeffect()
17 %l3 = load float, float* %p3
18 store float %l0, float* %p0
19 call void @llvm.sideeffect()
20 store float %l1, float* %p1
21 store float %l2, float* %p2
22 store float %l3, float* %p3
23 ret void
24 }