Author: Erich Keane
Date: 2026-10-07T00:06:05Z
New Revision: dfc6aa6330bb28011c5591388c3f9023b6f5aefb

URL: 
https://github.com/llvm/llvm-project/commit/dfc6aa6330bb28011c5591388c3f9023b6f5aefb
DIFF: 
https://github.com/llvm/llvm-project/commit/dfc6aa6330bb28011c5591388c3f9023b6f5aefb.diff

LOG: [CIR] Skip 'dead' branches when emitting an 'if' statement (#229553)

At one point, we actively decided not to skip these, as it would
possibly be useful for static-analysis. However, we're finding that this
is actually taken advantage of in quite a few places (particularly
    things that call undefined things in the false branch), so we are
going revert our previous decision and do the FE level omission.

This functionality could potentially be restored in the future, but we
probably would want a CIRSimplify patch to do the dead-branch
elimination that runs all the time, but that would require better
constant folding in CIR.

Added: 
    

Modified: 
    clang/lib/CIR/CodeGen/CIRGenStmt.cpp
    clang/test/CIR/CodeGen/if.cpp
    clang/test/CIR/CodeGen/non-odr-use-const-bool.cpp
    clang/test/CIR/CodeGen/requires-expr.cpp
    clang/test/CIR/CodeGenBuiltins/builtin-trivally-copyable.cpp
    clang/test/CIR/CodeGenBuiltins/builtin-types-compatible.c

Removed: 
    


################################################################################
diff  --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp 
b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
index 57576864bbe39..0bf3dfa94a030 100644
--- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
@@ -581,16 +581,20 @@ mlir::LogicalResult CIRGenFunction::emitIfStmt(const 
IfStmt &s) {
     if (s.getConditionVariable())
       emitDecl(*s.getConditionVariable());
 
-    // If the condition folds to a constant and this is an 'if constexpr',
-    // we simplify it early in CIRGen to avoid emitting the full 'if'.
+    // If the condition constant folds and can be elided, try to avoid
+    // emitting the condition and the dead arm of the if/else.
     bool condConstant;
     if (constantFoldsToBool(s.getCond(), condConstant, s.isConstexpr())) {
-      if (s.isConstexpr()) {
-        // Handle "if constexpr" explicitly here to avoid generating some
-        // ill-formed code since in CIR the "if" is no longer simplified
-        // in this lambda like in Clang but postponed to other MLIR
-        // passes.
-        if (const Stmt *executed = condConstant ? s.getThen() : s.getElse())
+      // Figure out which block (then or else) is executed.
+      const Stmt *executed = s.getThen();
+      const Stmt *skipped = s.getElse();
+      if (!condConstant) // Condition false?
+        std::swap(executed, skipped);
+
+      // If the skipped block has no labels in it, just emit the executed 
block.
+      // This avoids emitting dead code and simplifies the CFG substantially.
+      if (s.isConstexpr() || !containsLabel(skipped)) {
+        if (executed)
           return emitStmt(executed, /*useCurrentScope=*/true);
         // There is nothing to execute at runtime.
         // TODO(cir): there is still an empty cir.scope generated by the 
caller.

diff  --git a/clang/test/CIR/CodeGen/if.cpp b/clang/test/CIR/CodeGen/if.cpp
index 23ad507325580..189623e36c423 100644
--- a/clang/test/CIR/CodeGen/if.cpp
+++ b/clang/test/CIR/CodeGen/if.cpp
@@ -326,3 +326,50 @@ int if_init() {
 // OGCG: [[RETURN]]:
 // OGCG:   %[[RETVAL_FINAL:.*]] = load i32, ptr %[[RETVAL]], align 4
 // OGCG:   ret i32 %[[RETVAL_FINAL]]
+
+void if3() {
+  if (0) {
+foo:
+    if_init();
+  } goto foo;
+}
+// CIR: cir.func{{.*}} @_Z3if3v()
+// CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
+// CIR: %[[ZERO_BOOL:.*]] = cir.cast int_to_bool %[[ZERO]] : !s32i -> !cir.bool
+// CIR: cir.if %[[ZERO_BOOL]]
+// CIR: cir.call @_Z7if_initv() : () -> (!s32i {llvm.noundef})
+
+// Just making sure we don't lose either of the above 'if' branches because of
+// the label, so just making sure they have the 'call' should be sufficient.
+// LLVM: define{{.*}} void @_Z3if3v()
+// LLVM: call{{.*}}i32 @_Z7if_initv
+
+// OGCG: define{{.*}} void @_Z3if3v()
+// OGCG: call{{.*}}i32 @_Z7if_initv
+void if4(int a) {
+  switch (a) {
+    case 0:
+      if (0) {
+        case 1:
+          if0(false);
+      }
+  }
+}
+// CIR: cir.func{{.*}} @_Z3if4i(%[[ARG:.*]]: !s32i {{.*}})
+// CIR: %[[ARG_ALLOCA:.*]] = cir.alloca "a" align(4) init : !cir.ptr<!s32i>
+// CIR:  %[[LOAD_ARG:.*]] = cir.load align(4) %[[ARG_ALLOCA]] : 
!cir.ptr<!s32i>, !s32i
+// CIR:  cir.switch(%[[LOAD_ARG]] : !s32i) {
+// CIR:  cir.case(equal, [#cir.int<0> : !s32i]) {
+// CIR:    cir.scope {
+// CIR:      %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
+// CIR:      %[[ZERO_BOOL:.*]] = cir.cast int_to_bool %[[ZERO]] : !s32i -> 
!cir.bool
+// CIR:      cir.if %[[ZERO_BOOL]]
+// CIR:        cir.call @_Z3if0b(
+
+// LLVM: define{{.*}} void @_Z3if4i(i32 noundef %[[ARG:.*]])
+// LLVM: switch i32
+// LLVM: call{{.*}}i32 @_Z3if0b(
+
+// OGCG: define{{.*}} void @_Z3if4i(i32 noundef %[[ARG:.*]])
+// OGCG: switch i32
+// OGCG: call{{.*}}i32 @_Z3if0b(

diff  --git a/clang/test/CIR/CodeGen/non-odr-use-const-bool.cpp 
b/clang/test/CIR/CodeGen/non-odr-use-const-bool.cpp
index 34ba4162205c5..8775bdbb599b1 100644
--- a/clang/test/CIR/CodeGen/non-odr-use-const-bool.cpp
+++ b/clang/test/CIR/CodeGen/non-odr-use-const-bool.cpp
@@ -25,18 +25,38 @@ void pass_to_call(Foo x) {
 // OGCG:         call void @_Z9take_boolb(i1 {{.*}}true)
 
 int use_in_if(Foo x) {
-  if (x.flag) return 1;
+  if (x.flag && side()) return 1;
   return 0;
 }
 
 // CIR-LABEL: cir.func{{.*}} @_Z9use_in_if3Foo
-// CIR:         %[[B_IF:.+]] = cir.const #true
+// CIR:         %[[FLAG:.+]] = cir.const #true
+// CIR:         %[[B_IF:.*]] = cir.ternary(%[[FLAG]], true {
+// CIR-NEXT:    %[[SIDE:.*]] = cir.call @_Z4sidev()
+// CIR-NEXT:    %[[SIDE_CAST:.*]] = cir.cast int_to_bool %[[SIDE]]
+// CIR-NEXT:    cir.yield %[[SIDE_CAST]]
+// CIR-NEXT:    }, false {
+// CIR-NEXT:    %[[FALSE:.*]] = cir.const #false
+// CIR-NEXT:    cir.yield %[[FALSE]]
+// CIR-NEXT:    })
 // CIR:         cir.if %[[B_IF]]
 
 // LLVM-LABEL: define {{.*}}i32 @_Z9use_in_if3Foo
-// LLVM:         br i1 true,
+// LLVM:       br i1 true, label %[[TRUE:.*]], label %[[FALSE:.*]]
+// LLVM:       [[TRUE]]:
+// LLVM:       %[[SIDE:.*]] = call noundef i32 @_Z4sidev()
+// LLVM:       %[[CMP:.*]] = icmp ne i32 %[[SIDE]], 0
+// LLVM:       br label %[[END:.*]]
+// LLVM:       [[FALSE:.*]]:
+// LLVM:       br label %[[END]]
+// LLVM:       %[[PHI:.*]] = phi i1 [ false, %[[FALSE]] ], [ %[[CMP]], 
%[[TRUE]] ]
+// LLVM:       br i1 %[[PHI]]
 
+// Classic codegen const-folds the 'true' branch away.
 // OGCG-LABEL: define {{.*}}i32 @_Z9use_in_if3Foo
+// OGCG:       %[[SIDE:.*]] = call noundef i32 @_Z4sidev()
+// OGCG:       %[[CMP:.*]] = icmp ne i32 %[[SIDE]], 0
+// OGCG:       br i1 %[[CMP]]
 
 int short_circuit(Foo x) {
   return (x.flag && side()) ? 1 : 0;
@@ -47,5 +67,18 @@ int short_circuit(Foo x) {
 // CIR:         cir.ternary(%[[B_TERN]],
 
 // LLVM-LABEL: define {{.*}}i32 @_Z13short_circuit3Foo
+// LLVM:         br i1 true, label %[[TRUE:.*]], label %[[FALSE:.*]]
+// LLVM:       [[TRUE]]:
+// LLVM:       %[[SIDE:.*]] = call noundef i32 @_Z4sidev()
+// LLVM:       %[[CMP:.*]] = icmp ne i32 %[[SIDE]], 0
+// LLVM:       br label %[[END:.*]]
+// LLVM:       [[FALSE:.*]]:
+// LLVM:       br label %[[END]]
+// LLVM:       %[[PHI:.*]] = phi i1 [ false, %[[FALSE]] ], [ %[[CMP]], 
%[[TRUE]] ]
+// LLVM:       select i1 %[[PHI]]
 
+// Classic codegen const-folds the 'true' branch away.
 // OGCG-LABEL: define {{.*}}i32 @_Z13short_circuit3Foo
+// OGCG:       %[[SIDE:.*]] = call noundef i32 @_Z4sidev()
+// OGCG:       %[[CMP:.*]] = icmp ne i32 %[[SIDE]], 0
+// OGCG:       select i1 %[[CMP]], i32 1, i32 0

diff  --git a/clang/test/CIR/CodeGen/requires-expr.cpp 
b/clang/test/CIR/CodeGen/requires-expr.cpp
index 30bc4e36197f6..310a7438a25cd 100644
--- a/clang/test/CIR/CodeGen/requires-expr.cpp
+++ b/clang/test/CIR/CodeGen/requires-expr.cpp
@@ -1,9 +1,9 @@
 // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu 
-Wno-unused-value -fclangir -emit-cir %s -o %t.cir
 // RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
 // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu 
-Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll
-// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
+// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM,LLVMCIR
 // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu 
-Wno-unused-value -emit-llvm %s -o %t.ll
-// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM,OGCG
 
 template <typename T> void summable(T a) {
   if (requires { a + a; }) {
@@ -14,38 +14,24 @@ template <typename T> void summable(T a) {
 // CIR: %[[A_ADDR:.*]] = cir.alloca "a" {{.*}} init : !cir.ptr<!s32i>
 // CIR: cir.store %[[ARG_A:.*]], %[[A_ADDR]] : !s32i, !cir.ptr<!s32i>
 // CIR: cir.scope {
-// CIR:   %[[CONST_TRUE:.*]] = cir.const #true
-// CIR:   cir.if %[[CONST_TRUE]] {
-// CIR:     %[[B_ADDR:.*]] = cir.alloca "b" {{.*}} init : !cir.ptr<!s32i>
-// CIR:     %[[TMP_A_1:.*]] = cir.load {{.*}} %[[A_ADDR]] : !cir.ptr<!s32i>, 
!s32i
-// CIR:     %[[TMP_A_2:.*]] = cir.load {{.*}} %[[A_ADDR]] : !cir.ptr<!s32i>, 
!s32i
-// CIR:     %[[RESULT:.*]] = cir.add nsw %[[TMP_A_1]], %[[TMP_A_2]] : !s32i
-// CIR:     cir.store {{.*}} %[[RESULT]], %[[B_ADDR]] : !s32i, !cir.ptr<!s32i>
-// CIR:   }
+// CIR:   %[[B_ADDR:.*]] = cir.alloca "b" {{.*}} init : !cir.ptr<!s32i>
+// CIR:   %[[TMP_A_1:.*]] = cir.load {{.*}} %[[A_ADDR]] : !cir.ptr<!s32i>, 
!s32i
+// CIR:   %[[TMP_A_2:.*]] = cir.load {{.*}} %[[A_ADDR]] : !cir.ptr<!s32i>, 
!s32i
+// CIR:   %[[RESULT:.*]] = cir.add nsw %[[TMP_A_1]], %[[TMP_A_2]] : !s32i
+// CIR:   cir.store {{.*}} %[[RESULT]], %[[B_ADDR]] : !s32i, !cir.ptr<!s32i>
 // CIR: }
 
-// LLVM:   %[[B_ADDR:.*]] = alloca i32, align 4
-// LLVM:   %[[A_ADDR:.*]] = alloca i32, align 4
-// LLVM:   store i32 %[[ARG_A:.*]], ptr %[[A_ADDR]], align 4
-// LLVM:   br label %[[IF_COND:.*]]
-// LLVM: [[IF_COND]]:
-// LLVM:   br i1 true, label %[[IF_THEN:.*]], label %[[IF_END:.*]]
-// LLVM: [[IF_THEN]]:
-// LLVM:   %[[TMP_A_1:.*]] = load i32, ptr %[[A_ADDR]], align 4
-// LLVM:   %[[TMP_A_2:.*]] = load i32, ptr %[[A_ADDR]], align 4
-// LLVM:   %[[RESULT:.*]] = add nsw i32 %[[TMP_A_1]], %[[TMP_A_2]]
-// LLVM:   store i32 %[[RESULT]], ptr %[[B_ADDR]], align 4
-// LLVM:   br label %[[IF_END]]
-// LLVM: [[IF_END]]:
-// LLVM:   br label %[[RET:.*]]
-
+// Classic codegen and CIR reverse the order of these allocas.
+// LLVMCIR: %[[B_ADDR:.*]] = alloca i32, align 4
+// LLVMCIR: %[[A_ADDR:.*]] = alloca i32, align 4
+//
 // OGCG: %[[A_ADDR:.*]] = alloca i32, align 4
 // OGCG: %[[B_ADDR:.*]] = alloca i32, align 4
-// OGCG: store i32 %[[ARG_A:.*]], ptr %[[A_ADDR]], align 4
-// OGCG: %[[TMP_A_1:.*]] = load i32, ptr %[[A_ADDR]], align 4
-// OGCG: %[[TMP_A_2:.*]] = load i32, ptr %[[A_ADDR]], align 4
-// OGCG: %[[RESULT:.*]] = add nsw i32 %[[TMP_A_1]], %[[TMP_A_2]]
-// OGCG: store i32 %[[RESULT]], ptr %[[B_ADDR]], align 4
+//
+// LLVM: %[[TMP_A_1:.*]] = load i32, ptr %[[A_ADDR]], align 4
+// LLVM: %[[TMP_A_2:.*]] = load i32, ptr %[[A_ADDR]], align 4
+// LLVM: %[[RESULT:.*]] = add nsw i32 %[[TMP_A_1]], %[[TMP_A_2]]
+// LLVM: store i32 %[[RESULT]], ptr %[[B_ADDR]], align 4
 
 void call_function_with_requires_expr() { summable(1); }
 

diff  --git a/clang/test/CIR/CodeGenBuiltins/builtin-trivally-copyable.cpp 
b/clang/test/CIR/CodeGenBuiltins/builtin-trivally-copyable.cpp
index e8e0fbf3eeedd..9c105820f269f 100644
--- a/clang/test/CIR/CodeGenBuiltins/builtin-trivally-copyable.cpp
+++ b/clang/test/CIR/CodeGenBuiltins/builtin-trivally-copyable.cpp
@@ -1,9 +1,9 @@
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-cir %s -o %t.cir
 // RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o 
%t-cir.ll
-// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
+// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM,LLVMCIR
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
-// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM,OGCG
 
 bool g;
 void store_trivially_copyable_result() {
@@ -18,9 +18,6 @@ void store_trivially_copyable_result() {
 // LLVM: define{{.*}} void @_Z31store_trivially_copyable_resultv()
 // LLVM:   store i8 1, ptr @g
 
-// OGCG: define{{.*}} void @_Z31store_trivially_copyable_resultv()
-// OGCG:   store i8 1, ptr @g
-
 int test_trivially_copyable_as_bool() {
   if (!__is_trivially_copyable(int))
     return -1;
@@ -28,30 +25,15 @@ int test_trivially_copyable_as_bool() {
 }
 
 // CIR: cir.func {{.*}} @_Z31test_trivially_copyable_as_boolv()
-// CIR:   %[[FALSE:.*]] = cir.const #false
-// CIR:   cir.if %[[FALSE]] {
-// CIR:     %[[NEG_ONE:.*]] = cir.const #cir.int<-1> : !s32i
-// CIR:     cir.store %[[NEG_ONE]], %[[RETVAL:.*]]
-// CIR:     %[[RET:.*]] = cir.load %[[RETVAL:.*]] : !cir.ptr<!s32i>, !s32i
-// CIR:     cir.return %[[RET:.*]] : !s32i
-// CIR:   }
 // CIR:   %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
 // CIR:   cir.store %[[ZERO]], %[[RETVAL:.*]] : !s32i, !cir.ptr<!s32i>
 // CIR:   %[[RET:.*]] = cir.load %[[RETVAL:.*]] : !cir.ptr<!s32i>, !s32i
 // CIR:   cir.return %[[RET:.*]] : !s32i
 
-// LLVM: define{{.*}} i32 @_Z31test_trivially_copyable_as_boolv()
-// LLVM:   br i1 false, label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
-// LLVM: [[IF_THEN]]:
-// LLVM:   store i32 -1, ptr %[[RETVAL:.*]]
-// LLVM:   %[[RET:.*]] = load i32, ptr %[[RETVAL:.*]]
-// LLVM:   ret i32 %[[RET:.*]]
-// LLVM: [[IF_ELSE]]:
-// LLVM:   br label %[[IF_END:.*]]
-// LLVM: [[IF_END]]:
-// LLVM:   store i32 0, ptr %[[RETVAL:.*]]
-// LLVM:   %[[RET:.*]] = load i32, ptr %[[RETVAL:.*]]
-// LLVM:   ret i32 %[[RET:.*]]
+// LLVMCIR: define{{.*}} i32 @_Z31test_trivially_copyable_as_boolv()
+// LLVMCIR:   store i32 0, ptr %[[RETVAL:.*]]
+// LLVMCIR:   %[[RET:.*]] = load i32, ptr %[[RETVAL:.*]]
+// LLVMCIR:   ret i32 %[[RET:.*]]
 
 // OGCG: define{{.*}} i32 @_Z31test_trivially_copyable_as_boolv()
 // OGCG:   ret i32 0

diff  --git a/clang/test/CIR/CodeGenBuiltins/builtin-types-compatible.c 
b/clang/test/CIR/CodeGenBuiltins/builtin-types-compatible.c
index 85135ac66a5b3..59c99ba32e91c 100644
--- a/clang/test/CIR/CodeGenBuiltins/builtin-types-compatible.c
+++ b/clang/test/CIR/CodeGenBuiltins/builtin-types-compatible.c
@@ -28,29 +28,12 @@ int test_convert_bool_to_int() {
 }
 
 // CIR: cir.func {{.*}} @test_convert_bool_to_int()
-// CIR:   %[[ONE:.*]] = cir.const #cir.int<1> : !s32i
-// CIR:   %[[BOOL:.*]] = cir.cast int_to_bool %[[ONE]] : !s32i -> !cir.bool
-// CIR:   %[[NOT:.*]] = cir.not %[[BOOL]] : !cir.bool
-// CIR:   cir.if %[[NOT]] {
-// CIR:     %[[NEG_ONE:.*]] = cir.const #cir.int<-1> : !s32i
-// CIR:     cir.store %[[NEG_ONE]], %[[RETVAL:.*]]
-// CIR:     %[[RET:.*]] = cir.load %[[RETVAL:.*]] : !cir.ptr<!s32i>, !s32i
-// CIR:     cir.return %[[RET:.*]] : !s32i
-// CIR:   }
 // CIR:   %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
 // CIR:   cir.store %[[ZERO]], %[[RETVAL:.*]] : !s32i, !cir.ptr<!s32i>
 // CIR:   %[[RET:.*]] = cir.load %[[RETVAL:.*]] : !cir.ptr<!s32i>, !s32i
 // CIR:   cir.return %[[RET:.*]] : !s32i
 
 // LLVM: define{{.*}} i32 @test_convert_bool_to_int()
-// LLVM:   br i1 false, label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
-// LLVM: [[IF_THEN]]:
-// LLVM:   store i32 -1, ptr %[[RETVAL:.*]]
-// LLVM:   %[[RET:.*]] = load i32, ptr %[[RETVAL:.*]]
-// LLVM:   ret i32 %[[RET:.*]]
-// LLVM: [[IF_ELSE]]:
-// LLVM:   br label %[[IF_END:.*]]
-// LLVM: [[IF_END]]:
 // LLVM:   store i32 0, ptr %[[RETVAL:.*]]
 // LLVM:   %[[RET:.*]] = load i32, ptr %[[RETVAL:.*]]
 // LLVM:   ret i32 %[[RET:.*]]


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to