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
