[llvm] r264976 - Introduce a @llvm.experimental.guard intrinsic
Sanjoy Das via llvm-commits
llvm-commits at lists.llvm.org
Wed Mar 30 17:18:46 PDT 2016
Author: sanjoy
Date: Wed Mar 30 19:18:46 2016
New Revision: 264976
URL: http://llvm.org/viewvc/llvm-project?rev=264976&view=rev
Log:
Introduce a @llvm.experimental.guard intrinsic
Summary:
As discussed on llvm-dev[1].
This change adds the basic boilerplate code around having this intrinsic
in LLVM:
- Changes in Intrinsics.td, and the IR Verifier
- A lowering pass to lower @llvm.experimental.guard to normal
control flow
- Inliner support
[1]: http://lists.llvm.org/pipermail/llvm-dev/2016-February/095523.html
Reviewers: reames, atrick, chandlerc, rnk, JosephTremoulet, echristo
Subscribers: mcrosier, llvm-commits
Differential Revision: http://reviews.llvm.org/D18527
Added:
llvm/trunk/lib/Transforms/Scalar/LowerGuardIntrinsic.cpp
llvm/trunk/test/Transforms/Inline/guard-intrinsic.ll
llvm/trunk/test/Transforms/LowerGuardIntrinsic/
llvm/trunk/test/Transforms/LowerGuardIntrinsic/basic.ll
llvm/trunk/test/Verifier/guard-intrinsic.ll
Modified:
llvm/trunk/docs/LangRef.rst
llvm/trunk/include/llvm/IR/Intrinsics.td
llvm/trunk/include/llvm/InitializePasses.h
llvm/trunk/include/llvm/Transforms/Scalar.h
llvm/trunk/lib/IR/Verifier.cpp
llvm/trunk/lib/Transforms/Scalar/CMakeLists.txt
llvm/trunk/lib/Transforms/Scalar/Scalar.cpp
llvm/trunk/lib/Transforms/Utils/InlineFunction.cpp
Modified: llvm/trunk/docs/LangRef.rst
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/docs/LangRef.rst?rev=264976&r1=264975&r2=264976&view=diff
==============================================================================
--- llvm/trunk/docs/LangRef.rst (original)
+++ llvm/trunk/docs/LangRef.rst Wed Mar 30 19:18:46 2016
@@ -12181,6 +12181,50 @@ ensure that this symbol is defined). Th
arguments of the specified types, and not as varargs.
+'``llvm.experimental.guard``' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+::
+
+ declare void @llvm.experimental.guard(i1, ...) [ "deopt"(...) ]
+
+Overview:
+"""""""""
+
+This intrinsic, together with :ref:`deoptimization operand bundles
+<deopt_opbundles>`, allows frontends to express guards or checks on
+optimistic assumptions made during compilation. The semantics of
+``@llvm.experimental.guard`` is defined in terms of
+``@llvm.experimental.deoptimize`` -- its body is defined to be
+equivalent to:
+
+.. code-block:: llvm
+
+ define void @llvm.experimental.guard(i1 %pred, <args...>) {
+ %realPred = and i1 %pred, undef
+ br i1 %realPred, label %continue, label %leave
+
+ leave:
+ call void @llvm.experimental.deoptimize(<args...>) [ "deopt"() ]
+ ret void
+
+ continue:
+ ret void
+ }
+
+In words, ``@llvm.experimental.guard`` executes the attached
+``"deopt"`` continuation if (but **not** only if) its first argument
+is ``false``. Since the optimizer is allowed to replace the ``undef``
+with an arbitrary value, it can optimize guard to fail "spuriously",
+i.e. without the original condition being false (hence the "not only
+if"); and this allows for "check widening" type optimizations.
+
+``@llvm.experimental.guard`` cannot be invoked.
+
+
Stack Map Intrinsics
--------------------
Modified: llvm/trunk/include/llvm/IR/Intrinsics.td
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/IR/Intrinsics.td?rev=264976&r1=264975&r2=264976&view=diff
==============================================================================
--- llvm/trunk/include/llvm/IR/Intrinsics.td (original)
+++ llvm/trunk/include/llvm/IR/Intrinsics.td Wed Mar 30 19:18:46 2016
@@ -597,6 +597,10 @@ def int_debugtrap : Intrinsic<[]>,
def int_experimental_deoptimize : Intrinsic<[llvm_any_ty], [llvm_vararg_ty],
[Throws]>;
+// Support for speculative runtime guards
+def int_experimental_guard : Intrinsic<[], [llvm_i1_ty, llvm_vararg_ty],
+ [Throws]>;
+
// NOP: calls/invokes to this intrinsic are removed by codegen
def int_donothing : Intrinsic<[], [], [IntrNoMem]>;
Modified: llvm/trunk/include/llvm/InitializePasses.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/InitializePasses.h?rev=264976&r1=264975&r2=264976&view=diff
==============================================================================
--- llvm/trunk/include/llvm/InitializePasses.h (original)
+++ llvm/trunk/include/llvm/InitializePasses.h Wed Mar 30 19:18:46 2016
@@ -188,6 +188,7 @@ void initializeLoopIdiomRecognizePass(Pa
void initializeLowerAtomicPass(PassRegistry&);
void initializeLowerBitSetsPass(PassRegistry&);
void initializeLowerExpectIntrinsicPass(PassRegistry&);
+void initializeLowerGuardIntrinsicPass(PassRegistry&);
void initializeLowerIntrinsicsPass(PassRegistry&);
void initializeLowerInvokePass(PassRegistry&);
void initializeLowerSwitchPass(PassRegistry&);
Modified: llvm/trunk/include/llvm/Transforms/Scalar.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Transforms/Scalar.h?rev=264976&r1=264975&r2=264976&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Transforms/Scalar.h (original)
+++ llvm/trunk/include/llvm/Transforms/Scalar.h Wed Mar 30 19:18:46 2016
@@ -374,6 +374,12 @@ Pass *createLowerAtomicPass();
//===----------------------------------------------------------------------===//
//
+// LowerGuardIntrinsic - Lower guard intrinsics to normal control flow.
+//
+Pass *createLowerGuardIntrinsicPass();
+
+//===----------------------------------------------------------------------===//
+//
// ValuePropagation - Propagate CFG-derived value information
//
Pass *createCorrelatedValuePropagationPass();
Modified: llvm/trunk/lib/IR/Verifier.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/IR/Verifier.cpp?rev=264976&r1=264975&r2=264976&view=diff
==============================================================================
--- llvm/trunk/lib/IR/Verifier.cpp (original)
+++ llvm/trunk/lib/IR/Verifier.cpp Wed Mar 30 19:18:46 2016
@@ -4106,6 +4106,14 @@ void Verifier::visitIntrinsicCallSite(In
break;
}
+ case Intrinsic::experimental_guard: {
+ Assert(CS.isCall(), "experimental_guard cannot be invoked", CS);
+ Assert(CS.countOperandBundlesOfType(LLVMContext::OB_deopt) == 1,
+ "experimental_guard must have exactly one "
+ "\"deopt\" operand bundle");
+ break;
+ }
+
case Intrinsic::experimental_deoptimize: {
Assert(CS.isCall(), "experimental_deoptimize cannot be invoked", CS);
Assert(CS.countOperandBundlesOfType(LLVMContext::OB_deopt) == 1,
Modified: llvm/trunk/lib/Transforms/Scalar/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Scalar/CMakeLists.txt?rev=264976&r1=264975&r2=264976&view=diff
==============================================================================
--- llvm/trunk/lib/Transforms/Scalar/CMakeLists.txt (original)
+++ llvm/trunk/lib/Transforms/Scalar/CMakeLists.txt Wed Mar 30 19:18:46 2016
@@ -32,6 +32,7 @@ add_llvm_library(LLVMScalarOpts
LoopVersioningLICM.cpp
LowerAtomic.cpp
LowerExpectIntrinsic.cpp
+ LowerGuardIntrinsic.cpp
MemCpyOptimizer.cpp
MergedLoadStoreMotion.cpp
NaryReassociate.cpp
Added: llvm/trunk/lib/Transforms/Scalar/LowerGuardIntrinsic.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Scalar/LowerGuardIntrinsic.cpp?rev=264976&view=auto
==============================================================================
--- llvm/trunk/lib/Transforms/Scalar/LowerGuardIntrinsic.cpp (added)
+++ llvm/trunk/lib/Transforms/Scalar/LowerGuardIntrinsic.cpp Wed Mar 30 19:18:46 2016
@@ -0,0 +1,108 @@
+//===- LowerGuardIntrinsic.cpp - Lower the guard intrinsic ---------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This pass lowers the llvm.experimental.guard intrinsic to a conditional call
+// to @llvm.experimental.deoptimize. Once this happens, the guard can no longer
+// be widened.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/Scalar.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Pass.h"
+#include "llvm/Transforms/Utils/BasicBlockUtils.h"
+
+using namespace llvm;
+
+namespace {
+struct LowerGuardIntrinsic : public FunctionPass {
+ static char ID;
+ LowerGuardIntrinsic() : FunctionPass(ID) {
+ initializeLowerGuardIntrinsicPass(*PassRegistry::getPassRegistry());
+ }
+
+ bool runOnFunction(Function &F) override;
+};
+}
+
+static void MakeGuardControlFlowExplicit(Function *DeoptIntrinsic,
+ CallInst *CI) {
+ OperandBundleDef DeoptOB(*CI->getOperandBundle(LLVMContext::OB_deopt));
+ SmallVector<Value *, 4> Args(std::next(CI->arg_begin()), CI->arg_end());
+
+ auto *CheckBB = CI->getParent();
+ auto *DeoptBlockTerm =
+ SplitBlockAndInsertIfThen(CI->getArgOperand(0), CI, true);
+
+ auto *CheckBI = cast<BranchInst>(CheckBB->getTerminator());
+
+ // SplitBlockAndInsertIfThen inserts control flow that branches to
+ // DeoptBlockTerm if the condition is true. We want the opposite.
+ CheckBI->swapSuccessors();
+
+ CheckBI->getSuccessor(0)->setName("guarded");
+ CheckBI->getSuccessor(1)->setName("deopt");
+
+ IRBuilder<> B(DeoptBlockTerm);
+ auto *DeoptCall = B.CreateCall(DeoptIntrinsic, Args, {DeoptOB}, "");
+
+ if (DeoptIntrinsic->getReturnType()->isVoidTy()) {
+ B.CreateRetVoid();
+ } else {
+ DeoptCall->setName("deoptcall");
+ B.CreateRet(DeoptCall);
+ }
+
+ DeoptBlockTerm->eraseFromParent();
+}
+
+bool LowerGuardIntrinsic::runOnFunction(Function &F) {
+ // Check if we can cheaply rule out the possibility of not having any work to
+ // do.
+ auto *GuardDecl = F.getParent()->getFunction(
+ Intrinsic::getName(Intrinsic::experimental_guard));
+ if (!GuardDecl || GuardDecl->use_empty())
+ return false;
+
+ SmallVector<CallInst *, 8> ToLower;
+ for (auto &I : instructions(F))
+ if (auto *CI = dyn_cast<CallInst>(&I))
+ if (auto *F = CI->getCalledFunction())
+ if (F->getIntrinsicID() == Intrinsic::experimental_guard)
+ ToLower.push_back(CI);
+
+ if (ToLower.empty())
+ return false;
+
+ auto *DeoptIntrinsic = Intrinsic::getDeclaration(
+ F.getParent(), Intrinsic::experimental_deoptimize, {F.getReturnType()});
+
+ for (auto *CI : ToLower) {
+ MakeGuardControlFlowExplicit(DeoptIntrinsic, CI);
+ CI->eraseFromParent();
+ }
+
+ return true;
+}
+
+char LowerGuardIntrinsic::ID = 0;
+INITIALIZE_PASS(LowerGuardIntrinsic, "lower-guard-intrinsic",
+ "Lower the guard intrinsic to normal control flow", false,
+ false)
+
+Pass *llvm::createLowerGuardIntrinsicPass() {
+ return new LowerGuardIntrinsic();
+}
Modified: llvm/trunk/lib/Transforms/Scalar/Scalar.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Scalar/Scalar.cpp?rev=264976&r1=264975&r2=264976&view=diff
==============================================================================
--- llvm/trunk/lib/Transforms/Scalar/Scalar.cpp (original)
+++ llvm/trunk/lib/Transforms/Scalar/Scalar.cpp Wed Mar 30 19:18:46 2016
@@ -62,6 +62,7 @@ void llvm::initializeScalarOpts(PassRegi
initializeLoopIdiomRecognizePass(Registry);
initializeLowerAtomicPass(Registry);
initializeLowerExpectIntrinsicPass(Registry);
+ initializeLowerGuardIntrinsicPass(Registry);
initializeMemCpyOptPass(Registry);
initializeMergedLoadStoreMotionPass(Registry);
initializeNaryReassociatePass(Registry);
Modified: llvm/trunk/lib/Transforms/Utils/InlineFunction.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Utils/InlineFunction.cpp?rev=264976&r1=264975&r2=264976&view=diff
==============================================================================
--- llvm/trunk/lib/Transforms/Utils/InlineFunction.cpp (original)
+++ llvm/trunk/lib/Transforms/Utils/InlineFunction.cpp Wed Mar 30 19:18:46 2016
@@ -428,12 +428,14 @@ static BasicBlock *HandleCallsInBlockInl
continue;
// We do not need to (and in fact, cannot) convert possibly throwing calls
- // to @llvm.experimental_deoptimize into invokes. The caller's "segment" of
- // the deoptimization continuation attached to the newly inlined
- // @llvm.experimental_deoptimize call should contain the exception handling
- // logic, if any.
+ // to @llvm.experimental_deoptimize (resp. @llvm.experimental.guard) into
+ // invokes. The caller's "segment" of the deoptimization continuation
+ // attached to the newly inlined @llvm.experimental_deoptimize
+ // (resp. @llvm.experimental.guard) call should contain the exception
+ // handling logic, if any.
if (auto *F = CI->getCalledFunction())
- if (F->getIntrinsicID() == Intrinsic::experimental_deoptimize)
+ if (F->getIntrinsicID() == Intrinsic::experimental_deoptimize ||
+ F->getIntrinsicID() == Intrinsic::experimental_guard)
continue;
if (auto FuncletBundle = CI->getOperandBundle(LLVMContext::OB_funclet)) {
Added: llvm/trunk/test/Transforms/Inline/guard-intrinsic.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/Inline/guard-intrinsic.ll?rev=264976&view=auto
==============================================================================
--- llvm/trunk/test/Transforms/Inline/guard-intrinsic.ll (added)
+++ llvm/trunk/test/Transforms/Inline/guard-intrinsic.ll Wed Mar 30 19:18:46 2016
@@ -0,0 +1,39 @@
+; RUN: opt -S -always-inline < %s | FileCheck %s
+
+declare void @llvm.experimental.guard(i1, ...)
+
+define i8 @callee(i1* %c_ptr) alwaysinline {
+ %c = load volatile i1, i1* %c_ptr
+ call void(i1, ...) @llvm.experimental.guard(i1 %c, i32 1) [ "deopt"(i32 1) ]
+ ret i8 5
+}
+
+define void @caller_0(i1* %c, i8* %ptr) {
+; CHECK-LABEL: @caller_0(
+entry:
+; CHECK: [[COND:%[^ ]+]] = load volatile i1, i1* %c
+; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[COND]], i32 1) [ "deopt"(i32 2, i32 1) ]
+; CHECK-NEXT: store i8 5, i8* %ptr
+
+ %v = call i8 @callee(i1* %c) [ "deopt"(i32 2) ]
+ store i8 %v, i8* %ptr
+ ret void
+}
+
+define i32 @caller_1(i1* %c, i8* %ptr) personality i8 3 {
+; CHECK-LABEL: @caller_1(
+; CHECK: [[COND:%[^ ]+]] = load volatile i1, i1* %c
+; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[COND]], i32 1) [ "deopt"(i32 3, i32 1) ]
+; CHECK-NEXT: br label %normal
+entry:
+ %v = invoke i8 @callee(i1* %c) [ "deopt"(i32 3) ] to label %normal
+ unwind label %unwind
+
+unwind:
+ %lp = landingpad i32 cleanup
+ ret i32 43
+
+normal:
+ store i8 %v, i8* %ptr
+ ret i32 42
+}
Added: llvm/trunk/test/Transforms/LowerGuardIntrinsic/basic.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/LowerGuardIntrinsic/basic.ll?rev=264976&view=auto
==============================================================================
--- llvm/trunk/test/Transforms/LowerGuardIntrinsic/basic.ll (added)
+++ llvm/trunk/test/Transforms/LowerGuardIntrinsic/basic.ll Wed Mar 30 19:18:46 2016
@@ -0,0 +1,62 @@
+; RUN: opt -S -lower-guard-intrinsic < %s | FileCheck %s
+
+declare void @llvm.experimental.guard(i1, ...)
+
+define i8 @f_basic(i1* %c_ptr) {
+; CHECK-LABEL: @f_basic(
+
+ %c = load volatile i1, i1* %c_ptr
+ call void(i1, ...) @llvm.experimental.guard(i1 %c, i32 1) [ "deopt"(i32 1) ]
+ ret i8 5
+
+; CHECK: br i1 %c, label %guarded, label %deopt
+; CHECK: deopt:
+; CHECK-NEXT: %deoptcall = call i8 (...) @llvm.experimental.deoptimize.i8(i32 1) [ "deopt"(i32 1) ]
+; CHECK-NEXT: ret i8 %deoptcall
+; CHECK: guarded:
+; CHECK-NEXT: ret i8 5
+}
+
+define void @f_void_return_ty(i1* %c_ptr) {
+; CHECK-LABEL: @f_void_return_ty(
+
+ %c = load volatile i1, i1* %c_ptr
+ call void(i1, ...) @llvm.experimental.guard(i1 %c, i32 1) [ "deopt"() ]
+ ret void
+
+; CHECK: br i1 %c, label %guarded, label %deopt
+; CHECK: deopt:
+; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid(i32 1) [ "deopt"() ]
+; CHECK-NEXT: ret void
+; CHECK: guarded:
+; CHECK-NEXT: ret void
+}
+
+define void @f_multiple_args(i1* %c_ptr) {
+; CHECK-LABEL: @f_multiple_args(
+
+ %c = load volatile i1, i1* %c_ptr
+ call void(i1, ...) @llvm.experimental.guard(i1 %c, i32 1, i32 2, double 500.0) [ "deopt"(i32 2, i32 3) ]
+ ret void
+
+; CHECK: br i1 %c, label %guarded, label %deopt
+; CHECK: deopt:
+; CHECK-NEXT: call void (...) @llvm.experimental.deoptimize.isVoid(i32 1, i32 2, double 5.000000e+02) [ "deopt"(i32 2, i32 3) ]
+; CHECK-NEXT: ret void
+; CHECK: guarded:
+; CHECK-NEXT: ret void
+}
+
+define i32 @f_zero_args(i1* %c_ptr) {
+; CHECK-LABEL: @f_zero_args(
+ %c = load volatile i1, i1* %c_ptr
+ call void(i1, ...) @llvm.experimental.guard(i1 %c) [ "deopt"(i32 2, i32 3) ]
+ ret i32 500
+
+; CHECK: br i1 %c, label %guarded, label %deopt
+; CHECK: deopt:
+; CHECK-NEXT: %deoptcall = call i32 (...) @llvm.experimental.deoptimize.i32() [ "deopt"(i32 2, i32 3) ]
+; CHECK-NEXT: ret i32 %deoptcall
+; CHECK: guarded:
+; CHECK-NEXT: ret i32 500
+}
Added: llvm/trunk/test/Verifier/guard-intrinsic.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Verifier/guard-intrinsic.ll?rev=264976&view=auto
==============================================================================
--- llvm/trunk/test/Verifier/guard-intrinsic.ll (added)
+++ llvm/trunk/test/Verifier/guard-intrinsic.ll Wed Mar 30 19:18:46 2016
@@ -0,0 +1,26 @@
+; RUN: not opt -S -verify < %s 2>&1 | FileCheck %s
+
+declare void @llvm.experimental.guard(i1, ...)
+
+declare void @unknown()
+
+define void @f_nodeopt() {
+entry:
+ call void(i1, ...) @llvm.experimental.guard(i1 undef, i32 1, i32 2)
+; CHECK: guard must have exactly one "deopt" operand bundle
+ ret void
+}
+
+define void @f_invoke() personality i8 3 {
+entry:
+ invoke void(i1, ...) @llvm.experimental.guard(i1 undef, i32 0, float 0.0) [ "deopt"() ] to label %ok unwind label %not_ok
+; CHECK: guard cannot be invoked
+
+ok:
+ ret void
+
+not_ok:
+ %0 = landingpad { i8*, i32 }
+ filter [0 x i8*] zeroinitializer
+ ret void
+}
More information about the llvm-commits
mailing list