https://github.com/firmiana402 created
https://github.com/llvm/llvm-project/pull/207129
This PR fixes #178007.
Clang can emit an empty forwarding block after nested control flow in an `else`
body, commonly for an `else if` chain. The block only contains an unconditional
branch with no debug location. If that branch survives to assembly, it can
produce a line-0 entry in the line table and surface as an incorrect source
location in debuggers.
For example, for:
```c
int else_if_chain(int i, int *arr) {
int x = 0;
if (i == 1) {
x = i + 1;
} else if (i == 2) {
arr[i] = 42;
}
return x;
}
```
Clang could produce IR shaped like:
```c
if.else:
br i1 %cmp, label %if.then2, label %if.end
if.then2:
...
br label %if.end
if.end:
br label %if.end3
if.end3:
...
ret i32 %x
```
The `if.end` block is only a forwarding block. This patch records the exit
block produced by the `else` body and uses the existing
`SimplifyForwardingBlocks` helper to fold it when it only contains an
unconditional branch:
```c
if.else:
br i1 %cmp, label %if.then2, label %if.end
if.then2:
...
br label %if.end
if.end:
...
ret i32 %x
```
This follows the existing Clang CodeGen pattern used in `EmitWhileStmt` and
`EmitDoStmt`, where `SimplifyForwardingBlocks` is already used to remove
single-unconditional-branch blocks after structured control-flow emission.
This also removes a redundant unconditional jump in the generated assembly for
the affected cases, matching the cleaner CFG shape GCC emits for similar source
patterns.
## What Changed
- Updated Clang CodeGen to fold an empty forwarding block left after nested
control flow in an `else` body. This removes the unconditional branch without a
debug location instead of letting it survive to later codegen stages.
- Added a focused IR regression test covering `else if`, an explicitly nested
`if` inside `else`, and an `else if` chain with a final `else`.
- Updated `clang/test/CodeGen/builtin-cpu-supports.c`. That test contains
existing `else if` chains for PPC and RISC-V and previously checked the old IR
shape with multiple empty forwarding blocks. With this patch, those blocks are
intentionally folded, so the FileCheck expectations now match the cleaner CFG.
>From ba7fc7db1b7986fa976e738ffb6801e0aae143ee Mon Sep 17 00:00:00 2001
From: firmiana402 <[email protected]>
Date: Thu, 2 Jul 2026 14:09:03 +0800
Subject: [PATCH 1/2] [Clang][CodeGen] Fold forwarding blocks after nested else
control flow
---
clang/lib/CodeGen/CGStmt.cpp | 6 ++
.../else-nested-control-flow-forwarding.c | 56 +++++++++++++++++++
2 files changed, 62 insertions(+)
create mode 100644
clang/test/DebugInfo/Generic/else-nested-control-flow-forwarding.c
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 232094777f233..1e79138ed8f82 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -987,9 +987,15 @@ void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
EmitStmt(Else);
}
{
+ llvm::BasicBlock *ElseExitBlock = Builder.GetInsertBlock();
// There is no need to emit line number for an unconditional branch.
auto NL = ApplyDebugLocation::CreateEmpty(*this);
EmitBranch(ContBlock);
+
+ // If nested control flow in the else body left behind a synthetic
+ // continuation, fold it into this if's continuation when possible.
+ if (ElseExitBlock && ElseExitBlock != ElseBlock)
+ SimplifyForwardingBlocks(ElseExitBlock);
}
} else if (HasSkip) {
EmitBlock(ElseBlock);
diff --git a/clang/test/DebugInfo/Generic/else-nested-control-flow-forwarding.c
b/clang/test/DebugInfo/Generic/else-nested-control-flow-forwarding.c
new file mode 100644
index 0000000000000..39d19233763fc
--- /dev/null
+++ b/clang/test/DebugInfo/Generic/else-nested-control-flow-forwarding.c
@@ -0,0 +1,56 @@
+// RUN: %clang_cc1 -emit-llvm -debug-info-kind=limited -triple
x86_64-unknown-linux-gnu %s -o - | FileCheck %s
+
+// Nested control flow at the end of an else body can leave behind an empty
+// continuation block that only forwards to the outer if continuation. Avoid
+// emitting that extra block.
+
+// CHECK-LABEL: define {{.*}} @else_if_chain(
+// CHECK: if.end:
+// CHECK-NOT: if.end{{[0-9]+}}:
+// CHECK: ret
+
+int else_if_chain(int i, int *arr) {
+ int x = 0;
+ if (i == 1) {
+ x = i + 1;
+ } else if (i == 2) {
+ arr[i] = 42;
+ }
+ return x;
+}
+
+// CHECK-LABEL: define {{.*}} @nested_if_in_else(
+// CHECK: if.end:
+// CHECK-NOT: if.end{{[0-9]+}}:
+// CHECK: ret
+
+int nested_if_in_else(int i, int *arr) {
+ int x = 0;
+ if (i == 1) {
+ x = i + 1;
+ } else {
+ if (i == 2) {
+ arr[i] = 42;
+ }
+ }
+ return x;
+}
+
+// CHECK-LABEL: define {{.*}} @else_if_chain_with_final_else(
+// CHECK: if.end:
+// CHECK-NOT: if.end{{[0-9]+}}:
+// CHECK: ret
+
+int else_if_chain_with_final_else(int i) {
+ int x = 0;
+ if (i == 1) {
+ x = 1;
+ } else if (i == 2) {
+ x = 2;
+ } else if (i == 3) {
+ x = 3;
+ } else {
+ x = 4;
+ }
+ return x;
+}
>From 6e6673f9b8db8928089a88ee45453236ed8cbb61 Mon Sep 17 00:00:00 2001
From: firmiana402 <[email protected]>
Date: Thu, 2 Jul 2026 14:23:37 +0800
Subject: [PATCH 2/2] [Clang][test] Update CPU supports CFG checks
---
clang/test/CodeGen/builtin-cpu-supports.c | 32 ++---------------------
1 file changed, 2 insertions(+), 30 deletions(-)
diff --git a/clang/test/CodeGen/builtin-cpu-supports.c
b/clang/test/CodeGen/builtin-cpu-supports.c
index 1ace52b7de8f1..6e56b72c99dc6 100644
--- a/clang/test/CodeGen/builtin-cpu-supports.c
+++ b/clang/test/CodeGen/builtin-cpu-supports.c
@@ -196,25 +196,9 @@ int v4() { return __builtin_cpu_supports("x86-64-v4"); }
// CHECK-PPC-NEXT: store i32 [[SUB27]], ptr [[RETVAL]], align 4
// CHECK-PPC-NEXT: br label [[RETURN]]
// CHECK-PPC: if.end:
-// CHECK-PPC-NEXT: br label [[IF_END28:%.*]]
-// CHECK-PPC: if.end28:
-// CHECK-PPC-NEXT: br label [[IF_END29:%.*]]
-// CHECK-PPC: if.end29:
-// CHECK-PPC-NEXT: br label [[IF_END30:%.*]]
-// CHECK-PPC: if.end30:
-// CHECK-PPC-NEXT: br label [[IF_END31:%.*]]
-// CHECK-PPC: if.end31:
-// CHECK-PPC-NEXT: br label [[IF_END32:%.*]]
-// CHECK-PPC: if.end32:
-// CHECK-PPC-NEXT: br label [[IF_END33:%.*]]
-// CHECK-PPC: if.end33:
-// CHECK-PPC-NEXT: br label [[IF_END34:%.*]]
-// CHECK-PPC: if.end34:
-// CHECK-PPC-NEXT: br label [[IF_END35:%.*]]
-// CHECK-PPC: if.end35:
// CHECK-PPC-NEXT: [[TMP21:%.*]] = load i32, ptr [[A_ADDR]], align 4
-// CHECK-PPC-NEXT: [[ADD36:%.*]] = add nsw i32 [[TMP21]], 5
-// CHECK-PPC-NEXT: store i32 [[ADD36]], ptr [[RETVAL]], align 4
+// CHECK-PPC-NEXT: [[ADD28:%.*]] = add nsw i32 [[TMP21]], 5
+// CHECK-PPC-NEXT: store i32 [[ADD28]], ptr [[RETVAL]], align 4
// CHECK-PPC-NEXT: br label [[RETURN]]
// CHECK-PPC: return:
// CHECK-PPC-NEXT: [[TMP22:%.*]] = load i32, ptr [[RETVAL]], align 4
@@ -283,12 +267,6 @@ int test_ppc(int a) {
// CHECK-RV32-NEXT: store i32 13, ptr [[RETVAL]], align 4
// CHECK-RV32-NEXT: br label [[RETURN]]
// CHECK-RV32: if.end:
-// CHECK-RV32-NEXT: br label [[IF_END6:%.*]]
-// CHECK-RV32: if.end6:
-// CHECK-RV32-NEXT: br label [[IF_END7:%.*]]
-// CHECK-RV32: if.end7:
-// CHECK-RV32-NEXT: br label [[IF_END8:%.*]]
-// CHECK-RV32: if.end8:
// CHECK-RV32-NEXT: store i32 0, ptr [[RETVAL]], align 4
// CHECK-RV32-NEXT: br label [[RETURN]]
// CHECK-RV32: return:
@@ -334,12 +312,6 @@ int test_ppc(int a) {
// CHECK-RV64-NEXT: store i32 13, ptr [[RETVAL]], align 4
// CHECK-RV64-NEXT: br label [[RETURN]]
// CHECK-RV64: if.end:
-// CHECK-RV64-NEXT: br label [[IF_END6:%.*]]
-// CHECK-RV64: if.end6:
-// CHECK-RV64-NEXT: br label [[IF_END7:%.*]]
-// CHECK-RV64: if.end7:
-// CHECK-RV64-NEXT: br label [[IF_END8:%.*]]
-// CHECK-RV64: if.end8:
// CHECK-RV64-NEXT: store i32 0, ptr [[RETVAL]], align 4
// CHECK-RV64-NEXT: br label [[RETURN]]
// CHECK-RV64: return:
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits