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

Reply via email to