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 + .../CodeGen/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 ++ .../Transforms/Vectorize/LoadStoreVectorizer.cpp | 5 +++ llvm/lib/Transforms/Vectorize/LoopVectorize.cpp | 2 +- llvm/lib/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 +++++++ llvm/test/Transforms/EarlyCSE/int_sideeffect.ll | 27 ++++++++++++ .../Transforms/FunctionAttrs/int_sideeffect.ll | 21 +++++++++ llvm/test/Transforms/GVN/int_sideeffect.ll | 51 ++++++++++++++++++++++ llvm/test/Transforms/GVNHoist/int_sideeffect.ll | 30 +++++++++++++ llvm/test/Transforms/GVNSink/int_sideeffect.ll | 30 +++++++++++++ llvm/test/Transforms/GlobalOpt/int_sideeffect.ll | 16 +++++++ llvm/test/Transforms/InstCombine/int_sideeffect.ll | 14 ++++++ llvm/test/Transforms/LICM/int_sideeffect.ll | 27 ++++++++++++ .../LoadStoreVectorizer/int_sideeffect.ll | 26 +++++++++++ llvm/test/Transforms/LoopIdiom/int_sideeffect.ll | 23 ++++++++++ .../Transforms/LoopVectorize/int_sideeffect.ll | 24 ++++++++++ llvm/test/Transforms/NewGVN/int_sideeffect.ll | 27 ++++++++++++ .../Transforms/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 8cbed7d..b097230 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 ada8fc0..4f27f6a 100644 --- a/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h +++ b/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h @@ -152,6 +152,7 @@ public: 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 0334ed9..aa16f86 100644 --- a/llvm/include/llvm/CodeGen/BasicTTIImpl.h +++ b/llvm/include/llvm/CodeGen/BasicTTIImpl.h @@ -1023,6 +1023,7 @@ public: // 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 cb16c3d..9560ae6 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 025e811..b575944 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 2010858..9a0df74 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 554d132..2becfbf 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 6e245fe..acc67b8 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 f4f8879..4a1e715 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 6d1362a..1066dc3 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 d77ebdc..e90c89c 100644 --- a/llvm/lib/Transforms/Scalar/GVNHoist.cpp +++ b/llvm/lib/Transforms/Scalar/GVNHoist.cpp @@ -1110,7 +1110,8 @@ private: 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 a65c3ba..3c5e299 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 5d355c5..dc83b6d 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 ed29ca0..673c47f 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 4232252..6c60706 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 4d04786..6a51d2d 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 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 +} -- 2.7.4