commit e724226da753f10fd36fbb0ea392f04ab0fdbdab
Author: Florian Hahn <flo@fhahn.com>
Date:   Fri Oct 25 12:35:33 2024 +0100

    [VPlan] Return cost of 0 for VPWidenCastRecipe without underlying value.

    In some cases, VPWidenCastRecipes are created but not considered in the
    legacy cost model, including truncates/extends when evaluating a reduction
    in a smaller type. Return 0 for such casts for now, to avoid divergences
    between VPlan and legacy cost models.

    Fixes https://github.com/llvm/llvm-project/issues/113526.

diff --git a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
index 0eb4f7c7c88c..2080b77157b6 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
@@ -1524,6 +1524,11 @@ void VPWidenCastRecipe::execute(VPTransformState &State) {

 InstructionCost VPWidenCastRecipe::computeCost(ElementCount VF,
                                                VPCostContext &Ctx) const {
+  // TODO: In some cases, VPWidenCastRecipes are created but not considered in
+  // the legacy cost model, including truncates/extends when evaluating a
+  // reduction in a smaller type.
+  if (!getUnderlyingValue())
+    return 0;
   // Computes the CastContextHint from a recipes that may access memory.
   auto ComputeCCH = [&](const VPRecipeBase *R) -> TTI::CastContextHint {
     if (VF.isScalar())

opt < %s -passes=loop-vectorize -mtriple=x86_64-apple-macosx10.8.0 -mcpu=corei7-avx -S

target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.8.0"

define i32 @narrowed_reduction(ptr %a, i1 %cmp) #0 {
entry:
  %conv = zext i1 %cmp to i32
  br label %loop

loop:                                             ; preds = %loop, %entry
  %iv = phi i32 [ 1, %entry ], [ %inc, %loop ]
  %or13 = phi i32 [ 0, %entry ], [ %or, %loop ]
  %and = and i32 %or13, 1
  %or = or i32 %and, %conv
  %inc = add i32 %iv, 1
  %ec = icmp eq i32 %iv, 0
  br i1 %ec, label %exit, label %loop

exit:                                             ; preds = %loop
  ret i32 %or
}

attributes #0 = { "target-cpu"="penryn" }

target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.8.0"

define i64 @cost_assume(ptr %end, i64 %N) {
entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
  %0 = phi i64 [ 0, %entry ], [ %1, %loop ]
  %1 = add i64 %0, 1
  %iv.next = add nsw i64 %iv, 1
  %c = icmp ne i64 %N, 0
  tail call void @llvm.assume(i1 %c)
  %gep = getelementptr nusw [9 x i8], ptr null, i64 %iv.next
  %ec = icmp eq ptr %gep, %end
  br i1 %ec, label %exit, label %loop

exit:                                             ; preds = %loop
  ret i64 %1
}

; Function Attrs: nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write)
declare void @llvm.assume(i1 noundef) #0

attributes #0 = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) }

target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.8.0"

define float @PR27826(ptr nocapture readonly %a, ptr nocapture readonly %b, i32 %n) {
entry:
  %cmp = icmp sgt i32 %n, 0
  br i1 %cmp, label %preheader, label %for.end

preheader:                                        ; preds = %entry
  %t0 = sext i32 %n to i64
  br label %for

for:                                              ; preds = %for, %preheader
  %indvars.iv = phi i64 [ 0, %preheader ], [ %indvars.iv.next, %for ]
  %s.02 = phi float [ 0.000000e+00, %preheader ], [ %add4, %for ]
  %arrayidx = getelementptr inbounds float, ptr %a, i64 %indvars.iv
  %t1 = load float, ptr %arrayidx, align 4
  %arrayidx3 = getelementptr inbounds float, ptr %b, i64 %indvars.iv
  %t2 = load float, ptr %arrayidx3, align 4
  %add = fadd fast float %t1, %s.02
  %add4 = fadd fast float %add, %t2
  %indvars.iv.next = add nuw nsw i64 %indvars.iv, 32
  %cmp1 = icmp slt i64 %indvars.iv.next, %t0
  br i1 %cmp1, label %for, label %loopexit

loopexit:                                         ; preds = %for
  %add4.lcssa = phi float [ %add4, %for ]
  br label %for.end

for.end:                                          ; preds = %loopexit, %entry
  %s.0.lcssa = phi float [ 0.000000e+00, %entry ], [ %add4.lcssa, %loopexit ]
  ret float %s.0.lcssa
}

target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.8.0"

define void @reduction_store(ptr noalias %src, ptr %dst, i1 %x) #0 {
entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  %red = phi i32 [ 0, %entry ], [ %red.next, %loop ]
  %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
  %gep.src = getelementptr inbounds i32, ptr %src, i32 %iv
  %l = load i32, ptr %gep.src, align 4
  %l.and = and i32 %l, 3
  store i32 %l.and, ptr %dst, align 4
  %x.ext = zext i1 %x to i64
  %lshr = lshr i64 %x.ext, 12
  %t = trunc i64 %lshr to i32
  %red.next = and i32 %red, %t
  store i32 %red.next, ptr %dst, align 4
  %iv.next = add i32 %iv, 1
  %ec = icmp eq i32 %iv, 29
  br i1 %ec, label %exit, label %loop

exit:                                             ; preds = %loop
  ret void
}

attributes #0 = { "target-cpu"="znver3" }

target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.8.0"

define i64 @cost_loop_invariant_recipes(i1 %x, i64 %y) {
entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  %iv = phi i64 [ 0, %entry ], [ %iv.next.i.i.i, %loop ]
  %red = phi i64 [ 1, %entry ], [ %red.mul, %loop ]
  %not.x = xor i1 %x, true
  %ext = zext i1 %not.x to i64
  %shl = shl i64 %y, %ext
  %red.mul = mul i64 %shl, %red
  %iv.next.i.i.i = add i64 %iv, 1
  %ec = icmp eq i64 %iv, 1
  br i1 %ec, label %exit, label %loop

exit:                                             ; preds = %loop
  ret i64 %red.mul
}

target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.8.0"

define void @multi_exit(ptr %dst, ptr %src.1, ptr %src.2, i64 %A, i64 %B) #0 {
entry:
  br label %loop

loop:                                             ; preds = %loop.latch, %entry
  %iv.1.wide = phi i64 [ 0, %entry ], [ %iv.1.next.wide, %loop.latch ]
  %iv.1 = phi i32 [ 0, %entry ], [ %iv.1.next, %loop.latch ]
  %ec.1 = icmp ult i64 %iv.1.wide, %A
  br i1 %ec.1, label %loop.latch, label %exit

loop.latch:                                       ; preds = %loop
  %l.1 = load i64, ptr %src.1, align 8
  %l.2 = load i64, ptr %src.2, align 8
  %cmp55.us = icmp eq i64 %l.1, 0
  %cmp.i.us = icmp ne i64 %l.2, 0
  %and = and i1 %cmp.i.us, %cmp55.us
  %ext = zext i1 %and to i8
  store i8 %ext, ptr %dst, align 1
  %iv.1.next = add i32 %iv.1, 1
  %iv.1.next.wide = zext i32 %iv.1.next to i64
  %ec.2 = icmp ult i64 %iv.1.next.wide, %B
  br i1 %ec.2, label %loop, label %exit

exit:                                             ; preds = %loop.latch, %loop
  ret void
}

attributes #0 = { "target-cpu"="penryn" }

target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.8.0"

@c = external global [2048 x i32], align 16
@b = external global [2048 x i32], align 16
@d = external global [2048 x i32], align 16
@a = external global [2048 x i32], align 16

; Function Attrs: noinline nounwind ssp uwtable
define void @cost_model_1() #0 {
entry:
  br label %for.body

for.body:                                         ; preds = %for.body, %entry
  %indvars.iv = phi i64 [ 0, %entry ], [ %indvars.iv.next, %for.body ]
  %0 = shl nsw i64 %indvars.iv, 1
  %arrayidx = getelementptr inbounds [2048 x i32], ptr @c, i64 0, i64 %0
  %1 = load i32, ptr %arrayidx, align 8
  %idxprom1 = sext i32 %1 to i64
  %arrayidx2 = getelementptr inbounds [2048 x i32], ptr @b, i64 0, i64 %idxprom1
  %2 = load i32, ptr %arrayidx2, align 4
  %arrayidx4 = getelementptr inbounds [2048 x i32], ptr @d, i64 0, i64 %indvars.iv
  %3 = load i32, ptr %arrayidx4, align 4
  %idxprom5 = sext i32 %3 to i64
  %arrayidx6 = getelementptr inbounds [2048 x i32], ptr @a, i64 0, i64 %idxprom5
  store i32 %2, ptr %arrayidx6, align 4
  %indvars.iv.next = add i64 %indvars.iv, 1
  %lftr.wideiv = trunc i64 %indvars.iv.next to i32
  %exitcond = icmp eq i32 %lftr.wideiv, 256
  br i1 %exitcond, label %for.end, label %for.body

for.end:                                          ; preds = %for.body
  ret void
}

attributes #0 = { noinline nounwind ssp uwtable }

target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.8.0"

define i1 @any_of_cost(ptr %start, ptr %end) #0 {
entry:
  br label %loop

loop:                                             ; preds = %loop, %entry
  %any.of = phi i1 [ false, %entry ], [ %any.of.next, %loop ]
  %ptr.iv = phi ptr [ %start, %entry ], [ %ptr.iv.next, %loop ]
  %gep = getelementptr i8, ptr %ptr.iv, i64 8
  %l = load ptr, ptr %gep, align 8
  %cmp13.not.not = icmp eq ptr %l, null
  %any.of.next = select i1 %cmp13.not.not, i1 %any.of, i1 false
  %ptr.iv.next = getelementptr inbounds i8, ptr %ptr.iv, i64 40
  %cmp.not = icmp eq ptr %ptr.iv, %end
  br i1 %cmp.not, label %exit, label %loop

exit:                                             ; preds = %loop
  ret i1 %any.of.next
}

attributes #0 = { "target-cpu"="penryn" }

target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.8.0"

define void @cost_duplicate_recipe_for_sinking(ptr %A, i64 %N) #0 {
entry:
  br label %loop.header

loop.header:                                      ; preds = %loop.latch, %entry
  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
  %iv.shl = shl nsw i64 %iv, 2
  %gep.0 = getelementptr nusw double, ptr %A, i64 %iv.shl
  %l = load double, ptr %gep.0, align 8
  %c = fcmp oeq double %l, 0.000000e+00
  br i1 %c, label %if.then, label %loop.latch

if.then:                                          ; preds = %loop.header
  %gep.1 = getelementptr double, ptr %A, i64 %iv.shl
  store double 0.000000e+00, ptr %gep.1, align 8
  br label %loop.latch

loop.latch:                                       ; preds = %if.then, %loop.header
  %iv.next = add nsw i64 %iv, 1
  %ec = icmp eq i64 %iv, %N
  br i1 %ec, label %exit, label %loop.header

exit:                                             ; preds = %loop.latch
  ret void
}

attributes #0 = { "target-cpu"="znver3" }

target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.8.0"

define i64 @avx512_cond_load_cost(ptr %src, i32 %a, i64 %b, i32 %c, i32 %d) #0 {
entry:
  br label %loop.header

loop.header:                                      ; preds = %loop.latch, %entry
  %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop.latch ]
  %c.1 = icmp slt i32 %iv, 0
  br i1 %c.1, label %if.then, label %loop.latch

if.then:                                          ; preds = %loop.header
  %0 = urem i32 %a, %c
  %mul = sub i32 0, %0
  %div = udiv i32 %c, %d
  %or = or i32 %div, %mul
  %ext = sext i32 %or to i64
  %gep = getelementptr { i64, i64, i64 }, ptr %src, i64 %ext, i32 2
  %l = load i64, ptr %gep, align 8
  %or.2 = or i64 %l, %b
  br label %loop.latch

loop.latch:                                       ; preds = %if.then, %loop.header
  %res = phi i64 [ 0, %loop.header ], [ %or.2, %if.then ]
  %iv.next = add i32 %iv, 1
  %ec = icmp ult i32 %iv, %c
  br i1 %ec, label %loop.header, label %exit

exit:                                             ; preds = %loop.latch
  ret i64 %res
}

attributes #0 = { "target-features"="+avx512bw,+avx512cd,+avx512dq,+avx512f,+avx512vl" }

target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.8.0"

define i64 @live_in_known_1_via_scev() {
entry:
  %sel = select i1 false, i32 3, i32 0
  br label %ph

ph:                                               ; preds = %entry
  %p = phi i32 [ 1, %entry ]
  %N = add nuw nsw i32 %sel, 6
  %p.ext = zext nneg i32 %p to i64
  br label %loop

loop:                                             ; preds = %loop, %ph
  %iv = phi i32 [ 0, %ph ], [ %iv.next, %loop ]
  %red = phi i64 [ 3, %ph ], [ %red.mul, %loop ]
  %red.mul = mul nsw i64 %red, %p.ext
  %iv.next = add nuw nsw i32 %iv, 1
  %ec = icmp eq i32 %iv.next, %N
  br i1 %ec, label %exit, label %loop

exit:                                             ; preds = %loop
  %res = phi i64 [ %red.mul, %loop ]
  ret i64 %res
}

clang crashes at -O{s,2,3} on x86_64-linux-gnu: Assertion `(BestFactor.Width == LegacyVF.Width || planContainsAdditionalSimplifications(getPlanFor(BestFactor.Width), CostCtx, OrigLoop)) && " VPlan cost model and legacy cost model disagreed"' failed

It appears to be a recent regression as it doesn't reproduce with 19.1.0 and earlier.

Compiler Explorer: https://godbolt.org/z/jPaW9fnKj

```
[531] % clangtk -v
clang version 20.0.0git (https://github.com/llvm/llvm-project.git a9050525954cbe11d45b415e2248d9e25e004bfe)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /local/home/suz/suz-local/software/local/clang-trunk/bin
Build config: +assertions
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/11
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/12
Selected GCC installation: /usr/lib/gcc/x86_64-linux-gnu/12
Candidate multilib: .;@m64
Selected multilib: .;@m64
Found CUDA installation: /usr/local/cuda, version 11.8
[532] %
[532] % clangtk -O3 small.c
clang-20: /local/suz-local/software/clangbuild/llvm-project/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp:7519: llvm::VectorizationFactor llvm::LoopVectorizationPlanner::computeBestVF(): Assertion `(BestFactor.Width == LegacyVF.Width || planContainsAdditionalSimplifications(getPlanFor(BestFactor.Width), CostCtx, OrigLoop)) && " VPlan cost model and legacy cost model disagreed"' failed.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace, preprocessed source, and associated run script.
Stack dump:
0.      Program arguments: /local/home/suz/suz-local/software/local/clang-trunk/bin/clang-20 -cc1 -triple x86_64-unknown-linux-gnu -emit-obj -dumpdir a- -disable-free -clear-ast-before-backend -main-file-name small.c -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/local/suz-local/software/emitesting/bugs/20241024-clangtk-m64-Os-build-062114/delta -fcoverage-compilation-dir=/local/suz-local/software/emitesting/bugs/20241024-clangtk-m64-Os-build-062114/delta -resource-dir /local/home/suz/suz-local/software/local/clang-trunk/lib/clang/20 -I /usr/local/include -I /local/suz-local/software/local/include -internal-isystem /local/home/suz/suz-local/software/local/clang-trunk/lib/clang/20/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/12/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O3 -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcolor-diagnostics -vectorize-loops -vectorize-slp -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/small-22743c.o -x c small.c
1.      <eof> parser at end of file
2.      Optimizer
3.      Running pass "function<eager-inv>(float2int,lower-constant-intrinsics,chr,loop(loop-rotate<header-duplication;no-prepare-for-lto>,loop-deletion),loop-distribute,inject-tli-mappings,loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>,infer-alignment,loop-load-elim,instcombine<max-iterations=1;no-verify-fixpoint>,simplifycfg<bonus-inst-threshold=1;forward-switch-cond;switch-range-to-icmp;switch-to-lookup;no-keep-loops;hoist-common-insts;no-hoist-loads-stores-with-cond-faulting;sink-common-insts;speculate-blocks;simplify-cond-branch;no-speculate-unpredictables>,slp-vectorizer,vector-combine,instcombine<max-iterations=1;no-verify-fixpoint>,loop-unroll<O3>,transform-warning,sroa<preserve-cfg>,infer-alignment,instcombine<max-iterations=1;no-verify-fixpoint>,loop-mssa(licm<allowspeculation>),alignment-from-assumptions,loop-sink,instsimplify,div-rem-pairs,tailcallelim,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;hoist-loads-stores-with-cond-faulting;no-sink-common-insts;speculate-blocks;simplify-cond-branch;speculate-unpredictables>)" on module "small.c"
4.      Running pass "loop-vectorize<no-interleave-forced-only;no-vectorize-forced-only;>" on function "main"
 #0 0x000056385e381f80 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/local/home/suz/suz-local/software/local/clang-trunk/bin/clang-20+0x43e3f80)
 #1 0x000056385e37f38f llvm::sys::RunSignalHandlers() (/local/home/suz/suz-local/software/local/clang-trunk/bin/clang-20+0x43e138f)
 #2 0x000056385e37f4e5 SignalHandler(int) Signals.cpp:0:0
 #3 0x00007fe699585520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #4 0x00007fe6995d9a7c pthread_kill (/lib/x86_64-linux-gnu/libc.so.6+0x96a7c)
 #5 0x00007fe699585476 gsignal (/lib/x86_64-linux-gnu/libc.so.6+0x42476)
 #6 0x00007fe69956b7f3 abort (/lib/x86_64-linux-gnu/libc.so.6+0x287f3)
 #7 0x00007fe69956b71b (/lib/x86_64-linux-gnu/libc.so.6+0x2871b)
 #8 0x00007fe69957ce96 (/lib/x86_64-linux-gnu/libc.so.6+0x39e96)
 #9 0x000056385fd39986 llvm::LoopVectorizationPlanner::computeBestVF() (/local/home/suz/suz-local/software/local/clang-trunk/bin/clang-20+0x5d9b986)
#10 0x000056385fd50d8c llvm::LoopVectorizePass::processLoop(llvm::Loop*) (/local/home/suz/suz-local/software/local/clang-trunk/bin/clang-20+0x5db2d8c)
#11 0x000056385fd53ea1 llvm::LoopVectorizePass::runImpl(llvm::Function&) (/local/home/suz/suz-local/software/local/clang-trunk/bin/clang-20+0x5db5ea1)
#12 0x000056385fd54517 llvm::LoopVectorizePass::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/local/home/suz/suz-local/software/local/clang-trunk/bin/clang-20+0x5db6517)
#13 0x000056385f8a7d86 llvm::detail::PassModel<llvm::Function, llvm::LoopVectorizePass, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/local/home/suz/suz-local/software/local/clang-trunk/bin/clang-20+0x5909d86)
#14 0x000056385dd0d52f llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/local/home/suz/suz-local/software/local/clang-trunk/bin/clang-20+0x3d6f52f)
#15 0x000056385b779146 llvm::detail::PassModel<llvm::Function, llvm::PassManager<llvm::Function, llvm::AnalysisManager<llvm::Function>>, llvm::AnalysisManager<llvm::Function>>::run(llvm::Function&, llvm::AnalysisManager<llvm::Function>&) (/local/home/suz/suz-local/software/local/clang-trunk/bin/clang-20+0x17db146)
#16 0x000056385dd0b8bb llvm::ModuleToFunctionPassAdaptor::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/local/home/suz/suz-local/software/local/clang-trunk/bin/clang-20+0x3d6d8bb)
#17 0x000056385b777be6 llvm::detail::PassModel<llvm::Module, llvm::ModuleToFunctionPassAdaptor, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/local/home/suz/suz-local/software/local/clang-trunk/bin/clang-20+0x17d9be6)
#18 0x000056385dd0c41d llvm::PassManager<llvm::Module, llvm::AnalysisManager<llvm::Module>>::run(llvm::Module&, llvm::AnalysisManager<llvm::Module>&) (/local/home/suz/suz-local/software/local/clang-trunk/bin/clang-20+0x3d6e41d)
#19 0x000056385e6253b6 (anonymous namespace)::EmitAssemblyHelper::RunOptimizationPipeline(clang::BackendAction, std::unique_ptr<llvm::raw_pwrite_stream, std::default_delete<llvm::raw_pwrite_stream>>&, std::unique_ptr<llvm::ToolOutputFile, std::default_delete<llvm::ToolOutputFile>>&, clang::BackendConsumer*) BackendUtil.cpp:0:0
#20 0x000056385e628d53 clang::EmitBackendOutput(clang::DiagnosticsEngine&, clang::HeaderSearchOptions const&, clang::CodeGenOptions const&, clang::TargetOptions const&, clang::LangOptions const&, llvm::StringRef, llvm::Module*, clang::BackendAction, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>, std::unique_ptr<llvm::raw_pwrite_stream, std::default_delete<llvm::raw_pwrite_stream>>, clang::BackendConsumer*) (/local/home/suz/suz-local/software/local/clang-trunk/bin/clang-20+0x468ad53)
#21 0x000056385eceb3f5 clang::BackendConsumer::HandleTranslationUnit(clang::ASTContext&) (/local/home/suz/suz-local/software/local/clang-trunk/bin/clang-20+0x4d4d3f5)
#22 0x0000563860a56ffc clang::ParseAST(clang::Sema&, bool, bool) (/local/home/suz/suz-local/software/local/clang-trunk/bin/clang-20+0x6ab8ffc)
#23 0x000056385efcbdd9 clang::FrontendAction::Execute() (/local/home/suz/suz-local/software/local/clang-trunk/bin/clang-20+0x502ddd9)
#24 0x000056385ef45205 clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) (/local/home/suz/suz-local/software/local/clang-trunk/bin/clang-20+0x4fa7205)
#25 0x000056385f0aa403 clang::ExecuteCompilerInvocation(clang::CompilerInstance*) (/local/home/suz/suz-local/software/local/clang-trunk/bin/clang-20+0x510c403)
#26 0x000056385b330b57 cc1_main(llvm::ArrayRef<char const*>, char const*, void*) (/local/home/suz/suz-local/software/local/clang-trunk/bin/clang-20+0x1392b57)
#27 0x000056385b3275ea ExecuteCC1Tool(llvm::SmallVectorImpl<char const*>&, llvm::ToolContext const&) driver.cpp:0:0
#28 0x000056385b32b96f clang_main(int, char**, llvm::ToolContext const&) (/local/home/suz/suz-local/software/local/clang-trunk/bin/clang-20+0x138d96f)
#29 0x000056385b21ef2b main (/local/home/suz/suz-local/software/local/clang-trunk/bin/clang-20+0x1280f2b)
#30 0x00007fe69956cd90 (/lib/x86_64-linux-gnu/libc.so.6+0x29d90)
#31 0x00007fe69956ce40 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e40)
#32 0x000056385b326d15 _start (/local/home/suz/suz-local/software/local/clang-trunk/bin/clang-20+0x1388d15)
clangtk: error: unable to execute command: Aborted
clangtk: error: clang frontend command failed due to signal (use -v to see invocation)
clang version 20.0.0git (https://github.com/llvm/llvm-project.git a9050525954cbe11d45b415e2248d9e25e004bfe)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /local/home/suz/suz-local/software/local/clang-trunk/bin
Build config: +assertions
clangtk: note: diagnostic msg:
********************

PLEASE ATTACH THE FOLLOWING FILES TO THE BUG REPORT:
Preprocessed source(s) and associated run script(s) are located at:
clangtk: note: diagnostic msg: /tmp/small-ed84bf.c
clangtk: note: diagnostic msg: /tmp/small-ed84bf.sh
clangtk: note: diagnostic msg:

********************
[533] %
[533] % cat small.c
int a, b, c;
int main() {
  for (; a; a++) {
    c &= 1;
    c |= b == 1;
  }
  return 0;
}
```

@fhahn


commit 1c979ab7e59feb03c58cac631a93143b0c776c20
Author: AdityaK <hiraditya@msn.com>
Date:   Tue Apr 30 11:31:45 2024 -0700

    [GVNSink] Fix incorrect codegen with respect to GEPs #85333 (#88440)

    As mentioned in #68882 and
    https://discourse.llvm.org/t/rfc-replacing-getelementptr-with-ptradd/68699

    Gep arithmetic isn't consistent with different types. GVNSink didn't
    realize this and sank all geps
    as long as their operands can be wired via PHIs
    in a post-dominator.

    Fixes: #85333

diff --git a/llvm/lib/Transforms/Scalar/GVNSink.cpp b/llvm/lib/Transforms/Scalar/GVNSink.cpp
index d4907326eb0a..1d2577e1da33 100644
--- a/llvm/lib/Transforms/Scalar/GVNSink.cpp
+++ b/llvm/lib/Transforms/Scalar/GVNSink.cpp
@@ -719,12 +719,11 @@ GVNSink::analyzeInstructionForSinking(LockstepReverseIterator &LRI,
   // try and continue making progress.
   Instruction *I0 = NewInsts[0];

-  // If all instructions that are going to participate don't have the same
-  // number of operands, we can't do any useful PHI analysis for all operands.
-  auto hasDifferentNumOperands = [&I0](Instruction *I) {
-    return I->getNumOperands() != I0->getNumOperands();
+  auto isNotSameOperation = [&I0](Instruction *I) {
+    return !I0->isSameOperationAs(I);
   };
-  if (any_of(NewInsts, hasDifferentNumOperands))
+
+  if (any_of(NewInsts, isNotSameOperation))
     return std::nullopt;

   for (unsigned OpNum = 0, E = I0->getNumOperands(); OpNum != E; ++OpNum) {

target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"

%"struct.std::pair" = type <{ i32, %struct.substruct, [2 x i8] }>
%struct.substruct = type { i8, i8 }

define void @bar(ptr noundef nonnull align 4 dereferenceable(4) %__i, i32 noundef %__n) {
entry:
  %cmp = icmp eq i32 %__n, 1
  br i1 %cmp, label %if.then, label %if.else

if.then:                                          ; preds = %entry
  %0 = load ptr, ptr %__i, align 4
  %incdec.ptr4 = getelementptr inbounds i8, ptr %0, i32 -8
  br label %if.end6

if.else:                                          ; preds = %entry
  %1 = load ptr, ptr %__i, align 4
  %add.ptr = getelementptr inbounds %"struct.std::pair", ptr %1, i32 %__n
  br label %if.end6

if.end6:                                          ; preds = %if.else, %if.then
  %incdec.ptr.sink = phi ptr [ %incdec.ptr4, %if.then ], [ %add.ptr, %if.else ]
  store ptr %incdec.ptr.sink, ptr %__i, align 4
  ret void
}

target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"

define void @foo(ptr noundef nonnull align 4 dereferenceable(4) %__i, i32 noundef %__n) {
entry:
  %cmp = icmp eq i32 %__n, 1
  br i1 %cmp, label %if.then, label %if.else

if.then:                                          ; preds = %entry
  %0 = load ptr, ptr %__i, align 4
  %incdec.ptr = getelementptr inbounds i8, ptr %0, i32 8
  store ptr %incdec.ptr, ptr %__i, align 4
  br label %if.end6

if.else:                                          ; preds = %entry
  %cmp2 = icmp eq i32 %__n, -1
  br i1 %cmp2, label %if.then3, label %if.else5

if.then3:                                         ; preds = %if.else
  %1 = load ptr, ptr %__i, align 4
  %incdec.ptr4 = getelementptr inbounds i8, ptr %1, i32 -8
  store ptr %incdec.ptr4, ptr %__i, align 4
  br label %if.end6

if.else5:                                         ; preds = %if.else
  %2 = load ptr, ptr %__i, align 4
  %add.ptr = getelementptr inbounds i8, ptr %2, i32 -4
  store ptr %add.ptr, ptr %__i, align 4
  br label %if.end6

if.end6:                                          ; preds = %if.else5, %if.then3, %if.then
  ret void
}

GVNSink produces incorrect codegen with respect to GEPs

```llvm
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
target triple = "thumbv7em-none-unknown-eabi"

%"struct.std::pair" = type <{ i32, %struct.substruct, [2 x i8] }>
%struct.substruct = type { i8, i8 }
%"struct.std::random_access_iterator_tag" = type { i8 }

define linkonce_odr dso_local void @__advance(ptr noundef nonnull align 4 dereferenceable(4) %__i, i32 noundef %__n) local_unnamed_addr {
entry:
  %0 = call i1 @llvm.is.constant.i32(i32 %__n)
  %cmp = icmp eq i32 %__n, 1
  %or.cond = and i1 %0, %cmp
  br i1 %or.cond, label %if.then, label %if.else

if.then:                                          ; preds = %entry
  %1 = load ptr, ptr %__i, align 4
  %incdec.ptr = getelementptr inbounds i8, ptr %1, i32 8
  store ptr %incdec.ptr, ptr %__i, align 4
  br label %if.end6

if.else:                                          ; preds = %entry
  %2 = call i1 @llvm.is.constant.i32(i32 %__n)
  %cmp2 = icmp eq i32 %__n, -1
  %or.cond7 = and i1 %2, %cmp2
  br i1 %or.cond7, label %if.then3, label %if.else5

if.then3:                                         ; preds = %if.else
  %3 = load ptr, ptr %__i, align 4
  %incdec.ptr4 = getelementptr inbounds i8, ptr %3, i32 -8
  store ptr %incdec.ptr4, ptr %__i, align 4
  br label %if.end6

if.else5:                                         ; preds = %if.else
  %4 = load ptr, ptr %__i, align 4
  %add.ptr = getelementptr inbounds %"struct.std::pair", ptr %4, i32 %__n
  store ptr %add.ptr, ptr %__i, align 4
  br label %if.end6

if.end6:                                          ; preds = %if.then3, %if.else5, %if.then
  ret void
}
```

The above IR when piped through gvn-sink via `./bin/opt < /tmp/repro.ll -passes=gvn-sink -S` produces:

```llvm
; ModuleID = '<stdin>'
source_filename = "<stdin>"
target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
target triple = "thumbv7em-none-unknown-eabi"

define linkonce_odr dso_local void @__advance(ptr noundef nonnull align 4 dereferenceable(4) %__i, i32 noundef %__n) local_unnamed_addr {
entry:
  %0 = call i1 @llvm.is.constant.i32(i32 %__n)
  %cmp = icmp eq i32 %__n, 1
  %or.cond = and i1 %0, %cmp
  br i1 %or.cond, label %if.then, label %if.else

if.then:                                          ; preds = %entry
  br label %if.end6

if.else:                                          ; preds = %entry
  %1 = call i1 @llvm.is.constant.i32(i32 %__n)
  %cmp2 = icmp eq i32 %__n, -1
  %or.cond7 = and i1 %1, %cmp2
  br i1 %or.cond7, label %if.then3, label %if.else5

if.then3:                                         ; preds = %if.else
  br label %if.end6

if.else5:                                         ; preds = %if.else
  br label %if.end6

if.end6:                                          ; preds = %if.else5, %if.then3, %if.then
  %.sink1 = phi i32 [ 8, %if.then ], [ -8, %if.then3 ], [ %__n, %if.else5 ]
  %2 = load ptr, ptr %__i, align 4
  %incdec.ptr = getelementptr inbounds i8, ptr %2, i32 %.sink1
  store ptr %incdec.ptr, ptr %__i, align 4
  ret void
}

; Function Attrs: convergent nocallback nofree nosync nounwind willreturn memory(none)
declare i1 @llvm.is.constant.i32(i32) #0

attributes #0 = { convergent nocallback nofree nosync nounwind willreturn memory(none) }
```

The GEP in the result is incorrect. Prior, the GEPs were
```llvm
  %incdec.ptr = getelementptr inbounds i8, ptr %1, i32 8
  %incdec.ptr4 = getelementptr inbounds i8, ptr %3, i32 -8
  %add.ptr = getelementptr inbounds %"struct.std::pair", ptr %4, i32 %__n
```
but now it's
```llvm
%incdec.ptr = getelementptr inbounds i8, ptr %2, i32 %.sink1
```
This results in an incorrect offset for `%add.ptr = getelementptr inbounds %"struct.std::pair", ptr %4, i32 %__n` since we'd now just add `%.sink1` bytes (where `%.sink1 = %__n` which is the second argument), whereas before we'd add `%__n * 8` bytes (where 8 is the sizeof `%"struct.std::pair"`).

The before ASM is:
```asm
__advance:
        ldr     r2, [r0]
        add.w   r1, r2, r1, lsl #3
        str     r1, [r0]
        bx      lr
```
but the after asm is:
```asm
__advance:
        ldr     r2, [r0]
        add     r1, r2
        str     r1, [r0]
        bx      lr
```

The GEP should not be a candidate for sinking.


Negator::Negate Negator::Negator Negator::visitImpl Negator::~Negator

commit 48ae61470104e9d7a8be5beb8739c24f52cc33c0
Author: Yingwei Zheng <dtcxzyw2333@gmail.com>
Date:   Sat Aug 17 16:48:29 2024 +0800

    [InstCombine] Avoid infinite loop when negating phi nodes (#104581)

    Closes https://github.com/llvm/llvm-project/issues/96012

    ---------

    Co-authored-by: Nikita Popov <github@npopov.com>

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
index 16f1c3ba15eb..a0f2399972b5 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -780,11 +780,14 @@ class Negator final {
   using BuilderTy = IRBuilder<TargetFolder, IRBuilderCallbackInserter>;
   BuilderTy Builder;

+  const DominatorTree &DT;
+
   const bool IsTrulyNegation;

   SmallDenseMap<Value *, Value *> NegationsCache;

-  Negator(LLVMContext &C, const DataLayout &DL, bool IsTrulyNegation);
+  Negator(LLVMContext &C, const DataLayout &DL, const DominatorTree &DT,
+          bool IsTrulyNegation);

 #if LLVM_ENABLE_STATS
   unsigned NumValuesVisitedInThisNegator = 0;
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineNegator.cpp b/llvm/lib/Transforms/InstCombine/InstCombineNegator.cpp
index 2dd1db6a4a75..92293ef40146 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineNegator.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineNegator.cpp
@@ -90,13 +90,14 @@ static cl::opt<unsigned>
                     cl::desc("What is the maximal lookup depth when trying to "
                              "check for viability of negation sinking."));

-Negator::Negator(LLVMContext &C, const DataLayout &DL, bool IsTrulyNegation_)
+Negator::Negator(LLVMContext &C, const DataLayout &DL, const DominatorTree &DT_,
+                 bool IsTrulyNegation_)
     : Builder(C, TargetFolder(DL),
               IRBuilderCallbackInserter([&](Instruction *I) {
                 ++NegatorNumInstructionsCreatedTotal;
                 NewInstructions.push_back(I);
               })),
-      IsTrulyNegation(IsTrulyNegation_) {}
+      DT(DT_), IsTrulyNegation(IsTrulyNegation_) {}

 #if LLVM_ENABLE_STATS
 Negator::~Negator() {
@@ -309,6 +310,9 @@ std::array<Value *, 2> Negator::getSortedOperandsOfBinOp(Instruction *I) {
     auto *PHI = cast<PHINode>(I);
     SmallVector<Value *, 4> NegatedIncomingValues(PHI->getNumOperands());
     for (auto I : zip(PHI->incoming_values(), NegatedIncomingValues)) {
+      // Don't negate indvars to avoid infinite loops.
+      if (DT.dominates(PHI->getParent(), std::get<0>(I)))
+        return nullptr;
       if (!(std::get<1>(I) =
                 negate(std::get<0>(I), IsNSW, Depth + 1))) // Early return.
         return nullptr;
@@ -537,7 +541,8 @@ std::array<Value *, 2> Negator::getSortedOperandsOfBinOp(Instruction *I) {
   if (!NegatorEnabled || !DebugCounter::shouldExecute(NegatorCounter))
     return nullptr;

-  Negator N(Root->getContext(), IC.getDataLayout(), LHSIsZero);
+  Negator N(Root->getContext(), IC.getDataLayout(), IC.getDominatorTree(),
+            LHSIsZero);
   std::optional<Result> Res = N.run(Root, IsNSW);
   if (!Res) { // Negation failed.
     LLVM_DEBUG(dbgs() << "Negator: failed to sink negation into " << *Root

target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"

; Make sure we don't hang here.
define i32 @pr96012() {
;
entry:
  br label %for.cond

for.cond:
  %indvar = phi i32 [ 1, %entry ], [ %shl, %for.cond ]
  %conv1 = trunc i32 %indvar to i8
  %neg = sub i8 0, %conv1
  %conv2 = zext i8 %neg to i32
  %shl = shl nuw i32 %conv2, 24
  br label %for.cond
}

InstCombine gets stuck when sinking negation

Minimal reproducer (It looks over-reduced): https://godbolt.org/z/9zj5K8seP
```
; opt -passes=instcombine test.ll -S -debug
target datalayout = "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128"
target triple = "riscv64-unknown-linux-gnu"

define i32 @func_112() {
entry:
  br label %for.cond

for.cond:                                         ; preds = %for.cond, %entry
  %l_139.0 = phi i32 [ 1, %entry ], [ %0, %for.cond ]
  %conv5 = trunc i32 %l_139.0 to i8
  %call6 = sub i8 0, %conv5
  %conv7 = zext i8 %call6 to i32
  %0 = shl nuw i32 %conv7, 24
  br label %for.cond
}
```

```
INSTCOMBINE ITERATION #1 on func_112
ADD:   br label %for.cond
ADD:   %0 = shl nuw i32 %conv7, 24
ADD:   %conv7 = zext i8 %call6 to i32
ADD:   %call6 = sub i8 0, %conv5
ADD:   %conv5 = trunc i32 %l_139.0 to i8
ADD:   %l_139.0 = phi i32 [ 1, %entry ], [ %0, %for.cond ]
ADD:   br label %for.cond
IC: Visiting:   br label %for.cond
IC: Visiting:   %l_139.0 = phi i32 [ 1, %entry ], [ %0, %for.cond ]
IC: Visiting:   %conv5 = trunc i32 %l_139.0 to i8
IC: Visiting:   %call6 = sub i8 0, %conv5
Negator: attempting to sink negation into   %conv5 = trunc i32 %l_139.0 to i8
Negator: successfully sunk negation into   %conv5 = trunc i32 %l_139.0 to i8
         NEW:   %conv5.neg = trunc i32 %l_139.0.neg to i8
Negator: Propagating 3 instrs to InstCombine
ADD DEFERRED:   %.neg = mul i32 %conv7, -16777216
ADD DEFERRED:   %l_139.0.neg = phi i32 [ -1, %entry ], [ %.neg, %for.cond ]
ADD DEFERRED:   %conv5.neg = trunc i32 %l_139.0.neg to i8
IC: Old =   %call6 = sub i8 0, %conv5
    New =   <badref> = add i8 %conv5.neg, 0
ADD:   %call6 = add i8 %conv5.neg, 0
IC: ERASE   %0 = sub i8 0, %conv5
ADD DEFERRED:   %conv5 = trunc i32 %l_139.0 to i8
IC: ERASE   %conv5 = trunc i32 %l_139.0 to i8
ADD DEFERRED:   %l_139.0 = phi i32 [ 1, %entry ], [ %0, %for.cond ]
IC: ERASE   %l_139.0 = phi i32 [ 1, %entry ], [ %0, %for.cond ]
ADD DEFERRED:   %0 = shl nuw i32 %conv7, 24
IC: ERASE   %0 = shl nuw i32 %conv7, 24
ADD DEFERRED:   %conv7 = zext i8 %call6 to i32
ADD:   %conv5.neg = trunc i32 %l_139.0.neg to i8
ADD:   %l_139.0.neg = phi i32 [ -1, %entry ], [ %.neg, %for.cond ]
ADD:   %.neg = mul i32 %conv7, -16777216
IC: Visiting:   %.neg = mul i32 %conv7, -16777216
Negator: attempting to sink negation into   %conv7 = zext i8 %call6 to i32
Negator: failed to sink negation into   %conv7 = zext i8 %call6 to i32
ADD DEFERRED:   %call6.neg = sub i8 0, %call6
ADD DEFERRED:   %call6.neg.z = zext i8 %call6.neg to i32
IC: Old =   %.neg = mul i32 %conv7, -16777216
    New =   <badref> = shl i32 %call6.neg.z, 24
ADD:   %.neg = shl i32 %call6.neg.z, 24
IC: ERASE   %0 = mul i32 %conv7, -16777216
ADD DEFERRED:   %conv7 = zext i8 %call6 to i32
IC: ERASE   %conv7 = zext i8 %call6 to i32
ADD DEFERRED:   %call6 = add i8 %conv5.neg, 0
ADD:   %call6.neg.z = zext i8 %call6.neg to i32
ADD:   %call6.neg = sub i8 0, %call6
IC: Visiting:   %call6.neg = sub i8 0, %call6
IC: Old =   %call6.neg = sub i8 0, %call6
    New =   <badref> = sub i8 0, %conv5.neg
ADD:   %call6.neg = sub i8 0, %conv5.neg
IC: ERASE   %0 = sub i8 0, %call6
ADD DEFERRED:   %call6 = add i8 %conv5.neg, 0
IC: ERASE   %call6 = add i8 %conv5.neg, 0
ADD DEFERRED:   %conv5.neg = trunc i32 %l_139.0.neg to i8
ADD DEFERRED:   %call6.neg = sub i8 0, %conv5.neg
IC: Visiting:   %call6.neg = sub i8 0, %conv5.neg
Negator: attempting to sink negation into   %conv5.neg = trunc i32 %l_139.0.neg to i8
Negator: successfully sunk negation into   %conv5.neg = trunc i32 %l_139.0.neg to i8
         NEW:   %conv5.neg.neg = trunc i32 %l_139.0.neg.neg to i8
Negator: Propagating 3 instrs to InstCombine
ADD DEFERRED:   %.neg.neg = mul i32 %call6.neg.z, -16777216
ADD DEFERRED:   %l_139.0.neg.neg = phi i32 [ 1, %entry ], [ %.neg.neg, %for.cond ]
ADD DEFERRED:   %conv5.neg.neg = trunc i32 %l_139.0.neg.neg to i8
IC: Old =   %call6.neg = sub i8 0, %conv5.neg
    New =   <badref> = add i8 %conv5.neg.neg, 0
ADD:   %call6.neg = add i8 %conv5.neg.neg, 0
IC: ERASE   %0 = sub i8 0, %conv5.neg
ADD DEFERRED:   %conv5.neg = trunc i32 %l_139.0.neg to i8
IC: ERASE   %conv5.neg = trunc i32 %l_139.0.neg to i8
ADD DEFERRED:   %l_139.0.neg = phi i32 [ -1, %entry ], [ %.neg, %for.cond ]
IC: ERASE   %l_139.0.neg = phi i32 [ -1, %entry ], [ %.neg, %for.cond ]
ADD DEFERRED:   %.neg = shl i32 %call6.neg.z, 24
IC: ERASE   %.neg = shl i32 %call6.neg.z, 24
ADD DEFERRED:   %call6.neg.z = zext i8 %call6.neg to i32
ADD:   %conv5.neg.neg = trunc i32 %l_139.0.neg.neg to i8
ADD:   %l_139.0.neg.neg = phi i32 [ 1, %entry ], [ %.neg.neg, %for.cond ]
ADD:   %.neg.neg = mul i32 %call6.neg.z, -16777216
IC: Visiting:   %.neg.neg = mul i32 %call6.neg.z, -16777216
Negator: attempting to sink negation into   %call6.neg.z = zext i8 %call6.neg to i32
Negator: failed to sink negation into   %call6.neg.z = zext i8 %call6.neg to i32
ADD DEFERRED:   %call6.neg.neg = sub i8 0, %call6.neg
ADD DEFERRED:   %call6.neg.neg.z = zext i8 %call6.neg.neg to i32
IC: Old =   %.neg.neg = mul i32 %call6.neg.z, -16777216
    New =   <badref> = shl i32 %call6.neg.neg.z, 24
ADD:   %.neg.neg = shl i32 %call6.neg.neg.z, 24
IC: ERASE   %0 = mul i32 %call6.neg.z, -16777216
ADD DEFERRED:   %call6.neg.z = zext i8 %call6.neg to i32
IC: ERASE   %call6.neg.z = zext i8 %call6.neg to i32
ADD DEFERRED:   %call6.neg = add i8 %conv5.neg.neg, 0
ADD:   %call6.neg.neg.z = zext i8 %call6.neg.neg to i32
ADD:   %call6.neg.neg = sub i8 0, %call6.neg
IC: Visiting:   %call6.neg.neg = sub i8 0, %call6.neg
IC: Old =   %call6.neg.neg = sub i8 0, %call6.neg
    New =   <badref> = sub i8 0, %conv5.neg.neg
ADD:   %call6.neg.neg = sub i8 0, %conv5.neg.neg
IC: ERASE   %0 = sub i8 0, %call6.neg
ADD DEFERRED:   %call6.neg = add i8 %conv5.neg.neg, 0
IC: ERASE   %call6.neg = add i8 %conv5.neg.neg, 0
ADD DEFERRED:   %conv5.neg.neg = trunc i32 %l_139.0.neg.neg to i8
ADD DEFERRED:   %call6.neg.neg = sub i8 0, %conv5.neg.neg
IC: Visiting:   %call6.neg.neg = sub i8 0, %conv5.neg.neg
Negator: attempting to sink negation into   %conv5.neg.neg = trunc i32 %l_139.0.neg.neg to i8
Negator: successfully sunk negation into   %conv5.neg.neg = trunc i32 %l_139.0.neg.neg to i8
         NEW:   %conv5.neg.neg.neg = trunc i32 %l_139.0.neg.neg.neg to i8
Negator: Propagating 3 instrs to InstCombine
ADD DEFERRED:   %.neg.neg.neg = mul i32 %call6.neg.neg.z, -16777216
ADD DEFERRED:   %l_139.0.neg.neg.neg = phi i32 [ -1, %entry ], [ %.neg.neg.neg, %for.cond ]
ADD DEFERRED:   %conv5.neg.neg.neg = trunc i32 %l_139.0.neg.neg.neg to i8
IC: Old =   %call6.neg.neg = sub i8 0, %conv5.neg.neg
    New =   <badref> = add i8 %conv5.neg.neg.neg, 0
ADD:   %call6.neg.neg = add i8 %conv5.neg.neg.neg, 0
IC: ERASE   %0 = sub i8 0, %conv5.neg.neg
ADD DEFERRED:   %conv5.neg.neg = trunc i32 %l_139.0.neg.neg to i8
IC: ERASE   %conv5.neg.neg = trunc i32 %l_139.0.neg.neg to i8
ADD DEFERRED:   %l_139.0.neg.neg = phi i32 [ 1, %entry ], [ %.neg.neg, %for.cond ]
IC: ERASE   %l_139.0.neg.neg = phi i32 [ 1, %entry ], [ %.neg.neg, %for.cond ]
ADD DEFERRED:   %.neg.neg = shl i32 %call6.neg.neg.z, 24
IC: ERASE   %.neg.neg = shl i32 %call6.neg.neg.z, 24
ADD DEFERRED:   %call6.neg.neg.z = zext i8 %call6.neg.neg to i32
ADD:   %conv5.neg.neg.neg = trunc i32 %l_139.0.neg.neg.neg to i8
ADD:   %l_139.0.neg.neg.neg = phi i32 [ -1, %entry ], [ %.neg.neg.neg, %for.cond ]
ADD:   %.neg.neg.neg = mul i32 %call6.neg.neg.z, -16777216
IC: Visiting:   %.neg.neg.neg = mul i32 %call6.neg.neg.z, -16777216
Negator: attempting to sink negation into   %call6.neg.neg.z = zext i8 %call6.neg.neg to i32
Negator: failed to sink negation into   %call6.neg.neg.z = zext i8 %call6.neg.neg to i32
ADD DEFERRED:   %call6.neg.neg.neg = sub i8 0, %call6.neg.neg
ADD DEFERRED:   %call6.neg.neg.neg.z = zext i8 %call6.neg.neg.neg to i32
IC: Old =   %.neg.neg.neg = mul i32 %call6.neg.neg.z, -16777216
    New =   <badref> = shl i32 %call6.neg.neg.neg.z, 24
ADD:   %.neg.neg.neg = shl i32 %call6.neg.neg.neg.z, 24
IC: ERASE   %0 = mul i32 %call6.neg.neg.z, -16777216
ADD DEFERRED:   %call6.neg.neg.z = zext i8 %call6.neg.neg to i32
IC: ERASE   %call6.neg.neg.z = zext i8 %call6.neg.neg to i32
ADD DEFERRED:   %call6.neg.neg = add i8 %conv5.neg.neg.neg, 0
ADD:   %call6.neg.neg.neg.z = zext i8 %call6.neg.neg.neg to i32
ADD:   %call6.neg.neg.neg = sub i8 0, %call6.neg.neg
IC: Visiting:   %call6.neg.neg.neg = sub i8 0, %call6.neg.neg
IC: Old =   %call6.neg.neg.neg = sub i8 0, %call6.neg.neg
    New =   <badref> = sub i8 0, %conv5.neg.neg.neg
ADD:   %call6.neg.neg.neg = sub i8 0, %conv5.neg.neg.neg
IC: ERASE   %0 = sub i8 0, %call6.neg.neg
ADD DEFERRED:   %call6.neg.neg = add i8 %conv5.neg.neg.neg, 0
IC: ERASE   %call6.neg.neg = add i8 %conv5.neg.neg.neg, 0
ADD DEFERRED:   %conv5.neg.neg.neg = trunc i32 %l_139.0.neg.neg.neg to i8
ADD DEFERRED:   %call6.neg.neg.neg = sub i8 0, %conv5.neg.neg.neg
IC: Visiting:   %call6.neg.neg.neg = sub i8 0, %conv5.neg.neg.neg
Negator: attempting to sink negation into   %conv5.neg.neg.neg = trunc i32 %l_139.0.neg.neg.neg to i8
Negator: successfully sunk negation into   %conv5.neg.neg.neg = trunc i32 %l_139.0.neg.neg.neg to i8
         NEW:   %conv5.neg.neg.neg.neg = trunc i32 %l_139.0.neg.neg.neg.neg to i8
...
```


commit 71051deff27928cff908ea794e09806eee662801
Author: Nikita Popov <npopov@redhat.com>
Date:   Mon Jul 15 09:41:11 2024 +0200

    [MemCpyOpt] Fix infinite loop in memset+memcpy fold (#98638)

    For the case where the memcpy size is zero, this transform is a complex
    no-op. This can lead to an infinite loop when the size is zero in a way
    that BasicAA understands, because it can still understand that dst and
    dst + src_size are MustAlias.

    I've tried to mitigate this before using the isZeroSize() check, but we
    can hit cases where InstSimplify doesn't understand that the size is
    zero, but BasicAA does.

    As such, this bites the bullet and adds an explicit isKnownNonZero()
    check to guard against no-op transforms.

    Fixes https://github.com/llvm/llvm-project/issues/98610.

diff --git a/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp b/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp
index 4063762c88a2..cee34f0a6da1 100644
--- a/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp
+++ b/llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp
@@ -1296,6 +1296,15 @@ bool MemCpyOptPass::processMemSetMemCpyDependence(MemCpyInst *MemCpy,
   if (!BAA.isMustAlias(MemSet->getDest(), MemCpy->getDest()))
     return false;

+  // Don't perform the transform if src_size may be zero. In that case, the
+  // transform is essentially a complex no-op and may lead to an infinite
+  // loop if BasicAA is smart enough to understand that dst and dst + src_size
+  // are still MustAlias after the transform.
+  Value *SrcSize = MemCpy->getLength();
+  if (!isKnownNonZero(SrcSize,
+                      SimplifyQuery(MemCpy->getDataLayout(), DT, AC, MemCpy)))
+    return false;
+
   // Check that src and dst of the memcpy aren't the same. While memcpy
   // operands cannot partially overlap, exact equality is allowed.
   if (isModSet(BAA.getModRefInfo(MemCpy, MemoryLocation::getForSource(MemCpy))))
@@ -1312,7 +1321,6 @@ bool MemCpyOptPass::processMemSetMemCpyDependence(MemCpyInst *MemCpy,
   // Use the same i8* dest as the memcpy, killing the memset dest if different.
   Value *Dest = MemCpy->getRawDest();
   Value *DestSize = MemSet->getLength();
-  Value *SrcSize = MemCpy->getLength();

   if (mayBeVisibleThroughUnwinding(Dest, MemSet, MemCpy))
     return false;
@@ -1726,8 +1734,7 @@ bool MemCpyOptPass::processMemCpy(MemCpyInst *M, BasicBlock::iterator &BBI) {
     return true;
   }

-  // If the size is zero, remove the memcpy. This also prevents infinite loops
-  // in processMemSetMemCpyDependence, which is a no-op for zero-length memcpys.
+  // If the size is zero, remove the memcpy.
   if (isZeroSize(M->getLength())) {
     ++BBI;
     eraseInstruction(M);

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg) #0

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1

define void @pr64886(i64 %len, ptr noalias %p) {
  call void @llvm.memset.p0.i64(ptr inttoptr (i64 -1 to ptr), i8 0, i64 %len, i1 false)
  call void @llvm.memcpy.p0.p0.i64(ptr inttoptr (i64 -1 to ptr), ptr %p, i64 poison, i1 false)
  ret void
}

attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg) #0

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1

define void @pr98610(ptr %p, ptr noalias %p2) {
  call void @llvm.memset.p0.i64(ptr %p, i8 0, i64 1, i1 false)
  %zero.ext = zext i32 0 to i64
  %mul = mul i64 %zero.ext, 1
  call void @llvm.memcpy.p0.p0.i64(ptr %p, ptr %p2, i64 %mul, i1 false)
  ret void
}

attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }

opt -passes=memcpyopt -S %s -verify-memoryssa

target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"

@C = external constant [0 x i8]

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg) #0

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1

define void @test_constant(i64 %src_size, ptr %dst, i64 %dst_size, i8 %c) !dbg !5 {
  %non.zero = icmp ne i64 %src_size, 0
  call void @llvm.assume(i1 %non.zero)
  call void @llvm.memset.p0.i64(ptr %dst, i8 %c, i64 %dst_size, i1 false), !dbg !11
  call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr @C, i64 %src_size, i1 false), !dbg !12
  ret void, !dbg !13
}

; Function Attrs: nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write)
declare void @llvm.assume(i1 noundef) #2

attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
attributes #2 = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) }

!llvm.dbg.cu = !{!0}
!llvm.debugify = !{!2, !3}
!llvm.module.flags = !{!4}

!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
!1 = !DIFile(filename: "memset-memcpy-dbgloc.ll", directory: "/")
!2 = !{i32 3}
!3 = !{i32 1}
!4 = !{i32 2, !"Debug Info Version", i32 3}
!5 = distinct !DISubprogram(name: "test_constant", linkageName: "test_constant", scope: null, file: !1, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8)
!6 = !DISubroutineType(types: !7)
!7 = !{}
!8 = !{!9}
!9 = !DILocalVariable(name: "1", scope: !5, file: !1, line: 3, type: !10)
!10 = !DIBasicType(name: "ty32", size: 32, encoding: DW_ATE_unsigned)
!11 = !DILocation(line: 1, column: 1, scope: !5)
!12 = !DILocation(line: 2, column: 1, scope: !5)
!13 = !DILocation(line: 3, column: 1, scope: !5)

target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"

define void @test_different_types_i32_i128(ptr noalias %dst, ptr %src, i32 %dst_size, i128 %src_size, i8 %c) {
  %non.zero = icmp ne i128 %src_size, 0
  call void @llvm.assume(i1 %non.zero)
  call void @llvm.memset.p0.i32(ptr %dst, i8 %c, i32 %dst_size, i1 false)
  call void @llvm.memcpy.p0.p0.i128(ptr %dst, ptr %src, i128 %src_size, i1 false)
  ret void
}

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #0

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i128(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i128, i1 immarg) #1

; Function Attrs: nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write)
declare void @llvm.assume(i1 noundef) #2

attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
attributes #2 = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) }

target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"

define void @test_missing_noalias(ptr %src, i64 %src_size, ptr %dst, i64 %dst_size, i8 %c) {
  %non.zero = icmp ne i64 %src_size, 0
  call void @llvm.assume(i1 %non.zero)
  call void @llvm.memset.p0.i64(ptr %dst, i8 %c, i64 %dst_size, i1 false)
  call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %src, i64 %src_size, i1 false)
  ret void
}

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg) #0

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1

; Function Attrs: nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write)
declare void @llvm.assume(i1 noundef) #2

attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
attributes #2 = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) }

target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"

define void @test_different_types_i128_i32(ptr noalias %dst, ptr %src, i128 %dst_size, i32 %src_size, i8 %c) {
  %non.zero = icmp ne i32 %src_size, 0
  call void @llvm.assume(i1 %non.zero)
  call void @llvm.memset.p0.i128(ptr %dst, i8 %c, i128 %dst_size, i1 false)
  call void @llvm.memcpy.p0.p0.i32(ptr %dst, ptr %src, i32 %src_size, i1 false)
  ret void
}

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i32(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i32, i1 immarg) #0

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
declare void @llvm.memset.p0.i128(ptr nocapture writeonly, i8, i128, i1 immarg) #1

; Function Attrs: nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write)
declare void @llvm.assume(i1 noundef) #2

attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #2 = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) }

target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"

define void @test_align_memcpy(ptr %src, ptr noalias %dst, i64 %dst_size) {
  call void @llvm.memset.p0.i64(ptr %dst, i8 0, i64 %dst_size, i1 false)
  call void @llvm.memcpy.p0.p0.i64(ptr align 8 %dst, ptr align 8 %src, i64 80, i1 false)
  ret void
}

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg) #0

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1

attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }

target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"

define void @test_different_dst(ptr noalias %dst2, ptr %src, i64 %src_size, ptr noalias %dst, i64 %dst_size) {
  %non.zero = icmp ne i64 %src_size, 0
  call void @llvm.assume(i1 %non.zero)
  call void @llvm.memset.p0.i64(ptr %dst, i8 0, i64 %dst_size, i1 false)
  call void @llvm.memcpy.p0.p0.i64(ptr %dst2, ptr %src, i64 %src_size, i1 false)
  ret void
}

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg) #0

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1

; Function Attrs: nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write)
declare void @llvm.assume(i1 noundef) #2

attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
attributes #2 = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) }

target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"

define void @test_non_i8_dst_type(ptr %src, i64 %src_size, ptr noalias %dst_pi64, i64 %dst_size, i8 %c) {
  %non.zero = icmp ne i64 %src_size, 0
  call void @llvm.assume(i1 %non.zero)
  call void @llvm.memset.p0.i64(ptr %dst_pi64, i8 %c, i64 %dst_size, i1 false)
  call void @llvm.memcpy.p0.p0.i64(ptr %dst_pi64, ptr %src, i64 %src_size, i1 false)
  ret void
}

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg) #0

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1

; Function Attrs: nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write)
declare void @llvm.assume(i1 noundef) #2

attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
attributes #2 = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) }

target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"

define void @test_different_types_i64_i32(ptr noalias %dst, ptr %src, i64 %dst_size, i32 %src_size, i8 %c) {
  %non.zero = icmp ne i32 %src_size, 0
  call void @llvm.assume(i1 %non.zero)
  call void @llvm.memset.p0.i64(ptr %dst, i8 %c, i64 %dst_size, i1 false)
  call void @llvm.memcpy.p0.p0.i32(ptr %dst, ptr %src, i32 %src_size, i1 false)
  ret void
}

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg) #0

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i32(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i32, i1 immarg) #1

; Function Attrs: nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write)
declare void @llvm.assume(i1 noundef) #2

attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
attributes #2 = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) }

target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"

%struct = type { [8 x i8], [8 x i8] }

define void @test_intermediate_write(ptr %b) {
  %a = alloca %struct, align 8
  %a1 = getelementptr %struct, ptr %a, i32 0, i32 1, i32 0
  call void @llvm.memset.p0.i64(ptr %a, i8 0, i64 16, i1 false)
  store i8 1, ptr %a1, align 1
  call void @llvm.memcpy.p0.p0.i64(ptr %a, ptr %b, i64 8, i1 false)
  ret void
}

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg) #0

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1

attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }

target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"

define void @test(ptr %src, i64 %src_size, ptr noalias %dst, i64 %dst_size, i8 %c) {
  %non.zero = icmp ne i64 %src_size, 0
  call void @llvm.assume(i1 %non.zero)
  call void @llvm.memset.p0.i64(ptr %dst, i8 %c, i64 %dst_size, i1 false)
  call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %src, i64 %src_size, i1 false)
  ret void
}

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg) #0

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1

; Function Attrs: nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write)
declare void @llvm.assume(i1 noundef) #2

attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
attributes #2 = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) }

target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"

define void @test_same_dynamic_size(ptr noalias %src, ptr noalias %dst, i64 %size, i8 %c) {
  %non.zero = icmp ne i64 %size, 0
  call void @llvm.assume(i1 %non.zero)
  call void @llvm.memset.p0.i64(ptr %dst, i8 %c, i64 %size, i1 false)
  call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %src, i64 %size, i1 false)
  ret void
}

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg) #0

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1

; Function Attrs: nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write)
declare void @llvm.assume(i1 noundef) #2

attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
attributes #2 = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) }

target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"

define void @test_weird_element_type(ptr %src, i64 %src_size, ptr noalias %dst, i64 %dst_size, i8 %c) {
  %non.zero = icmp ne i64 %src_size, 0
  call void @llvm.assume(i1 %non.zero)
  call void @llvm.memset.p0.i64(ptr %dst, i8 %c, i64 %dst_size, i1 false)
  call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %src, i64 %src_size, i1 false)
  ret void
}

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg) #0

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1

; Function Attrs: nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write)
declare void @llvm.assume(i1 noundef) #2

attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
attributes #2 = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) }

target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"

define void @test_throwing_call(ptr %src, i64 %src_size, ptr noalias %dst, i64 %dst_size, i8 %c) {
  %non.zero = icmp ne i64 %src_size, 0
  call void @llvm.assume(i1 %non.zero)
  call void @llvm.memset.p0.i64(ptr %dst, i8 %c, i64 %dst_size, i1 false)
  call void @call() #3
  call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %src, i64 %src_size, i1 false)
  ret void
}

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg) #0

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1

declare void @call()

; Function Attrs: nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write)
declare void @llvm.assume(i1 noundef) #2

attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
attributes #2 = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) }
attributes #3 = { memory(none) }

target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"

define void @test_same_const_size(ptr noalias %src, ptr noalias %dst, i8 %c) {
  call void @llvm.memset.p0.i64(ptr %dst, i8 %c, i64 16, i1 false)
  call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %src, i64 16, i1 false)
  ret void
}

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg) #0

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1

attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }

target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"

define void @test_must_alias_different_size(ptr noalias %src, i64 %src_size, ptr noalias %dst, i64 %dst_size, i8 %c) {
  %non.zero = icmp ne i64 %src_size, 0
  call void @llvm.assume(i1 %non.zero)
  %gep1 = getelementptr i8, ptr %dst, i64 16
  call void @llvm.memset.p0.i64(ptr %gep1, i8 %c, i64 %dst_size, i1 false)
  %gep2 = getelementptr i8, ptr %dst, i64 16
  call void @llvm.memcpy.p0.p0.i64(ptr %gep2, ptr %src, i64 %src_size, i1 false)
  ret void
}

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg) #0

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1

; Function Attrs: nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write)
declare void @llvm.assume(i1 noundef) #2

attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
attributes #2 = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) }

target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"

define void @test_throwing_call_alloca(ptr %src, i64 %src_size, i64 %dst_size, i8 %c) {
  %dst = alloca i8, align 1
  %non.zero = icmp ne i64 %src_size, 0
  call void @llvm.assume(i1 %non.zero)
  call void @llvm.memset.p0.i64(ptr %dst, i8 %c, i64 %dst_size, i1 false)
  call void @call() #3
  call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %src, i64 %src_size, i1 false)
  ret void
}

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg) #0

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1

declare void @call()

; Function Attrs: nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write)
declare void @llvm.assume(i1 noundef) #2

attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
attributes #2 = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) }
attributes #3 = { memory(none) }

target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"

define void @test_addrspace(ptr addrspace(1) %src, i64 %src_size, ptr addrspace(1) noalias %dst, i64 %dst_size, i8 %c) {
  %non.zero = icmp ne i64 %src_size, 0
  call void @llvm.assume(i1 %non.zero)
  call void @llvm.memset.p1.i64(ptr addrspace(1) %dst, i8 %c, i64 %dst_size, i1 false)
  call void @llvm.memcpy.p1.p1.i64(ptr addrspace(1) %dst, ptr addrspace(1) %src, i64 %src_size, i1 false)
  ret void
}

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
declare void @llvm.memset.p1.i64(ptr addrspace(1) nocapture writeonly, i8, i64, i1 immarg) #0

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p1.p1.i64(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(1) noalias nocapture readonly, i64, i1 immarg) #1

; Function Attrs: nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write)
declare void @llvm.assume(i1 noundef) #2

attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
attributes #2 = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) }

target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"

@C = external constant [0 x i8]

define void @test_constant(i64 %src_size, ptr %dst, i64 %dst_size, i8 %c) {
  %non.zero = icmp ne i64 %src_size, 0
  call void @llvm.assume(i1 %non.zero)
  call void @llvm.memset.p0.i64(ptr %dst, i8 %c, i64 %dst_size, i1 false)
  call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr @C, i64 %src_size, i1 false)
  ret void
}

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg) #0

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1

; Function Attrs: nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write)
declare void @llvm.assume(i1 noundef) #2

attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
attributes #2 = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) }

target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"

define void @test_different_types_i32_i64(ptr noalias %dst, ptr %src, i32 %dst_size, i64 %src_size, i8 %c) {
  %non.zero = icmp ne i64 %src_size, 0
  call void @llvm.assume(i1 %non.zero)
  call void @llvm.memset.p0.i32(ptr %dst, i8 %c, i32 %dst_size, i1 false)
  call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %src, i64 %src_size, i1 false)
  ret void
}

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
declare void @llvm.memset.p0.i32(ptr nocapture writeonly, i8, i32, i1 immarg) #1

; Function Attrs: nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write)
declare void @llvm.assume(i1 noundef) #2

attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #2 = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: write) }

target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"

define void @test_must_alias_same_size(ptr noalias %src, ptr noalias %dst, i8 %c) {
  %gep1 = getelementptr i8, ptr %dst, i64 16
  call void @llvm.memset.p0.i64(ptr %gep1, i8 %c, i64 16, i1 false)
  %gep2 = getelementptr i8, ptr %dst, i64 16
  call void @llvm.memcpy.p0.p0.i64(ptr %gep2, ptr %src, i64 16, i1 false)
  ret void
}

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg) #0

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1

attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }

define void @test_memset_memcpy(ptr %src, i64 range(i64 1, 42) %src_size, ptr noalias %dst, i64 %dst_size, i8 %c) {
  call void @llvm.memset.p0.i64(ptr %dst, i8 %c, i64 %dst_size, i1 false)
  call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %src, i64 %src_size, i1 false)
  ret void
}

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write)
declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg) #0

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1

attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: write) }
attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }

Clang hangs forever building under -O1

ToT Clang hangs when building under -O1.

```
clang++ test.i -fno-exceptions -O1 -c -emit-llvm
```

The reproducer, `test.i`:

```c++
class MockDevice;
struct sp {
  MockDevice operator*();
};
void memset(void *__dest, int __ch, unsigned __len) {
  long __trans_tmp_1 = __builtin_object_size(__dest, 0);
  __builtin___memset_chk(__dest, __ch, __len, __trans_tmp_1);
}
template <class>
class function;
template <class _Rp, class... _ArgTypes>
class function<_Rp(_ArgTypes...)> {
 public:
  template <class _Fp>
  function operator=(_Fp);
};
template <typename T>
struct hidl_vec {
  hidl_vec() : mSize() { memset(mPad, 0, 0); }
  hidl_vec(hidl_vec &other) { *this = other; }
  hidl_vec &operator=(hidl_vec &other) {
    copyFrom(other, other.mSize);
    return *this;
  }
  T &operator[](unsigned index) { return mBuffer[index]; }
  T* mBuffer;
  unsigned mSize;
  unsigned mPad[];
  template <typename Array>
  void copyFrom(Array &data, unsigned size) {
    mSize = size;
    if (mSize) mBuffer = new T();
    for (int i = 0; i < size; ++i) mBuffer[i] = data[i];
  }
};
struct Capabilities {
  struct OperandPerformance {
    int a, b, c;
  };
  hidl_vec<OperandPerformance> operandPerformance;
};
template <typename>
class Action;
template <typename R, typename... Args>
class Action<R(Args...)> {
  using F = R();
 public:
  template <typename G>
  Action(G fun) {
    fun_ = fun;
  }
  function<F> fun_;
};
struct OnCallSpec {
  OnCallSpec WillByDefault(Action<void()>);
};
struct MockSpec {
  OnCallSpec InternalDefaultActionSetAt();
};
struct MockDevice {
  MockSpec gmock_getCapabilities();
};
sp createMockDevice_mockDevice;
MockDevice createMockDevice() {
  Capabilities getCapabilities_ret;
  (*createMockDevice_mockDevice)
      .gmock_getCapabilities()
      .InternalDefaultActionSetAt()
      .WillByDefault(getCapabilities_ret);
  return *createMockDevice_mockDevice;
}
```

This seems to be hanging somewhere in this vicinity:
```
>	clang.exe!llvm::detail::CheckedInt::from<int,0>(int FromValue) Line 136	C++
 	clang.exe!llvm::detail::CheckedInt::from<enum llvm::IRMemLocation,0>(llvm::IRMemLocation FromValue) Line 143	C++
 	clang.exe!llvm::detail::SafeIntIterator<enum llvm::IRMemLocation,0>::SafeIntIterator<enum llvm::IRMemLocation,0>(llvm::IRMemLocation Value) Line 196	C++
 	clang.exe!llvm::iota_range<enum llvm::IRMemLocation>::iota_range<enum llvm::IRMemLocation>(llvm::IRMemLocation Begin, llvm::IRMemLocation End, bool Inclusive) Line 275	C++
 	clang.exe!llvm::enum_seq_inclusive<enum llvm::IRMemLocation,void>(llvm::IRMemLocation Begin, llvm::IRMemLocation End, llvm::force_iteration_on_noniterable_enum_t __formal) Line 381	C++
 	clang.exe!llvm::MemoryEffectsBase<enum llvm::IRMemLocation>::locations() Line 96	C++
 	clang.exe!llvm::MemoryEffectsBase<enum llvm::IRMemLocation>::MemoryEffectsBase<enum llvm::IRMemLocation>(llvm::ModRefInfo MR) Line 107	C++
 	clang.exe!llvm::MemoryEffectsBase<enum llvm::IRMemLocation>::unknown() Line 113	C++
 	clang.exe!llvm::GlobalsAAResult::getMemoryEffects(const llvm::Function * F) Line 244	C++
 	clang.exe!llvm::AAResults::Model<llvm::GlobalsAAResult>::getMemoryEffects(const llvm::Function * F) Line 784	C++
 	clang.exe!llvm::AAResults::getMemoryEffects(const llvm::Function * F) Line 396	C++
 	clang.exe!llvm::BasicAAResult::getMemoryEffects(const llvm::CallBase * Call, llvm::AAQueryInfo & AAQI) Line 830	C++
 	clang.exe!llvm::AAResults::Model<llvm::BasicAAResult>::getMemoryEffects(const llvm::CallBase * Call, llvm::AAQueryInfo & AAQI) Line 780	C++
 	clang.exe!llvm::AAResults::getMemoryEffects(const llvm::CallBase * Call, llvm::AAQueryInfo & AAQI) Line 377	C++
 	clang.exe!llvm::AAResults::getModRefInfo(const llvm::CallBase * Call, const llvm::MemoryLocation & Loc, llvm::AAQueryInfo & AAQI) Line 230	C++
 	clang.exe!llvm::AAResults::getModRefInfo(const llvm::Instruction * I, const std::optional<llvm::MemoryLocation> & OptLoc, llvm::AAQueryInfo & AAQIP) Line 628	C++
 	clang.exe!llvm::BatchAAResults::getModRefInfo(const llvm::Instruction * I, const std::optional<llvm::MemoryLocation> & OptLoc) Line 646	C++
 	clang.exe!llvm::MemCpyOptPass::processMemSetMemCpyDependence(llvm::MemCpyInst * MemCpy, llvm::MemSetInst * MemSet, llvm::BatchAAResults & BAA) Line 1252	C++
 	clang.exe!llvm::MemCpyOptPass::processMemCpy(llvm::MemCpyInst * M, llvm::ilist_iterator_w_bits<llvm::ilist_detail::node_options<llvm::Instruction,1,0,void,1,llvm::BasicBlock>,0,0> & BBI) Line 1725	C++
 	clang.exe!llvm::MemCpyOptPass::iterateOnFunction(llvm::Function & F) Line 2013	C++
 	clang.exe!llvm::MemCpyOptPass::runImpl(llvm::Function & F, llvm::TargetLibraryInfo * TLI_, llvm::AAResults * AA_, llvm::AssumptionCache * AC_, llvm::DominatorTree * DT_, llvm::PostDominatorTree * PDT_, llvm::MemorySSA * MSSA_) Line 2070	C++
 	clang.exe!llvm::MemCpyOptPass::run(llvm::Function & F, llvm::AnalysisManager<llvm::Function> & AM) Line 2045	C++
 	clang.exe!llvm::detail::PassModel<llvm::Function,llvm::MemCpyOptPass,llvm::AnalysisManager<llvm::Function>>::run(llvm::Function & IR, llvm::AnalysisManager<llvm::Function> & AM) Line 90	C++
 	clang.exe!llvm::PassManager<llvm::Function,llvm::AnalysisManager<llvm::Function>>::run(llvm::Function & IR, llvm::AnalysisManager<llvm::Function> & AM) Line 85	C++
 	clang.exe!llvm::detail::PassModel<llvm::Function,llvm::PassManager<llvm::Function,llvm::AnalysisManager<llvm::Function>>,llvm::AnalysisManager<llvm::Function>>::run(llvm::Function & IR, llvm::AnalysisManager<llvm::Function> & AM) Line 90	C++
 	clang.exe!llvm::CGSCCToFunctionPassAdaptor::run(llvm::LazyCallGraph::SCC & C, llvm::AnalysisManager<llvm::LazyCallGraph::SCC,llvm::LazyCallGraph &> & AM, llvm::LazyCallGraph & CG, llvm::CGSCCUpdateResult & UR) Line 543	C++
 	clang.exe!llvm::detail::PassModel<llvm::LazyCallGraph::SCC,llvm::CGSCCToFunctionPassAdaptor,llvm::AnalysisManager<llvm::LazyCallGraph::SCC,llvm::LazyCallGraph &>,llvm::LazyCallGraph &,llvm::CGSCCUpdateResult &>::run(llvm::LazyCallGraph::SCC & IR, llvm::AnalysisManager<llvm::LazyCallGraph::SCC,llvm::LazyCallGraph &> & AM, llvm::LazyCallGraph & <ExtraArgs_0>, llvm::CGSCCUpdateResult & <ExtraArgs_1>) Line 90	C++
 	clang.exe!llvm::PassManager<llvm::LazyCallGraph::SCC,llvm::AnalysisManager<llvm::LazyCallGraph::SCC,llvm::LazyCallGraph &>,llvm::LazyCallGraph &,llvm::CGSCCUpdateResult &>::run(llvm::LazyCallGraph::SCC & InitialC, llvm::AnalysisManager<llvm::LazyCallGraph::SCC,llvm::LazyCallGraph &> & AM, llvm::LazyCallGraph & G, llvm::CGSCCUpdateResult & UR) Line 90	C++
 	clang.exe!llvm::detail::PassModel<llvm::LazyCallGraph::SCC,llvm::PassManager<llvm::LazyCallGraph::SCC,llvm::AnalysisManager<llvm::LazyCallGraph::SCC,llvm::LazyCallGraph &>,llvm::LazyCallGraph &,llvm::CGSCCUpdateResult &>,llvm::AnalysisManager<llvm::LazyCallGraph::SCC,llvm::LazyCallGraph &>,llvm::LazyCallGraph &,llvm::CGSCCUpdateResult &>::run(llvm::LazyCallGraph::SCC & IR, llvm::AnalysisManager<llvm::LazyCallGraph::SCC,llvm::LazyCallGraph &> & AM, llvm::LazyCallGraph & <ExtraArgs_0>, llvm::CGSCCUpdateResult & <ExtraArgs_1>) Line 90	C++
 	clang.exe!llvm::DevirtSCCRepeatedPass::run(llvm::LazyCallGraph::SCC & InitialC, llvm::AnalysisManager<llvm::LazyCallGraph::SCC,llvm::LazyCallGraph &> & AM, llvm::LazyCallGraph & CG, llvm::CGSCCUpdateResult & UR) Line 415	C++
 	clang.exe!llvm::detail::PassModel<llvm::LazyCallGraph::SCC,llvm::DevirtSCCRepeatedPass,llvm::AnalysisManager<llvm::LazyCallGraph::SCC,llvm::LazyCallGraph &>,llvm::LazyCallGraph &,llvm::CGSCCUpdateResult &>::run(llvm::LazyCallGraph::SCC & IR, llvm::AnalysisManager<llvm::LazyCallGraph::SCC,llvm::LazyCallGraph &> & AM, llvm::LazyCallGraph & <ExtraArgs_0>, llvm::CGSCCUpdateResult & <ExtraArgs_1>) Line 90	C++
 	clang.exe!llvm::ModuleToPostOrderCGSCCPassAdaptor::run(llvm::Module & M, llvm::AnalysisManager<llvm::Module> & AM) Line 277	C++
 	clang.exe!llvm::detail::PassModel<llvm::Module,llvm::ModuleToPostOrderCGSCCPassAdaptor,llvm::AnalysisManager<llvm::Module>>::run(llvm::Module & IR, llvm::AnalysisManager<llvm::Module> & AM) Line 90	C++
 	clang.exe!llvm::PassManager<llvm::Module,llvm::AnalysisManager<llvm::Module>>::run(llvm::Module & IR, llvm::AnalysisManager<llvm::Module> & AM) Line 85	C++
 	clang.exe!llvm::ModuleInlinerWrapperPass::run(llvm::Module & M, llvm::AnalysisManager<llvm::Module> & MAM) Line 631	C++
 	clang.exe!llvm::detail::PassModel<llvm::Module,llvm::ModuleInlinerWrapperPass,llvm::AnalysisManager<llvm::Module>>::run(llvm::Module & IR, llvm::AnalysisManager<llvm::Module> & AM) Line 90	C++
 	clang.exe!llvm::PassManager<llvm::Module,llvm::AnalysisManager<llvm::Module>>::run(llvm::Module & IR, llvm::AnalysisManager<llvm::Module> & AM) Line 85	C++
 	clang.exe!`anonymous namespace'::EmitAssemblyHelper::RunOptimizationPipeline(clang::BackendAction Action, std::unique_ptr<llvm::raw_pwrite_stream,std::default_delete<llvm::raw_pwrite_stream>> & OS, std::unique_ptr<llvm::ToolOutputFile,std::default_delete<llvm::ToolOutputFile>> & ThinLinkOS, clang::BackendConsumer * BC) Line 1114	C++
 	clang.exe!`anonymous namespace'::EmitAssemblyHelper::EmitAssembly(clang::BackendAction Action, std::unique_ptr<llvm::raw_pwrite_stream,std::default_delete<llvm::raw_pwrite_stream>> OS, clang::BackendConsumer * BC) Line 1180	C++
 	clang.exe!clang::EmitBackendOutput(clang::DiagnosticsEngine & Diags, const clang::HeaderSearchOptions & HeaderOpts, const clang::CodeGenOptions & CGOpts, const clang::TargetOptions & TOpts, const clang::LangOptions & LOpts, llvm::StringRef TDesc, llvm::Module * M, clang::BackendAction Action, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, std::unique_ptr<llvm::raw_pwrite_stream,std::default_delete<llvm::raw_pwrite_stream>> OS, clang::BackendConsumer * BC) Line 1341	C++
 	clang.exe!clang::BackendConsumer::HandleTranslationUnit(clang::ASTContext & C) Line 354	C++
 	clang.exe!clang::ParseAST(clang::Sema & S, bool PrintStats, bool SkipFunctionBodies) Line 183	C++
 	clang.exe!clang::ASTFrontendAction::ExecuteAction() Line 1192	C++
 	clang.exe!clang::CodeGenAction::ExecuteAction() Line 1144	C++
 	clang.exe!clang::FrontendAction::Execute() Line 1078	C++
 	clang.exe!clang::CompilerInstance::ExecuteAction(clang::FrontendAction & Act) Line 1061	C++
 	clang.exe!clang::ExecuteCompilerInvocation(clang::CompilerInstance * Clang) Line 280	C++
 	clang.exe!cc1_main(llvm::ArrayRef<char const *> Argv, const char * Argv0, void * MainAddr) Line 283	C++
 	clang.exe!ExecuteCC1Tool(llvm::SmallVectorImpl<char const *> & ArgV, const llvm::ToolContext & ToolContext) Line 215	C++
 	clang.exe!clang_main(int Argc, char * * Argv, const llvm::ToolContext & ToolContext) Line 256	C++
 	clang.exe!main(int argc, char * * argv) Line 17	C++
 	clang.exe!invoke_main() Line 79	C++
 	clang.exe!__scrt_common_main_seh() Line 288	C++
 	clang.exe!__scrt_common_main() Line 331	C++
 	clang.exe!mainCRTStartup(void * __formal) Line 17	C++
 	kernel32.dll!00007ff830e07344()	Unknown
 	ntdll.dll!00007ff83149cc91()	Unknown
```

Reduced:
```llvm
; RUN: opt -S -passes=memcpyopt < %s
define void @test(ptr %p, ptr noalias %p2) {
  call void @llvm.memset.p0.i64(ptr %p, i8 0, i64 1, i1 false)
  %zero.ext = zext i32 0 to i64
  %mul = mul i64 %zero.ext, 1
  call void @llvm.memcpy.p0.p0.i64(ptr %p, ptr %p2, i64 %mul, i1 false)
  ret void
}
```

This is an infinite loop in the processMemSetMemCpyDependence() transform. I've tried to mitigate this in the past, but it looks like the mitigation doesn't work for this case. I guess we'll have to add an isKnownNonZero() check to prevent it.


commit 1af627b592dd15bbe58136f902ced46251fc344d
Author: Yingwei Zheng <dtcxzyw2333@gmail.com>
Date:   Sun Feb 2 19:04:23 2025 +0800

    [InstSimplify] Add additional checks when substituting pointers (#125385)

    Compile-time impact:
    https://llvm-compile-time-tracker.com/compare.php?from=d09b521624f263b5f1296f8d4771836b97e600cb&to=e437ba2cb83bb965e13ef00727671896f03ff84f&stat=instructions:u
    IR diff looks acceptable.
    Closes https://github.com/llvm/llvm-project/issues/115574

diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 21c937530cd8..3cbc4107433e 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -27,6 +27,7 @@
 #include "llvm/Analysis/CmpInstAnalysis.h"
 #include "llvm/Analysis/ConstantFolding.h"
 #include "llvm/Analysis/InstSimplifyFolder.h"
+#include "llvm/Analysis/Loads.h"
 #include "llvm/Analysis/LoopAnalysisManager.h"
 #include "llvm/Analysis/MemoryBuiltins.h"
 #include "llvm/Analysis/OverflowInstAnalysis.h"
@@ -4731,12 +4732,16 @@ static Value *simplifySelectWithICmpCond(Value *CondVal, Value *TrueVal,
   // the arms of the select. See if substituting this value into the arm and
   // simplifying the result yields the same value as the other arm.
   if (Pred == ICmpInst::ICMP_EQ) {
-    if (Value *V = simplifySelectWithEquivalence({{CmpLHS, CmpRHS}}, TrueVal,
-                                                 FalseVal, Q, MaxRecurse))
-      return V;
-    if (Value *V = simplifySelectWithEquivalence({{CmpRHS, CmpLHS}}, TrueVal,
-                                                 FalseVal, Q, MaxRecurse))
-      return V;
+    if (CmpLHS->getType()->isIntOrIntVectorTy() ||
+        canReplacePointersIfEqual(CmpLHS, CmpRHS, Q.DL))
+      if (Value *V = simplifySelectWithEquivalence({{CmpLHS, CmpRHS}}, TrueVal,
+                                                   FalseVal, Q, MaxRecurse))
+        return V;
+    if (CmpLHS->getType()->isIntOrIntVectorTy() ||
+        canReplacePointersIfEqual(CmpRHS, CmpLHS, Q.DL))
+      if (Value *V = simplifySelectWithEquivalence({{CmpRHS, CmpLHS}}, TrueVal,
+                                                   FalseVal, Q, MaxRecurse))
+        return V;

     Value *X;
     Value *Y;

target datalayout = "p:8:8:8"

define ptr @icmp_ptr_eq_replace_null(ptr %a) {
  %cmp = icmp eq ptr %a, null
  %sel = select i1 %cmp, ptr null, ptr %a
  ret ptr %sel
}

target datalayout = "p:8:8:8"

define ptr @ptr_eq_replace_same_underlying_object(ptr %st, i64 %i, i64 %j) {
  %a = getelementptr inbounds i8, ptr %st, i64 %i
  %b = getelementptr inbounds i8, ptr %st, i64 %j
  %cmp = icmp eq ptr %a, %b
  %sel = select i1 %cmp, ptr %a, ptr %b
  ret ptr %sel
}

target datalayout = "p:8:8:8"

define ptr @icmp_ptr_eq_replace(ptr %a, ptr %b) {
  %cmp = icmp eq ptr %a, %b
  %sel = select i1 %cmp, ptr %a, ptr %b
  ret ptr %sel
}

[InstSimplify] wrong folding of pointer comparison `select (icmp eq ptr ...)`

https://github.com/llvm/llvm-project/blob/c93e001ca695e905cb965b36d63f7a348d1dd809/llvm/lib/Analysis/InstructionSimplify.cpp#L148-L167

Alive2 report: https://alive2.llvm.org/ce/z/aux2zY

```llvm
----------------------------------------
define ptr @smin_test8.2(ptr %a, ptr %b, ptr %c) {
#0:
  %cmp1 = icmp eq ptr %a, %b
  %umin1 = select i1 %cmp1, ptr %a, ptr %b
  %cmp2 = icmp slt ptr %b, %c
  %umin2 = select i1 %cmp2, ptr %b, ptr %c
  %cmp3 = icmp ult ptr %umin2, %a
  %umin3 = select i1 %cmp3, ptr %umin2, ptr %a
  %cmp4 = icmp slt ptr %c, %umin3
  %res = select i1 %cmp4, ptr %umin1, ptr %umin3
  ret ptr %res
}
=>
define ptr @smin_test8.2(ptr %a, ptr %b, ptr %c) {
#0:
  %cmp2 = icmp slt ptr %b, %c
  %umin2 = select i1 %cmp2, ptr %b, ptr %c
  %cmp3 = icmp ult ptr %umin2, %a
  %umin3 = select i1 %cmp3, ptr %umin2, ptr %a
  %cmp4 = icmp slt ptr %c, %umin3
  %res = select i1 %cmp4, ptr %b, ptr %umin3
  ret ptr %res
}
Transformation doesn't verify!

ERROR: Value mismatch

Example:
ptr %a = pointer(non-local, block_id=0, offset=1) / Address=#x1
ptr %b = pointer(non-local, block_id=1, offset=0) / Address=#x1
ptr %c = pointer(non-local, block_id=0, offset=-8) / Address=#x8

Source:
i1 %cmp1 = #x1 (1)
ptr %umin1 = pointer(non-local, block_id=0, offset=1) / Address=#x1
i1 %cmp2 = #x0 (0)
ptr %umin2 = pointer(non-local, block_id=0, offset=-8) / Address=#x8
i1 %cmp3 = #x0 (0)
ptr %umin3 = pointer(non-local, block_id=0, offset=1) / Address=#x1
i1 %cmp4 = #x1 (1)
ptr %res = pointer(non-local, block_id=0, offset=1) / Address=#x1

SOURCE MEMORY STATE
===================
NON-LOCAL BLOCKS:
Block 0 >	size: 0	align: 1	alloc type: 0	alive: false	address: 0
Block 1 >	size: 1	align: 1	alloc type: 0	alive: true	address: 1
Block 2 >	size: 1	align: 1	alloc type: 0	alive: true	address: 2
Block 3 >	size: 3	align: 1	alloc type: 0	alive: true	address: 8

Target:
i1 %cmp2 = #x0 (0)
ptr %umin2 = pointer(non-local, block_id=0, offset=-8) / Address=#x8
i1 %cmp3 = #x0 (0)
ptr %umin3 = pointer(non-local, block_id=0, offset=1) / Address=#x1
i1 %cmp4 = #x1 (1)
ptr %res = pointer(non-local, block_id=1, offset=0) / Address=#x1
Source value: pointer(non-local, block_id=0, offset=1) / Address=#x1
Target value: pointer(non-local, block_id=1, offset=0) / Address=#x1

Summary:
  0 correct transformations
  1 incorrect transformations
  0 failed-to-prove transformations
  0 Alive2 errors
```


commit 52361d0368b79841be12156bf03cf8c1851e5df7
Author: Yingwei Zheng <dtcxzyw2333@gmail.com>
Date:   Mon Nov 18 23:41:04 2024 +0800

    [ConstraintElim] Bail out on non-dedicated exits when adding exiting conditions (#116627)

    This patch bails out non-dedicated exits to avoid adding exiting
    conditions to invalid context.
    Closes https://github.com/llvm/llvm-project/issues/116553.

diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index d2e532d29d07..64433cb9216c 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -1034,9 +1034,9 @@ void State::addInfoForInductions(BasicBlock &BB) {
       DTN, CmpInst::ICMP_SLT, PN, B,
       ConditionTy(CmpInst::ICMP_SLE, StartValue, B)));

-  // Try to add condition from header to the exit blocks. When exiting either
-  // with EQ or NE in the header, we know that the induction value must be u<=
-  // B, as other exits may only exit earlier.
+  // Try to add condition from header to the dedicated exit blocks. When exiting
+  // either with EQ or NE in the header, we know that the induction value must
+  // be u<= B, as other exits may only exit earlier.
   assert(!StepOffset.isNegative() && "induction must be increasing");
   assert((Pred == CmpInst::ICMP_EQ || Pred == CmpInst::ICMP_NE) &&
          "unsupported predicate");
@@ -1044,8 +1044,11 @@ void State::addInfoForInductions(BasicBlock &BB) {
   SmallVector<BasicBlock *> ExitBBs;
   L->getExitBlocks(ExitBBs);
   for (BasicBlock *EB : ExitBBs) {
-    WorkList.emplace_back(FactOrCheck::getConditionFact(
-        DT.getNode(EB), CmpInst::ICMP_ULE, A, B, Precond));
+    // Bail out on non-dedicated exits.
+    if (DT.dominates(&BB, EB)) {
+      WorkList.emplace_back(FactOrCheck::getConditionFact(
+          DT.getNode(EB), CmpInst::ICMP_ULE, A, B, Precond));
+    }
   }
 }

define i1 @test_non_dedicated_exit(i16 %n) {
entry:
  %cond = icmp slt i16 %n, 1
  br i1 %cond, label %exit, label %loop.preheader

loop.preheader:                                   ; preds = %entry
  %sub = add nsw i16 %n, -1
  %ext = zext nneg i16 %sub to i32
  br label %loop

loop:                                             ; preds = %loop.latch, %loop.preheader
  %indvar = phi i32 [ %indvar.inc, %loop.latch ], [ 0, %loop.preheader ]
  %exitcond = icmp eq i32 %indvar, %ext
  br i1 %exitcond, label %exit, label %loop.latch

loop.latch:                                       ; preds = %loop
  %indvar.inc = add nuw nsw i32 %indvar, 1
  br label %loop

exit:                                             ; preds = %loop, %entry
  %cmp = icmp sgt i16 %n, 0
  ret i1 %cmp
}

[clang] Miscompile at O3 with SIGKILL

When I compiled this code with O3, it triggered SIGKILL. With O0/1/2, it returned 0.

Bisected to https://github.com/llvm/llvm-project/commit/5b927130b0e15a442a6ed171f43a612e6a40bbcd, which was committed by @fhahn

Compiler Explorer: https://godbolt.org/z/q9j75feYG

Code:
```c
int printf(const char *, ...);
int a, c, d;
int *b;
char e;
short f;
int g(int i) {
  if (i <= 0)
    return 1;
  int h = 0;
  for (; h < i - 1; h++) {
    for (; d - h - 1;)
      if (c)
        b[d] = e = 1;
    if (e)
      break;
  }
  return 0;
}
void j() { g(f); }
int main(int i, char *k[]) {
  j();
  while (0 < f)
    ;
  printf("%X\n", a);
}
```

Reproducer: https://alive2.llvm.org/ce/z/fLN5gw
```
; bin/opt -passes=constraint-elimination test.ll -S
define i1 @test(i16 %n) {
entry:
  %cond = icmp slt i16 %n, 1
  br i1 %cond, label %exit, label %loop.preheader

loop.preheader:
  %sub = add nsw i16 %n, -1
  %ext = zext nneg i16 %sub to i32
  br label %loop

loop:
  %indvar = phi i32 [ %indvar.inc, %loop.latch ], [ 0, %loop.preheader ]
  %12 = icmp eq i32 %indvar, %ext
  br i1 %12, label %exit, label %loop.latch

loop.latch:
  %indvar.inc = add nuw nsw i32 %indvar, 1
  br label %loop

exit:
  %cmp = icmp sgt i16 %n, 0
  ret i1 %cmp
}

```
I will post a fix later.