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

llvm-svn: 317729
diff --git a/llvm/test/CodeGen/Generic/intrinsics.ll b/llvm/test/CodeGen/Generic/intrinsics.ll
index 4d04786..6a51d2d 100644
--- a/llvm/test/CodeGen/Generic/intrinsics.ll
+++ b/llvm/test/CodeGen/Generic/intrinsics.ll
@@ -45,3 +45,12 @@
         %q = call i8* @llvm.invariant.group.barrier(i8* %p)
         ret i8* %q
 }
+
+; sideeffect
+
+declare void @llvm.sideeffect()
+
+define void @test_sideeffect() {
+    call void @llvm.sideeffect()
+    ret void
+}
diff --git a/llvm/test/Transforms/DCE/int_sideeffect.ll b/llvm/test/Transforms/DCE/int_sideeffect.ll
new file mode 100644
index 0000000..af06303
--- /dev/null
+++ b/llvm/test/Transforms/DCE/int_sideeffect.ll
@@ -0,0 +1,12 @@
+; RUN: opt -S < %s -instcombine | FileCheck %s
+
+declare void @llvm.sideeffect()
+
+; Don't DCE llvm.sideeffect calls.
+
+; CHECK-LABEL: dce
+; CHECK: call void @llvm.sideeffect()
+define void @dce() {
+    call void @llvm.sideeffect()
+    ret void
+}
diff --git a/llvm/test/Transforms/DeadStoreElimination/int_sideeffect.ll b/llvm/test/Transforms/DeadStoreElimination/int_sideeffect.ll
new file mode 100644
index 0000000..035e787
--- /dev/null
+++ b/llvm/test/Transforms/DeadStoreElimination/int_sideeffect.ll
@@ -0,0 +1,15 @@
+; RUN: opt -S < %s -dse | FileCheck %s
+
+declare void @llvm.sideeffect()
+
+; Dead store elimination across a @llvm.sideeffect.
+
+; CHECK-LABEL: dse
+; CHECK: store
+; CHECK-NOT: store
+define void @dse(float* %p) {
+    store float 0.0, float* %p
+    call void @llvm.sideeffect()
+    store float 0.0, float* %p
+    ret void
+}
diff --git a/llvm/test/Transforms/EarlyCSE/int_sideeffect.ll b/llvm/test/Transforms/EarlyCSE/int_sideeffect.ll
new file mode 100644
index 0000000..1dccaab
--- /dev/null
+++ b/llvm/test/Transforms/EarlyCSE/int_sideeffect.ll
@@ -0,0 +1,27 @@
+; RUN: opt -S < %s -early-cse | FileCheck %s
+
+declare void @llvm.sideeffect()
+
+; Store-to-load forwarding across a @llvm.sideeffect.
+
+; CHECK-LABEL: s2l
+; CHECK-NOT: load
+define float @s2l(float* %p) {
+    store float 0.0, float* %p
+    call void @llvm.sideeffect()
+    %t = load float, float* %p
+    ret float %t
+}
+
+; Redundant load elimination across a @llvm.sideeffect.
+
+; CHECK-LABEL: rle
+; CHECK: load
+; CHECK-NOT: load
+define float @rle(float* %p) {
+    %r = load float, float* %p
+    call void @llvm.sideeffect()
+    %s = load float, float* %p
+    %t = fadd float %r, %s
+    ret float %t
+}
diff --git a/llvm/test/Transforms/FunctionAttrs/int_sideeffect.ll b/llvm/test/Transforms/FunctionAttrs/int_sideeffect.ll
new file mode 100644
index 0000000..13091e3
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/int_sideeffect.ll
@@ -0,0 +1,21 @@
+; RUN: opt -S < %s -functionattrs | FileCheck %s
+
+declare void @llvm.sideeffect()
+
+; Don't add readnone or similar attributes when an @llvm.sideeffect() intrinsic
+; is present.
+
+; CHECK: define void @test() {
+define void @test() {
+    call void @llvm.sideeffect()
+    ret void
+}
+
+; CHECK: define void @loop() {
+define void @loop() {
+    br label %loop
+
+loop:
+    call void @llvm.sideeffect()
+    br label %loop
+}
diff --git a/llvm/test/Transforms/GVN/int_sideeffect.ll b/llvm/test/Transforms/GVN/int_sideeffect.ll
new file mode 100644
index 0000000..02ee2fc
--- /dev/null
+++ b/llvm/test/Transforms/GVN/int_sideeffect.ll
@@ -0,0 +1,51 @@
+; RUN: opt -S < %s -gvn | FileCheck %s
+
+declare void @llvm.sideeffect()
+
+; Store-to-load forwarding across a @llvm.sideeffect.
+
+; CHECK-LABEL: s2l
+; CHECK-NOT: load
+define float @s2l(float* %p) {
+    store float 0.0, float* %p
+    call void @llvm.sideeffect()
+    %t = load float, float* %p
+    ret float %t
+}
+
+; Redundant load elimination across a @llvm.sideeffect.
+
+; CHECK-LABEL: rle
+; CHECK: load
+; CHECK-NOT: load
+define float @rle(float* %p) {
+    %r = load float, float* %p
+    call void @llvm.sideeffect()
+    %s = load float, float* %p
+    %t = fadd float %r, %s
+    ret float %t
+}
+
+; LICM across a @llvm.sideeffect.
+
+; CHECK-LABEL: licm
+; CHECK: load
+; CHECK: loop:
+; CHECK-NOT: load
+define float @licm(i64 %n, float* nocapture readonly %p) #0 {
+bb0:
+  br label %loop
+
+loop:
+  %i = phi i64 [ 0, %bb0 ], [ %t5, %loop ]
+  %sum = phi float [ 0.000000e+00, %bb0 ], [ %t4, %loop ]
+  call void @llvm.sideeffect()
+  %t3 = load float, float* %p
+  %t4 = fadd float %sum, %t3
+  %t5 = add i64 %i, 1
+  %t6 = icmp ult i64 %t5, %n
+  br i1 %t6, label %loop, label %bb2
+
+bb2:
+  ret float %t4
+}
diff --git a/llvm/test/Transforms/GVNHoist/int_sideeffect.ll b/llvm/test/Transforms/GVNHoist/int_sideeffect.ll
new file mode 100644
index 0000000..26029fe
--- /dev/null
+++ b/llvm/test/Transforms/GVNHoist/int_sideeffect.ll
@@ -0,0 +1,30 @@
+; RUN: opt -S < %s -gvn-hoist | FileCheck %s
+
+declare void @llvm.sideeffect()
+
+; GVN hoisting across a @llvm.sideeffect.
+
+; CHECK-LABEL: scalarsHoisting
+; CHECK: = fsub
+; CHECK: br i1 %cmp,
+; CHECK-NOT: fsub
+define float @scalarsHoisting(float %d, float %m, float %a, i1 %cmp) {
+entry:
+  br i1 %cmp, label %if.then, label %if.else
+
+if.then:
+  call void @llvm.sideeffect()
+  %sub0 = fsub float %m, %a
+  %mul = fmul float %sub0, %d
+  br label %if.end
+
+if.else:
+  %sub1 = fsub float %m, %a
+  %div = fdiv float %sub1, %d
+  br label %if.end
+
+if.end:
+  %phi = phi float [ %mul, %if.then ], [ %div, %if.else ]
+  ret float %phi
+}
+
diff --git a/llvm/test/Transforms/GVNSink/int_sideeffect.ll b/llvm/test/Transforms/GVNSink/int_sideeffect.ll
new file mode 100644
index 0000000..0ea7736
--- /dev/null
+++ b/llvm/test/Transforms/GVNSink/int_sideeffect.ll
@@ -0,0 +1,30 @@
+; RUN: opt -S < %s -gvn-sink | FileCheck %s
+
+declare void @llvm.sideeffect()
+
+; GVN sinking across a @llvm.sideeffect.
+
+; CHECK-LABEL: scalarsSinking
+; CHECK-NOT: fmul
+; CHECK: = phi
+; CHECK: = fmul
+define float @scalarsSinking(float %d, float %m, float %a, i1 %cmp) {
+entry:
+  br i1 %cmp, label %if.then, label %if.else
+
+if.then:
+  call void @llvm.sideeffect()
+  %sub = fsub float %m, %a
+  %mul0 = fmul float %sub, %d
+  br label %if.end
+
+if.else:
+  %add = fadd float %m, %a
+  %mul1 = fmul float %add, %d
+  br label %if.end
+
+if.end:
+  %phi = phi float [ %mul0, %if.then ], [ %mul1, %if.else ]
+  ret float %phi
+}
+
diff --git a/llvm/test/Transforms/GlobalOpt/int_sideeffect.ll b/llvm/test/Transforms/GlobalOpt/int_sideeffect.ll
new file mode 100644
index 0000000..59c3a8a
--- /dev/null
+++ b/llvm/test/Transforms/GlobalOpt/int_sideeffect.ll
@@ -0,0 +1,16 @@
+; RUN: opt -S < %s -globalopt | FileCheck %s
+
+; Static evaluation across a @llvm.sideeffect.
+
+; CHECK-NOT: store
+
+declare void @llvm.sideeffect()
+
+@llvm.global_ctors = appending global [1 x { i32, void ()* }] [ { i32, void ()* } { i32 65535, void ()* @ctor } ]
+@G = global i32 0
+
+define internal void @ctor() {
+    store i32 1, i32* @G
+    call void @llvm.sideeffect()
+    ret void
+}
diff --git a/llvm/test/Transforms/InstCombine/int_sideeffect.ll b/llvm/test/Transforms/InstCombine/int_sideeffect.ll
new file mode 100644
index 0000000..6355c45
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/int_sideeffect.ll
@@ -0,0 +1,14 @@
+; RUN: opt -S < %s -instcombine | FileCheck %s
+
+declare void @llvm.sideeffect()
+
+; Store-to-load forwarding across a @llvm.sideeffect.
+
+; CHECK-LABEL: s2l
+; CHECK-NOT: load
+define float @s2l(float* %p) {
+    store float 0.0, float* %p
+    call void @llvm.sideeffect()
+    %t = load float, float* %p
+    ret float %t
+}
diff --git a/llvm/test/Transforms/LICM/int_sideeffect.ll b/llvm/test/Transforms/LICM/int_sideeffect.ll
new file mode 100644
index 0000000..842efe5
--- /dev/null
+++ b/llvm/test/Transforms/LICM/int_sideeffect.ll
@@ -0,0 +1,27 @@
+; RUN: opt -S < %s -licm | FileCheck %s
+
+declare void @llvm.sideeffect()
+
+; LICM across a @llvm.sideeffect.
+
+; CHECK-LABEL: licm
+; CHECK: load
+; CHECK: loop:
+; CHECK-NOT: load
+define float @licm(i64 %n, float* nocapture readonly %p) #0 {
+bb0:
+  br label %loop
+
+loop:
+  %i = phi i64 [ 0, %bb0 ], [ %t5, %loop ]
+  %sum = phi float [ 0.000000e+00, %bb0 ], [ %t4, %loop ]
+  call void @llvm.sideeffect()
+  %t3 = load float, float* %p
+  %t4 = fadd float %sum, %t3
+  %t5 = add i64 %i, 1
+  %t6 = icmp ult i64 %t5, %n
+  br i1 %t6, label %loop, label %bb2
+
+bb2:
+  ret float %t4
+}
diff --git a/llvm/test/Transforms/LoadStoreVectorizer/int_sideeffect.ll b/llvm/test/Transforms/LoadStoreVectorizer/int_sideeffect.ll
new file mode 100644
index 0000000..07bdc91
--- /dev/null
+++ b/llvm/test/Transforms/LoadStoreVectorizer/int_sideeffect.ll
@@ -0,0 +1,26 @@
+; RUN: opt -S < %s -load-store-vectorizer | FileCheck %s
+
+declare void @llvm.sideeffect()
+
+; load-store vectorization across a @llvm.sideeffect.
+
+; CHECK-LABEL: test
+; CHECK: load <4 x float>
+; CHECK: store <4 x float>
+define void @test(float* %p) {
+    %p0 = getelementptr float, float* %p, i64 0
+    %p1 = getelementptr float, float* %p, i64 1
+    %p2 = getelementptr float, float* %p, i64 2
+    %p3 = getelementptr float, float* %p, i64 3
+    %l0 = load float, float* %p0, align 16
+    %l1 = load float, float* %p1
+    %l2 = load float, float* %p2
+    call void @llvm.sideeffect()
+    %l3 = load float, float* %p3
+    store float %l0, float* %p0, align 16
+    call void @llvm.sideeffect()
+    store float %l1, float* %p1
+    store float %l2, float* %p2
+    store float %l3, float* %p3
+    ret void
+}
diff --git a/llvm/test/Transforms/LoopIdiom/int_sideeffect.ll b/llvm/test/Transforms/LoopIdiom/int_sideeffect.ll
new file mode 100644
index 0000000..40acf3c
--- /dev/null
+++ b/llvm/test/Transforms/LoopIdiom/int_sideeffect.ll
@@ -0,0 +1,23 @@
+; RUN: opt -S < %s -loop-idiom | FileCheck %s
+
+declare void @llvm.sideeffect()
+
+; Loop idiom recognition across a @llvm.sideeffect.
+
+; CHECK-LABEL: zero
+; CHECK: llvm.memset
+define void @zero(float* %p, i64 %n) nounwind {
+bb7.lr.ph:
+  br label %bb7
+
+bb7:
+  %i.02 = phi i64 [ 0, %bb7.lr.ph ], [ %tmp13, %bb7 ]
+  %tmp10 = getelementptr inbounds float, float* %p, i64 %i.02
+  store float 0.000000e+00, float* %tmp10, align 4
+  %tmp13 = add i64 %i.02, 1
+  %tmp6 = icmp ult i64 %tmp13, %n
+  br i1 %tmp6, label %bb7, label %bb14
+
+bb14:
+  ret void
+}
diff --git a/llvm/test/Transforms/LoopVectorize/int_sideeffect.ll b/llvm/test/Transforms/LoopVectorize/int_sideeffect.ll
new file mode 100644
index 0000000..ec72bed
--- /dev/null
+++ b/llvm/test/Transforms/LoopVectorize/int_sideeffect.ll
@@ -0,0 +1,24 @@
+; RUN: opt -S < %s -loop-vectorize -force-vector-width=4 | FileCheck %s
+
+declare void @llvm.sideeffect()
+
+; Vectorization across a @llvm.sideeffect.
+
+; CHECK-LABEL: store_ones
+; CHECK: store <4 x float>
+define void @store_ones(float* %p, i64 %n) nounwind {
+bb7.lr.ph:
+  br label %bb7
+
+bb7:
+  %i.02 = phi i64 [ 0, %bb7.lr.ph ], [ %tmp13, %bb7 ]
+  call void @llvm.sideeffect()
+  %tmp10 = getelementptr inbounds float, float* %p, i64 %i.02
+  store float 1.0, float* %tmp10, align 4
+  %tmp13 = add i64 %i.02, 1
+  %tmp6 = icmp ult i64 %tmp13, %n
+  br i1 %tmp6, label %bb7, label %bb14
+
+bb14:
+  ret void
+}
diff --git a/llvm/test/Transforms/NewGVN/int_sideeffect.ll b/llvm/test/Transforms/NewGVN/int_sideeffect.ll
new file mode 100644
index 0000000..75a798d
--- /dev/null
+++ b/llvm/test/Transforms/NewGVN/int_sideeffect.ll
@@ -0,0 +1,27 @@
+; RUN: opt -S < %s -newgvn | FileCheck %s
+
+declare void @llvm.sideeffect()
+
+; Store-to-load forwarding across a @llvm.sideeffect.
+
+; CHECK-LABEL: s2l
+; CHECK-NOT: load
+define float @s2l(float* %p) {
+    store float 0.0, float* %p
+    call void @llvm.sideeffect()
+    %t = load float, float* %p
+    ret float %t
+}
+
+; Redundant load elimination across a @llvm.sideeffect.
+
+; CHECK-LABEL: rle
+; CHECK: load
+; CHECK-NOT: load
+define float @rle(float* %p) {
+    %r = load float, float* %p
+    call void @llvm.sideeffect()
+    %s = load float, float* %p
+    %t = fadd float %r, %s
+    ret float %t
+}
diff --git a/llvm/test/Transforms/SLPVectorizer/int_sideeffect.ll b/llvm/test/Transforms/SLPVectorizer/int_sideeffect.ll
new file mode 100644
index 0000000..a6123c1
--- /dev/null
+++ b/llvm/test/Transforms/SLPVectorizer/int_sideeffect.ll
@@ -0,0 +1,25 @@
+; RUN: opt -S < %s -slp-vectorizer -slp-max-reg-size=128 -slp-min-reg-size=128 | FileCheck %s
+
+declare void @llvm.sideeffect()
+
+; SLP vectorization across a @llvm.sideeffect.
+
+; CHECK-LABEL: test
+; CHECK: store <4 x float>
+define void @test(float* %p) {
+    %p0 = getelementptr float, float* %p, i64 0
+    %p1 = getelementptr float, float* %p, i64 1
+    %p2 = getelementptr float, float* %p, i64 2
+    %p3 = getelementptr float, float* %p, i64 3
+    %l0 = load float, float* %p0
+    %l1 = load float, float* %p1
+    %l2 = load float, float* %p2
+    call void @llvm.sideeffect()
+    %l3 = load float, float* %p3
+    store float %l0, float* %p0
+    call void @llvm.sideeffect()
+    store float %l1, float* %p1
+    store float %l2, float* %p2
+    store float %l3, float* %p3
+    ret void
+}