[llvm] 7314ad7 - Revert "[SimplifyCFG] Allow SimplifyCFG hoisting to skip over non-matching instructions"
Nikita Popov via llvm-commits
llvm-commits at lists.llvm.org
Mon Aug 1 00:21:03 PDT 2022
Author: Nikita Popov
Date: 2022-08-01T09:20:56+02:00
New Revision: 7314ad7a0661579e19eeec20c91e49f51f8c8d5e
URL: https://github.com/llvm/llvm-project/commit/7314ad7a0661579e19eeec20c91e49f51f8c8d5e
DIFF: https://github.com/llvm/llvm-project/commit/7314ad7a0661579e19eeec20c91e49f51f8c8d5e.diff
LOG: Revert "[SimplifyCFG] Allow SimplifyCFG hoisting to skip over non-matching instructions"
This reverts commit 7b0f6378e211e881c574748090a86beeab264ab3.
As commented on the review, this patch has a correctness issue
regarding the modelling of memory effects.
Added:
Modified:
llvm/lib/Transforms/Utils/SimplifyCFG.cpp
Removed:
llvm/test/Transforms/SimplifyCFG/hoist-common-skip-limit.ll
llvm/test/Transforms/SimplifyCFG/hoist-common-skip.ll
################################################################################
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 1b148aeb794d3..1806081678a86 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -115,12 +115,6 @@ static cl::opt<bool>
HoistCommon("simplifycfg-hoist-common", cl::Hidden, cl::init(true),
cl::desc("Hoist common instructions up to the parent block"));
-static cl::opt<unsigned>
- HoistCommonSkipLimit("simplifycfg-hoist-common-skip-limit", cl::Hidden,
- cl::init(20),
- cl::desc("Allow reordering across at most this many "
- "instructions when hoisting"));
-
static cl::opt<bool>
SinkCommon("simplifycfg-sink-common", cl::Hidden, cl::init(true),
cl::desc("Sink common instructions down to the end block"));
@@ -1436,32 +1430,6 @@ static bool isSafeToHoistInvoke(BasicBlock *BB1, BasicBlock *BB2,
return true;
}
-// Returns true if it is safe to reorder an instruction across preceding
-// instructions in a basic block.
-static bool isSafeToHoistInstr(Instruction *I, bool ForceNoSideEffects,
- bool ForceNoSpeculation) {
- // If we have seen an instruction with side effects, it's unsafe to reorder an
- // instruction which reads memory or itself has side effects.
- if (ForceNoSideEffects && (I->mayReadFromMemory() || I->mayHaveSideEffects()))
- return false;
-
- // Reordering across an instruction which does not necessarily transfer
- // control to the next instruction is speculation.
- if (ForceNoSpeculation && !isSafeToSpeculativelyExecute(I))
- return false;
-
- // It's also unsafe/illegal to hoist an instruction above its instruction
- // operands
- BasicBlock *BB = I->getParent();
- for (Value *Op : I->operands()) {
- if (auto *J = dyn_cast<Instruction>(Op))
- if (J->getParent() == BB)
- return false;
- }
-
- return true;
-}
-
static bool passingValueIsAlwaysUndefined(Value *V, Instruction *I, bool PtrValueMayBeModified = false);
/// Given a conditional branch that goes to BB1 and BB2, hoist any common code
@@ -1476,8 +1444,7 @@ bool SimplifyCFGOpt::HoistThenElseCodeToIf(BranchInst *BI,
// instructions in the two blocks. In particular, we don't want to get into
// O(M*N) situations here where M and N are the sizes of BB1 and BB2. As
// such, we currently just scan for obviously identical instructions in an
- // identical order, possibly separated by the same number of non-identical
- // instructions.
+ // identical order.
BasicBlock *BB1 = BI->getSuccessor(0); // The true destination.
BasicBlock *BB2 = BI->getSuccessor(1); // The false destination
@@ -1500,7 +1467,7 @@ bool SimplifyCFGOpt::HoistThenElseCodeToIf(BranchInst *BI,
while (isa<DbgInfoIntrinsic>(I2))
I2 = &*BB2_Itr++;
}
- if (isa<PHINode>(I1))
+ if (isa<PHINode>(I1) || !I1->isIdenticalToWhenDefined(I2))
return false;
BasicBlock *BIParent = BI->getParent();
@@ -1526,122 +1493,75 @@ bool SimplifyCFGOpt::HoistThenElseCodeToIf(BranchInst *BI,
// terminator. Let the loop below handle those 2 cases.
}
- // Count how many instructions were not hoisted so far. There's a limit on how
- // many instructions we skip, serving as a compilation time control as well as
- // preventing excessive increase of life ranges.
- unsigned NumSkipped = 0;
-
- // Record if any non-hoisted instruction contains side-effects, as it could
- // make it illegal to reorder some instructions across.
- bool ForceNoReadMemOrSideEffectsBB1 = false;
- bool ForceNoReadMemOrSideEffectsBB2 = false;
-
- // Record if any non-hoisted instructions does not necessarily transfer
- // control to the next instruction. Then we can hoist only instrucions which
- // are safe to speculate.
- bool ForceNoSpeculationBB1 = false;
- bool ForceNoSpeculationBB2 = false;
-
- for (;;) {
+ do {
// If we are hoisting the terminator instruction, don't move one (making a
// broken BB), instead clone it, and remove BI.
- if (I1->isTerminator() || I2->isTerminator()) {
- // If any instructions remain in the block, we cannot hoist terminators.
- if (NumSkipped || !I1->isIdenticalToWhenDefined(I2))
- return Changed;
+ if (I1->isTerminator())
goto HoistTerminator;
- }
- if (I1->isIdenticalToWhenDefined(I2)) {
- // Hoisting token-returning instructions would obscure the origin.
- if (I1->getType()->isTokenTy())
+ // If we're going to hoist a call, make sure that the two instructions we're
+ // commoning/hoisting are both marked with musttail, or neither of them is
+ // marked as such. Otherwise, we might end up in a situation where we hoist
+ // from a block where the terminator is a `ret` to a block where the terminator
+ // is a `br`, and `musttail` calls expect to be followed by a return.
+ auto *C1 = dyn_cast<CallInst>(I1);
+ auto *C2 = dyn_cast<CallInst>(I2);
+ if (C1 && C2)
+ if (C1->isMustTailCall() != C2->isMustTailCall())
return Changed;
- // Even if the instructions are identical, it may not be safe to hoist
- // them if we have skipped over instructions with side effects or their
- // operands weren't hoisted.
- if (!isSafeToHoistInstr(I1, ForceNoReadMemOrSideEffectsBB1, ForceNoSpeculationBB1) ||
- !isSafeToHoistInstr(I2, ForceNoReadMemOrSideEffectsBB2, ForceNoSpeculationBB2))
- return Changed;
+ if (!TTI.isProfitableToHoist(I1) || !TTI.isProfitableToHoist(I2))
+ return Changed;
- // If we're going to hoist a call, make sure that the two instructions
- // we're commoning/hoisting are both marked with musttail, or neither of
- // them is marked as such. Otherwise, we might end up in a situation where
- // we hoist from a block where the terminator is a `ret` to a block where
- // the terminator is a `br`, and `musttail` calls expect to be followed by
- // a return.
- auto *C1 = dyn_cast<CallInst>(I1);
- auto *C2 = dyn_cast<CallInst>(I2);
- if (C1 && C2)
- if (C1->isMustTailCall() != C2->isMustTailCall())
- return Changed;
-
- if (!TTI.isProfitableToHoist(I1) || !TTI.isProfitableToHoist(I2))
+ // If any of the two call sites has nomerge attribute, stop hoisting.
+ if (const auto *CB1 = dyn_cast<CallBase>(I1))
+ if (CB1->cannotMerge())
+ return Changed;
+ if (const auto *CB2 = dyn_cast<CallBase>(I2))
+ if (CB2->cannotMerge())
return Changed;
- // If any of the two call sites has nomerge attribute, stop hoisting.
- if (const auto *CB1 = dyn_cast<CallBase>(I1))
- if (CB1->cannotMerge())
- return Changed;
- if (const auto *CB2 = dyn_cast<CallBase>(I2))
- if (CB2->cannotMerge())
- return Changed;
-
- if (isa<DbgInfoIntrinsic>(I1) || isa<DbgInfoIntrinsic>(I2)) {
- assert(isa<DbgInfoIntrinsic>(I1) && isa<DbgInfoIntrinsic>(I2));
- // The debug location is an integral part of a debug info intrinsic
- // and can't be separated from it or replaced. Instead of attempting
- // to merge locations, simply hoist both copies of the intrinsic.
- BIParent->getInstList().splice(BI->getIterator(), BB1->getInstList(),
- I1);
- BIParent->getInstList().splice(BI->getIterator(), BB2->getInstList(),
- I2);
- } else {
- // For a normal instruction, we just move one to right before the
- // branch, then replace all uses of the other with the first. Finally,
- // we remove the now redundant second instruction.
- BIParent->getInstList().splice(BI->getIterator(), BB1->getInstList(),
- I1);
- if (!I2->use_empty())
- I2->replaceAllUsesWith(I1);
- I1->andIRFlags(I2);
- unsigned KnownIDs[] = {LLVMContext::MD_tbaa,
- LLVMContext::MD_range,
- LLVMContext::MD_fpmath,
- LLVMContext::MD_invariant_load,
- LLVMContext::MD_nonnull,
- LLVMContext::MD_invariant_group,
- LLVMContext::MD_align,
- LLVMContext::MD_dereferenceable,
- LLVMContext::MD_dereferenceable_or_null,
- LLVMContext::MD_mem_parallel_loop_access,
- LLVMContext::MD_access_group,
- LLVMContext::MD_preserve_access_index};
- combineMetadata(I1, I2, KnownIDs, true);
-
- // I1 and I2 are being combined into a single instruction. Its debug
- // location is the merged locations of the original instructions.
- I1->applyMergedLocation(I1->getDebugLoc(), I2->getDebugLoc());
-
- I2->eraseFromParent();
- }
+ if (isa<DbgInfoIntrinsic>(I1) || isa<DbgInfoIntrinsic>(I2)) {
+ assert (isa<DbgInfoIntrinsic>(I1) && isa<DbgInfoIntrinsic>(I2));
+ // The debug location is an integral part of a debug info intrinsic
+ // and can't be separated from it or replaced. Instead of attempting
+ // to merge locations, simply hoist both copies of the intrinsic.
+ BIParent->getInstList().splice(BI->getIterator(),
+ BB1->getInstList(), I1);
+ BIParent->getInstList().splice(BI->getIterator(),
+ BB2->getInstList(), I2);
Changed = true;
- ++NumHoistCommonInstrs;
} else {
- if (NumSkipped >= HoistCommonSkipLimit)
- return Changed;
- // We are about to skip over a pair of non-identical instructions. Record
- // if any of them has side effects.
- if (I1->mayHaveSideEffects())
- ForceNoReadMemOrSideEffectsBB1 = true;
- if (I2->mayHaveSideEffects())
- ForceNoReadMemOrSideEffectsBB2 = true;
- if (!isGuaranteedToTransferExecutionToSuccessor(I1))
- ForceNoSpeculationBB1 = true;
- if (!isGuaranteedToTransferExecutionToSuccessor(I2))
- ForceNoSpeculationBB2 = true;
- ++NumSkipped;
+ // For a normal instruction, we just move one to right before the branch,
+ // then replace all uses of the other with the first. Finally, we remove
+ // the now redundant second instruction.
+ BIParent->getInstList().splice(BI->getIterator(),
+ BB1->getInstList(), I1);
+ if (!I2->use_empty())
+ I2->replaceAllUsesWith(I1);
+ I1->andIRFlags(I2);
+ unsigned KnownIDs[] = {LLVMContext::MD_tbaa,
+ LLVMContext::MD_range,
+ LLVMContext::MD_fpmath,
+ LLVMContext::MD_invariant_load,
+ LLVMContext::MD_nonnull,
+ LLVMContext::MD_invariant_group,
+ LLVMContext::MD_align,
+ LLVMContext::MD_dereferenceable,
+ LLVMContext::MD_dereferenceable_or_null,
+ LLVMContext::MD_mem_parallel_loop_access,
+ LLVMContext::MD_access_group,
+ LLVMContext::MD_preserve_access_index};
+ combineMetadata(I1, I2, KnownIDs, true);
+
+ // I1 and I2 are being combined into a single instruction. Its debug
+ // location is the merged locations of the original instructions.
+ I1->applyMergedLocation(I1->getDebugLoc(), I2->getDebugLoc());
+
+ I2->eraseFromParent();
+ Changed = true;
}
+ ++NumHoistCommonInstrs;
I1 = &*BB1_Itr++;
I2 = &*BB2_Itr++;
@@ -1654,9 +1574,9 @@ bool SimplifyCFGOpt::HoistThenElseCodeToIf(BranchInst *BI,
while (isa<DbgInfoIntrinsic>(I2))
I2 = &*BB2_Itr++;
}
- }
+ } while (I1->isIdenticalToWhenDefined(I2));
- return Changed;
+ return true;
HoistTerminator:
// It may not be possible to hoist an invoke.
diff --git a/llvm/test/Transforms/SimplifyCFG/hoist-common-skip-limit.ll b/llvm/test/Transforms/SimplifyCFG/hoist-common-skip-limit.ll
deleted file mode 100644
index 0e1a171dd9ebf..0000000000000
--- a/llvm/test/Transforms/SimplifyCFG/hoist-common-skip-limit.ll
+++ /dev/null
@@ -1,97 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -S --passes='simplifycfg<hoist-common-insts>' -simplifycfg-hoist-common-skip-limit=0 %s | FileCheck %s --check-prefix=LIMIT0
-; RUN: opt -S --passes='simplifycfg<hoist-common-insts>' -simplifycfg-hoist-common-skip-limit=1 %s | FileCheck %s --check-prefix=LIMIT1
-; RUN: opt -S --passes='simplifycfg<hoist-common-insts>' -simplifycfg-hoist-common-skip-limit=2 %s | FileCheck %s --check-prefix=LIMIT2
-
-define void @f(i1 %c, ptr nocapture noundef %d, ptr nocapture noundef readonly %m, ptr nocapture noundef readonly %b) {
-; LIMIT0-LABEL: @f(
-; LIMIT0-NEXT: entry:
-; LIMIT0-NEXT: [[TMP0:%.*]] = load i16, ptr [[B:%.*]], align 2
-; LIMIT0-NEXT: br i1 [[C:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
-; LIMIT0: if.then:
-; LIMIT0-NEXT: call void @no_side_effects0()
-; LIMIT0-NEXT: [[ADD:%.*]] = add nsw i16 [[TMP0]], 1
-; LIMIT0-NEXT: [[TMP1:%.*]] = load i16, ptr [[M:%.*]], align 2
-; LIMIT0-NEXT: [[U:%.*]] = add i16 [[ADD]], [[TMP1]]
-; LIMIT0-NEXT: br label [[IF_END:%.*]]
-; LIMIT0: if.else:
-; LIMIT0-NEXT: call void @no_side_effects1()
-; LIMIT0-NEXT: [[SUB:%.*]] = sub nsw i16 [[TMP0]], 1
-; LIMIT0-NEXT: [[TMP2:%.*]] = load i16, ptr [[M]], align 2
-; LIMIT0-NEXT: [[V:%.*]] = add i16 [[SUB]], [[TMP2]]
-; LIMIT0-NEXT: br label [[IF_END]]
-; LIMIT0: if.end:
-; LIMIT0-NEXT: [[UV:%.*]] = phi i16 [ [[V]], [[IF_ELSE]] ], [ [[U]], [[IF_THEN]] ]
-; LIMIT0-NEXT: store i16 [[UV]], ptr [[D:%.*]], align 2
-; LIMIT0-NEXT: ret void
-;
-; LIMIT1-LABEL: @f(
-; LIMIT1-NEXT: entry:
-; LIMIT1-NEXT: [[TMP0:%.*]] = load i16, ptr [[B:%.*]], align 2
-; LIMIT1-NEXT: br i1 [[C:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
-; LIMIT1: if.then:
-; LIMIT1-NEXT: call void @no_side_effects0()
-; LIMIT1-NEXT: [[ADD:%.*]] = add nsw i16 [[TMP0]], 1
-; LIMIT1-NEXT: [[TMP1:%.*]] = load i16, ptr [[M:%.*]], align 2
-; LIMIT1-NEXT: [[U:%.*]] = add i16 [[ADD]], [[TMP1]]
-; LIMIT1-NEXT: br label [[IF_END:%.*]]
-; LIMIT1: if.else:
-; LIMIT1-NEXT: call void @no_side_effects1()
-; LIMIT1-NEXT: [[SUB:%.*]] = sub nsw i16 [[TMP0]], 1
-; LIMIT1-NEXT: [[TMP2:%.*]] = load i16, ptr [[M]], align 2
-; LIMIT1-NEXT: [[V:%.*]] = add i16 [[SUB]], [[TMP2]]
-; LIMIT1-NEXT: br label [[IF_END]]
-; LIMIT1: if.end:
-; LIMIT1-NEXT: [[UV:%.*]] = phi i16 [ [[V]], [[IF_ELSE]] ], [ [[U]], [[IF_THEN]] ]
-; LIMIT1-NEXT: store i16 [[UV]], ptr [[D:%.*]], align 2
-; LIMIT1-NEXT: ret void
-;
-; LIMIT2-LABEL: @f(
-; LIMIT2-NEXT: entry:
-; LIMIT2-NEXT: [[TMP0:%.*]] = load i16, ptr [[B:%.*]], align 2
-; LIMIT2-NEXT: [[TMP1:%.*]] = load i16, ptr [[M:%.*]], align 2
-; LIMIT2-NEXT: br i1 [[C:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
-; LIMIT2: if.then:
-; LIMIT2-NEXT: call void @no_side_effects0()
-; LIMIT2-NEXT: [[ADD:%.*]] = add nsw i16 [[TMP0]], 1
-; LIMIT2-NEXT: [[U:%.*]] = add i16 [[ADD]], [[TMP1]]
-; LIMIT2-NEXT: br label [[IF_END:%.*]]
-; LIMIT2: if.else:
-; LIMIT2-NEXT: call void @no_side_effects1()
-; LIMIT2-NEXT: [[SUB:%.*]] = sub nsw i16 [[TMP0]], 1
-; LIMIT2-NEXT: [[V:%.*]] = add i16 [[SUB]], [[TMP1]]
-; LIMIT2-NEXT: br label [[IF_END]]
-; LIMIT2: if.end:
-; LIMIT2-NEXT: [[UV:%.*]] = phi i16 [ [[V]], [[IF_ELSE]] ], [ [[U]], [[IF_THEN]] ]
-; LIMIT2-NEXT: store i16 [[UV]], ptr [[D:%.*]], align 2
-; LIMIT2-NEXT: ret void
-;
-entry:
- br i1 %c, label %if.then, label %if.else
-
-if.then:
- %0 = load i16, ptr %b, align 2
- call void @no_side_effects0()
- %add = add nsw i16 %0, 1
- %1 = load i16, ptr %m, align 2
- %u = add i16 %add, %1
- br label %if.end
-
-if.else:
- %2 = load i16, ptr %b, align 2
- call void @no_side_effects1()
- %sub = sub nsw i16 %2, 1
- %3 = load i16, ptr %m, align 2
- %v = add i16 %sub, %3
- br label %if.end
-
-if.end:
- %uv = phi i16 [ %v, %if.else ], [ %u, %if.then ]
- store i16 %uv, ptr %d, align 2
- ret void
-}
-
-declare void @side_effects0()
-declare void @side_effects1()
-declare void @no_side_effects0() readonly nounwind willreturn
-declare void @no_side_effects1() readonly nounwind willreturn
diff --git a/llvm/test/Transforms/SimplifyCFG/hoist-common-skip.ll b/llvm/test/Transforms/SimplifyCFG/hoist-common-skip.ll
deleted file mode 100644
index 400f981cf552e..0000000000000
--- a/llvm/test/Transforms/SimplifyCFG/hoist-common-skip.ll
+++ /dev/null
@@ -1,277 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -S --passes='simplifycfg<hoist-common-insts>' %s | FileCheck %s
-
-;; Check that the two loads are hoisted to the common predecessor, skipping
-;; over the add/sub instructions.
-
-define void @f0(i1 %c, ptr nocapture noundef %d, ptr nocapture noundef readonly %m, ptr nocapture noundef readonly %b) {
-; CHECK-LABEL: @f0(
-; CHECK-NEXT: entry:
-; CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[B:%.*]], align 2
-; CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[M:%.*]], align 2
-; CHECK-NEXT: br i1 [[C:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
-; CHECK: if.then:
-; CHECK-NEXT: [[ADD:%.*]] = add nsw i16 [[TMP0]], 1
-; CHECK-NEXT: [[U:%.*]] = add i16 [[ADD]], [[TMP1]]
-; CHECK-NEXT: br label [[IF_END:%.*]]
-; CHECK: if.else:
-; CHECK-NEXT: [[SUB:%.*]] = sub nsw i16 [[TMP0]], 1
-; CHECK-NEXT: [[TMP2:%.*]] = add i16 [[SUB]], 3
-; CHECK-NEXT: [[V:%.*]] = add i16 [[SUB]], [[TMP2]]
-; CHECK-NEXT: br label [[IF_END]]
-; CHECK: if.end:
-; CHECK-NEXT: [[UV:%.*]] = phi i16 [ [[V]], [[IF_ELSE]] ], [ [[U]], [[IF_THEN]] ]
-; CHECK-NEXT: store i16 [[UV]], ptr [[D:%.*]], align 2
-; CHECK-NEXT: ret void
-;
-entry:
- br i1 %c, label %if.then, label %if.else
-
-if.then:
- %0 = load i16, ptr %b, align 2
- %add = add nsw i16 %0, 1
- %1 = load i16, ptr %m, align 2
- %u = add i16 %add, %1
- br label %if.end
-
-if.else:
- %2 = load i16, ptr %b, align 2
- %sub = sub nsw i16 %2, 1
- %3 = load i16, ptr %m, align 2
- %4 = add i16 %sub, 3
- %v = add i16 %sub, %4
- br label %if.end
-
-if.end:
- %uv = phi i16 [ %v, %if.else ], [ %u, %if.then ]
- store i16 %uv, ptr %d, align 2
- ret void
-}
-
-
-;; Check some instructions (e.g. add) can be reordered across instructions with side
-;; effects, while others (e.g. load) can't.
-define void @f2(i1 %c, ptr nocapture noundef %d, ptr nocapture noundef readonly %m, ptr nocapture noundef readonly %b) {
-; CHECK-LABEL: @f2(
-; CHECK-NEXT: entry:
-; CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[B:%.*]], align 2
-; CHECK-NEXT: [[ADD_0:%.*]] = add nsw i16 [[TMP0]], 1
-; CHECK-NEXT: br i1 [[C:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
-; CHECK: if.then:
-; CHECK-NEXT: call void @side_effects0()
-; CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[M:%.*]], align 2
-; CHECK-NEXT: [[U:%.*]] = add i16 [[ADD_0]], [[TMP1]]
-; CHECK-NEXT: br label [[IF_END:%.*]]
-; CHECK: if.else:
-; CHECK-NEXT: call void @no_side_effects0()
-; CHECK-NEXT: [[TMP2:%.*]] = load i16, ptr [[M]], align 2
-; CHECK-NEXT: [[V:%.*]] = add i16 [[ADD_0]], [[TMP2]]
-; CHECK-NEXT: br label [[IF_END]]
-; CHECK: if.end:
-; CHECK-NEXT: [[UV:%.*]] = phi i16 [ [[V]], [[IF_ELSE]] ], [ [[U]], [[IF_THEN]] ]
-; CHECK-NEXT: store i16 [[UV]], ptr [[D:%.*]], align 2
-; CHECK-NEXT: ret void
-;
-entry:
- br i1 %c, label %if.then, label %if.else
-
-if.then:
- %0 = load i16, ptr %b, align 2
- call void @side_effects0()
- %add.0 = add nsw i16 %0, 1
- %1 = load i16, ptr %m, align 2
- %u = add i16 %add.0, %1
- br label %if.end
-
-if.else:
- %2 = load i16, ptr %b, align 2
- call void @no_side_effects0()
- %add.1 = add nsw i16 %2, 1
- %3 = load i16, ptr %m, align 2
- %v = add i16 %add.1, %3
- br label %if.end
-
-if.end:
- %uv = phi i16 [ %v, %if.else ], [ %u, %if.then ]
- store i16 %uv, ptr %d, align 2
- ret void
-}
-
-
-;; Check indeed it was the side effects that prevented hoisting the load
-;; in the previous test.
-define void @f3(i1 %c, ptr nocapture noundef %d, ptr nocapture noundef readonly %m, ptr nocapture noundef readonly %b) {
-; CHECK-LABEL: @f3(
-; CHECK-NEXT: entry:
-; CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[B:%.*]], align 2
-; CHECK-NEXT: [[ADD_0:%.*]] = add nsw i16 [[TMP0]], 1
-; CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[M:%.*]], align 2
-; CHECK-NEXT: [[U:%.*]] = add i16 [[ADD_0]], [[TMP1]]
-; CHECK-NEXT: br i1 [[C:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
-; CHECK: if.then:
-; CHECK-NEXT: call void @no_side_effects0()
-; CHECK-NEXT: br label [[IF_END:%.*]]
-; CHECK: if.else:
-; CHECK-NEXT: call void @no_side_effects1()
-; CHECK-NEXT: br label [[IF_END]]
-; CHECK: if.end:
-; CHECK-NEXT: store i16 [[U]], ptr [[D:%.*]], align 2
-; CHECK-NEXT: ret void
-;
-entry:
- br i1 %c, label %if.then, label %if.else
-
-if.then:
- %0 = load i16, ptr %b, align 2
- call void @no_side_effects0()
- %add.0 = add nsw i16 %0, 1
- %1 = load i16, ptr %m, align 2
- %u = add i16 %add.0, %1
- br label %if.end
-
-if.else:
- %2 = load i16, ptr %b, align 2
- call void @no_side_effects1()
- %add.1 = add nsw i16 %2, 1
- %3 = load i16, ptr %m, align 2
- %v = add i16 %add.1, %3
- br label %if.end
-
-if.end:
- %uv = phi i16 [ %v, %if.else ], [ %u, %if.then ]
- store i16 %uv, ptr %d, align 2
- ret void
-}
-
-;; Check some instructions (e.g. sdiv) are not speculatively executed.
-
-;; Division by non-zero constant OK to speculate ...
-define void @f4(i1 %c, ptr nocapture noundef %d, ptr nocapture noundef readonly %m, ptr nocapture noundef readonly %b) {
-; CHECK-LABEL: @f4(
-; CHECK-NEXT: entry:
-; CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[B:%.*]], align 2
-; CHECK-NEXT: [[DIV_0:%.*]] = sdiv i16 [[TMP0]], 2
-; CHECK-NEXT: [[U:%.*]] = add i16 [[DIV_0]], [[TMP0]]
-; CHECK-NEXT: br i1 [[C:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
-; CHECK: if.then:
-; CHECK-NEXT: call void @side_effects0()
-; CHECK-NEXT: br label [[IF_END:%.*]]
-; CHECK: if.else:
-; CHECK-NEXT: call void @side_effects1()
-; CHECK-NEXT: br label [[IF_END]]
-; CHECK: if.end:
-; CHECK-NEXT: store i16 [[U]], ptr [[D:%.*]], align 2
-; CHECK-NEXT: ret void
-;
-entry:
- br i1 %c, label %if.then, label %if.else
-
-if.then:
- %0 = load i16, ptr %b, align 2
- call void @side_effects0()
- %div.0 = sdiv i16 %0, 2
- %u = add i16 %div.0, %0
- br label %if.end
-
-if.else:
- %1 = load i16, ptr %b, align 2
- call void @side_effects1()
- %div.1 = sdiv i16 %1, 2
- %v = add i16 %div.1, %1
- br label %if.end
-
-if.end:
- %uv = phi i16 [ %v, %if.else ], [ %u, %if.then ]
- store i16 %uv, ptr %d, align 2
- ret void
-}
-
-;; ... but not a general division ...
-define void @f5(i1 %c, ptr nocapture noundef %d, ptr nocapture noundef readonly %m, ptr nocapture noundef readonly %b) {
-; CHECK-LABEL: @f5(
-; CHECK-NEXT: entry:
-; CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[B:%.*]], align 2
-; CHECK-NEXT: br i1 [[C:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
-; CHECK: if.then:
-; CHECK-NEXT: call void @side_effects0()
-; CHECK-NEXT: [[DIV_0:%.*]] = sdiv i16 211, [[TMP0]]
-; CHECK-NEXT: [[U:%.*]] = add i16 [[DIV_0]], [[TMP0]]
-; CHECK-NEXT: br label [[IF_END:%.*]]
-; CHECK: if.else:
-; CHECK-NEXT: call void @side_effects1()
-; CHECK-NEXT: [[DIV_1:%.*]] = sdiv i16 211, [[TMP0]]
-; CHECK-NEXT: [[V:%.*]] = add i16 [[DIV_1]], [[TMP0]]
-; CHECK-NEXT: br label [[IF_END]]
-; CHECK: if.end:
-; CHECK-NEXT: [[UV:%.*]] = phi i16 [ [[V]], [[IF_ELSE]] ], [ [[U]], [[IF_THEN]] ]
-; CHECK-NEXT: store i16 [[UV]], ptr [[D:%.*]], align 2
-; CHECK-NEXT: ret void
-;
-entry:
- br i1 %c, label %if.then, label %if.else
-
-if.then:
- %0 = load i16, ptr %b, align 2
- call void @side_effects0()
- %div.0 = sdiv i16 211, %0
- %u = add i16 %div.0, %0
- br label %if.end
-
-if.else:
- %1 = load i16, ptr %b, align 2
- call void @side_effects1()
- %div.1 = sdiv i16 211, %1
- %v = add i16 %div.1, %1
- br label %if.end
-
-if.end:
- %uv = phi i16 [ %v, %if.else ], [ %u, %if.then ]
- store i16 %uv, ptr %d, align 2
- ret void
-}
-
-;; ... and it's also OK to hoist the division when there's no speculation happening.
-define void @f6(i1 %c, ptr nocapture noundef %d, ptr nocapture noundef readonly %m, ptr nocapture noundef readonly %b) {
-; CHECK-LABEL: @f6(
-; CHECK-NEXT: entry:
-; CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[B:%.*]], align 2
-; CHECK-NEXT: [[DIV_0:%.*]] = sdiv i16 211, [[TMP0]]
-; CHECK-NEXT: [[U:%.*]] = add i16 [[DIV_0]], [[TMP0]]
-; CHECK-NEXT: br i1 [[C:%.*]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
-; CHECK: if.then:
-; CHECK-NEXT: call void @no_side_effects0()
-; CHECK-NEXT: br label [[IF_END:%.*]]
-; CHECK: if.else:
-; CHECK-NEXT: call void @no_side_effects1()
-; CHECK-NEXT: br label [[IF_END]]
-; CHECK: if.end:
-; CHECK-NEXT: store i16 [[U]], ptr [[D:%.*]], align 2
-; CHECK-NEXT: ret void
-;
-entry:
- br i1 %c, label %if.then, label %if.else
-
-if.then:
- %0 = load i16, ptr %b, align 2
- call void @no_side_effects0()
- %div.0 = sdiv i16 211, %0
- %u = add i16 %div.0, %0
- br label %if.end
-
-if.else:
- %1 = load i16, ptr %b, align 2
- call void @no_side_effects1()
- %div.1 = sdiv i16 211, %1
- %v = add i16 %div.1, %1
- br label %if.end
-
-if.end:
- %uv = phi i16 [ %v, %if.else ], [ %u, %if.then ]
- store i16 %uv, ptr %d, align 2
- ret void
-}
-
-declare void @side_effects0()
-declare void @side_effects1()
-declare void @no_side_effects0() readonly nounwind willreturn
-declare void @no_side_effects1() readonly nounwind willreturn
More information about the llvm-commits
mailing list