| // 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: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll |
| // RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG |
| |
| int a = 42; |
| |
| /* --- Compound literals */ |
| |
| struct foo { int x, y; }; |
| |
| int y; |
| struct foo f = (struct foo){ __builtin_constant_p(y), 42 }; |
| |
| // CIR: cir.global external @f = #cir.const_record<{#cir.int<0> : !s32i, #cir.int<42> : !s32i}> : !rec_foo |
| // LLVM: @f = global %struct.foo { i32 0, i32 42 } |
| // OGCG: @f = global %struct.foo { i32 0, i32 42 } |
| |
| struct foo test0(int expr) { |
| struct foo f = (struct foo){ __builtin_constant_p(expr), 42 }; |
| return f; |
| } |
| |
| // CIR: cir.func {{.*}} @test0(%[[ARG0:.*]]: !s32i {{.*}}) -> !rec_foo |
| // CIR: %[[EXPR_ADDR:.*]] = cir.alloca "expr" {{.*}} init : !cir.ptr<!s32i> |
| // CIR: cir.store %[[ARG0]], %[[EXPR_ADDR]] |
| // CIR: %[[EXPR:.*]] = cir.load{{.*}} %[[EXPR_ADDR]] |
| // CIR: %[[IS_CONSTANT:.*]] = cir.is_constant %[[EXPR]] : !s32i -> !cir.bool |
| |
| // LLVM: define{{.*}} %struct.foo @test0(i32 {{.*}} %[[ARG0:.*]]) |
| // LLVM: %[[EXPR_ADDR:.*]] = alloca i32 |
| // LLVM: store i32 %[[ARG0]], ptr %[[EXPR_ADDR]] |
| // LLVM: %[[EXPR:.*]] = load i32, ptr %[[EXPR_ADDR]] |
| // LLVM: %[[IS_CONSTANT:.*]] = call i1 @llvm.is.constant.i32(i32 %[[EXPR]]) |
| |
| // OGCG: define{{.*}} i64 @test0(i32 {{.*}} %[[ARG0:.*]]) |
| // OGCG: %[[EXPR_ADDR:.*]] = alloca i32 |
| // OGCG: store i32 %[[ARG0]], ptr %[[EXPR_ADDR]] |
| // OGCG: %[[EXPR:.*]] = load i32, ptr %[[EXPR_ADDR]] |
| // OGCG: %[[IS_CONSTANT:.*]] = call i1 @llvm.is.constant.i32(i32 %[[EXPR]]) |
| |
| /* --- Pointer types */ |
| |
| int test1(void) { |
| return __builtin_constant_p(&a - 13); |
| } |
| |
| // CIR: cir.func {{.*}} @test1() -> !s32i |
| // CIR: %[[TMP1:.*]] = cir.alloca "__retval" {{.*}} : !cir.ptr<!s32i> |
| // CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i |
| // CIR: cir.store %[[ZERO]], %[[TMP1]] : !s32i, !cir.ptr<!s32i> |
| // CIR: %[[TMP2:.*]] = cir.load %[[TMP1]] : !cir.ptr<!s32i>, !s32i |
| // CIR: cir.return %[[TMP2]] : !s32i |
| |
| // LLVM: define{{.*}} i32 @test1() |
| // LLVM: %[[TMP1:.*]] = alloca i32 |
| // LLVM: store i32 0, ptr %[[TMP1]] |
| // LLVM: %[[TMP2:.*]] = load i32, ptr %[[TMP1]] |
| // LLVM: ret i32 %[[TMP2]] |
| |
| // OGCG: define{{.*}} i32 @test1() |
| // OGCG: ret i32 0 |
| |
| /* --- Aggregate types */ |
| |
| int b[] = {1, 2, 3}; |
| |
| int test2(void) { |
| return __builtin_constant_p(b); |
| } |
| |
| // CIR: cir.func {{.*}} @test2() -> !s32i |
| // CIR: %[[TMP1:.*]] = cir.alloca "__retval" {{.*}} : !cir.ptr<!s32i> |
| // CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i |
| // CIR: cir.store %[[ZERO]], %[[TMP1]] : !s32i, !cir.ptr<!s32i> |
| // CIR: %[[TMP2:.*]] = cir.load %[[TMP1]] : !cir.ptr<!s32i>, !s32i |
| // CIR: cir.return %[[TMP2]] : !s32i |
| |
| // LLVM: define{{.*}} i32 @test2() |
| // LLVM: %[[TMP1:.*]] = alloca i32 |
| // LLVM: store i32 0, ptr %[[TMP1]] |
| // LLVM: %[[TMP2:.*]] = load i32, ptr %[[TMP1]] |
| // LLVM: ret i32 %[[TMP2]] |
| |
| // OGCG: define{{.*}} i32 @test2() |
| // OGCG: ret i32 0 |
| |
| const char test3_c[] = {1, 2, 3, 0}; |
| |
| int test3(void) { |
| return __builtin_constant_p(test3_c); |
| } |
| |
| // CIR: cir.func {{.*}} @test3() -> !s32i |
| // CIR: %[[TMP1:.*]] = cir.alloca "__retval" {{.*}} : !cir.ptr<!s32i> |
| // CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i |
| // CIR: cir.store %[[ZERO]], %[[TMP1]] : !s32i, !cir.ptr<!s32i> |
| // CIR: %[[TMP2:.*]] = cir.load %[[TMP1]] : !cir.ptr<!s32i>, !s32i |
| // CIR: cir.return %[[TMP2]] : !s32i |
| |
| // LLVM: define{{.*}} i32 @test3() |
| // LLVM: %[[TMP1:.*]] = alloca i32 |
| // LLVM: store i32 0, ptr %[[TMP1]] |
| // LLVM: %[[TMP2:.*]] = load i32, ptr %[[TMP1]] |
| // LLVM: ret i32 %[[TMP2]] |
| |
| // OGCG: define{{.*}} i32 @test3() |
| // OGCG: ret i32 0 |
| |
| inline char test4_i(const char *x) { |
| return x[1]; |
| } |
| |
| int test4(void) { |
| return __builtin_constant_p(test4_i(test3_c)); |
| } |
| |
| // CIR: cir.func {{.*}} @test4() -> !s32i |
| // CIR: %[[TMP1:.*]] = cir.alloca "__retval" {{.*}} : !cir.ptr<!s32i> |
| // CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i |
| // CIR: cir.store %[[ZERO]], %[[TMP1]] : !s32i, !cir.ptr<!s32i> |
| // CIR: %[[TMP2:.*]] = cir.load %[[TMP1]] : !cir.ptr<!s32i>, !s32i |
| // CIR: cir.return %[[TMP2]] : !s32i |
| |
| // LLVM: define{{.*}} i32 @test4() |
| // LLVM: %[[TMP1:.*]] = alloca i32 |
| // LLVM: store i32 0, ptr %[[TMP1]] |
| // LLVM: %[[TMP2:.*]] = load i32, ptr %[[TMP1]] |
| // LLVM: ret i32 %[[TMP2]] |
| |
| // OGCG: define{{.*}} i32 @test4() |
| // OGCG: ret i32 0 |
| |
| /* --- Constant global variables */ |
| |
| const int c = 42; |
| |
| int test5(void) { |
| return __builtin_constant_p(c); |
| } |
| |
| // CIR: cir.func {{.*}} @test5() -> !s32i |
| // CIR: %[[TMP1:.*]] = cir.alloca "__retval" {{.*}} : !cir.ptr<!s32i> |
| // CIR: %[[ONE:.*]] = cir.const #cir.int<1> : !s32i |
| // CIR: cir.store %[[ONE]], %[[TMP1]] : !s32i, !cir.ptr<!s32i> |
| // CIR: %[[TMP2:.*]] = cir.load %[[TMP1]] : !cir.ptr<!s32i>, !s32i |
| // CIR: cir.return %[[TMP2]] : !s32i |
| |
| // LLVM: define{{.*}} i32 @test5() |
| // LLVM: %[[TMP1:.*]] = alloca i32 |
| // LLVM: store i32 1, ptr %[[TMP1]] |
| // LLVM: %[[TMP2:.*]] = load i32, ptr %[[TMP1]] |
| // LLVM: ret i32 %[[TMP2]] |
| |
| // OGCG: define{{.*}} i32 @test5() |
| // OGCG: ret i32 1 |
| |
| /* --- Array types */ |
| |
| int arr[] = { 1, 2, 3 }; |
| |
| int test6(void) { |
| return __builtin_constant_p(arr[2]); |
| } |
| |
| // CIR: cir.func {{.*}} @test6() -> !s32i |
| // CIR: %[[TWO:.*]] = cir.const #cir.int<2> : !s64i |
| // CIR: %[[ARR:.*]] = cir.get_global @arr : !cir.ptr<!cir.array<!s32i x 3>> |
| // CIR: %[[ELE_PTR:.*]] = cir.get_element %[[ARR]][%[[TWO]] : !s64i] : !cir.ptr<!cir.array<!s32i x 3>> -> !cir.ptr<!s32i> |
| // CIR: %[[ELE:.*]] = cir.load{{.*}} %[[ELE_PTR]] : !cir.ptr<!s32i>, !s32i |
| // CIR: %[[IS_CONSTANT:.*]] = cir.is_constant %[[ELE]] : !s32i -> !cir.bool |
| |
| // LLVM: define {{.*}} i32 @test6() |
| // LLVM: %[[TMP1:.*]] = load i32, ptr getelementptr inbounds nuw (i8, ptr @arr, i64 8) |
| // LLVM: %[[TMP2:.*]] = call i1 @llvm.is.constant.i32(i32 %[[TMP1]]) |
| |
| // OGCG: define {{.*}} i32 @test6() |
| // OGCG: %[[TMP1:.*]] = load i32, ptr getelementptr inbounds nuw (i8, ptr @arr, i64 8) |
| // OGCG: %[[TMP2:.*]] = call i1 @llvm.is.constant.i32(i32 %[[TMP1]]) |
| |
| const int c_arr[] = { 1, 2, 3 }; |
| |
| int test7(void) { |
| return __builtin_constant_p(c_arr[2]); |
| } |
| |
| // CIR: cir.func {{.*}} @test7() -> !s32i |
| // CIR: %[[TMP1:.*]] = cir.alloca "__retval" {{.*}} : !cir.ptr<!s32i> |
| // CIR: %[[ONE:.*]] = cir.const #cir.int<1> : !s32i |
| // CIR: cir.store %[[ONE]], %[[TMP1]] : !s32i, !cir.ptr<!s32i> |
| // CIR: %[[TMP2:.*]] = cir.load %[[TMP1]] : !cir.ptr<!s32i>, !s32i |
| // CIR: cir.return %[[TMP2]] : !s32i |
| |
| // LLVM: define{{.*}} i32 @test7() |
| // LLVM: %[[TMP1:.*]] = alloca i32 |
| // LLVM: store i32 1, ptr %[[TMP1]] |
| // LLVM: %[[TMP2:.*]] = load i32, ptr %[[TMP1]] |
| // LLVM: ret i32 %[[TMP2]] |
| |
| // OGCG: define{{.*}} i32 @test7() |
| // OGCG: ret i32 1 |
| |
| int test8(void) { |
| return __builtin_constant_p(c_arr); |
| } |
| |
| // CIR: cir.func {{.*}} @test8() -> !s32i |
| // CIR: %[[TMP1:.*]] = cir.alloca "__retval" {{.*}} : !cir.ptr<!s32i> |
| // CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i |
| // CIR: cir.store %[[ZERO]], %[[TMP1]] : !s32i, !cir.ptr<!s32i> |
| // CIR: %[[TMP2:.*]] = cir.load %[[TMP1]] : !cir.ptr<!s32i>, !s32i |
| // CIR: cir.return %[[TMP2]] : !s32i |
| |
| // LLVM: define{{.*}} i32 @test8() |
| // LLVM: %[[TMP1:.*]] = alloca i32 |
| // LLVM: store i32 0, ptr %[[TMP1]] |
| // LLVM: %[[TMP2:.*]] = load i32, ptr %[[TMP1]] |
| // LLVM: ret i32 %[[TMP2]] |
| |
| // OGCG: define{{.*}} i32 @test8() |
| // OGCG: ret i32 0 |
| |
| /* --- Function pointers */ |
| |
| int test9(void) { |
| return __builtin_constant_p(&test9); |
| } |
| |
| // CIR: cir.func {{.*}} @test9() -> !s32i |
| // CIR: %[[TMP1:.*]] = cir.alloca "__retval" {{.*}} : !cir.ptr<!s32i> |
| // CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i |
| // CIR: cir.store %[[ZERO]], %[[TMP1]] : !s32i, !cir.ptr<!s32i> |
| // CIR: %[[TMP2:.*]] = cir.load %[[TMP1]] : !cir.ptr<!s32i>, !s32i |
| // CIR: cir.return %[[TMP2]] : !s32i |
| |
| // LLVM: define{{.*}} i32 @test9() |
| // LLVM: %[[TMP1:.*]] = alloca i32 |
| // LLVM: store i32 0, ptr %[[TMP1]] |
| // LLVM: %[[TMP2:.*]] = load i32, ptr %[[TMP1]] |
| // LLVM: ret i32 %[[TMP2]] |
| |
| // OGCG: define{{.*}} i32 @test9() |
| // OGCG: ret i32 0 |
| |
| int test10(void) { |
| return __builtin_constant_p(&test10 != 0); |
| } |
| |
| // CIR: cir.func {{.*}} @test10() -> !s32i |
| // CIR: %[[TMP1:.*]] = cir.alloca "__retval" {{.*}} : !cir.ptr<!s32i> |
| // CIR: %[[ONE:.*]] = cir.const #cir.int<1> : !s32i |
| // CIR: cir.store %[[ONE]], %[[TMP1]] : !s32i, !cir.ptr<!s32i> |
| // CIR: %[[TMP2:.*]] = cir.load %[[TMP1]] : !cir.ptr<!s32i>, !s32i |
| // CIR: cir.return %[[TMP2]] : !s32i |
| |
| // LLVM: define{{.*}} i32 @test10() |
| // LLVM: %[[TMP1:.*]] = alloca i32 |
| // LLVM: store i32 1, ptr %[[TMP1]] |
| // LLVM: %[[TMP2:.*]] = load i32, ptr %[[TMP1]] |
| // LLVM: ret i32 %[[TMP2]] |
| |
| // OGCG: define{{.*}} i32 @test10() |
| // OGCG: ret i32 1 |
| |
| int test11_f(void); |
| void test11(void) { |
| int a, b; |
| (void)__builtin_constant_p((a = b, test11_f())); |
| } |
| |
| // CIR: cir.func {{.*}} @test11() |
| // CIR-NOT: call {{.*}}test11_f |
| |
| // LLVM: define{{.*}} void @test11() |
| // LLVM-NOT: call {{.*}}test11_f |
| |
| // OGCG: define{{.*}} void @test11() |
| // OGCG-NOT: call {{.*}}test11_f |