1+ {
2+ "bug_id" : " 127534" ,
3+ "issue_url" : " https://github.com/llvm/llvm-project/issues/127534" ,
4+ "bug_type" : " crash" ,
5+ "base_commit" : " 6de5d1e46d1812de2bbbbe8d8d2c811e4d16acbe" ,
6+ "knowledge_cutoff" : " 2025-02-17T18:29:15Z" ,
7+ "lit_test_dir" : [
8+ " llvm/test/Transforms/LoopSimplifyCFG"
9+ ],
10+ "hints" : {
11+ "fix_commit" : " 41437a6067b2f9dc8e7c458ff6417397796be31c" ,
12+ "components" : [
13+ " LoopSimplifyCFG"
14+ ],
15+ "bug_location_lineno" : {
16+ "llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp" : [
17+ [
18+ 369 ,
19+ 375
20+ ]
21+ ]
22+ },
23+ "bug_location_funcname" : {
24+ "llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp" : [
25+ " handleDeadExits"
26+ ]
27+ }
28+ },
29+ "patch" : " commit 41437a6067b2f9dc8e7c458ff6417397796be31c\n Author: Aleksandr Popov <42888396+aleks-tmb@users.noreply.github.com>\n Date: Fri Feb 21 12:26:39 2025 +0100\n\n [LoopSimplifyCFG] Fix SCEV invalidation after removing dead exit (#127536)\n \n Fixes #127534\n\n diff --git a/llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp b/llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp\n index 765b76e54068..4524446e5947 100644\n --- a/llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp\n +++ b/llvm/lib/Transforms/Scalar/LoopSimplifyCFG.cpp\n @@ -369,7 +369,7 @@ private:\n DeadInstructions.emplace_back(LandingPad);\n \n for (Instruction *I : DeadInstructions) {\n - SE.forgetBlockAndLoopDispositions(I);\n + SE.forgetValue(I);\n I->replaceAllUsesWith(PoisonValue::get(I->getType()));\n I->eraseFromParent();\n }\n " ,
30+ "tests" : [
31+ {
32+ "file" : " llvm/test/Transforms/LoopSimplifyCFG/pr117537.ll" ,
33+ "commands" : [
34+ " opt -S -passes='print<scalar-evolution>,loop-mssa(licm,loop-simplifycfg,loop-predication)' -verify-scev < %s"
35+ ],
36+ "tests" : [
37+ {
38+ "test_name" : " <module>" ,
39+ "test_body": "\n; Make sure we don't assert due to insufficient SCEV invalidation.\n\ndefine i64 @\"main\"(ptr addrspace(1) %p, i1 %check) {\n;\nentry:\n br label %loop0.pre\n\nloop0.pre:\n br i1 %check, label %exit, label %loop0\n\nloop0:\n %length = load atomic i32, ptr addrspace(1) %p unordered, align 4\n %28 = icmp ugt i32 %length, 1\n br i1 %28, label %loop0.out, label %loop1.preheader\n\nloop0.out:\n %t = add i32 0, 1\n br i1 false, label %loop1.preheader, label %mid\n\nloop1.preheader:\n %length.lcssa = phi i32 [ %length, %loop0.out ], [ %length, %loop0 ]\n %local = phi i32 [ 0, %loop0 ], [ %t, %loop0.out ]\n br label %loop1\n\nloop1:\n %iv1 = phi i32 [ 4, %loop1.preheader ], [ %iv1.next, %loop1.guarded ]\n %82 = icmp ult i32 %iv1, %length.lcssa\n %wc = call i1 @llvm.experimental.widenable.condition()\n %guard.chk = and i1 %82, %wc\n br i1 %guard.chk, label %loop1.guarded, label %deopt-exit\n\nloop1.guarded:\n %iv1.next = add nuw nsw i32 %iv1, 1\n %chk = icmp ugt i32 %iv1, 310\n br i1 %chk, label %loop1.exit, label %loop1\n\ndeopt-exit:\n %100 = call i64 (...) @llvm.experimental.deoptimize.i64(i32 13) [ \"deopt\"() ]\n ret i64 %100\n\nloop1.exit:\n ret i64 0\n\nmid:\n br label %loop0.pre\n\nexit:\n ret i64 0\n}\n\ndeclare i64 @foo()\n\ndeclare i64 @llvm.experimental.deoptimize.i64(...)\n\n; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(inaccessiblemem: readwrite)\ndeclare noundef i1 @llvm.experimental.widenable.condition()"
40+ }
41+ ]
42+ }
43+ ],
44+ "issue" : {
45+ "title" : " [SCEV] Assertion `isAvailableAtLoopEntry(RHS, L) && \" RHS is not available at Loop Entry\" ' failed" ,
46+ "body": "During our local testing, we encountered the assertion failure `isAvailableAtLoopEntry(RHS, L) && \"RHS is not available at Loop Entry\".\n\nReduced reproducer:\n\n```llvm\ntarget datalayout = \"e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128-ni:1-p2:32:8:8:32-ni:2\"\ntarget triple = \"x86_64-unknown-linux-gnu\"\n\ndefine i64 @\"main\"(ptr addrspace(1) %p, i1 %check) {\nentry:\n br label %loop0.pre\n\nloop0.pre:\n br i1 %check, label %exit, label %loop0\n\nloop0:\n %length = load atomic i32, ptr addrspace(1) %p unordered, align 4\n %28 = icmp ugt i32 %length, 1\n br i1 %28, label %loop0.out, label %loop1.preheader\n\nloop0.out:\n %t = add i32 0, 1\n br i1 false, label %loop1.preheader, label %mid\n\nloop1.preheader:\n %length.lcssa = phi i32 [ %length, %loop0.out ], [ %length, %loop0 ]\n %local = phi i32 [ 0, %loop0 ], [ %t, %loop0.out ]\n br label %loop1\n\nloop1:\n %iv1 = phi i32 [ 4, %loop1.preheader ], [ %iv1.next, %loop1.guarded ]\n %82 = icmp ult i32 %iv1, %length.lcssa\n %wc = call i1 @llvm.experimental.widenable.condition()\n %guard.chk = and i1 %82, %wc\n br i1 %guard.chk, label %loop1.guarded, label %deopt-exit\n\nloop1.guarded:\n %iv1.next = add nuw nsw i32 %iv1, 1\n %chk = icmp ugt i32 %iv1, 310\n br i1 %chk, label %loop1.exit, label %loop1\n\ndeopt-exit:\n %100 = call i64 (...) @llvm.experimental.deoptimize.i64(i32 13) [ \"deopt\"() ]\n ret i64 %100\n\nloop1.exit:\n ret i64 0\n\nmid:\n br label %loop0.pre\n\nexit:\n ret i64 0\n}\n\ndeclare i64 @foo()\n\ndeclare i64 @llvm.experimental.deoptimize.i64(...)\n\n; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(inaccessiblemem: readwrite)\ndeclare noundef i1 @llvm.experimental.widenable.condition()\n```\n\n\nSteps to reproduce:\n`$opt -passes='print<scalar-evolution>,loop-mssa(licm,loop-simplifycfg,loop-predication)'`\n\nProof: https://godbolt.org/z/8hsbahTjq\n\nLooks like `LoopSimplifyCFGPass` is guilty - it doesn\u2019t invalidate SCEV when removing a dead exit:\n\n```c++\n void handleDeadExits() {\n...\n for (Instruction *I : DeadInstructions) {\n SE.forgetBlockAndLoopDispositions(I);\n I->replaceAllUsesWith(PoisonValue::get(I->getType()));\n I->eraseFromParent();\n }\n```\n\n",
47+ "author" : " aleks-tmb" ,
48+ "labels" : [
49+ " crash" ,
50+ " llvm:transforms"
51+ ],
52+ "comments" : [
53+ {
54+ "author" : " aleks-tmb" ,
55+ "body" : " The `LoopSimplifyCFG` pass removes `%length.lcssa.ph`, but later, in the `LoopPredication` pass, we encounter an assertion failure for the `%length.lcssa` SCEV, which remains equal to `%length`\n\n <img width=\" 935\" alt=\" Image\" src=\" https://github.com/user-attachments/assets/4d630238-569e-4d2b-b760-b09d54b4450f\" />\n\n "
56+ },
57+ {
58+ "author" : " aleks-tmb" ,
59+ "body" : " It seems the reason why forgetBlockAndLoopDispositions doesn't cover this case is that `%length.lcssa.ph` doesn't have an existing SCEV, since the instruction was just added by the previous LICM pass."
60+ },
61+ {
62+ "author" : " nikic" ,
63+ "body" : " There is some pretty tricky interaction between LCSSA formation and SCEV invalidation, see:\n https://github.com/llvm/llvm-project/blob/7f69a399df384c86428d0c97e3afbc8146324226/llvm/lib/Transforms/Utils/LCSSA.cpp#L206-L211\n\n Basically, when forming LCSSA form, we take care not to break the invalidation chain, so that invalidation of the LCSSA phi node is sufficient.\n\n I think the LICM predecessor splitting effectively does manual LCSSA formation and doesn't maintain this special logic.\n\n I think the extra forgetValue() call works around this issue and variants of this might appear elsewhere. But TBH I'm not totally sure."
64+ }
65+ ]
66+ },
67+ "verified" : true
68+ }
0 commit comments