From 2c74fe977daf8d1d4c8b64cdd6d3a9999a4b4655 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Wed, 8 Nov 2017 21:59:51 +0000 Subject: [PATCH] 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 --- llvm/docs/LangRef.rst | 30 +++++++++++ .../llvm/Analysis/TargetTransformInfoImpl.h | 1 + llvm/include/llvm/CodeGen/BasicTTIImpl.h | 1 + llvm/include/llvm/IR/Intrinsics.td | 6 +++ llvm/lib/Analysis/AliasSetTracker.cpp | 1 + llvm/lib/Analysis/ValueTracking.cpp | 4 +- llvm/lib/Analysis/VectorUtils.cpp | 3 +- llvm/lib/CodeGen/SelectionDAG/FastISel.cpp | 2 + .../SelectionDAG/SelectionDAGBuilder.cpp | 3 +- llvm/lib/Transforms/Scalar/EarlyCSE.cpp | 6 +++ llvm/lib/Transforms/Scalar/GVNHoist.cpp | 3 +- llvm/lib/Transforms/Utils/Evaluator.cpp | 4 ++ .../Vectorize/LoadStoreVectorizer.cpp | 5 ++ .../Transforms/Vectorize/LoopVectorize.cpp | 2 +- .../Transforms/Vectorize/SLPVectorizer.cpp | 4 +- llvm/test/CodeGen/Generic/intrinsics.ll | 9 ++++ llvm/test/Transforms/DCE/int_sideeffect.ll | 12 +++++ .../DeadStoreElimination/int_sideeffect.ll | 15 ++++++ .../Transforms/EarlyCSE/int_sideeffect.ll | 27 ++++++++++ .../FunctionAttrs/int_sideeffect.ll | 21 ++++++++ llvm/test/Transforms/GVN/int_sideeffect.ll | 51 +++++++++++++++++++ .../Transforms/GVNHoist/int_sideeffect.ll | 30 +++++++++++ .../test/Transforms/GVNSink/int_sideeffect.ll | 30 +++++++++++ .../Transforms/GlobalOpt/int_sideeffect.ll | 16 ++++++ .../Transforms/InstCombine/int_sideeffect.ll | 14 +++++ llvm/test/Transforms/LICM/int_sideeffect.ll | 27 ++++++++++ .../LoadStoreVectorizer/int_sideeffect.ll | 26 ++++++++++ .../Transforms/LoopIdiom/int_sideeffect.ll | 23 +++++++++ .../LoopVectorize/int_sideeffect.ll | 24 +++++++++ llvm/test/Transforms/NewGVN/int_sideeffect.ll | 27 ++++++++++ .../SLPVectorizer/int_sideeffect.ll | 25 +++++++++ 31 files changed, 446 insertions(+), 6 deletions(-) create mode 100644 llvm/test/Transforms/DCE/int_sideeffect.ll create mode 100644 llvm/test/Transforms/DeadStoreElimination/int_sideeffect.ll create mode 100644 llvm/test/Transforms/EarlyCSE/int_sideeffect.ll create mode 100644 llvm/test/Transforms/FunctionAttrs/int_sideeffect.ll create mode 100644 llvm/test/Transforms/GVN/int_sideeffect.ll create mode 100644 llvm/test/Transforms/GVNHoist/int_sideeffect.ll create mode 100644 llvm/test/Transforms/GVNSink/int_sideeffect.ll create mode 100644 llvm/test/Transforms/GlobalOpt/int_sideeffect.ll create mode 100644 llvm/test/Transforms/InstCombine/int_sideeffect.ll create mode 100644 llvm/test/Transforms/LICM/int_sideeffect.ll create mode 100644 llvm/test/Transforms/LoadStoreVectorizer/int_sideeffect.ll create mode 100644 llvm/test/Transforms/LoopIdiom/int_sideeffect.ll create mode 100644 llvm/test/Transforms/LoopVectorize/int_sideeffect.ll create mode 100644 llvm/test/Transforms/NewGVN/int_sideeffect.ll create mode 100644 llvm/test/Transforms/SLPVectorizer/int_sideeffect.ll diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index 8cbed7d87d169..b097230591fac 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -14267,6 +14267,36 @@ not overflow at link time under the medium code model if ``x`` is an a constant initializer folded into a function body. This intrinsic can be used to avoid the possibility of overflows when loading from such a constant. +'``llvm.sideeffect``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +:: + + declare void @llvm.sideeffect() inaccessiblememonly nounwind + +Overview: +""""""""" + +The ``llvm.sideeffect`` intrinsic doesn't perform any operation. Optimizers +treat it as having side effects, so it can be inserted into a loop to +indicate that the loop shouldn't be assumed to terminate (which could +potentially lead to the loop being optimized away entirely), even if it's +an infinite loop with no other side effects. + +Arguments: +"""""""""" + +None. + +Semantics: +"""""""""" + +This intrinsic actually does nothing, but optimizers must assume that it +has externally observable side effects. + Stack Map Intrinsics -------------------- diff --git a/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h b/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h index ada8fc0facbd6..4f27f6a14106d 100644 --- a/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h +++ b/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h @@ -152,6 +152,7 @@ class TargetTransformInfoImplBase { case Intrinsic::annotation: case Intrinsic::assume: + case Intrinsic::sideeffect: case Intrinsic::dbg_declare: case Intrinsic::dbg_value: case Intrinsic::invariant_start: diff --git a/llvm/include/llvm/CodeGen/BasicTTIImpl.h b/llvm/include/llvm/CodeGen/BasicTTIImpl.h index 0334ed9eacbb0..aa16f86c3072c 100644 --- a/llvm/include/llvm/CodeGen/BasicTTIImpl.h +++ b/llvm/include/llvm/CodeGen/BasicTTIImpl.h @@ -1023,6 +1023,7 @@ class BasicTTIImplBase : public TargetTransformInfoImplCRTPBase { // FIXME: We should return 0 whenever getIntrinsicCost == TCC_Free. case Intrinsic::lifetime_start: case Intrinsic::lifetime_end: + case Intrinsic::sideeffect: return 0; case Intrinsic::masked_store: return static_cast(this) diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td index cb16c3d48495f..9560ae642ddef 100644 --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -810,6 +810,12 @@ def int_experimental_guard : Intrinsic<[], [llvm_i1_ty, llvm_vararg_ty], // NOP: calls/invokes to this intrinsic are removed by codegen def int_donothing : Intrinsic<[], [], [IntrNoMem]>; +// This instruction has no actual effect, though it is treated by the optimizer +// has having opaque side effects. This may be inserted into loops to ensure +// that they are not removed even if they turn out to be empty, for languages +// which specify that infinite loops must be preserved. +def int_sideeffect : Intrinsic<[], [], [IntrInaccessibleMemOnly]>; + // Intrisics to support half precision floating point format let IntrProperties = [IntrNoMem] in { def int_convert_to_fp16 : Intrinsic<[llvm_i16_ty], [llvm_anyfloat_ty]>; diff --git a/llvm/lib/Analysis/AliasSetTracker.cpp b/llvm/lib/Analysis/AliasSetTracker.cpp index 025e8119debb1..b575944092a9f 100644 --- a/llvm/lib/Analysis/AliasSetTracker.cpp +++ b/llvm/lib/Analysis/AliasSetTracker.cpp @@ -436,6 +436,7 @@ void AliasSetTracker::addUnknown(Instruction *Inst) { break; // FIXME: Add lifetime/invariant intrinsics (See: PR30807). case Intrinsic::assume: + case Intrinsic::sideeffect: return; } } diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp index 2010858139a6b..9a0df74fd5757 100644 --- a/llvm/lib/Analysis/ValueTracking.cpp +++ b/llvm/lib/Analysis/ValueTracking.cpp @@ -433,6 +433,7 @@ static bool isAssumeLikeIntrinsic(const Instruction *I) { default: break; // FIXME: This list is repeated from NoTTI::getIntrinsicCost. case Intrinsic::assume: + case Intrinsic::sideeffect: case Intrinsic::dbg_declare: case Intrinsic::dbg_value: case Intrinsic::invariant_start: @@ -3857,7 +3858,8 @@ bool llvm::isGuaranteedToTransferExecutionToSuccessor(const Instruction *I) { // FIXME: This isn't aggressive enough; a call which only writes to a global // is guaranteed to return. return CS.onlyReadsMemory() || CS.onlyAccessesArgMemory() || - match(I, m_Intrinsic()); + match(I, m_Intrinsic()) || + match(I, m_Intrinsic()); } // Other instructions return normally. diff --git a/llvm/lib/Analysis/VectorUtils.cpp b/llvm/lib/Analysis/VectorUtils.cpp index 554d132c2ab77..2becfbfe8a8d3 100644 --- a/llvm/lib/Analysis/VectorUtils.cpp +++ b/llvm/lib/Analysis/VectorUtils.cpp @@ -91,7 +91,8 @@ Intrinsic::ID llvm::getVectorIntrinsicIDForCall(const CallInst *CI, return Intrinsic::not_intrinsic; if (isTriviallyVectorizable(ID) || ID == Intrinsic::lifetime_start || - ID == Intrinsic::lifetime_end || ID == Intrinsic::assume) + ID == Intrinsic::lifetime_end || ID == Intrinsic::assume || + ID == Intrinsic::sideeffect) return ID; return Intrinsic::not_intrinsic; } diff --git a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp index 6e245feb73582..acc67b827edb2 100644 --- a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp @@ -1132,6 +1132,8 @@ bool FastISel::selectIntrinsicCall(const IntrinsicInst *II) { case Intrinsic::lifetime_end: // The donothing intrinsic does, well, nothing. case Intrinsic::donothing: + // Neither does the sideeffect intrinsic. + case Intrinsic::sideeffect: // Neither does the assume intrinsic; it's also OK not to codegen its operand. case Intrinsic::assume: return true; diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index f4f8879b5d853..4a1e71583ebbc 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -5702,7 +5702,8 @@ SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, unsigned Intrinsic) { return nullptr; case Intrinsic::assume: case Intrinsic::var_annotation: - // Discard annotate attributes and assumptions + case Intrinsic::sideeffect: + // Discard annotate attributes, assumptions, and artificial side-effects. return nullptr; case Intrinsic::codeview_annotation: { diff --git a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp index 6d1362a6a28e6..1066dc33007b8 100644 --- a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp +++ b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp @@ -709,6 +709,12 @@ bool EarlyCSE::processNode(DomTreeNode *Node) { continue; } + // Skip sideeffect intrinsics, for the same reason as assume intrinsics. + if (match(Inst, m_Intrinsic())) { + DEBUG(dbgs() << "EarlyCSE skipping sideeffect: " << *Inst << '\n'); + continue; + } + // Skip invariant.start intrinsics since they only read memory, and we can // forward values across it. Also, we dont need to consume the last store // since the semantics of invariant.start allow us to perform DSE of the diff --git a/llvm/lib/Transforms/Scalar/GVNHoist.cpp b/llvm/lib/Transforms/Scalar/GVNHoist.cpp index d77ebdcd97536..e90c89c2e3aa6 100644 --- a/llvm/lib/Transforms/Scalar/GVNHoist.cpp +++ b/llvm/lib/Transforms/Scalar/GVNHoist.cpp @@ -1110,7 +1110,8 @@ class GVNHoist { else if (auto *Call = dyn_cast(&I1)) { if (auto *Intr = dyn_cast(Call)) { if (isa(Intr) || - Intr->getIntrinsicID() == Intrinsic::assume) + Intr->getIntrinsicID() == Intrinsic::assume || + Intr->getIntrinsicID() == Intrinsic::sideeffect) continue; } if (Call->mayHaveSideEffects()) diff --git a/llvm/lib/Transforms/Utils/Evaluator.cpp b/llvm/lib/Transforms/Utils/Evaluator.cpp index a65c3bac5e549..3c5e299fae983 100644 --- a/llvm/lib/Transforms/Utils/Evaluator.cpp +++ b/llvm/lib/Transforms/Utils/Evaluator.cpp @@ -433,6 +433,10 @@ bool Evaluator::EvaluateBlock(BasicBlock::iterator CurInst, DEBUG(dbgs() << "Skipping assume intrinsic.\n"); ++CurInst; continue; + } else if (II->getIntrinsicID() == Intrinsic::sideeffect) { + DEBUG(dbgs() << "Skipping sideeffect intrinsic.\n"); + ++CurInst; + continue; } DEBUG(dbgs() << "Unknown intrinsic. Can not evaluate.\n"); diff --git a/llvm/lib/Transforms/Vectorize/LoadStoreVectorizer.cpp b/llvm/lib/Transforms/Vectorize/LoadStoreVectorizer.cpp index 5d355c520210c..dc83b6d4d2927 100644 --- a/llvm/lib/Transforms/Vectorize/LoadStoreVectorizer.cpp +++ b/llvm/lib/Transforms/Vectorize/LoadStoreVectorizer.cpp @@ -34,6 +34,7 @@ #include "llvm/IR/InstrTypes.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Module.h" #include "llvm/IR/Type.h" #include "llvm/IR/User.h" @@ -500,6 +501,10 @@ Vectorizer::getVectorizablePrefix(ArrayRef Chain) { MemoryInstrs.push_back(&I); else ChainInstrs.push_back(&I); + } else if (isa(&I) && + cast(&I)->getIntrinsicID() == + Intrinsic::sideeffect) { + // Ignore llvm.sideeffect calls. } else if (IsLoadChain && (I.mayWriteToMemory() || I.mayThrow())) { DEBUG(dbgs() << "LSV: Found may-write/throw operation: " << I << '\n'); break; diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp index ed29ca0b5731e..673c47f473019 100644 --- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp +++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp @@ -8117,7 +8117,7 @@ bool LoopVectorizationPlanner::tryToWiden(Instruction *I, VPBasicBlock *VPBB, if (CallInst *CI = dyn_cast(I)) { Intrinsic::ID ID = getVectorIntrinsicIDForCall(CI, TLI); if (ID && (ID == Intrinsic::assume || ID == Intrinsic::lifetime_end || - ID == Intrinsic::lifetime_start)) + ID == Intrinsic::lifetime_start || ID == Intrinsic::sideeffect)) return false; } diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp index 4232252af36dc..6c607062f258f 100644 --- a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp +++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp @@ -3612,7 +3612,9 @@ void BoUpSLP::BlockScheduling::initScheduleData(Instruction *FromI, "new ScheduleData already in scheduling region"); SD->init(SchedulingRegionID, I); - if (I->mayReadOrWriteMemory()) { + if (I->mayReadOrWriteMemory() && + (!isa(I) || + cast(I)->getIntrinsicID() != Intrinsic::sideeffect)) { // Update the linked list of memory accessing instructions. if (CurrentLoadStore) { CurrentLoadStore->NextLoadStore = SD; diff --git a/llvm/test/CodeGen/Generic/intrinsics.ll b/llvm/test/CodeGen/Generic/intrinsics.ll index 4d04786a58d46..6a51d2d371bba 100644 --- a/llvm/test/CodeGen/Generic/intrinsics.ll +++ b/llvm/test/CodeGen/Generic/intrinsics.ll @@ -45,3 +45,12 @@ define i8* @barrier(i8* %p) { %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 0000000000000..af06303fa8667 --- /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 0000000000000..035e787f6bd7a --- /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 0000000000000..1dccaab5e5f7d --- /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 0000000000000..13091e3e8cb78 --- /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 0000000000000..02ee2fcbdb063 --- /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 0000000000000..26029fe52ce0b --- /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 0000000000000..0ea773654c4de --- /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 0000000000000..59c3a8aa4ba59 --- /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 0000000000000..6355c4557ef98 --- /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 0000000000000..842efe56585aa --- /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 0000000000000..07bdc9123f9ab --- /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 0000000000000..40acf3cbe2a54 --- /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 0000000000000..ec72bed7c7b04 --- /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 0000000000000..75a798d6f0a51 --- /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 0000000000000..a6123c11d9256 --- /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 +}