https://github.com/gamesh411 created https://github.com/llvm/llvm-project/pull/178911
Loop unrolling has been introduced and stable since 2017 but remained off by default. This patch enables it to improve analysis coverage for loops with known bounds for small runtime performance cost. As a side effect of this change, analysis option `cfg-loopexit` is also enabled by default as a requirement for loop unrolling. Tests are either updated to use the old behavior if they only made sense to run without loop-unrolling, in other cases the expectations are updated to reflect the now default addition of `LoopExit` blocks to the CFG. From 4b7e62d85af4339ef0af8a0f809ac4e9cf247527 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Sun, 25 Jan 2026 00:01:26 +0100 Subject: [PATCH 01/13] [clang][analyzer] Enable loop unrolling by default Loop unrolling has been introduced and stable since 2017 but remained off by default. This patch enables it to improve analysis coverage for loops with known bounds for small runtime performance cost. As a side effect of this change, analysis option `cfg-loopexit` is also enabled by default as a requirement for loop unrolling. Tests are either updated to use the old behavior if they only made sense to run without loop-unrolling, in other cases the expectations are updated to reflect the now default addition of `LoopExit` blocks to the CFG. --- clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def | 4 ++-- clang/test/Analysis/analyzer-config.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def index 90b80e5201aa8..eee152cc1c504 100644 --- a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def +++ b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def @@ -103,7 +103,7 @@ ANALYZER_OPTION( ANALYZER_OPTION(bool, ShouldIncludeLoopExitInCFG, "cfg-loopexit", "Whether or not the end of the loop information should " "be included in the CFG.", - false) + true) ANALYZER_OPTION(bool, ShouldIncludeRichConstructorsInCFG, "cfg-rich-constructors", @@ -295,7 +295,7 @@ ANALYZER_OPTION(bool, ShouldWidenLoops, "widen-loops", ANALYZER_OPTION( bool, ShouldUnrollLoops, "unroll-loops", - "Whether the analysis should try to unroll loops with known bounds.", false) + "Whether the analysis should try to unroll loops with known bounds.", true) ANALYZER_OPTION( bool, ShouldAssumeAtLeastOneIteration, "assume-at-least-one-iteration", diff --git a/clang/test/Analysis/analyzer-config.c b/clang/test/Analysis/analyzer-config.c index 96b0c12821746..099a334b1a180 100644 --- a/clang/test/Analysis/analyzer-config.c +++ b/clang/test/Analysis/analyzer-config.c @@ -24,7 +24,7 @@ // CHECK-NEXT: cfg-expand-default-aggr-inits = false // CHECK-NEXT: cfg-implicit-dtors = true // CHECK-NEXT: cfg-lifetime = false -// CHECK-NEXT: cfg-loopexit = false +// CHECK-NEXT: cfg-loopexit = true // CHECK-NEXT: cfg-rich-constructors = true // CHECK-NEXT: cfg-scopes = false // CHECK-NEXT: cfg-temporary-dtors = true @@ -139,6 +139,6 @@ // CHECK-NEXT: unix.StdCLibraryFunctions:DisplayLoadedSummaries = false // CHECK-NEXT: unix.StdCLibraryFunctions:ModelPOSIX = true // CHECK-NEXT: unix.Stream:Pedantic = false -// CHECK-NEXT: unroll-loops = false +// CHECK-NEXT: unroll-loops = true // CHECK-NEXT: verbose-report-filename = false // CHECK-NEXT: widen-loops = false From 9fe5ddf87da88a7233c17f7ccd996549be3aac08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Mon, 26 Jan 2026 11:27:39 +0100 Subject: [PATCH 02/13] update analyzer-stats.c The test now validates both configurations (with and without loop-unrolling). --- clang/test/Analysis/analyzer-stats.c | 32 +++++++++++++++++++++------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/clang/test/Analysis/analyzer-stats.c b/clang/test/Analysis/analyzer-stats.c index 7e2e9164ff35d..2133c328519ea 100644 --- a/clang/test/Analysis/analyzer-stats.c +++ b/clang/test/Analysis/analyzer-stats.c @@ -1,8 +1,23 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,deadcode.DeadStores,debug.Stats -verify -Wno-unreachable-code -analyzer-max-loop 4 %s +// RUN: %clang_analyze_cc1 \ +// RUN: -analyzer-checker=core,deadcode.DeadStores,debug.Stats \ +// RUN: -Wno-unreachable-code \ +// RUN: -verify=default %s + +// NOTE: analyzer-max-loop option is only meaningful if unroll-loops is false, +// that's why we do not pass it in the first case, as unroll-loops is +// true by default. + +// RUN: %clang_analyze_cc1 \ +// RUN: -analyzer-config unroll-loops=false \ +// RUN: -analyzer-max-loop 4 \ +// RUN: -analyzer-checker=core,deadcode.DeadStores,debug.Stats \ +// RUN: -Wno-unreachable-code \ +// RUN: -verify=no-unroll %s int foo(void); -int test(void) { // expected-warning-re{{test -> Total CFGBlocks: {{[0-9]+}} | Unreachable CFGBlocks: 0 | Exhausted Block: no | Empty WorkList: yes}} +int test(void) { // default-warning-re{{test -> Total CFGBlocks: {{[0-9]+}} | Unreachable CFGBlocks: 0 | Exhausted Block: no | Empty WorkList: yes}} + // no-unroll-warning-re@-1{{test -> Total CFGBlocks: {{[0-9]+}} | Unreachable CFGBlocks: 0 | Exhausted Block: no | Empty WorkList: yes}} int a = 1; a = 34 / 12; @@ -14,17 +29,18 @@ int test(void) { // expected-warning-re{{test -> Total CFGBlocks: {{[0-9]+}} | U } -int sink(void) // expected-warning-re{{sink -> Total CFGBlocks: {{[0-9]+}} | Unreachable CFGBlocks: 1 | Exhausted Block: yes | Empty WorkList: yes}} -{ - for (int i = 0; i < 10; ++i) // expected-warning {{(sink): The analyzer generated a sink at this point}} +int sink(void) // default-warning-re{{sink -> Total CFGBlocks: {{[0-9]+}} | Unreachable CFGBlocks: 0 | Exhausted Block: no | Empty WorkList: yes}} +{ // no-unroll-warning-re@-1{{sink -> Total CFGBlocks: {{[0-9]+}} | Unreachable CFGBlocks: 1 | Exhausted Block: yes | Empty WorkList: yes}} + for (int i = 0; i < 10; ++i) // no-unroll-warning {{(sink): The analyzer generated a sink at this point}} ++i; return 0; } -int emptyConditionLoop(void) // expected-warning-re{{emptyConditionLoop -> Total CFGBlocks: {{[0-9]+}} | Unreachable CFGBlocks: 0 | Exhausted Block: yes | Empty WorkList: yes}} -{ +int emptyConditionLoop(void) // default-warning-re{{emptyConditionLoop -> Total CFGBlocks: {{[0-9]+}} | Unreachable CFGBlocks: 1 | Exhausted Block: yes | Empty WorkList: yes}} +{ // no-unroll-warning-re@-1{{emptyConditionLoop -> Total CFGBlocks: {{[0-9]+}} | Unreachable CFGBlocks: 1 | Exhausted Block: yes | Empty WorkList: yes}} int num = 1; - for (;;) + for (;;) // Infinite loop - cannot be unrolled (no compile-time bound) + // Both with and without unrolling: 1 unreachable block (the exit after loop) num++; } From 17cded4d1c75de5006905b943143efc40684978c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Mon, 26 Jan 2026 13:32:04 +0100 Subject: [PATCH 03/13] update auto-obj-dtors-cfg-output.cpp Update the test to account for the newly default `LoopExit` CFG blocks. --- .../Analysis/auto-obj-dtors-cfg-output.cpp | 82 +++++++++++-------- 1 file changed, 46 insertions(+), 36 deletions(-) diff --git a/clang/test/Analysis/auto-obj-dtors-cfg-output.cpp b/clang/test/Analysis/auto-obj-dtors-cfg-output.cpp index 96b9a5508cc08..331abb8d43b00 100644 --- a/clang/test/Analysis/auto-obj-dtors-cfg-output.cpp +++ b/clang/test/Analysis/auto-obj-dtors-cfg-output.cpp @@ -660,8 +660,9 @@ void test_if_jumps() { // CHECK: [B6 (ENTRY)] // CHECK-NEXT: Succs (1): B5 // CHECK: [B1] -// CHECK-NEXT: 1: [B4.4].~A() (Implicit destructor) -// CHECK-NEXT: 2: [B5.2].~A() (Implicit destructor) +// CHECK-NEXT: 1: WhileStmt (LoopExit) +// CHECK-NEXT: 2: [B4.4].~A() (Implicit destructor) +// CHECK-NEXT: 3: [B5.2].~A() (Implicit destructor) // CHECK-NEXT: Preds (1): B4 // CHECK-NEXT: Succs (1): B0 // CHECK: [B2] @@ -707,12 +708,13 @@ void test_while_implicit_scope() { // CHECK: [B12 (ENTRY)] // CHECK-NEXT: Succs (1): B11 // CHECK: [B1] -// CHECK-NEXT: 1: [B10.4].~A() (Implicit destructor) -// WARNINGS-NEXT: 2: (CXXConstructExpr, A) -// ANALYZER-NEXT: 2: (CXXConstructExpr, [B1.3], A) -// CHECK-NEXT: 3: A e; -// CHECK-NEXT: 4: [B1.3].~A() (Implicit destructor) -// CHECK-NEXT: 5: [B11.2].~A() (Implicit destructor) +// CHECK-NEXT: 1: WhileStmt (LoopExit) +// CHECK-NEXT: 2: [B10.4].~A() (Implicit destructor) +// WARNINGS-NEXT: 3: (CXXConstructExpr, A) +// ANALYZER-NEXT: 3: (CXXConstructExpr, [B1.4], A) +// CHECK-NEXT: 4: A e; +// CHECK-NEXT: 5: [B1.4].~A() (Implicit destructor) +// CHECK-NEXT: 6: [B11.2].~A() (Implicit destructor) // CHECK-NEXT: Preds (2): B8 B10 // CHECK-NEXT: Succs (1): B0 // CHECK: [B2] @@ -801,24 +803,28 @@ void test_while_jumps() { A e; } -// CHECK: [B4 (ENTRY)] -// CHECK-NEXT: Succs (1): B2 +// CHECK: [B5 (ENTRY)] +// CHECK-NEXT: Succs (1): B3 // CHECK: [B1] -// CHECK-NEXT: 1: UV -// CHECK-NEXT: 2: [B1.1] (ImplicitCastExpr, LValueToRValue, _Bool) -// CHECK-NEXT: T: do ... while [B1.2] +// CHECK-NEXT: 1: DoStmt (LoopExit) // CHECK-NEXT: Preds (1): B2 -// CHECK-NEXT: Succs (2): B3 B0 +// CHECK-NEXT: Succs (1): B0 // CHECK: [B2] +// CHECK-NEXT: 1: UV +// CHECK-NEXT: 2: [B2.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK-NEXT: T: do ... while [B2.2] +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (2): B4 B1 +// CHECK: [B3] // WARNINGS-NEXT: 1: (CXXConstructExpr, A) -// ANALYZER-NEXT: 1: (CXXConstructExpr, [B2.2], A) +// ANALYZER-NEXT: 1: (CXXConstructExpr, [B3.2], A) // CHECK-NEXT: 2: A a; -// CHECK-NEXT: 3: [B2.2].~A() (Implicit destructor) -// CHECK-NEXT: Preds (2): B3 B4 -// CHECK-NEXT: Succs (1): B1 -// CHECK: [B3] -// CHECK-NEXT: Preds (1): B1 +// CHECK-NEXT: 3: [B3.2].~A() (Implicit destructor) +// CHECK-NEXT: Preds (2): B4 B5 // CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B3 // CHECK: [B0 (EXIT)] // CHECK-NEXT: Preds (1): B1 void test_do_implicit_scope() { @@ -829,11 +835,12 @@ void test_do_implicit_scope() { // CHECK: [B12 (ENTRY)] // CHECK-NEXT: Succs (1): B11 // CHECK: [B1] -// WARNINGS-NEXT: 1: (CXXConstructExpr, A) -// ANALYZER-NEXT: 1: (CXXConstructExpr, [B1.2], A) -// CHECK-NEXT: 2: A d; -// CHECK-NEXT: 3: [B1.2].~A() (Implicit destructor) -// CHECK-NEXT: 4: [B11.2].~A() (Implicit destructor) +// CHECK-NEXT: 1: DoStmt (LoopExit) +// WARNINGS-NEXT: 2: (CXXConstructExpr, A) +// ANALYZER-NEXT: 2: (CXXConstructExpr, [B1.3], A) +// CHECK-NEXT: 3: A d; +// CHECK-NEXT: 4: [B1.3].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B11.2].~A() (Implicit destructor) // CHECK-NEXT: Preds (2): B8 B2 // CHECK-NEXT: Succs (1): B0 // CHECK: [B2] @@ -1035,8 +1042,9 @@ void test_switch_jumps() { // CHECK: [B6 (ENTRY)] // CHECK-NEXT: Succs (1): B5 // CHECK: [B1] -// CHECK-NEXT: 1: [B4.4].~A() (Implicit destructor) -// CHECK-NEXT: 2: [B5.2].~A() (Implicit destructor) +// CHECK-NEXT: 1: ForStmt (LoopExit) +// CHECK-NEXT: 2: [B4.4].~A() (Implicit destructor) +// CHECK-NEXT: 3: [B5.2].~A() (Implicit destructor) // CHECK-NEXT: Preds (1): B4 // CHECK-NEXT: Succs (1): B0 // CHECK: [B2] @@ -1137,13 +1145,14 @@ void test_for_range_implicit_scope() { // CHECK: [B12 (ENTRY)] // CHECK-NEXT: Succs (1): B11 // CHECK: [B1] -// CHECK-NEXT: 1: [B10.4].~A() (Implicit destructor) -// CHECK-NEXT: 2: [B11.4].~A() (Implicit destructor) -// WARNINGS-NEXT: 3: (CXXConstructExpr, A) -// ANALYZER-NEXT: 3: (CXXConstructExpr, [B1.4], A) -// CHECK-NEXT: 4: A f; -// CHECK-NEXT: 5: [B1.4].~A() (Implicit destructor) -// CHECK-NEXT: 6: [B11.2].~A() (Implicit destructor) +// CHECK-NEXT: 1: ForStmt (LoopExit) +// CHECK-NEXT: 2: [B10.4].~A() (Implicit destructor) +// CHECK-NEXT: 3: [B11.4].~A() (Implicit destructor) +// WARNINGS-NEXT: 4: (CXXConstructExpr, A) +// ANALYZER-NEXT: 4: (CXXConstructExpr, [B1.5], A) +// CHECK-NEXT: 5: A f; +// CHECK-NEXT: 6: [B1.5].~A() (Implicit destructor) +// CHECK-NEXT: 7: [B11.2].~A() (Implicit destructor) // CHECK-NEXT: Preds (2): B8 B10 // CHECK-NEXT: Succs (1): B0 // CHECK: [B2] @@ -1238,8 +1247,9 @@ void test_for_jumps() { // CHECK: [B9 (ENTRY)] // CHECK-NEXT: Succs (1): B8 // CHECK: [B1] -// CHECK-NEXT: 1: [B7.4].~A() (Implicit destructor) -// CHECK-NEXT: 2: [B8.2].~A() (Implicit destructor) +// CHECK-NEXT: 1: ForStmt (LoopExit) +// CHECK-NEXT: 2: [B7.4].~A() (Implicit destructor) +// CHECK-NEXT: 3: [B8.2].~A() (Implicit destructor) // CHECK-NEXT: Preds (1): B7 // CHECK-NEXT: Succs (1): B0 // CHECK: [B2] From ed80590d3909f2641de775712db08c266616197a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Mon, 26 Jan 2026 13:36:14 +0100 Subject: [PATCH 04/13] update cfg-rich-constructors.cpp Update the test to expect the newly default `LoopExit` blocks. --- clang/test/Analysis/cfg-rich-constructors.cpp | 99 ++++++++++++------- 1 file changed, 64 insertions(+), 35 deletions(-) diff --git a/clang/test/Analysis/cfg-rich-constructors.cpp b/clang/test/Analysis/cfg-rich-constructors.cpp index aea983f1f74da..afe32b8e05fa7 100644 --- a/clang/test/Analysis/cfg-rich-constructors.cpp +++ b/clang/test/Analysis/cfg-rich-constructors.cpp @@ -497,59 +497,88 @@ void temporaryInConditionVariable() { // CHECK: void temporaryInForLoopConditionVariable() +// CHECK: [B5 (ENTRY)] +// CHECK-NEXT: Succs (1): B4 +// CHECK: [B1] +// CHECK-NEXT: 1: ForStmt (LoopExit) +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B0 // CHECK: [B2] -// CXX11-ELIDE-NEXT: 1: C() (CXXConstructExpr, [B2.2], [B2.3], C) -// CXX11-NOELIDE-NEXT: 1: C() (CXXConstructExpr, [B2.2], C) -// CXX11-NEXT: 2: [B2.1] -// CXX11-NEXT: 3: [B2.2] (CXXConstructExpr, [B2.4], C) -// CXX11-NEXT: 4: C c2 = C(); -// CXX11-NEXT: 5: c2 -// CXX11-NEXT: 6: [B2.5] (ImplicitCastExpr, NoOp, const class C) -// CXX11-NEXT: 7: [B2.6].operator bool -// CXX11-NEXT: 8: [B2.6] -// CXX11-NEXT: 9: [B2.8] (ImplicitCastExpr, UserDefinedConversion, _Bool) -// CXX11-NEXT: T: for (...; [B2.9]; ) -// CXX17-NEXT: 1: C() (CXXConstructExpr, [B2.2], C) -// CXX17-NEXT: 2: C c2 = C(); -// CXX17-NEXT: 3: c2 -// CXX17-NEXT: 4: [B2.3] (ImplicitCastExpr, NoOp, const class C) -// CXX17-NEXT: 5: [B2.4].operator bool -// CXX17-NEXT: 6: [B2.4] -// CXX17-NEXT: 7: [B2.6] (ImplicitCastExpr, UserDefinedConversion, _Bool) -// CXX17-NEXT: T: for (...; [B2.7]; ) +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B3 // CHECK: [B3] // CXX11-ELIDE-NEXT: 1: C() (CXXConstructExpr, [B3.2], [B3.3], C) // CXX11-NOELIDE-NEXT: 1: C() (CXXConstructExpr, [B3.2], C) // CXX11-NEXT: 2: [B3.1] // CXX11-NEXT: 3: [B3.2] (CXXConstructExpr, [B3.4], C) -// CXX11-NEXT: 4: C c1 = C(); +// CXX11-NEXT: 4: C c2 = C(); +// CXX11-NEXT: 5: c2 +// CXX11-NEXT: 6: [B3.5] (ImplicitCastExpr, NoOp, const class C) +// CXX11-NEXT: 7: [B3.6].operator bool +// CXX11-NEXT: 8: [B3.6] +// CXX11-NEXT: 9: [B3.8] (ImplicitCastExpr, UserDefinedConversion, _Bool) +// CXX11-NEXT: T: for (...; [B3.9]; ) // CXX17-NEXT: 1: C() (CXXConstructExpr, [B3.2], C) +// CXX17-NEXT: 2: C c2 = C(); +// CXX17-NEXT: 3: c2 +// CXX17-NEXT: 4: [B3.3] (ImplicitCastExpr, NoOp, const class C) +// CXX17-NEXT: 5: [B3.4].operator bool +// CXX17-NEXT: 6: [B3.4] +// CXX17-NEXT: 7: [B3.6] (ImplicitCastExpr, UserDefinedConversion, _Bool) +// CXX17-NEXT: T: for (...; [B3.7]; ) +// CHECK-NEXT: Preds (2): B2 B4 +// CHECK-NEXT: Succs (2): B2 B1 +// CHECK: [B4] +// CXX11-ELIDE-NEXT: 1: C() (CXXConstructExpr, [B4.2], [B4.3], C) +// CXX11-NOELIDE-NEXT: 1: C() (CXXConstructExpr, [B4.2], C) +// CXX11-NEXT: 2: [B4.1] +// CXX11-NEXT: 3: [B4.2] (CXXConstructExpr, [B4.4], C) +// CXX11-NEXT: 4: C c1 = C(); +// CXX17-NEXT: 1: C() (CXXConstructExpr, [B4.2], C) // CXX17-NEXT: 2: C c1 = C(); +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B3 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 void temporaryInForLoopConditionVariable() { for (C c1 = C(); C c2 = C(); ); } // CHECK: void temporaryInWhileLoopConditionVariable() -// CXX11-ELIDE: 1: C() (CXXConstructExpr, [B2.2], [B2.3], C) -// CXX11-NOELIDE: 1: C() (CXXConstructExpr, [B2.2], C) -// CXX11-NEXT: 2: [B2.1] -// CXX11-NEXT: 3: [B2.2] (CXXConstructExpr, [B2.4], C) +// CHECK: [B4 (ENTRY)] +// CHECK-NEXT: Succs (1): B3 +// CHECK: [B1] +// CHECK-NEXT: 1: WhileStmt (LoopExit) +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: Preds (1): B3 +// CHECK-NEXT: Succs (1): B3 +// CHECK: [B3] +// CXX11-ELIDE-NEXT: 1: C() (CXXConstructExpr, [B3.2], [B3.3], C) +// CXX11-NOELIDE-NEXT: 1: C() (CXXConstructExpr, [B3.2], C) +// CXX11-NEXT: 2: [B3.1] +// CXX11-NEXT: 3: [B3.2] (CXXConstructExpr, [B3.4], C) // CXX11-NEXT: 4: C c = C(); // CXX11-NEXT: 5: c -// CXX11-NEXT: 6: [B2.5] (ImplicitCastExpr, NoOp, const class C) -// CXX11-NEXT: 7: [B2.6].operator bool -// CXX11-NEXT: 8: [B2.6] -// CXX11-NEXT: 9: [B2.8] (ImplicitCastExpr, UserDefinedConversion, _Bool) -// CXX11-NEXT: T: while [B2.9] -// CXX17: 1: C() (CXXConstructExpr, [B2.2], C) +// CXX11-NEXT: 6: [B3.5] (ImplicitCastExpr, NoOp, const class C) +// CXX11-NEXT: 7: [B3.6].operator bool +// CXX11-NEXT: 8: [B3.6] +// CXX11-NEXT: 9: [B3.8] (ImplicitCastExpr, UserDefinedConversion, _Bool) +// CXX11-NEXT: T: while [B3.9] +// CXX17-NEXT: 1: C() (CXXConstructExpr, [B3.2], C) // CXX17-NEXT: 2: C c = C(); // CXX17-NEXT: 3: c -// CXX17-NEXT: 4: [B2.3] (ImplicitCastExpr, NoOp, const class C) -// CXX17-NEXT: 5: [B2.4].operator bool -// CXX17-NEXT: 6: [B2.4] -// CXX17-NEXT: 7: [B2.6] (ImplicitCastExpr, UserDefinedConversion, _Bool) -// CXX17-NEXT: T: while [B2.7] +// CXX17-NEXT: 4: [B3.3] (ImplicitCastExpr, NoOp, const class C) +// CXX17-NEXT: 5: [B3.4].operator bool +// CXX17-NEXT: 6: [B3.4] +// CXX17-NEXT: 7: [B3.6] (ImplicitCastExpr, UserDefinedConversion, _Bool) +// CXX17-NEXT: T: while [B3.7] +// CHECK-NEXT: Preds (2): B2 B4 +// CHECK-NEXT: Succs (2): B2 B1 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 void temporaryInWhileLoopConditionVariable() { while (C c = C()); } From bee3e6263722ce3b792879a2aa17197cedcfd867 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Mon, 26 Jan 2026 13:45:52 +0100 Subject: [PATCH 05/13] update cfg.c for loop unrolling Update the expectations for the new `LoopExit` blocks. --- clang/test/Analysis/cfg.c | 61 +++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/clang/test/Analysis/cfg.c b/clang/test/Analysis/cfg.c index 0db82ef2f3d70..aff5065b24453 100644 --- a/clang/test/Analysis/cfg.c +++ b/clang/test/Analysis/cfg.c @@ -124,7 +124,7 @@ void vla_type_indirect(int x) { #if __STDC_VERSION__ >= 202400L // If C26 or above // SINCE-C26: int labeled_break_and_continue(int x) -// SINCE-C26-NEXT: [B17 (ENTRY)] +// SINCE-C26-NEXT: [B18 (ENTRY)] // SINCE-C26-NEXT: Succs (1): B2 // SINCE-C26-EMPTY: // SINCE-C26-NEXT: [B1] @@ -138,8 +138,8 @@ void vla_type_indirect(int x) { // SINCE-C26-NEXT: 1: x // SINCE-C26-NEXT: 2: [B2.1] (ImplicitCastExpr, LValueToRValue, int) // SINCE-C26-NEXT: T: switch [B2.2] -// SINCE-C26-NEXT: Preds (1): B17 -// SINCE-C26-NEXT: Succs (3): B9 B16 B8 +// SINCE-C26-NEXT: Preds (1): B18 +// SINCE-C26-NEXT: Succs (3): B9 B17 B8 // SINCE-C26-EMPTY: // SINCE-C26-NEXT: [B3] // SINCE-C26-NEXT: 1: x @@ -186,52 +186,57 @@ void vla_type_indirect(int x) { // SINCE-C26-NEXT: [B9] // SINCE-C26-NEXT: case 2: // SINCE-C26-NEXT: T: break a; -// SINCE-C26-NEXT: Preds (2): B2 B11 +// SINCE-C26-NEXT: Preds (2): B2 B10 // SINCE-C26-NEXT: Succs (1): B1 // SINCE-C26-EMPTY: // SINCE-C26-NEXT: [B10] -// SINCE-C26-NEXT: 1: 1 -// SINCE-C26-NEXT: T: do ... while [B10.1] +// SINCE-C26-NEXT: 1: DoStmt (LoopExit) // SINCE-C26-NEXT: Preds (1): B12 -// SINCE-C26-NEXT: Succs (2): B14 NULL +// SINCE-C26-NEXT: Succs (1): B9 // SINCE-C26-EMPTY: // SINCE-C26-NEXT: [B11] -// SINCE-C26-NEXT: T: break b; +// SINCE-C26-NEXT: 1: 1 +// SINCE-C26-NEXT: T: do ... while [B11.1] // SINCE-C26-NEXT: Preds (1): B13 -// SINCE-C26-NEXT: Succs (1): B9 +// SINCE-C26-NEXT: Succs (2): B15 NULL // SINCE-C26-EMPTY: // SINCE-C26-NEXT: [B12] -// SINCE-C26-NEXT: 1: x -// SINCE-C26-NEXT: 2: ++[B12.1] -// SINCE-C26-NEXT: T: continue b; -// SINCE-C26-NEXT: Preds (1): B13 +// SINCE-C26-NEXT: T: break b; +// SINCE-C26-NEXT: Preds (1): B14 // SINCE-C26-NEXT: Succs (1): B10 // SINCE-C26-EMPTY: // SINCE-C26-NEXT: [B13] // SINCE-C26-NEXT: 1: x -// SINCE-C26-NEXT: 2: [B13.1] (ImplicitCastExpr, LValueToRValue, int) -// SINCE-C26-NEXT: 3: x -// SINCE-C26-NEXT: 4: [B13.3] (ImplicitCastExpr, LValueToRValue, int) -// SINCE-C26-NEXT: 5: [B13.2] * [B13.4] -// SINCE-C26-NEXT: 6: 100 -// SINCE-C26-NEXT: 7: [B13.5] > [B13.6] -// SINCE-C26-NEXT: T: if [B13.7] -// SINCE-C26-NEXT: Preds (2): B14 B15 -// SINCE-C26-NEXT: Succs (2): B12 B11 +// SINCE-C26-NEXT: 2: ++[B13.1] +// SINCE-C26-NEXT: T: continue b; +// SINCE-C26-NEXT: Preds (1): B14 +// SINCE-C26-NEXT: Succs (1): B11 // SINCE-C26-EMPTY: // SINCE-C26-NEXT: [B14] -// SINCE-C26-NEXT: Preds (1): B10 -// SINCE-C26-NEXT: Succs (1): B13 +// SINCE-C26-NEXT: 1: x +// SINCE-C26-NEXT: 2: [B14.1] (ImplicitCastExpr, LValueToRValue, int) +// SINCE-C26-NEXT: 3: x +// SINCE-C26-NEXT: 4: [B14.3] (ImplicitCastExpr, LValueToRValue, int) +// SINCE-C26-NEXT: 5: [B14.2] * [B14.4] +// SINCE-C26-NEXT: 6: 100 +// SINCE-C26-NEXT: 7: [B14.5] > [B14.6] +// SINCE-C26-NEXT: T: if [B14.7] +// SINCE-C26-NEXT: Preds (2): B15 B16 +// SINCE-C26-NEXT: Succs (2): B13 B12 // SINCE-C26-EMPTY: // SINCE-C26-NEXT: [B15] -// SINCE-C26-NEXT: b: -// SINCE-C26-NEXT: Preds (1): B16 -// SINCE-C26-NEXT: Succs (1): B13 +// SINCE-C26-NEXT: Preds (1): B11 +// SINCE-C26-NEXT: Succs (1): B14 // SINCE-C26-EMPTY: // SINCE-C26-NEXT: [B16] +// SINCE-C26-NEXT: b: +// SINCE-C26-NEXT: Preds (1): B17 +// SINCE-C26-NEXT: Succs (1): B14 +// SINCE-C26-EMPTY: +// SINCE-C26-NEXT: [B17] // SINCE-C26-NEXT: case 1: // SINCE-C26-NEXT: Preds (1): B2 -// SINCE-C26-NEXT: Succs (1): B15 +// SINCE-C26-NEXT: Succs (1): B16 // SINCE-C26-EMPTY: // SINCE-C26-NEXT: [B0 (EXIT)] // SINCE-C26-NEXT: Preds (3): B1 B3 B5 From 45c37c08a9ebab09dd9017457bdf54f299ef05f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Mon, 26 Jan 2026 15:32:16 +0100 Subject: [PATCH 06/13] update cfg.cpp Newly created CFG blocks a are added to the expectations. --- clang/test/Analysis/cfg.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/clang/test/Analysis/cfg.cpp b/clang/test/Analysis/cfg.cpp index 2a88b73d27756..34051496fd39c 100644 --- a/clang/test/Analysis/cfg.cpp +++ b/clang/test/Analysis/cfg.cpp @@ -455,9 +455,10 @@ int unknown(); // CHECK-NEXT: Preds (1): B5 // CHECK-NEXT: Succs (1): B0 // CHECK: [B2] -// CHECK-NEXT: 1: 0 -// CHECK-NEXT: 2: ({ ... ; [B2.1] }) -// CHECK-NEXT: 3: return [B2.2]; +// CHECK-NEXT: 1: WhileStmt (LoopExit) +// CHECK-NEXT: 2: 0 +// CHECK-NEXT: 3: ({ ... ; [B2.2] }) +// CHECK-NEXT: 4: return [B2.3]; // CHECK-NEXT: Preds (1): B4 // CHECK-NEXT: Succs (1): B0 // FIXME: Why do we have [B3] at all? From 35424d22bc953df2ce5368728eb2447ed8315a10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Mon, 26 Jan 2026 15:36:17 +0100 Subject: [PATCH 07/13] document coverage.c This test specifically checks coverage/inlining behavior when loops cause the analyzer to give up. Loop unrolling changes this behavior and finds 2 additional bugs (coverage5 leak, coverage9 null deref), so we keep unroll-loops=false to test the give-up behavior. --- clang/test/Analysis/coverage.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/test/Analysis/coverage.c b/clang/test/Analysis/coverage.c index e257e1c0f18ec..3fe21c9faa9a7 100644 --- a/clang/test/Analysis/coverage.c +++ b/clang/test/Analysis/coverage.c @@ -1,4 +1,5 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Malloc -analyzer-max-loop 4 -verify %s +// RUN: %clang_analyze_cc1 -analyzer-config unroll-loops=false -analyzer-checker=core,unix.Malloc -analyzer-max-loop 4 -verify %s +// Note: unroll-loops=false is intentional - this test checks coverage behavior when loops cause the analyzer to give up. #include "Inputs/system-header-simulator.h" typedef __typeof(sizeof(int)) size_t; From d6c7f4b413850bddf3c895a3ec180453ada5a04f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Mon, 26 Jan 2026 15:41:36 +0100 Subject: [PATCH 08/13] update domtest.c Update dominator tree test expectations to work with loop unrolling enabled by default. --- clang/test/Analysis/domtest.c | 166 +++++++++++++++++++--------------- 1 file changed, 92 insertions(+), 74 deletions(-) diff --git a/clang/test/Analysis/domtest.c b/clang/test/Analysis/domtest.c index 77b2685e98c9e..aa516d5f1666d 100644 --- a/clang/test/Analysis/domtest.c +++ b/clang/test/Analysis/domtest.c @@ -91,31 +91,34 @@ int test2(void) // [B7 (ENTRY)] -> [B6] -> [B5] -> [B1] -> [B0 (EXIT)] // CHECK: Control dependencies (Node#,Dependency#): -// CHECK-NEXT: (2,4) -// CHECK-NEXT: (2,6) -// CHECK-NEXT: (3,4) -// CHECK-NEXT: (3,6) -// CHECK-NEXT: (4,6) -// CHECK-NEXT: (4,4) -// CHECK-NEXT: (5,6) +// CHECK-NEXT: (2,7) +// CHECK-NEXT: (3,5) +// CHECK-NEXT: (3,7) +// CHECK-NEXT: (4,5) +// CHECK-NEXT: (4,7) +// CHECK-NEXT: (5,7) +// CHECK-NEXT: (5,5) +// CHECK-NEXT: (6,7) // CHECK-NEXT: Immediate dominance tree (Node#,IDom#): // CHECK-NEXT: (0,1) -// CHECK-NEXT: (1,6) -// CHECK-NEXT: (2,3) +// CHECK-NEXT: (1,7) +// CHECK-NEXT: (2,5) // CHECK-NEXT: (3,4) -// CHECK-NEXT: (4,6) -// CHECK-NEXT: (5,6) +// CHECK-NEXT: (4,5) +// CHECK-NEXT: (5,7) // CHECK-NEXT: (6,7) -// CHECK-NEXT: (7,7) +// CHECK-NEXT: (7,8) +// CHECK-NEXT: (8,8) // CHECK-NEXT: Immediate post dominance tree (Node#,IDom#): // CHECK-NEXT: (0,0) // CHECK-NEXT: (1,0) -// CHECK-NEXT: (2,4) -// CHECK-NEXT: (3,2) -// CHECK-NEXT: (4,1) -// CHECK-NEXT: (5,1) +// CHECK-NEXT: (2,1) +// CHECK-NEXT: (3,5) +// CHECK-NEXT: (4,3) +// CHECK-NEXT: (5,2) // CHECK-NEXT: (6,1) -// CHECK-NEXT: (7,6) +// CHECK-NEXT: (7,1) +// CHECK-NEXT: (8,7) int test3(void) { @@ -144,39 +147,46 @@ int test3(void) // --------> [B1] -> [B0 (EXIT)] // CHECK: Control dependencies (Node#,Dependency#): -// CHECK-NEXT: (2,6) -// CHECK-NEXT: (2,7) -// CHECK-NEXT: (3,5) -// CHECK-NEXT: (3,6) -// CHECK-NEXT: (3,7) -// CHECK-NEXT: (4,5) -// CHECK-NEXT: (4,6) -// CHECK-NEXT: (4,7) -// CHECK-NEXT: (5,6) -// CHECK-NEXT: (5,5) +// CHECK-NEXT: (2,9) +// CHECK-NEXT: (3,8) +// CHECK-NEXT: (3,9) +// CHECK-NEXT: (4,8) +// CHECK-NEXT: (4,9) // CHECK-NEXT: (5,7) +// CHECK-NEXT: (5,8) +// CHECK-NEXT: (5,9) // CHECK-NEXT: (6,7) -// CHECK-NEXT: (6,6) +// CHECK-NEXT: (6,8) +// CHECK-NEXT: (6,9) +// CHECK-NEXT: (7,8) +// CHECK-NEXT: (7,7) +// CHECK-NEXT: (7,9) +// CHECK-NEXT: (8,9) +// CHECK-NEXT: (8,8) // CHECK-NEXT: Immediate dominance tree (Node#,IDom#): // CHECK-NEXT: (0,1) -// CHECK-NEXT: (1,7) -// CHECK-NEXT: (2,5) +// CHECK-NEXT: (1,9) +// CHECK-NEXT: (2,8) // CHECK-NEXT: (3,4) -// CHECK-NEXT: (4,5) +// CHECK-NEXT: (4,7) // CHECK-NEXT: (5,6) // CHECK-NEXT: (6,7) // CHECK-NEXT: (7,8) -// CHECK-NEXT: (8,8) +// CHECK-NEXT: (8,9) +// CHECK-NEXT: (9,10) +// CHECK-NEXT: (10,10) // CHECK-NEXT: Immediate post dominance tree (Node#,IDom#): // CHECK-NEXT: (0,0) // CHECK-NEXT: (1,0) -// CHECK-NEXT: (2,6) -// CHECK-NEXT: (3,5) +// CHECK-NEXT: (2,1) +// CHECK-NEXT: (3,8) // CHECK-NEXT: (4,3) -// CHECK-NEXT: (5,2) -// CHECK-NEXT: (6,1) -// CHECK-NEXT: (7,1) -// CHECK-NEXT: (8,7) +// CHECK-NEXT: (5,7) +// CHECK-NEXT: (6,5) +// CHECK-NEXT: (7,4) +// CHECK-NEXT: (8,2) +// CHECK-NEXT: (9,1) +// CHECK-NEXT: (10,9) int test4(void) { @@ -207,55 +217,63 @@ int test4(void) // -> [B1] -> [B0 (EXIT)] // CHECK: Control dependencies (Node#,Dependency#): -// CHECK-NEXT: (2,10) -// CHECK-NEXT: (3,5) -// CHECK-NEXT: (3,9) -// CHECK-NEXT: (3,10) -// CHECK-NEXT: (4,5) -// CHECK-NEXT: (4,9) -// CHECK-NEXT: (4,10) -// CHECK-NEXT: (5,9) -// CHECK-NEXT: (5,5) -// CHECK-NEXT: (5,10) -// CHECK-NEXT: (6,8) -// CHECK-NEXT: (6,9) -// CHECK-NEXT: (6,10) -// CHECK-NEXT: (7,8) -// CHECK-NEXT: (7,9) -// CHECK-NEXT: (7,10) -// CHECK-NEXT: (8,9) -// CHECK-NEXT: (8,8) +// CHECK-NEXT: (2,12) +// CHECK-NEXT: (3,11) +// CHECK-NEXT: (3,12) +// CHECK-NEXT: (4,6) +// CHECK-NEXT: (4,11) +// CHECK-NEXT: (4,12) +// CHECK-NEXT: (5,6) +// CHECK-NEXT: (5,11) +// CHECK-NEXT: (5,12) +// CHECK-NEXT: (6,11) +// CHECK-NEXT: (6,6) +// CHECK-NEXT: (6,12) +// CHECK-NEXT: (7,11) +// CHECK-NEXT: (7,12) // CHECK-NEXT: (8,10) +// CHECK-NEXT: (8,11) +// CHECK-NEXT: (8,12) // CHECK-NEXT: (9,10) +// CHECK-NEXT: (9,11) +// CHECK-NEXT: (9,12) +// CHECK-NEXT: (10,11) // CHECK-NEXT: (10,10) +// CHECK-NEXT: (10,12) +// CHECK-NEXT: (11,12) +// CHECK-NEXT: (12,12) // CHECK-NEXT: Immediate dominance tree (Node#,IDom#): // CHECK-NEXT: (0,1) -// CHECK-NEXT: (1,10) -// CHECK-NEXT: (2,9) -// CHECK-NEXT: (3,4) +// CHECK-NEXT: (1,12) +// CHECK-NEXT: (2,11) +// CHECK-NEXT: (3,6) // CHECK-NEXT: (4,5) -// CHECK-NEXT: (5,9) -// CHECK-NEXT: (6,7) -// CHECK-NEXT: (7,8) +// CHECK-NEXT: (5,6) +// CHECK-NEXT: (6,11) +// CHECK-NEXT: (7,10) // CHECK-NEXT: (8,9) // CHECK-NEXT: (9,10) // CHECK-NEXT: (10,11) // CHECK-NEXT: (11,12) -// CHECK-NEXT: (12,12) +// CHECK-NEXT: (12,13) +// CHECK-NEXT: (13,14) +// CHECK-NEXT: (14,14) // CHECK-NEXT: Immediate post dominance tree (Node#,IDom#): // CHECK-NEXT: (0,0) // CHECK-NEXT: (1,0) -// CHECK-NEXT: (2,10) -// CHECK-NEXT: (3,5) -// CHECK-NEXT: (4,3) -// CHECK-NEXT: (5,2) -// CHECK-NEXT: (6,8) -// CHECK-NEXT: (7,6) -// CHECK-NEXT: (8,2) -// CHECK-NEXT: (9,2) -// CHECK-NEXT: (10,1) -// CHECK-NEXT: (11,10) -// CHECK-NEXT: (12,11) +// CHECK-NEXT: (2,12) +// CHECK-NEXT: (3,2) +// CHECK-NEXT: (4,6) +// CHECK-NEXT: (5,4) +// CHECK-NEXT: (6,3) +// CHECK-NEXT: (7,2) +// CHECK-NEXT: (8,10) +// CHECK-NEXT: (9,8) +// CHECK-NEXT: (10,7) +// CHECK-NEXT: (11,2) +// CHECK-NEXT: (12,1) +// CHECK-NEXT: (13,12) +// CHECK-NEXT: (14,13) int test5(void) { From 9fd553678405b38d956d2be4b74ca93826bf6035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Mon, 26 Jan 2026 16:18:33 +0100 Subject: [PATCH 09/13] update lifetime-cfg-output Update test expectations for `LoopExit` CFG blocks. --- clang/test/Analysis/lifetime-cfg-output.cpp | 186 ++++++++++---------- 1 file changed, 96 insertions(+), 90 deletions(-) diff --git a/clang/test/Analysis/lifetime-cfg-output.cpp b/clang/test/Analysis/lifetime-cfg-output.cpp index 0a75c5bcc0bcc..ed224a85ca816 100644 --- a/clang/test/Analysis/lifetime-cfg-output.cpp +++ b/clang/test/Analysis/lifetime-cfg-output.cpp @@ -337,10 +337,11 @@ void test_if_jumps() { // CHECK: [B6 (ENTRY)] // CHECK-NEXT: Succs (1): B5 // CHECK: [B1] -// CHECK-NEXT: 1: [B4.4].~A() (Implicit destructor) -// CHECK-NEXT: 2: [B4.4] (Lifetime ends) -// CHECK-NEXT: 3: [B5.2].~A() (Implicit destructor) -// CHECK-NEXT: 4: [B5.2] (Lifetime ends) +// CHECK-NEXT: 1: WhileStmt (LoopExit) +// CHECK-NEXT: 2: [B4.4].~A() (Implicit destructor) +// CHECK-NEXT: 3: [B4.4] (Lifetime ends) +// CHECK-NEXT: 4: [B5.2].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B5.2] (Lifetime ends) // CHECK-NEXT: Preds (1): B4 // CHECK-NEXT: Succs (1): B0 // CHECK: [B2] @@ -385,14 +386,15 @@ void test_while_implicit_scope() { // CHECK: [B12 (ENTRY)] // CHECK-NEXT: Succs (1): B11 // CHECK: [B1] -// CHECK-NEXT: 1: [B10.4].~A() (Implicit destructor) -// CHECK-NEXT: 2: [B10.4] (Lifetime ends) -// CHECK-NEXT: 3: (CXXConstructExpr, [B1.4], A) -// CHECK-NEXT: 4: A e; -// CHECK-NEXT: 5: [B1.4].~A() (Implicit destructor) -// CHECK-NEXT: 6: [B1.4] (Lifetime ends) -// CHECK-NEXT: 7: [B11.2].~A() (Implicit destructor) -// CHECK-NEXT: 8: [B11.2] (Lifetime ends) +// CHECK-NEXT: 1: WhileStmt (LoopExit) +// CHECK-NEXT: 2: [B10.4].~A() (Implicit destructor) +// CHECK-NEXT: 3: [B10.4] (Lifetime ends) +// CHECK-NEXT: 4: (CXXConstructExpr, [B1.5], A) +// CHECK-NEXT: 5: A e; +// CHECK-NEXT: 6: [B1.5].~A() (Implicit destructor) +// CHECK-NEXT: 7: [B1.5] (Lifetime ends) +// CHECK-NEXT: 8: [B11.2].~A() (Implicit destructor) +// CHECK-NEXT: 9: [B11.2] (Lifetime ends) // CHECK-NEXT: Preds (2): B8 B10 // CHECK-NEXT: Succs (1): B0 // CHECK: [B2] @@ -492,12 +494,13 @@ void test_while_jumps() { // CHECK: [B12 (ENTRY)] // CHECK-NEXT: Succs (1): B11 // CHECK: [B1] -// CHECK-NEXT: 1: (CXXConstructExpr, [B1.2], A) -// CHECK-NEXT: 2: A d; -// CHECK-NEXT: 3: [B1.2].~A() (Implicit destructor) -// CHECK-NEXT: 4: [B1.2] (Lifetime ends) -// CHECK-NEXT: 5: [B11.2].~A() (Implicit destructor) -// CHECK-NEXT: 6: [B11.2] (Lifetime ends) +// CHECK-NEXT: 1: DoStmt (LoopExit) +// CHECK-NEXT: 2: (CXXConstructExpr, [B1.3], A) +// CHECK-NEXT: 3: A d; +// CHECK-NEXT: 4: [B1.3].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B1.3] (Lifetime ends) +// CHECK-NEXT: 6: [B11.2].~A() (Implicit destructor) +// CHECK-NEXT: 7: [B11.2] (Lifetime ends) // CHECK-NEXT: Preds (2): B8 B2 // CHECK-NEXT: Succs (1): B0 // CHECK: [B2] @@ -583,10 +586,11 @@ void test_do_jumps() { // CHECK: [B6 (ENTRY)] // CHECK-NEXT: Succs (1): B5 // CHECK: [B1] -// CHECK-NEXT: 1: [B4.4].~A() (Implicit destructor) -// CHECK-NEXT: 2: [B4.4] (Lifetime ends) -// CHECK-NEXT: 3: [B5.2].~A() (Implicit destructor) -// CHECK-NEXT: 4: [B5.2] (Lifetime ends) +// CHECK-NEXT: 1: ForStmt (LoopExit) +// CHECK-NEXT: 2: [B4.4].~A() (Implicit destructor) +// CHECK-NEXT: 3: [B4.4] (Lifetime ends) +// CHECK-NEXT: 4: [B5.2].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B5.2] (Lifetime ends) // CHECK-NEXT: Preds (1): B4 // CHECK-NEXT: Succs (1): B0 // CHECK: [B2] @@ -632,16 +636,17 @@ void test_for_implicit_scope() { // CHECK: [B12 (ENTRY)] // CHECK-NEXT: Succs (1): B11 // CHECK: [B1] -// CHECK-NEXT: 1: [B10.4].~A() (Implicit destructor) -// CHECK-NEXT: 2: [B10.4] (Lifetime ends) -// CHECK-NEXT: 3: [B11.4].~A() (Implicit destructor) -// CHECK-NEXT: 4: [B11.4] (Lifetime ends) -// CHECK-NEXT: 5: (CXXConstructExpr, [B1.6], A) -// CHECK-NEXT: 6: A f; -// CHECK-NEXT: 7: [B1.6].~A() (Implicit destructor) -// CHECK-NEXT: 8: [B1.6] (Lifetime ends) -// CHECK-NEXT: 9: [B11.2].~A() (Implicit destructor) -// CHECK-NEXT: 10: [B11.2] (Lifetime ends) +// CHECK-NEXT: 1: ForStmt (LoopExit) +// CHECK-NEXT: 2: [B10.4].~A() (Implicit destructor) +// CHECK-NEXT: 3: [B10.4] (Lifetime ends) +// CHECK-NEXT: 4: [B11.4].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B11.4] (Lifetime ends) +// CHECK-NEXT: 6: (CXXConstructExpr, [B1.7], A) +// CHECK-NEXT: 7: A f; +// CHECK-NEXT: 8: [B1.7].~A() (Implicit destructor) +// CHECK-NEXT: 9: [B1.7] (Lifetime ends) +// CHECK-NEXT: 10: [B11.2].~A() (Implicit destructor) +// CHECK-NEXT: 11: [B11.2] (Lifetime ends) // CHECK-NEXT: Preds (2): B8 B10 // CHECK-NEXT: Succs (1): B0 // CHECK: [B2] @@ -740,64 +745,65 @@ void test_for_jumps() { A f; } -// CHECK: [B9 (ENTRY)] -// CHECK-NEXT: Succs (1): B8 -// CHECK: [B1] -// CHECK-NEXT: 1: [B7.4].~A() (Implicit destructor) -// CHECK-NEXT: 2: [B7.4] (Lifetime ends) -// CHECK-NEXT: 3: [B8.2].~A() (Implicit destructor) -// CHECK-NEXT: 4: [B8.2] (Lifetime ends) -// CHECK-NEXT: Preds (1): B7 -// CHECK-NEXT: Succs (1): B0 -// CHECK: [B2] -// CHECK-NEXT: 1: [B5.4] ? [B3.3] : [B4.2] -// CHECK-NEXT: 2: [B7.4].~A() (Implicit destructor) -// CHECK-NEXT: 3: [B7.4] (Lifetime ends) -// CHECK-NEXT: Preds (2): B3 B4 -// CHECK-NEXT: Succs (1): B7 -// CHECK: [B3] -// CHECK-NEXT: 1: b -// CHECK-NEXT: 2: [B3.1].p -// CHECK-NEXT: 3: [B3.2]++ -// CHECK-NEXT: Preds (1): B5 -// CHECK-NEXT: Succs (1): B2 -// CHECK: [B4] -// CHECK-NEXT: 1: 0 -// CHECK-NEXT: 2: [B4.1] (ImplicitCastExpr, NullToPointer, int *) -// CHECK-NEXT: Preds (1): B5 -// CHECK-NEXT: Succs (1): B2 -// CHECK: [B5] -// CHECK-NEXT: 1: b -// CHECK-NEXT: 2: [B5.1].p -// CHECK-NEXT: 3: [B5.2] (ImplicitCastExpr, LValueToRValue, int *) -// CHECK-NEXT: 4: [B5.3] (ImplicitCastExpr, PointerToBoolean, _Bool) -// CHECK-NEXT: T: [B5.4] ? ... : ... -// CHECK-NEXT: Preds (1): B6 -// CHECK-NEXT: Succs (2): B3 B4 -// CHECK: [B6] -// CHECK-NEXT: 1: 0 -// CHECK-NEXT: 2: (void)[B6.1] (CStyleCastExpr, ToVoid, void) -// CHECK-NEXT: Preds (1): B7 -// CHECK-NEXT: Succs (1): B5 -// CHECK: [B7] -// CHECK-NEXT: 1: a -// CHECK-NEXT: 2: [B7.1] (ImplicitCastExpr, NoOp, const A) -// CHECK-NEXT: 3: [B7.2] (CXXConstructExpr, [B7.4], A) -// CHECK-NEXT: 4: A b = a; -// CHECK-NEXT: 5: b -// CHECK-NEXT: 6: [B7.5] (ImplicitCastExpr, NoOp, const class A) -// CHECK-NEXT: 7: [B7.6].operator int -// CHECK-NEXT: 8: [B7.6] -// CHECK-NEXT: 9: [B7.8] (ImplicitCastExpr, UserDefinedConversion, int) -// CHECK-NEXT: 10: [B7.9] (ImplicitCastExpr, IntegralToBoolean, _Bool) -// CHECK-NEXT: T: for (...; [B7.10]; ...) -// CHECK-NEXT: Preds (2): B2 B8 -// CHECK-NEXT: Succs (2): B6 B1 -// CHECK: [B8] -// CHECK-NEXT: 1: (CXXConstructExpr, [B8.2], A) -// CHECK-NEXT: 2: A a; -// CHECK-NEXT: Preds (1): B9 -// CHECK-NEXT: Succs (1): B7 +// CHECK: [B9 (ENTRY)] +// CHECK-NEXT: Succs (1): B8 +// CHECK: [B1] +// CHECK-NEXT: 1: ForStmt (LoopExit) +// CHECK-NEXT: 2: [B7.4].~A() (Implicit destructor) +// CHECK-NEXT: 3: [B7.4] (Lifetime ends) +// CHECK-NEXT: 4: [B8.2].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B8.2] (Lifetime ends) +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B2] +// CHECK-NEXT: 1: [B5.4] ? [B3.3] : [B4.2] +// CHECK-NEXT: 2: [B7.4].~A() (Implicit destructor) +// CHECK-NEXT: 3: [B7.4] (Lifetime ends) +// CHECK-NEXT: Preds (2): B3 B4 +// CHECK-NEXT: Succs (1): B7 +// CHECK: [B3] +// CHECK-NEXT: 1: b +// CHECK-NEXT: 2: [B3.1].p +// CHECK-NEXT: 3: [B3.2]++ +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B4] +// CHECK-NEXT: 1: 0 +// CHECK-NEXT: 2: [B4.1] (ImplicitCastExpr, NullToPointer, int *) +// CHECK-NEXT: Preds (1): B5 +// CHECK-NEXT: Succs (1): B2 +// CHECK: [B5] +// CHECK-NEXT: 1: b +// CHECK-NEXT: 2: [B5.1].p +// CHECK-NEXT: 3: [B5.2] (ImplicitCastExpr, LValueToRValue, int *) +// CHECK-NEXT: 4: [B5.3] (ImplicitCastExpr, PointerToBoolean, _Bool) +// CHECK-NEXT: T: [B5.4] ? ... : ... +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (2): B3 B4 +// CHECK: [B6] +// CHECK-NEXT: 1: 0 +// CHECK-NEXT: 2: (void)[B6.1] (CStyleCastExpr, ToVoid, void) +// CHECK-NEXT: Preds (1): B7 +// CHECK-NEXT: Succs (1): B5 +// CHECK: [B7] +// CHECK-NEXT: 1: a +// CHECK-NEXT: 2: [B7.1] (ImplicitCastExpr, NoOp, const A) +// CHECK-NEXT: 3: [B7.2] (CXXConstructExpr, [B7.4], A) +// CHECK-NEXT: 4: A b = a; +// CHECK-NEXT: 5: b +// CHECK-NEXT: 6: [B7.5] (ImplicitCastExpr, NoOp, const class A) +// CHECK-NEXT: 7: [B7.6].operator int +// CHECK-NEXT: 8: [B7.6] +// CHECK-NEXT: 9: [B7.8] (ImplicitCastExpr, UserDefinedConversion, int) +// CHECK-NEXT: 10: [B7.9] (ImplicitCastExpr, IntegralToBoolean, _Bool) +// CHECK-NEXT: T: for (...; [B7.10]; ...) +// CHECK-NEXT: Preds (2): B2 B8 +// CHECK-NEXT: Succs (2): B6 B1 +// CHECK: [B8] +// CHECK-NEXT: 1: (CXXConstructExpr, [B8.2], A) +// CHECK-NEXT: 2: A a; +// CHECK-NEXT: Preds (1): B9 +// CHECK-NEXT: Succs (1): B7 void test_for_inc_conditional() { for (A a; A b = a; b.p ? b.p++ : 0) (void)0; From 9cdb2bb8992f895ffc0410359ef90d1c0c2314ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Mon, 26 Jan 2026 16:20:02 +0100 Subject: [PATCH 10/13] update loop-assumptions.c Update expectations for clearTrueCondition() which now gets fully unrolled (10 iterations instead of 4). Opaque condition tests remain unchanged as they cannot be unrolled. --- clang/test/Analysis/loop-assumptions.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/clang/test/Analysis/loop-assumptions.c b/clang/test/Analysis/loop-assumptions.c index b61ed8815e3f6..2f3e24aa7d35b 100644 --- a/clang/test/Analysis/loop-assumptions.c +++ b/clang/test/Analysis/loop-assumptions.c @@ -9,6 +9,8 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection \ // RUN: -analyzer-config assume-at-least-one-iteration=true,eagerly-assume=false \ // RUN: -verify=expected,noeagerlyassume %s +// Note: With loop unrolling enabled by default, simple loops with known bounds +// are fully unrolled, which changes the behavior of this test. // The verify tag "combo" is used for one unique warning which is produced in three // of the four RUN combinations. @@ -25,14 +27,13 @@ void clang_analyzer_numTimesReached(void); void clang_analyzer_dump(int); void clearTrueCondition(void) { - // If the analyzer can definitely determine that the loop condition is true, - // then this corrective logic doesn't activate and the engine executes - // `-analyzer-max-loop` iterations (by default, 4). + // With loop unrolling enabled, simple loops with known bounds are fully unrolled. + // This loop has a compile-time constant bound (i < 10), so it's unrolled completely. int i; for (i = 0; i < 10; i++) - clang_analyzer_numTimesReached(); // expected-warning {{4}} + clang_analyzer_numTimesReached(); // expected-warning {{10}} - clang_analyzer_dump(i); // Unreachable, no reports. + clang_analyzer_dump(i); // expected-warning {{10 S32b}} } void clearFalseCondition(void) { From 28521fb54d647d0a7949fb4501dbfd2e122147fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Mon, 26 Jan 2026 16:20:57 +0100 Subject: [PATCH 11/13] document loop-based-inlining-prevention.c This test validates the heuristic that prevents inlining when loops reach analyzer-max-loop limit. With loop unrolling, this heuristic behaves differently, so we keep unroll-loops=false to test the specific non-unrolling behavior. --- clang/test/Analysis/loop-based-inlining-prevention.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/clang/test/Analysis/loop-based-inlining-prevention.c b/clang/test/Analysis/loop-based-inlining-prevention.c index 73627112e2d32..d7ba2286b2aaa 100644 --- a/clang/test/Analysis/loop-based-inlining-prevention.c +++ b/clang/test/Analysis/loop-based-inlining-prevention.c @@ -1,5 +1,7 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify=expected,default %s -// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config inline-functions-with-ambiguous-loops=true -verify=expected,enabled %s +// RUN: %clang_analyze_cc1 -analyzer-config unroll-loops=false -analyzer-checker=core,debug.ExprInspection -verify=expected,default %s +// RUN: %clang_analyze_cc1 -analyzer-config unroll-loops=false -analyzer-checker=core,debug.ExprInspection -analyzer-config inline-functions-with-ambiguous-loops=true -verify=expected,enabled %s +// Note: unroll-loops=false is intentional - this test validates the heuristic that prevents +// inlining when loops reach analyzer-max-loop limit. With unrolling, this heuristic behaves differently. // This file tests some heuristics in the engine that put functions on a // "do not inline" list if their analyisis reaches the `analyzer-max-loop` From 63ff92c4695579989e2f0b35639c67a019679131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Mon, 26 Jan 2026 16:21:52 +0100 Subject: [PATCH 12/13] document loop-widening.c flag change This test validates loop widening behavior, which is a different technique from loop unrolling. Keep unroll-loops=false to test the specific widening behavior. --- clang/test/Analysis/loop-widening.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/clang/test/Analysis/loop-widening.c b/clang/test/Analysis/loop-widening.c index ed1a750b373ff..41255dd995031 100644 --- a/clang/test/Analysis/loop-widening.c +++ b/clang/test/Analysis/loop-widening.c @@ -1,5 +1,8 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-max-loop 4 -analyzer-config widen-loops=true -verify -analyzer-config eagerly-assume=false %s -// RUN: %clang_analyze_cc1 -DTEST_NULL_TERM -analyzer-checker=core,unix.Malloc,debug.ExprInspection,alpha.cplusplus.IteratorRange -analyzer-max-loop 4 -analyzer-config widen-loops=true -verify -analyzer-config eagerly-assume=false %s +// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-max-loop 4 -analyzer-config widen-loops=true,unroll-loops=false -verify -analyzer-config eagerly-assume=false %s +// RUN: %clang_analyze_cc1 -DTEST_NULL_TERM -analyzer-checker=core,unix.Malloc,debug.ExprInspection,alpha.cplusplus.IteratorRange -analyzer-max-loop 4 -analyzer-config widen-loops=true,unroll-loops=false -verify -analyzer-config eagerly-assume=false %s +// Note: unroll-loops=false is intentional - this test validates loop widening behavior, +// which is a different technique from loop unrolling. Widening invalidates variables after +// a certain number of iterations, and this test checks that specific behavior. void clang_analyzer_eval(int); void clang_analyzer_warnIfReached(void); From a3cce8764a0072ae61cf64ddfa2578bd1207cbc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Wed, 28 Jan 2026 15:48:27 +0100 Subject: [PATCH 13/13] update scopes-cfg-output.cpp Update CFG expectations to include LoopExit elements. --- clang/test/Analysis/scopes-cfg-output.cpp | 84 +++++++++++++---------- 1 file changed, 46 insertions(+), 38 deletions(-) diff --git a/clang/test/Analysis/scopes-cfg-output.cpp b/clang/test/Analysis/scopes-cfg-output.cpp index 6ed6f3638f75b..435d179aea6d8 100644 --- a/clang/test/Analysis/scopes-cfg-output.cpp +++ b/clang/test/Analysis/scopes-cfg-output.cpp @@ -274,10 +274,11 @@ void test_if_jumps() { // CHECK: [B6 (ENTRY)] // CHECK-NEXT: Succs (1): B5 // CHECK: [B1] -// CHECK-NEXT: 1: [B4.5].~A() (Implicit destructor) -// CHECK-NEXT: 2: CFGScopeEnd(b) -// CHECK-NEXT: 3: [B5.3].~A() (Implicit destructor) -// CHECK-NEXT: 4: CFGScopeEnd(a) +// CHECK-NEXT: 1: WhileStmt (LoopExit) +// CHECK-NEXT: 2: [B4.5].~A() (Implicit destructor) +// CHECK-NEXT: 3: CFGScopeEnd(b) +// CHECK-NEXT: 4: [B5.3].~A() (Implicit destructor) +// CHECK-NEXT: 5: CFGScopeEnd(a) // CHECK-NEXT: Preds (1): B4 // CHECK-NEXT: Succs (1): B0 // CHECK: [B2] @@ -325,13 +326,14 @@ void test_while_implicit_scope() { // CHECK: [B12 (ENTRY)] // CHECK-NEXT: Succs (1): B11 // CHECK: [B1] -// CHECK-NEXT: 1: [B10.5].~A() (Implicit destructor) -// CHECK-NEXT: 2: CFGScopeEnd(b) -// CHECK-NEXT: 3: (CXXConstructExpr, [B1.4], A) -// CHECK-NEXT: 4: A e; -// CHECK-NEXT: 5: [B1.4].~A() (Implicit destructor) -// CHECK-NEXT: 6: [B11.3].~A() (Implicit destructor) -// CHECK-NEXT: 7: CFGScopeEnd(a) +// CHECK-NEXT: 1: WhileStmt (LoopExit) +// CHECK-NEXT: 2: [B10.5].~A() (Implicit destructor) +// CHECK-NEXT: 3: CFGScopeEnd(b) +// CHECK-NEXT: 4: (CXXConstructExpr, [B1.5], A) +// CHECK-NEXT: 5: A e; +// CHECK-NEXT: 6: [B1.5].~A() (Implicit destructor) +// CHECK-NEXT: 7: [B11.3].~A() (Implicit destructor) +// CHECK-NEXT: 8: CFGScopeEnd(a) // CHECK-NEXT: Preds (2): B8 B10 // CHECK-NEXT: Succs (1): B0 // CHECK: [B2] @@ -430,11 +432,12 @@ void test_while_jumps() { // CHECK: [B12 (ENTRY)] // CHECK-NEXT: Succs (1): B11 // CHECK: [B1] -// CHECK-NEXT: 1: (CXXConstructExpr, [B1.2], A) -// CHECK-NEXT: 2: A d; -// CHECK-NEXT: 3: [B1.2].~A() (Implicit destructor) -// CHECK-NEXT: 4: [B11.3].~A() (Implicit destructor) -// CHECK-NEXT: 5: CFGScopeEnd(a) +// CHECK-NEXT: 1: DoStmt (LoopExit) +// CHECK-NEXT: 2: (CXXConstructExpr, [B1.3], A) +// CHECK-NEXT: 3: A d; +// CHECK-NEXT: 4: [B1.3].~A() (Implicit destructor) +// CHECK-NEXT: 5: [B11.3].~A() (Implicit destructor) +// CHECK-NEXT: 6: CFGScopeEnd(a) // CHECK-NEXT: Preds (2): B8 B2 // CHECK-NEXT: Succs (1): B0 // CHECK: [B2] @@ -518,10 +521,11 @@ void test_do_jumps() { // CHECK: [B6 (ENTRY)] // CHECK-NEXT: Succs (1): B5 // CHECK: [B1] -// CHECK-NEXT: 1: [B4.5].~A() (Implicit destructor) -// CHECK-NEXT: 2: CFGScopeEnd(b) -// CHECK-NEXT: 3: [B5.3].~A() (Implicit destructor) -// CHECK-NEXT: 4: CFGScopeEnd(a) +// CHECK-NEXT: 1: ForStmt (LoopExit) +// CHECK-NEXT: 2: [B4.5].~A() (Implicit destructor) +// CHECK-NEXT: 3: CFGScopeEnd(b) +// CHECK-NEXT: 4: [B5.3].~A() (Implicit destructor) +// CHECK-NEXT: 5: CFGScopeEnd(a) // CHECK-NEXT: Preds (1): B4 // CHECK-NEXT: Succs (1): B0 // CHECK: [B2] @@ -570,15 +574,16 @@ void test_for_implicit_scope() { // CHECK: [B12 (ENTRY)] // CHECK-NEXT: Succs (1): B11 // CHECK: [B1] -// CHECK-NEXT: 1: [B10.5].~A() (Implicit destructor) -// CHECK-NEXT: 2: CFGScopeEnd(c) -// CHECK-NEXT: 3: [B11.6].~A() (Implicit destructor) -// CHECK-NEXT: 4: CFGScopeEnd(b) -// CHECK-NEXT: 5: (CXXConstructExpr, [B1.6], A) -// CHECK-NEXT: 6: A f; -// CHECK-NEXT: 7: [B1.6].~A() (Implicit destructor) -// CHECK-NEXT: 8: [B11.3].~A() (Implicit destructor) -// CHECK-NEXT: 9: CFGScopeEnd(a) +// CHECK-NEXT: 1: ForStmt (LoopExit) +// CHECK-NEXT: 2: [B10.5].~A() (Implicit destructor) +// CHECK-NEXT: 3: CFGScopeEnd(c) +// CHECK-NEXT: 4: [B11.6].~A() (Implicit destructor) +// CHECK-NEXT: 5: CFGScopeEnd(b) +// CHECK-NEXT: 6: (CXXConstructExpr, [B1.7], A) +// CHECK-NEXT: 7: A f; +// CHECK-NEXT: 8: [B1.7].~A() (Implicit destructor) +// CHECK-NEXT: 9: [B11.3].~A() (Implicit destructor) +// CHECK-NEXT: 10: CFGScopeEnd(a) // CHECK-NEXT: Preds (2): B8 B10 // CHECK-NEXT: Succs (1): B0 // CHECK: [B2] @@ -680,10 +685,11 @@ void test_for_jumps() { // CHECK: [B9 (ENTRY)] // CHECK-NEXT: Succs (1): B8 // CHECK: [B1] -// CHECK-NEXT: 1: [B7.5].~A() (Implicit destructor) -// CHECK-NEXT: 2: CFGScopeEnd(b) -// CHECK-NEXT: 3: [B8.3].~A() (Implicit destructor) -// CHECK-NEXT: 4: CFGScopeEnd(a) +// CHECK-NEXT: 1: ForStmt (LoopExit) +// CHECK-NEXT: 2: [B7.5].~A() (Implicit destructor) +// CHECK-NEXT: 3: CFGScopeEnd(b) +// CHECK-NEXT: 4: [B8.3].~A() (Implicit destructor) +// CHECK-NEXT: 5: CFGScopeEnd(a) // CHECK-NEXT: Preds (1): B7 // CHECK-NEXT: Succs (1): B0 // CHECK: [B2] @@ -825,10 +831,11 @@ void test_goto() { // CHECK: [B7 (ENTRY)] // CHECK-NEXT: Succs (1): B6 // CHECK: [B1] -// CHECK-NEXT: 1: CFGScopeEnd(i) -// CHECK-NEXT: 2: CFGScopeBegin(unused2) -// CHECK-NEXT: 3: int unused2; -// CHECK-NEXT: 4: CFGScopeEnd(unused2) +// CHECK-NEXT: 1: ForStmt (LoopExit) +// CHECK-NEXT: 2: CFGScopeEnd(i) +// CHECK-NEXT: 3: CFGScopeBegin(unused2) +// CHECK-NEXT: 4: int unused2; +// CHECK-NEXT: 5: CFGScopeEnd(unused2) // CHECK-NEXT: Preds (2): B4 B5 // CHECK-NEXT: Succs (1): B0 // CHECK: [B2] @@ -1142,7 +1149,8 @@ void test_without_compound() { // CHECK: [B12 (ENTRY)] // CHECK-NEXT: Succs (1): B11 // CHECK: [B1] -// CHECK-NEXT: 1: CFGScopeEnd(i) +// CHECK-NEXT: 1: ForStmt (LoopExit) +// CHECK-NEXT: 2: CFGScopeEnd(i) // CHECK-NEXT: Preds (2): B4 B10 // CHECK-NEXT: Succs (1): B0 // CHECK: [B2] _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
