[InstCombine] Widen guards with conditions between

The previous handling for guard widening in InstCombine was extremely restrictive. In particular, it didn't handle the common case where we had two guards separated by a single icmp. Handle this by scanning through a small fixed window of instructions to find the next guard if needed.

Differential Revision: https://reviews.llvm.org/D46203

llvm-svn: 331935
This commit is contained in:
Philip Reames 2018-05-09 22:56:32 +00:00
parent b11f8b3cbc
commit 79e917d117
2 changed files with 100 additions and 1 deletions
llvm
lib/Transforms/InstCombine
test/Transforms/InstCombine

View File

@ -79,6 +79,13 @@ static cl::opt<unsigned> UnfoldElementAtomicMemcpyMaxElements(
cl::desc("Maximum number of elements in atomic memcpy the optimizer is "
"allowed to unfold"));
static cl::opt<unsigned> GuardWideningWindow(
"instcombine-guard-widening-window",
cl::init(3),
cl::desc("How wide an instruction window to bypass looking for "
"another guard"));
/// Return the specified type promoted as it would be to pass though a va_arg
/// area.
static Type *getPromotedType(Type *Ty) {
@ -3624,8 +3631,16 @@ Instruction *InstCombiner::visitCallInst(CallInst &CI) {
}
case Intrinsic::experimental_guard: {
// Is this guard followed by another guard?
// Is this guard followed by another guard? We scan forward over a small
// fixed window of instructions to handle common cases with conditions
// computed between guards.
Instruction *NextInst = II->getNextNode();
for (int i = 0; i < GuardWideningWindow; i++) {
// Note: Using context-free form to avoid compile time blow up
if (!isSafeToSpeculativelyExecute(NextInst))
break;
NextInst = NextInst->getNextNode();
}
Value *NextCond = nullptr;
if (match(NextInst,
m_Intrinsic<Intrinsic::experimental_guard>(m_Value(NextCond)))) {
@ -3636,6 +3651,12 @@ Instruction *InstCombiner::visitCallInst(CallInst &CI) {
return eraseInstFromFunction(*NextInst);
// Otherwise canonicalize guard(a); guard(b) -> guard(a & b).
Instruction* MoveI = II->getNextNode();
while (MoveI != NextInst) {
auto *Temp = MoveI;
MoveI = MoveI->getNextNode();
Temp->moveBefore(II);
}
II->setArgOperand(0, Builder.CreateAnd(CurrCond, NextCond));
return eraseInstFromFunction(*NextInst);
}

View File

@ -30,3 +30,81 @@ define void @test_guard_adjacent_diff_cond(i1 %A, i1 %B, i1 %C) {
call void(i1, ...) @llvm.experimental.guard( i1 %C, i32 789 )[ "deopt"() ]
ret void
}
; This version tests for the common form where the conditions are
; between the guards
define void @test_guard_adjacent_diff_cond2(i32 %V1, i32 %V2) {
; CHECK-LABEL: @test_guard_adjacent_diff_cond2(
; CHECK-NEXT: %1 = and i32 %V1, %V2
; CHECK-NEXT: %2 = icmp slt i32 %1, 0
; CHECK-NEXT: %and = and i32 %V1, 255
; CHECK-NEXT: %C = icmp ult i32 %and, 129
; CHECK-NEXT: %3 = and i1 %2, %C
; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %3, i32 123) [ "deopt"() ]
; CHECK-NEXT: ret void
%A = icmp slt i32 %V1, 0
call void(i1, ...) @llvm.experimental.guard( i1 %A, i32 123 )[ "deopt"() ]
%B = icmp slt i32 %V2, 0
call void(i1, ...) @llvm.experimental.guard( i1 %B, i32 456 )[ "deopt"() ]
%and = and i32 %V1, 255
%C = icmp sle i32 %and, 128
call void(i1, ...) @llvm.experimental.guard( i1 %C, i32 789 )[ "deopt"() ]
ret void
}
; Might not be legal to hoist the load above the first guard since the
; guard might control dereferenceability
define void @negative_load(i32 %V1, i32* %P) {
; CHECK-LABEL: @negative_load
; CHECK: @llvm.experimental.guard
; CHECK: @llvm.experimental.guard
%A = icmp slt i32 %V1, 0
call void(i1, ...) @llvm.experimental.guard( i1 %A, i32 123 )[ "deopt"() ]
%V2 = load i32, i32* %P
%B = icmp slt i32 %V2, 0
call void(i1, ...) @llvm.experimental.guard( i1 %B, i32 456 )[ "deopt"() ]
ret void
}
define void @deref_load(i32 %V1, i32* dereferenceable(4) %P) {
; CHECK-LABEL: @deref_load
; CHECK-NEXT: %V2 = load i32, i32* %P, align 4
; CHECK-NEXT: %1 = and i32 %V2, %V1
; CHECK-NEXT: %2 = icmp slt i32 %1, 0
; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 %2, i32 123) [ "deopt"() ]
%A = icmp slt i32 %V1, 0
call void(i1, ...) @llvm.experimental.guard( i1 %A, i32 123 )[ "deopt"() ]
%V2 = load i32, i32* %P
%B = icmp slt i32 %V2, 0
call void(i1, ...) @llvm.experimental.guard( i1 %B, i32 456 )[ "deopt"() ]
ret void
}
; The divide might fault above the guard
define void @negative_div(i32 %V1, i32 %D) {
; CHECK-LABEL: @negative_div
; CHECK: @llvm.experimental.guard
; CHECK: @llvm.experimental.guard
%A = icmp slt i32 %V1, 0
call void(i1, ...) @llvm.experimental.guard( i1 %A, i32 123 )[ "deopt"() ]
%V2 = udiv i32 %V1, %D
%B = icmp slt i32 %V2, 0
call void(i1, ...) @llvm.experimental.guard( i1 %B, i32 456 )[ "deopt"() ]
ret void
}
; Highlight the limit of the window in a case which would otherwise be mergable
define void @negative_window(i32 %V1, i32 %a, i32 %b, i32 %c, i32 %d) {
; CHECK-LABEL: @negative_window
; CHECK: @llvm.experimental.guard
; CHECK: @llvm.experimental.guard
%A = icmp slt i32 %V1, 0
call void(i1, ...) @llvm.experimental.guard( i1 %A, i32 123 )[ "deopt"() ]
%V2 = add i32 %a, %b
%V3 = add i32 %V2, %c
%V4 = add i32 %V3, %d
%B = icmp slt i32 %V4, 0
call void(i1, ...) @llvm.experimental.guard( i1 %B, i32 456 )[ "deopt"() ]
ret void
}