llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-codegen
Author: firmiana402
<details>
<summary>Changes</summary>
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.
---
Full diff: https://github.com/llvm/llvm-project/pull/207129.diff
3 Files Affected:
- (modified) clang/lib/CodeGen/CGStmt.cpp (+6)
- (modified) clang/test/CodeGen/builtin-cpu-supports.c (+2-30)
- (added) clang/test/DebugInfo/Generic/else-nested-control-flow-forwarding.c
(+56)
``````````diff
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/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:
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;
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/207129
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits