Skip to content

Commit

Permalink
[CIR][CIRGen] Implement "if constexpr" code generation (#436)
Browse files Browse the repository at this point in the history
  • Loading branch information
keryell authored and lanza committed Mar 23, 2024
1 parent 84f410e commit 78b01a3
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 0 deletions.
11 changes: 11 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,17 @@ mlir::LogicalResult CIRGenFunction::buildIfStmt(const IfStmt &S) {
bool CondConstant;
if (ConstantFoldsToSimpleInteger(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())
return buildStmt(Executed, /*useCurrentScope=*/true);
// There is nothing to execute at runtime.
// TODO(cir): there is still an empty cir.scope generated by the caller.
return mlir::success();
}
assert(!UnimplementedFeature::constantFoldsToSimpleInteger());
}

Expand Down
95 changes: 95 additions & 0 deletions clang/test/CIR/CodeGen/if-constexpr.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir-enable -emit-cir %s -o %t.cir
// RUN: FileCheck --input-file=%t.cir %s

void if0() {
int x = 0;
if constexpr (0 == 0) {
// Declare a variable with same name to be sure we handle the
// scopes correctly
int x = 2;
} else {
int x = 3;
}
if constexpr (0 == 1) {
int x = 4;
} else {
int x = 5;
}
if constexpr (int x = 7; 8 == 8) {
int y = x;
} else {
int y = 2*x;
}
if constexpr (int x = 9; 8 == 10) {
int y = x;
} else {
int y = 3*x;
}
if constexpr (10 == 10) {
int x = 20;
}
if constexpr (10 == 11) {
int x = 30;
}
if constexpr (int x = 70; 80 == 80) {
int y = 10*x;
}
if constexpr (int x = 90; 80 == 100) {
int y = 11*x;
}
}

// CHECK: cir.func @_Z3if0v() {{.*}}
// CHECK: cir.store %1, %0 : !s32i, cir.ptr <!s32i> loc({{.*}})
// CHECK-NEXT: cir.scope {
// CHECK-NEXT: %2 = cir.alloca !s32i, cir.ptr <!s32i>, ["x", init] {{.*}}
// CHECK-NEXT: %3 = cir.const(#cir.int<2> : !s32i) : !s32i loc({{.*}})
// CHECK-NEXT: cir.store %3, %2 : !s32i, cir.ptr <!s32i> loc({{.*}})
// CHECK-NEXT: } loc({{.*}})
// CHECK-NEXT: cir.scope {
// CHECK-NEXT: %2 = cir.alloca !s32i, cir.ptr <!s32i>, ["x", init] {{.*}}
// CHECK-NEXT: %3 = cir.const(#cir.int<5> : !s32i) : !s32i loc({{.*}})
// CHECK-NEXT: cir.store %3, %2 : !s32i, cir.ptr <!s32i> loc({{.*}})
// CHECK-NEXT: } loc({{.*}})
// CHECK-NEXT: cir.scope {
// CHECK-NEXT: %2 = cir.alloca !s32i, cir.ptr <!s32i>, ["x", init] {{.*}}
// CHECK-NEXT: %3 = cir.alloca !s32i, cir.ptr <!s32i>, ["y", init] {{.*}}
// CHECK-NEXT: %4 = cir.const(#cir.int<7> : !s32i) : !s32i loc({{.*}})
// CHECK-NEXT: cir.store %4, %2 : !s32i, cir.ptr <!s32i> loc({{.*}})
// CHECK-NEXT: %5 = cir.load %2 : cir.ptr <!s32i>, !s32i loc({{.*}})
// CHECK-NEXT: cir.store %5, %3 : !s32i, cir.ptr <!s32i> loc({{.*}})
// CHECK-NEXT: } loc({{.*}})
// CHECK-NEXT: cir.scope {
// CHECK-NEXT: %2 = cir.alloca !s32i, cir.ptr <!s32i>, ["x", init] {{.*}}
// CHECK-NEXT: %3 = cir.alloca !s32i, cir.ptr <!s32i>, ["y", init] {{.*}}
// CHECK-NEXT: %4 = cir.const(#cir.int<9> : !s32i) : !s32i loc({{.*}})
// CHECK-NEXT: cir.store %4, %2 : !s32i, cir.ptr <!s32i> loc({{.*}})
// CHECK-NEXT: %5 = cir.const(#cir.int<3> : !s32i) : !s32i loc({{.*}})
// CHECK-NEXT: %6 = cir.load %2 : cir.ptr <!s32i>, !s32i loc({{.*}})
// CHECK-NEXT: %7 = cir.binop(mul, %5, %6) : !s32i loc({{.*}})
// CHECK-NEXT: cir.store %7, %3 : !s32i, cir.ptr <!s32i> loc({{.*}})
// CHECK-NEXT: } loc({{.*}})
// CHECK-NEXT: cir.scope {
// CHECK-NEXT: %2 = cir.alloca !s32i, cir.ptr <!s32i>, ["x", init] {{.*}}
// CHECK-NEXT: %3 = cir.const(#cir.int<20> : !s32i) : !s32i loc({{.*}})
// CHECK-NEXT: cir.store %3, %2 : !s32i, cir.ptr <!s32i> loc({{.*}})
// CHECK-NEXT: } loc({{.*}})
// CHECK-NEXT: cir.scope {
// Note that Clang does not even emit a block in this case
// CHECK-NEXT: } loc({{.*}})
// CHECK-NEXT: cir.scope {
// CHECK-NEXT: %2 = cir.alloca !s32i, cir.ptr <!s32i>, ["x", init] {{.*}}
// CHECK-NEXT: %3 = cir.alloca !s32i, cir.ptr <!s32i>, ["y", init] {{.*}}
// CHECK-NEXT: %4 = cir.const(#cir.int<70> : !s32i) : !s32i loc({{.*}})
// CHECK-NEXT: cir.store %4, %2 : !s32i, cir.ptr <!s32i> loc({{.*}})
// CHECK-NEXT: %5 = cir.const(#cir.int<10> : !s32i) : !s32i loc({{.*}})
// CHECK-NEXT: %6 = cir.load %2 : cir.ptr <!s32i>, !s32i loc({{.*}})
// CHECK-NEXT: %7 = cir.binop(mul, %5, %6) : !s32i loc({{.*}})
// CHECK-NEXT: cir.store %7, %3 : !s32i, cir.ptr <!s32i> loc({{.*}})
// CHECK-NEXT: } loc({{.*}})
// CHECK-NEXT: cir.scope {
// CHECK-NEXT: %2 = cir.alloca !s32i, cir.ptr <!s32i>, ["x", init] {{.*}}
// CHECK-NEXT: %3 = cir.const(#cir.int<90> : !s32i) : !s32i loc({{.*}})
// CHECK-NEXT: cir.store %3, %2 : !s32i, cir.ptr <!s32i> loc({{.*}})
// CHECK-NEXT: } loc({{.*}})
// CHECK-NEXT: cir.return loc({{.*}})

0 comments on commit 78b01a3

Please sign in to comment.