diff --git a/llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp b/llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombinePHI.cpp @@ -1270,8 +1270,15 @@ // \ / // phi [true] [false] // Make sure all inputs are constants. - if (!all_of(PN.operands(), [](Value *V) { return isa(V); })) - return nullptr; + + unsigned BiggestSize = 0; + for (Use &Op : PN.operands()) { + if (const auto *CI = dyn_cast_or_null(Op)) { + BiggestSize = std::max(BiggestSize, CI->getBitWidth()); + } else { + return nullptr; + } + } BasicBlock *BB = PN.getParent(); // Do not bother with unreachable instructions. @@ -1282,10 +1289,10 @@ LLVMContext &Context = PN.getContext(); auto *IDom = DT.getNode(BB)->getIDom()->getBlock(); Value *Cond; - SmallDenseMap SuccForValue; + SmallDenseMap SuccForValue; SmallDenseMap SuccCount; auto AddSucc = [&](ConstantInt *C, BasicBlock *Succ) { - SuccForValue[C] = Succ; + SuccForValue[C->getValue().sextOrSelf(BiggestSize)] = Succ; ++SuccCount[Succ]; }; if (auto *BI = dyn_cast(IDom->getTerminator())) { @@ -1303,9 +1310,6 @@ return nullptr; } - if (Cond->getType() != PN.getType()) - return nullptr; - // Check that edges outgoing from the idom's terminators dominate respective // inputs of the Phi. Optional Invert; @@ -1316,7 +1320,7 @@ // The input needs to be dominated by the corresponding edge of the idom. // This edge cannot be a multi-edge, as that would imply that multiple // different condition values follow the same edge. - auto It = SuccForValue.find(Input); + auto It = SuccForValue.find(Input->getValue().sextOrSelf(BiggestSize)); return It != SuccForValue.end() && SuccCount[It->second] == 1 && DT.dominates(BasicBlockEdge(IDom, It->second), BasicBlockEdge(Pred, BB)); @@ -1338,6 +1342,11 @@ Invert = NeedsInvert; } + unsigned PNTypeSize = PN.getType()->getPrimitiveSizeInBits(), + CondTypeSize = Cond->getType()->getPrimitiveSizeInBits(); + if (PNTypeSize > CondTypeSize) + Cond = Self.Builder.CreateSExt(Cond, PN.getType()); + if (!*Invert) return Cond; diff --git a/llvm/test/Transforms/InstCombine/pr54561.ll b/llvm/test/Transforms/InstCombine/pr54561.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/pr54561.ll @@ -0,0 +1,233 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S < %s -passes=sroa,instcombine | FileCheck %s + +define i32 @switch_to_sext(i8 %cond) { +; CHECK-LABEL: @switch_to_sext( +; CHECK-NEXT: entry: +; CHECK-NEXT: switch i8 [[COND:%.*]], label [[DEFAULT:%.*]] [ +; CHECK-NEXT: i8 -1, label [[SW__1:%.*]] +; CHECK-NEXT: i8 0, label [[SW_0:%.*]] +; CHECK-NEXT: i8 1, label [[SW_1:%.*]] +; CHECK-NEXT: ] +; CHECK: default: +; CHECK-NEXT: unreachable +; CHECK: sw.-1: +; CHECK-NEXT: br label [[MERGE:%.*]] +; CHECK: sw.0: +; CHECK-NEXT: br label [[MERGE]] +; CHECK: sw.1: +; CHECK-NEXT: br label [[MERGE]] +; CHECK: merge: +; CHECK-NEXT: [[TMP0:%.*]] = sext i8 [[COND]] to i32 +; CHECK-NEXT: ret i32 [[TMP0]] +; +entry: + switch i8 %cond, label %default [ + i8 -1, label %sw.-1 + i8 0, label %sw.0 + i8 1, label %sw.1 + ] + +default: + unreachable + +sw.-1: + br label %merge + +sw.0: + br label %merge + +sw.1: + br label %merge + +merge: + %ret = phi i32 [ 1, %sw.1 ], [ 0, %sw.0 ], [ -1, %sw.-1 ] + ret i32 %ret +} + +; Copied from . +define i32 @switch_to_sext_plane(i8 noundef %0) { +; CHECK-LABEL: @switch_to_sext_plane( +; CHECK-NEXT: start: +; CHECK-NEXT: switch i8 [[TMP0:%.*]], label [[BB2:%.*]] [ +; CHECK-NEXT: i8 -1, label [[BB3:%.*]] +; CHECK-NEXT: i8 0, label [[BB4:%.*]] +; CHECK-NEXT: i8 1, label [[BB1:%.*]] +; CHECK-NEXT: ] +; CHECK: bb2: +; CHECK-NEXT: unreachable +; CHECK: bb3: +; CHECK-NEXT: br label [[BB5:%.*]] +; CHECK: bb4: +; CHECK-NEXT: br label [[BB5]] +; CHECK: bb1: +; CHECK-NEXT: br label [[BB5]] +; CHECK: bb5: +; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[TMP0]] to i32 +; CHECK-NEXT: ret i32 [[TMP1]] +; +start: + %1 = alloca i32, align 4 + %order = alloca i8, align 1 + store i8 %0, i8* %order, align 1 + %_2 = load i8, i8* %order, align 1, !range !2, !noundef !3 + switch i8 %_2, label %bb2 [ + i8 -1, label %bb3 + i8 0, label %bb4 + i8 1, label %bb1 + ] + +bb2: ; preds = %start + unreachable + +bb3: ; preds = %start + store i32 -1, i32* %1, align 4 + br label %bb5 + +bb4: ; preds = %start + store i32 0, i32* %1, align 4 + br label %bb5 + +bb1: ; preds = %start + store i32 1, i32* %1, align 4 + br label %bb5 + +bb5: ; preds = %bb3, %bb4, %bb1 + %2 = load i32, i32* %1, align 4 + ret i32 %2 +} + +!2 = !{i8 -1, i8 2} +!3 = !{} + +; SROA'd variant of switch_to_sext_plane. +define i32 @switch_to_sext_plane_sroad(i8 noundef %0) { +; CHECK-LABEL: @switch_to_sext_plane_sroad( +; CHECK-NEXT: switch i8 [[TMP0:%.*]], label [[BB2:%.*]] [ +; CHECK-NEXT: i8 -1, label [[BB3:%.*]] +; CHECK-NEXT: i8 0, label [[BB4:%.*]] +; CHECK-NEXT: i8 1, label [[BB1:%.*]] +; CHECK-NEXT: ] +; CHECK: bb2: +; CHECK-NEXT: unreachable +; CHECK: bb3: +; CHECK-NEXT: br label [[BB5:%.*]] +; CHECK: bb4: +; CHECK-NEXT: br label [[BB5]] +; CHECK: bb1: +; CHECK-NEXT: br label [[BB5]] +; CHECK: bb5: +; CHECK-NEXT: [[TMP2:%.*]] = sext i8 [[TMP0]] to i32 +; CHECK-NEXT: ret i32 [[TMP2]] +; + switch i8 %0, label %bb2 [ + i8 -1, label %bb3 + i8 0, label %bb4 + i8 1, label %bb1 + ] + +bb2: ; preds = %start + unreachable + +bb3: ; preds = %start + br label %bb5 + +bb4: ; preds = %start + br label %bb5 + +bb1: ; preds = %start + br label %bb5 + +bb5: ; preds = %bb1, %bb4, %bb3 + %.0 = phi i32 [ 1, %bb1 ], [ 0, %bb4 ], [ -1, %bb3 ] + ret i32 %.0 +} + +define i32 @switch_to_sext_wrong_value(i8 %cond) { +; CHECK-LABEL: @switch_to_sext_wrong_value( +; CHECK-NEXT: entry: +; CHECK-NEXT: switch i8 [[COND:%.*]], label [[DEFAULT:%.*]] [ +; CHECK-NEXT: i8 -1, label [[SW__1:%.*]] +; CHECK-NEXT: i8 0, label [[SW_0:%.*]] +; CHECK-NEXT: i8 1, label [[SW_1:%.*]] +; CHECK-NEXT: ] +; CHECK: default: +; CHECK-NEXT: unreachable +; CHECK: sw.-1: +; CHECK-NEXT: br label [[MERGE:%.*]] +; CHECK: sw.0: +; CHECK-NEXT: br label [[MERGE]] +; CHECK: sw.1: +; CHECK-NEXT: br label [[MERGE]] +; CHECK: merge: +; CHECK-NEXT: [[RET:%.*]] = phi i32 [ 1, [[SW_1]] ], [ 0, [[SW_0]] ], [ 42, [[SW__1]] ] +; CHECK-NEXT: ret i32 [[RET]] +; +entry: + switch i8 %cond, label %default [ + i8 -1, label %sw.-1 + i8 0, label %sw.0 + i8 1, label %sw.1 + ] + +default: + unreachable + +sw.-1: + br label %merge + +sw.0: + br label %merge + +sw.1: + br label %merge + +merge: + %ret = phi i32 [ 1, %sw.1 ], [ 0, %sw.0 ], [ 42, %sw.-1 ] + ret i32 %ret +} + +define i32 @switch_to_sext_inverted(i8 %cond) { +; CHECK-LABEL: @switch_to_sext_inverted( +; CHECK-NEXT: entry: +; CHECK-NEXT: switch i8 [[COND:%.*]], label [[DEFAULT:%.*]] [ +; CHECK-NEXT: i8 -1, label [[SW__1:%.*]] +; CHECK-NEXT: i8 0, label [[SW_0:%.*]] +; CHECK-NEXT: i8 1, label [[SW_1:%.*]] +; CHECK-NEXT: ] +; CHECK: default: +; CHECK-NEXT: unreachable +; CHECK: sw.-1: +; CHECK-NEXT: br label [[MERGE:%.*]] +; CHECK: sw.0: +; CHECK-NEXT: br label [[MERGE]] +; CHECK: sw.1: +; CHECK-NEXT: br label [[MERGE]] +; CHECK: merge: +; CHECK-NEXT: [[TMP0:%.*]] = xor i8 [[COND]], -1 +; CHECK-NEXT: [[TMP1:%.*]] = sext i8 [[TMP0]] to i32 +; CHECK-NEXT: ret i32 [[TMP1]] +; +entry: + switch i8 %cond, label %default [ + i8 -1, label %sw.-1 + i8 0, label %sw.0 + i8 1, label %sw.1 + ] + +default: + unreachable + +sw.-1: + br label %merge + +sw.0: + br label %merge + +sw.1: + br label %merge + +merge: + %ret = phi i32 [ -2, %sw.1 ], [ -1, %sw.0 ], [ 0, %sw.-1 ] + ret i32 %ret +}