// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -disable-llvm-optzns -o - %s -O2 | FileCheck %s
// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -disable-llvm-optzns -o - %s -O0 | FileCheck %s

int a = 42;

/* --- Compound literals */

struct foo { int x, y; };

int y;
struct foo f = (struct foo){ __builtin_constant_p(y), 42 };

struct foo test0(int expr) {
  // CHECK-LABEL: test0
  // CHECK: call i1 @llvm.is.constant.i32
  struct foo f = (struct foo){ __builtin_constant_p(expr), 42 };
  return f;
}

/* --- Pointer types */

int test1() {
  // CHECK-LABEL: test1
  // CHECK: ret i32 0
  return __builtin_constant_p(&a - 13);
}

/* --- Aggregate types */

int b[] = {1, 2, 3};

int test2() {
  // CHECK-LABEL: test2
  // CHECK: ret i32 0
  return __builtin_constant_p(b);
}

const char test3_c[] = {1, 2, 3, 0};

int test3() {
  // CHECK-LABEL: test3
  // CHECK: ret i32 0
  return __builtin_constant_p(test3_c);
}

inline char test4_i(const char *x) {
  return x[1];
}

int test4() {
  // CHECK: define i32 @test4
  // CHECK: ret i32 0
  return __builtin_constant_p(test4_i(test3_c));
}

/* --- Constant global variables */

const int c = 42;

int test5() {
  // CHECK-LABEL: test5
  // CHECK: ret i32 1
  return __builtin_constant_p(c);
}

/* --- Array types */

int arr[] = { 1, 2, 3 };
const int c_arr[] = { 1, 2, 3 };

int test6() {
  // CHECK-LABEL: test6
  // CHECK: call i1 @llvm.is.constant.i32
  return __builtin_constant_p(arr[2]);
}

int test7() {
  // CHECK-LABEL: test7
  // CHECK: call i1 @llvm.is.constant.i32
  return __builtin_constant_p(c_arr[2]);
}

int test8() {
  // CHECK-LABEL: test8
  // CHECK: ret i32 0
  return __builtin_constant_p(c_arr);
}

/* --- Function pointers */

int test9() {
  // CHECK-LABEL: test9
  // CHECK: ret i32 0
  return __builtin_constant_p(&test9);
}

int test10() {
  // CHECK-LABEL: test10
  // CHECK: ret i32 1
  return __builtin_constant_p(&test10 != 0);
}

typedef unsigned long uintptr_t;
#define assign(p, v) ({ \
  uintptr_t _r_a_p__v = (uintptr_t)(v);                           \
  if (__builtin_constant_p(v) && _r_a_p__v == (uintptr_t)0) {     \
    union {                                                       \
      uintptr_t __val;                                            \
      char __c[1];                                                \
    } __u = {                                                     \
      .__val = (uintptr_t)_r_a_p__v                               \
    };                                                            \
    *(volatile unsigned int*)&p = *(unsigned int*)(__u.__c);      \
    __u.__val;                                                    \
  }                                                               \
  _r_a_p__v;                                                      \
})

typedef void fn_p(void);
extern fn_p *dest_p;

static void src_fn(void) {
}

void test11() {
  assign(dest_p, src_fn);
}

extern int test12_v;

struct { const char *t; int a; } test12[] = {
    { "tag", __builtin_constant_p(test12_v) && !test12_v ? 1 : 0 }
};

extern char test13_v;
struct { int a; } test13 = { __builtin_constant_p(test13_v) };

extern unsigned long long test14_v;

void test14() {
  // CHECK-LABEL: test14
  // CHECK: call void asm sideeffect "", {{.*}}(i32 -1) 
  __asm__ __volatile__("" :: "n"( (__builtin_constant_p(test14_v) || 0) ? 1 : -1));
}

int test15_f();
// CHECK-LABEL: define void @test15
// CHECK-NOT: call {{.*}}test15_f
void test15() {
  int a, b;
  (void)__builtin_constant_p((a = b, test15_f()));
}
