diff --git a/llvm/lib/Transforms/Scalar/GVNHoist.cpp b/llvm/lib/Transforms/Scalar/GVNHoist.cpp --- a/llvm/lib/Transforms/Scalar/GVNHoist.cpp +++ b/llvm/lib/Transforms/Scalar/GVNHoist.cpp @@ -808,15 +808,20 @@ void GVNHoist::checkSafety(CHIArgs C, BasicBlock *BB, GVNHoist::InsKind K, SmallVectorImpl &Safe) { int NumBBsOnAllPaths = MaxNumberOfBBSInPath; + const Instruction *T = BB->getTerminator(); for (auto CHI : C) { Instruction *Insn = CHI.I; if (!Insn) // No instruction was inserted in this CHI. continue; + // If the Terminator is some kind of "exotic terminator" that produces a + // value (such as InvokeInst, CallBrInst, or CatchSwitchInst) which the CHI + // uses, it is not safe to hoist the use above the def. + if (!T->use_empty() && is_contained(Insn->operands(), T)) + continue; if (K == InsKind::Scalar) { if (safeToHoistScalar(BB, Insn->getParent(), NumBBsOnAllPaths)) Safe.push_back(CHI); } else { - auto *T = BB->getTerminator(); if (MemoryUseOrDef *UD = MSSA->getMemoryAccess(Insn)) if (safeToHoistLdSt(T, Insn, UD, K, NumBBsOnAllPaths)) Safe.push_back(CHI); diff --git a/llvm/test/Transforms/GVNHoist/hoist-call.ll b/llvm/test/Transforms/GVNHoist/hoist-call.ll --- a/llvm/test/Transforms/GVNHoist/hoist-call.ll +++ b/llvm/test/Transforms/GVNHoist/hoist-call.ll @@ -1,13 +1,20 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt -S -passes=gvn-hoist < %s | FileCheck %s ; Check that the call and fcmp are hoisted. -; CHECK-LABEL: define void @fun( -; CHECK: call float -; CHECK: fcmp oeq -; CHECK-NOT: call float -; CHECK-NOT: fcmp oeq - define void @fun(float %__b) minsize { +; CHECK-LABEL: @fun( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[IF_THEN:%.*]] +; CHECK: if.then: +; CHECK-NEXT: [[TMP0:%.*]] = call float @llvm.fabs.f32(float [[__B:%.*]]) +; CHECK-NEXT: [[CMPINF7:%.*]] = fcmp oeq float [[TMP0]], 0x7FF0000000000000 +; CHECK-NEXT: br i1 undef, label [[IF_THEN8:%.*]], label [[LOR_LHS_FALSE:%.*]] +; CHECK: lor.lhs.false: +; CHECK-NEXT: unreachable +; CHECK: if.then8: +; CHECK-NEXT: ret void +; entry: br label %if.then @@ -26,3 +33,171 @@ } declare float @llvm.fabs.f32(float) + +; Check that extractvalues are not hoisted into entry, but that non-dependent +; adds are. +define i32 @foo(i32 %x) { +; CHECK-LABEL: @foo( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[X:%.*]], 1 +; CHECK-NEXT: [[TMP0:%.*]] = callbr { i32, i32 } asm sideeffect "somestuff", "=r,=r,!i"() +; CHECK-NEXT: to label [[ASM_FALLTHROUGH:%.*]] [label %err.split] +; CHECK: asm.fallthrough: +; CHECK-NEXT: [[ASMRESULT:%.*]] = extractvalue { i32, i32 } [[TMP0]], 0 +; CHECK-NEXT: ret i32 [[ADD]] +; CHECK: err.split: +; CHECK-NEXT: [[ASMRESULT2:%.*]] = extractvalue { i32, i32 } [[TMP0]], 0 +; CHECK-NEXT: ret i32 [[ADD]] +; +entry: + %0 = callbr { i32, i32 } asm sideeffect "somestuff", "=r,=r,!i"() + to label %asm.fallthrough [label %err.split] + +asm.fallthrough: ; preds = %entry + %asmresult = extractvalue { i32, i32 } %0, 0 + %add = add nsw i32 %x, 1 + ret i32 %add + +err.split: ; preds = %entry + %asmresult2 = extractvalue { i32, i32 } %0, 0 + %add2 = add nsw i32 %x, 1 + ret i32 %add2 +} + +; Check that extractvalues and dependent adds are not hoisted into entry. +define i32 @foo2() { +; CHECK-LABEL: @foo2( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP0:%.*]] = callbr { i32, i32 } asm sideeffect "somestuff", "=r,=r,!i"() +; CHECK-NEXT: to label [[ASM_FALLTHROUGH:%.*]] [label %err.split] +; CHECK: asm.fallthrough: +; CHECK-NEXT: [[ASMRESULT:%.*]] = extractvalue { i32, i32 } [[TMP0]], 0 +; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[ASMRESULT]], 1 +; CHECK-NEXT: ret i32 [[ADD]] +; CHECK: err.split: +; CHECK-NEXT: [[ASMRESULT2:%.*]] = extractvalue { i32, i32 } [[TMP0]], 0 +; CHECK-NEXT: [[ADD2:%.*]] = add nsw i32 [[ASMRESULT2]], 1 +; CHECK-NEXT: ret i32 [[ADD2]] +; +entry: + %0 = callbr { i32, i32 } asm sideeffect "somestuff", "=r,=r,!i"() + to label %asm.fallthrough [label %err.split] + +asm.fallthrough: ; preds = %entry + %asmresult = extractvalue { i32, i32 } %0, 0 + %add = add nsw i32 %asmresult, 1 + ret i32 %add + +err.split: ; preds = %entry + %asmresult2 = extractvalue { i32, i32 } %0, 0 + %add2 = add nsw i32 %asmresult2, 1 + ret i32 %add2 +} + +; Ensure we don't hoist loads that are modified by callbr. +@x = global i32 0 +define i32 @foo3() { +; CHECK-LABEL: @foo3( +; CHECK-NEXT: entry: +; CHECK-NEXT: callbr void asm "", "=*m,!i"(ptr elementtype(i32) @x) +; CHECK-NEXT: to label [[ASM_FALLTHROUGH:%.*]] [label %err.split] +; CHECK: asm.fallthrough: +; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr @x, align 4 +; CHECK-NEXT: ret i32 [[TMP0]] +; CHECK: err.split: +; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr @x, align 4 +; CHECK-NEXT: ret i32 [[TMP1]] +; +entry: + callbr void asm "", "=*m,!i"(ptr elementtype(i32) @x) + to label %asm.fallthrough [label %err.split] + +asm.fallthrough: ; preds = %entry + %0 = load i32, ptr @x + ret i32 %0 + +err.split: ; preds = %entry + %1 = load i32, ptr @x + ret i32 %1 +} + +; Ensure we do hoist loads that aren't modified by callbr, if the callbr has +; the attribute memory(argmem:readwrite). +@y = global i32 0 +define i32 @foo4() { +; CHECK-LABEL: @foo4( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr @y, align 4 +; CHECK-NEXT: callbr void asm "", "=*m,!i"(ptr elementtype(i32) @x) #[[ATTR2:[0-9]+]] +; CHECK-NEXT: to label [[A:%.*]] [label %b] +; CHECK: a: +; CHECK-NEXT: ret i32 [[TMP0]] +; CHECK: b: +; CHECK-NEXT: ret i32 [[TMP0]] +; +entry: + callbr void asm "", "=*m,!i"(ptr elementtype(i32) @x) memory(argmem: readwrite) + to label %a [label %b] + +a: ; preds = %entry + %0 = load i32, ptr @y + ret i32 %0 + +b: ; preds = %entry + %1 = load i32, ptr @y + ret i32 %1 +} + +; Ensure we don't hoist loads that are modified by callbr, if the callbr has +; the attribute memory(argmem:readwrite). +define i32 @foo5() { +; CHECK-LABEL: @foo5( +; CHECK-NEXT: entry: +; CHECK-NEXT: callbr void asm "", "=*m,!i"(ptr elementtype(i32) @x) #[[ATTR2]] +; CHECK-NEXT: to label [[A:%.*]] [label %b] +; CHECK: a: +; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr @x, align 4 +; CHECK-NEXT: ret i32 [[TMP0]] +; CHECK: b: +; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr @x, align 4 +; CHECK-NEXT: ret i32 [[TMP1]] +; +entry: + callbr void asm "", "=*m,!i"(ptr elementtype(i32) @x) memory(argmem: readwrite) + to label %a [label %b] + +a: ; preds = %entry + %0 = load i32, ptr @x + ret i32 %0 + +b: ; preds = %entry + %1 = load i32, ptr @x + ret i32 %1 +} + +; Ensure we hoist loads that are modified by callbr, if the callbr has the +; attribute memory(argmem:none). +define i32 @foo6() { +; CHECK-LABEL: @foo6( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr @x, align 4 +; CHECK-NEXT: callbr void asm "", "=*m,!i"(ptr elementtype(i32) @x) #[[ATTR3:[0-9]+]] +; CHECK-NEXT: to label [[A:%.*]] [label %b] +; CHECK: a: +; CHECK-NEXT: ret i32 [[TMP0]] +; CHECK: b: +; CHECK-NEXT: ret i32 [[TMP0]] +; +entry: + callbr void asm "", "=*m,!i"(ptr elementtype(i32) @x) memory(argmem: none) + to label %a [label %b] + +a: ; preds = %entry + %0 = load i32, ptr @x + ret i32 %0 + +b: ; preds = %entry + %1 = load i32, ptr @x + ret i32 %1 +} +