// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir // RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR // RUN: %clang_cc1 -std=c++17 -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: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll // RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG int if0(bool a) { if (a) return 2; return 3; } // CIR: cir.func{{.*}} @_Z3if0b(%arg0: !cir.bool loc({{.*}})) -> !s32i // CIR: cir.scope { // CIR: %4 = cir.load{{.*}} %0 : !cir.ptr, !cir.bool // CIR-NEXT: cir.if %4 { // CIR-NEXT: %5 = cir.const #cir.int<2> : !s32i // CIR-NEXT: cir.store{{.*}} %5, %1 : !s32i, !cir.ptr // CIR-NEXT: %6 = cir.load{{.*}} %1 : !cir.ptr, !s32i // CIR-NEXT: cir.return %6 : !s32i // CIR-NEXT: } // CIR-NEXT: } // LLVM: define{{.*}} i32 @_Z3if0b(i1 %0) // LLVM: br label %[[ENTRY:.*]] // LLVM: [[ENTRY]]: // LLVM: %6 = load i8, ptr %2, align 1 // LLVM: %7 = trunc i8 %6 to i1 // LLVM: br i1 %7, label %[[THEN:.*]], label %[[END:.*]] // LLVM: [[THEN]]: // LLVM: store i32 2, ptr %3, align 4 // LLVM: %9 = load i32, ptr %3, align 4 // LLVM: ret i32 %9 // LLVM: [[END]]: // LLVM: br label %[[LABEL4:.*]] // LLVM: [[LABEL4]]: // LLVM: store i32 3, ptr %3, align 4 // LLVM: %12 = load i32, ptr %3, align 4 // LLVM: ret i32 %12 // OGCG: define{{.*}} i32 @_Z3if0b(i1 noundef zeroext %a) // OGCG: entry: // OGCG: %[[RETVAL:.*]] = alloca i32, align 4 // OGCG: %[[A_ADDR:.*]] = alloca i8, align 1 // OGCG: %[[STOREDV:.*]] = zext i1 %a to i8 // OGCG: store i8 %[[STOREDV]], ptr %[[A_ADDR]], align 1 // OGCG: %[[LOADTMP:.*]] = load i8, ptr %[[A_ADDR]], align 1 // OGCG: %[[LOADEDV:.*]] = trunc i8 %[[LOADTMP]] to i1 // OGCG: br i1 %[[LOADEDV]], label %[[THEN_LABEL:.*]], label %[[END_LABEL:.*]] // OGCG: [[THEN_LABEL]]: // OGCG: store i32 2, ptr %[[RETVAL]], align 4 // OGCG: br label %[[RETURN_LABEL:.*]] // OGCG: [[END_LABEL]]: // OGCG: store i32 3, ptr %[[RETVAL]], align 4 // OGCG: br label %[[RETURN_LABEL]] // OGCG: [[RETURN_LABEL]]: // OGCG: %[[FINALLOAD:.*]] = load i32, ptr %[[RETVAL]], align 4 // OGCG: ret i32 %[[FINALLOAD]] void if1(int a) { int x = 0; if (a) { x = 3; } else { x = 4; } } // CIR: cir.func{{.*}} @_Z3if1i(%arg0: !s32i loc({{.*}})) // CIR: cir.scope { // CIR: %3 = cir.load{{.*}} %0 : !cir.ptr, !s32i // CIR: %4 = cir.cast(int_to_bool, %3 : !s32i), !cir.bool // CIR-NEXT: cir.if %4 { // CIR-NEXT: %5 = cir.const #cir.int<3> : !s32i // CIR-NEXT: cir.store{{.*}} %5, %1 : !s32i, !cir.ptr // CIR-NEXT: } else { // CIR-NEXT: %5 = cir.const #cir.int<4> : !s32i // CIR-NEXT: cir.store{{.*}} %5, %1 : !s32i, !cir.ptr // CIR-NEXT: } // CIR: } // LLVM: define{{.*}} void @_Z3if1i(i32 %0) // LLVM: %[[A:.*]] = alloca i32, i64 1, align 4 // LLVM: %[[X:.*]] = alloca i32, i64 1, align 4 // LLVM: store i32 %0, ptr %[[A]], align 4 // LLVM: store i32 0, ptr %[[X]], align 4 // LLVM: br label %[[ENTRY:.*]] // LLVM: [[ENTRY]]: // LLVM: %[[LOADED:.*]] = load i32, ptr %[[A]], align 4 // LLVM: %[[COND:.*]] = icmp ne i32 %[[LOADED]], 0 // LLVM: br i1 %[[COND]], label %[[THEN:.*]], label %[[ELSE:.*]] // LLVM: [[THEN]]: // LLVM: store i32 3, ptr %[[X]], align 4 // LLVM: br label %[[END:.*]] // LLVM: [[ELSE]]: // LLVM: store i32 4, ptr %[[X]], align 4 // LLVM: br label %[[END]] // LLVM: [[END]]: // LLVM: br label %[[EXIT:.*]] // LLVM: [[EXIT]]: // LLVM: ret void // OGCG: define{{.*}} void @_Z3if1i(i32 noundef %[[A:.*]]) // OGCG: entry: // OGCG: %[[A_ADDR:.*]] = alloca i32, align 4 // OGCG: %[[X:.*]] = alloca i32, align 4 // OGCG: store i32 %[[A]], ptr %[[A_ADDR]], align 4 // OGCG: store i32 0, ptr %[[X]], align 4 // OGCG: %[[LOADED_A:.*]] = load i32, ptr %[[A_ADDR]], align 4 // OGCG: %[[TOBOOL:.*]] = icmp ne i32 %[[LOADED_A]], 0 // OGCG: br i1 %[[TOBOOL]], label %[[THEN_LABEL:.*]], label %[[ELSE_LABEL:.*]] // OGCG: [[THEN_LABEL]]: // OGCG: store i32 3, ptr %[[X]], align 4 // OGCG: br label %[[END_LABEL:.*]] // OGCG: [[ELSE_LABEL]]: // OGCG: store i32 4, ptr %[[X]], align 4 // OGCG: br label %[[END_LABEL]] // OGCG: [[END_LABEL]]: // OGCG: ret void void if2(int a, bool b, bool c) { int x = 0; if (a) { x = 3; if (b) { x = 8; } } else { if (c) { x = 14; } x = 4; } } // CIR: cir.func{{.*}} @_Z3if2ibb(%arg0: !s32i loc({{.*}}), %arg1: !cir.bool loc({{.*}}), %arg2: !cir.bool loc({{.*}})) // CIR: cir.scope { // CIR: %5 = cir.load{{.*}} %0 : !cir.ptr, !s32i // CIR: %6 = cir.cast(int_to_bool, %5 : !s32i), !cir.bool // CIR: cir.if %6 { // CIR: %7 = cir.const #cir.int<3> : !s32i // CIR: cir.store{{.*}} %7, %3 : !s32i, !cir.ptr // CIR: cir.scope { // CIR: %8 = cir.load{{.*}} %1 : !cir.ptr, !cir.bool // CIR-NEXT: cir.if %8 { // CIR-NEXT: %9 = cir.const #cir.int<8> : !s32i // CIR-NEXT: cir.store{{.*}} %9, %3 : !s32i, !cir.ptr // CIR-NEXT: } // CIR: } // CIR: } else { // CIR: cir.scope { // CIR: %8 = cir.load{{.*}} %2 : !cir.ptr, !cir.bool // CIR-NEXT: cir.if %8 { // CIR-NEXT: %9 = cir.const #cir.int<14> : !s32i // CIR-NEXT: cir.store{{.*}} %9, %3 : !s32i, !cir.ptr // CIR-NEXT: } // CIR: } // CIR: %7 = cir.const #cir.int<4> : !s32i // CIR: cir.store{{.*}} %7, %3 : !s32i, !cir.ptr // CIR: } // CIR: } // LLVM: define{{.*}} void @_Z3if2ibb(i32 %[[A:.*]], i1 %[[B:.*]], i1 %[[C:.*]]) // LLVM: %[[VARA:.*]] = alloca i32, i64 1, align 4 // LLVM: %[[VARB:.*]] = alloca i8, i64 1, align 1 // LLVM: %[[VARC:.*]] = alloca i8, i64 1, align 1 // LLVM: %[[VARX:.*]] = alloca i32, i64 1, align 4 // LLVM: store i32 %[[A]], ptr %[[VARA]], align 4 // LLVM: %[[B_EXT:.*]] = zext i1 %[[B]] to i8 // LLVM: store i8 %[[B_EXT]], ptr %[[VARB]], align 1 // LLVM: %[[C_EXT:.*]] = zext i1 %[[C]] to i8 // LLVM: store i8 %[[C_EXT]], ptr %[[VARC]], align 1 // LLVM: store i32 0, ptr %[[VARX]], align 4 // LLVM: br label %[[ENTRY:.*]] // LLVM: [[ENTRY]]: // LLVM: %[[LOAD_A:.*]] = load i32, ptr %[[VARA]], align 4 // LLVM: %[[CMP_A:.*]] = icmp ne i32 %[[LOAD_A]], 0 // LLVM: br i1 %[[CMP_A]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]] // LLVM: [[IF_THEN]]: // LLVM: store i32 3, ptr %[[VARX]], align 4 // LLVM: br label %[[LABEL14:.*]] // LLVM: [[LABEL14]]: // LLVM: %[[LOAD_B:.*]] = load i8, ptr %[[VARB]], align 1 // LLVM: %[[TRUNC_B:.*]] = trunc i8 %[[LOAD_B]] to i1 // LLVM: br i1 %[[TRUNC_B]], label %[[IF_THEN2:.*]], label %[[IF_END2:.*]] // LLVM: [[IF_THEN2]]: // LLVM: store i32 8, ptr %[[VARX]], align 4 // LLVM: br label %[[IF_END2]] // LLVM: [[IF_END2]]: // LLVM: br label %[[LABEL19:.*]] // LLVM: [[LABEL19]]: // LLVM: br label %[[LABEL27:.*]] // LLVM: [[IF_ELSE]]: // LLVM: br label %[[LABEL21:.*]] // LLVM: [[LABEL21]]: // LLVM: %[[LOAD_C:.*]] = load i8, ptr %[[VARC]], align 1 // LLVM: %[[TRUNC_C:.*]] = trunc i8 %[[LOAD_C]] to i1 // LLVM: br i1 %[[TRUNC_C]], label %[[IF_THEN3:.*]], label %[[IF_END3:.*]] // LLVM: [[IF_THEN3]]: // LLVM: store i32 14, ptr %[[VARX]], align 4 // LLVM: br label %[[IF_END3]] // LLVM: [[IF_END3]]: // LLVM: br label %[[LABEL26:.*]] // LLVM: [[LABEL26]]: // LLVM: store i32 4, ptr %[[VARX]], align 4 // LLVM: br label %[[LABEL27]] // LLVM: [[LABEL27]]: // LLVM: br label %[[LABEL28:.*]] // LLVM: [[LABEL28]]: // LLVM: ret void // OGCG: define{{.*}} void @_Z3if2ibb(i32 noundef %[[A:.*]], i1 noundef zeroext %[[B:.*]], i1 noundef zeroext %[[C:.*]]) // OGCG: entry: // OGCG: %[[A_ADDR:.*]] = alloca i32, align 4 // OGCG: %[[B_ADDR:.*]] = alloca i8, align 1 // OGCG: %[[C_ADDR:.*]] = alloca i8, align 1 // OGCG: %[[X:.*]] = alloca i32, align 4 // OGCG: store i32 %[[A]], ptr %[[A_ADDR]], align 4 // OGCG: %[[B_EXT:.*]] = zext i1 %[[B]] to i8 // OGCG: store i8 %[[B_EXT]], ptr %[[B_ADDR]], align 1 // OGCG: %[[C_EXT:.*]] = zext i1 %[[C]] to i8 // OGCG: store i8 %[[C_EXT]], ptr %[[C_ADDR]], align 1 // OGCG: store i32 0, ptr %[[X]], align 4 // OGCG: %[[A_VAL:.*]] = load i32, ptr %[[A_ADDR]], align 4 // OGCG: %[[A_BOOL:.*]] = icmp ne i32 %[[A_VAL]], 0 // OGCG: br i1 %[[A_BOOL]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]] // OGCG: [[IF_THEN]]: // OGCG: store i32 3, ptr %[[X]], align 4 // OGCG: %[[B_LOAD:.*]] = load i8, ptr %[[B_ADDR]], align 1 // OGCG: %[[B_TRUNC:.*]] = trunc i8 %[[B_LOAD]] to i1 // OGCG: br i1 %[[B_TRUNC]], label %[[IF_THEN2:.*]], label %[[IF_END:.*]] // OGCG: [[IF_THEN2]]: // OGCG: store i32 8, ptr %[[X]], align 4 // OGCG: br label %[[IF_END]] // OGCG: [[IF_END]]: // OGCG: br label %[[IF_END6:.*]] // OGCG: [[IF_ELSE]]: // OGCG: %[[C_LOAD:.*]] = load i8, ptr %[[C_ADDR]], align 1 // OGCG: %[[C_TRUNC:.*]] = trunc i8 %[[C_LOAD]] to i1 // OGCG: br i1 %[[C_TRUNC]], label %[[IF_THEN4:.*]], label %[[IF_END5:.*]] // OGCG: [[IF_THEN4]]: // OGCG: store i32 14, ptr %[[X]], align 4 // OGCG: br label %[[IF_END5]] // OGCG: [[IF_END5]]: // OGCG: store i32 4, ptr %[[X]], align 4 // OGCG: br label %[[IF_END6]] // OGCG: [[IF_END6]]: // OGCG: ret void int if_init() { if (int x = 42 ; x) { return x + 1; // x should be visible here } else { return x - 1; // x should also be visible here } } // CIR: cir.func{{.*}} @_Z7if_initv() -> !s32i // CIR: %[[RETVAL:.*]] = cir.alloca !s32i, !cir.ptr // CIR: cir.scope { // CIR: %[[X:.*]] = cir.alloca !s32i, !cir.ptr, // CIR: %[[CONST42:.*]] = cir.const #cir.int<42> : !s32i // CIR: cir.store{{.*}} %[[CONST42]], %[[X]] : !s32i, !cir.ptr // CIR: %[[X_VAL:.*]] = cir.load{{.*}} %[[X]] : !cir.ptr, !s32i // CIR: %[[COND:.*]] = cir.cast(int_to_bool, %[[X_VAL]] : !s32i), !cir.bool // CIR: cir.if %[[COND]] { // CIR: %[[X_IF:.*]] = cir.load{{.*}} %[[X]] : !cir.ptr, !s32i // CIR: %[[ONE:.*]] = cir.const #cir.int<1> : !s32i // CIR: %[[ADD:.*]] = cir.binop(add, %[[X_IF]], %[[ONE]]) nsw : !s32i // CIR: cir.store{{.*}} %[[ADD]], %[[RETVAL]] : !s32i, !cir.ptr // CIR: %[[RETVAL_LOAD1:.*]] = cir.load{{.*}} %[[RETVAL]] : !cir.ptr, !s32i // CIR: cir.return %[[RETVAL_LOAD1]] : !s32i // CIR: } else { // CIR: %[[X_ELSE:.*]] = cir.load{{.*}} %[[X]] : !cir.ptr, !s32i // CIR: %[[ONE2:.*]] = cir.const #cir.int<1> : !s32i // CIR: %[[SUB:.*]] = cir.binop(sub, %[[X_ELSE]], %[[ONE2]]) nsw : !s32i // CIR: cir.store{{.*}} %[[SUB]], %[[RETVAL]] : !s32i, !cir.ptr // CIR: %[[RETVAL_LOAD2:.*]] = cir.load{{.*}} %[[RETVAL]] : !cir.ptr, !s32i // CIR: cir.return %[[RETVAL_LOAD2]] : !s32i // CIR: } // CIR: } // LLVM: define{{.*}} i32 @_Z7if_initv() // LLVM: %[[X:.*]] = alloca i32, i64 1, align 4 // LLVM: %[[RETVAL:.*]] = alloca i32, i64 1, align 4 // LLVM: store i32 42, ptr %[[X]], align 4 // LLVM: %[[X_VAL:.*]] = load i32, ptr %[[X]], align 4 // LLVM: %[[COND:.*]] = icmp ne i32 %[[X_VAL]], 0 // LLVM: br i1 %[[COND]], label %[[THEN:.*]], label %[[ELSE:.*]] // LLVM: [[THEN]]: // LLVM: %[[X_LOAD:.*]] = load i32, ptr %[[X]], align 4 // LLVM: %[[ADD:.*]] = add nsw i32 %[[X_LOAD]], 1 // LLVM: store i32 %[[ADD]], ptr %[[RETVAL]], align 4 // LLVM: %[[RETVAL_LOAD1:.*]] = load i32, ptr %[[RETVAL]], align 4 // LLVM: ret i32 %[[RETVAL_LOAD1]] // LLVM: [[ELSE]]: // LLVM: %[[X_LOAD2:.*]] = load i32, ptr %[[X]], align 4 // LLVM: %[[SUB:.*]] = sub nsw i32 %[[X_LOAD2]], 1 // LLVM: store i32 %[[SUB]], ptr %[[RETVAL]], align 4 // LLVM: %[[RETVAL_LOAD2:.*]] = load i32, ptr %[[RETVAL]], align 4 // LLVM: ret i32 %[[RETVAL_LOAD2]] // OGCG: define{{.*}} i32 @_Z7if_initv() // OGCG: entry: // OGCG: %[[RETVAL:.*]] = alloca i32, align 4 // OGCG: %[[X:.*]] = alloca i32, align 4 // OGCG: store i32 42, ptr %[[X]], align 4 // OGCG: %[[X_VAL:.*]] = load i32, ptr %[[X]], align 4 // OGCG: %[[COND:.*]] = icmp ne i32 %[[X_VAL]], 0 // OGCG: br i1 %[[COND]], label %[[THEN:.*]], label %[[ELSE:.*]] // OGCG: [[THEN]]: // OGCG: %[[X_LOAD:.*]] = load i32, ptr %[[X]], align 4 // OGCG: %[[ADD:.*]] = add nsw i32 %[[X_LOAD]], 1 // OGCG: store i32 %[[ADD]], ptr %[[RETVAL]], align 4 // OGCG: br label %[[RETURN:.*]] // OGCG: [[ELSE]]: // OGCG: %[[X_LOAD2:.*]] = load i32, ptr %[[X]], align 4 // OGCG: %[[SUB:.*]] = sub nsw i32 %[[X_LOAD2]], 1 // OGCG: store i32 %[[SUB]], ptr %[[RETVAL]], align 4 // OGCG: br label %[[RETURN]] // OGCG: [[RETURN]]: // OGCG: %[[RETVAL_FINAL:.*]] = load i32, ptr %[[RETVAL]], align 4 // OGCG: ret i32 %[[RETVAL_FINAL]]