// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s

union U1 {
  int n;
  char c;
};

// CIR:  !rec_U1 = !cir.record<union "U1" {!s32i, !s8i}>
// LLVM: %union.U1 = type { i32 }
// OGCG: %union.U1 = type { i32 }

union U2 {
  char b;
  short s;
  int i;
  float f;
  double d;
};

// CIR:  !rec_U2 = !cir.record<union "U2" {!s8i, !s16i, !s32i, !cir.float, !cir.double}>
// LLVM: %union.U2 = type { double }
// OGCG: %union.U2 = type { double }

union U3 {
  char c[5];
  int i;
} __attribute__((packed));

// CIR:  !rec_U3 = !cir.record<union "U3" packed padded {!cir.array<!s8i x 5>, !s32i, !u8i}>
// LLVM: %union.U3 = type <{ i32, i8 }>
// OGCG: %union.U3 = type <{ i32, i8 }>

union U4 {
  char c[5];
  int i;
};

// CIR:  !rec_U4 = !cir.record<union "U4" padded {!cir.array<!s8i x 5>, !s32i, !cir.array<!u8i x 4>}>
// LLVM: %union.U4 = type { i32, [4 x i8] }
// OGCG: %union.U4 = type { i32, [4 x i8] }

union IncompleteU *p;

// CIR:  cir.global external @p = #cir.ptr<null> : !cir.ptr<!rec_IncompleteU>
// LLVM: @p = global ptr null
// OGCG: @p = global ptr null, align 8

void f1(void) {
  union IncompleteU *p;
}

// CIR:      cir.func{{.*}} @f1()
// CIR-NEXT:   cir.alloca !cir.ptr<!rec_IncompleteU>, !cir.ptr<!cir.ptr<!rec_IncompleteU>>, ["p"]
// CIR-NEXT:   cir.return

// LLVM:      define{{.*}} void @f1()
// LLVM-NEXT:   %[[P:.*]] = alloca ptr, i64 1, align 8
// LLVM-NEXT:   ret void

// OGCG:      define{{.*}} void @f1()
// OGCG-NEXT: entry:
// OGCG-NEXT:   %[[P:.*]] = alloca ptr, align 8
// OGCG-NEXT:   ret void

int f2(void) {
  union U1 u;
  u.n = 42;
  return u.n;
}

// CIR:      cir.func{{.*}} @f2() -> !s32i
// CIR-NEXT:   %[[RETVAL_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__retval"] {alignment = 4 : i64}
// CIR-NEXT:   %[[U:.*]] = cir.alloca !rec_U1, !cir.ptr<!rec_U1>, ["u"] {alignment = 4 : i64}
// CIR-NEXT:   %[[I:.*]] = cir.const #cir.int<42> : !s32i
// CIR-NEXT:   %[[N:.*]] = cir.get_member %[[U]][0] {name = "n"} : !cir.ptr<!rec_U1> -> !cir.ptr<!s32i>
// CIR-NEXT:   cir.store{{.*}} %[[I]], %[[N]] : !s32i, !cir.ptr<!s32i>
// CIR-NEXT:   %[[N2:.*]] = cir.get_member %[[U]][0] {name = "n"} : !cir.ptr<!rec_U1> -> !cir.ptr<!s32i>
// CIR-NEXT:   %[[VAL:.*]] = cir.load{{.*}} %[[N2]] : !cir.ptr<!s32i>, !s32i
// CIR-NEXT:   cir.store{{.*}} %[[VAL]], %[[RETVAL_ADDR]] : !s32i, !cir.ptr<!s32i>
// CIR-NEXT:   %[[RET:.*]] = cir.load{{.*}} %[[RETVAL_ADDR]] : !cir.ptr<!s32i>, !s32i
// CIR-NEXT:   cir.return %[[RET]] : !s32i

// LLVM:      define{{.*}} i32 @f2()
// LLVM-NEXT:   %[[RETVAL:.*]] = alloca i32, i64 1, align 4
// LLVM-NEXT:   %[[U:.*]] = alloca %union.U1, i64 1, align 4
// LLVM-NEXT:   store i32 42, ptr %[[U]], align 4
// LLVM-NEXT:   %[[N_VAL:.*]] = load i32, ptr %[[U]], align 4
// LLVM-NEXT:   store i32 %[[N_VAL]], ptr %[[RETVAL]], align 4
// LLVM-NEXT:   %[[RET:.*]] = load i32, ptr %[[RETVAL]], align 4
// LLVM-NEXT:   ret i32 %[[RET]]

//      OGCG: define{{.*}} i32 @f2()
// OGCG-NEXT: entry:
// OGCG-NEXT: %[[U:.*]] = alloca %union.U1, align 4
// OGCG-NEXT: store i32 42, ptr %[[U]], align 4
// OGCG-NEXT: %[[N_VAL:.*]] = load i32, ptr %[[U]], align 4
// OGCG-NEXT: ret i32 %[[N_VAL]]

void shouldGenerateUnionAccess(union U2 u) {
  u.b = 0;
  u.b;
  u.i = 1;
  u.i;
  u.f = 0.1F;
  u.f;
  u.d = 0.1;
  u.d;
}

// CIR:      cir.func{{.*}} @shouldGenerateUnionAccess(%[[ARG:.*]]: !rec_U2
// CIR-NEXT:   %[[U:.*]] = cir.alloca !rec_U2, !cir.ptr<!rec_U2>, ["u", init] {alignment = 8 : i64}
// CIR-NEXT:   cir.store{{.*}} %[[ARG]], %[[U]] : !rec_U2, !cir.ptr<!rec_U2>
// CIR-NEXT:   %[[ZERO:.*]] = cir.const #cir.int<0> : !s8i
// CIR-NEXT:   %[[B_PTR:.*]] = cir.get_member %[[U]][0] {name = "b"} : !cir.ptr<!rec_U2> -> !cir.ptr<!s8i>
// CIR-NEXT:   cir.store{{.*}} %[[ZERO]], %[[B_PTR]] : !s8i, !cir.ptr<!s8i>
// CIR-NEXT:   %[[B_PTR2:.*]] = cir.get_member %[[U]][0] {name = "b"} : !cir.ptr<!rec_U2> -> !cir.ptr<!s8i>
// CIR-NEXT:   %[[B_VAL:.*]] = cir.load{{.*}} %[[B_PTR2]] : !cir.ptr<!s8i>, !s8i
// CIR-NEXT:   %[[ONE:.*]] = cir.const #cir.int<1> : !s32i
// CIR-NEXT:   %[[I_PTR:.*]] = cir.get_member %[[U]][2] {name = "i"} : !cir.ptr<!rec_U2> -> !cir.ptr<!s32i>
// CIR-NEXT:   cir.store{{.*}} %[[ONE]], %[[I_PTR]] : !s32i, !cir.ptr<!s32i>
// CIR-NEXT:   %[[I_PTR2:.*]] = cir.get_member %[[U]][2] {name = "i"} : !cir.ptr<!rec_U2> -> !cir.ptr<!s32i>
// CIR-NEXT:   %[[I_VAL:.*]] = cir.load{{.*}} %[[I_PTR2]] : !cir.ptr<!s32i>, !s32i
// CIR-NEXT:   %[[FLOAT_VAL:.*]] = cir.const #cir.fp<1.000000e-01> : !cir.float
// CIR-NEXT:   %[[F_PTR:.*]] = cir.get_member %[[U]][3] {name = "f"} : !cir.ptr<!rec_U2> -> !cir.ptr<!cir.float>
// CIR-NEXT:   cir.store{{.*}} %[[FLOAT_VAL]], %[[F_PTR]] : !cir.float, !cir.ptr<!cir.float>
// CIR-NEXT:   %[[F_PTR2:.*]] = cir.get_member %[[U]][3] {name = "f"} : !cir.ptr<!rec_U2> -> !cir.ptr<!cir.float>
// CIR-NEXT:   %[[F_VAL:.*]] = cir.load{{.*}} %[[F_PTR2]] : !cir.ptr<!cir.float>, !cir.float
// CIR-NEXT:   %[[DOUBLE_VAL:.*]] = cir.const #cir.fp<1.000000e-01> : !cir.double
// CIR-NEXT:   %[[D_PTR:.*]] = cir.get_member %[[U]][4] {name = "d"} : !cir.ptr<!rec_U2> -> !cir.ptr<!cir.double>
// CIR-NEXT:   cir.store{{.*}} %[[DOUBLE_VAL]], %[[D_PTR]] : !cir.double, !cir.ptr<!cir.double>
// CIR-NEXT:   %[[D_PTR2:.*]] = cir.get_member %[[U]][4] {name = "d"} : !cir.ptr<!rec_U2> -> !cir.ptr<!cir.double>
// CIR-NEXT:   %[[D_VAL:.*]] = cir.load{{.*}} %[[D_PTR2]] : !cir.ptr<!cir.double>, !cir.double
// CIR-NEXT:   cir.return

// LLVM:      define{{.*}} void @shouldGenerateUnionAccess(%union.U2 %[[ARG:.*]])
// LLVM-NEXT:   %[[U:.*]] = alloca %union.U2, i64 1, align 8
// LLVM-NEXT:   store %union.U2 %[[ARG]], ptr %[[U]], align 8
// LLVM-NEXT:   store i8 0, ptr %[[U]], align 8
// LLVM-NEXT:   %[[B_VAL:.*]] = load i8, ptr %[[U]], align 8
// LLVM-NEXT:   store i32 1, ptr %[[U]], align 8
// LLVM-NEXT:   %[[I_VAL:.*]] = load i32, ptr %[[U]], align 8
// LLVM-NEXT:   store float 0x3FB99999A0000000, ptr %[[U]], align 8
// LLVM-NEXT:   %[[F_VAL:.*]] = load float, ptr %[[U]], align 8
// LLVM-NEXT:   store double 1.000000e-01, ptr %[[U]], align 8
// LLVM-NEXT:   %[[D_VAL:.*]] = load double, ptr %[[U]], align 8
// LLVM-NEXT:   ret void

// OGCG:      define{{.*}} void @shouldGenerateUnionAccess(i64 %[[ARG:.*]])
// OGCG-NEXT: entry:
// OGCG-NEXT:   %[[U:.*]] = alloca %union.U2, align 8
// OGCG-NEXT:   %[[COERCE_DIVE:.*]] = getelementptr inbounds nuw %union.U2, ptr %[[U]], i32 0, i32 0
// OGCG-NEXT:   store i64 %[[ARG]], ptr %[[COERCE_DIVE]], align 8
// OGCG-NEXT:   store i8 0, ptr %[[U]], align 8
// OGCG-NEXT:   %[[B_VAL:.*]] = load i8, ptr %[[U]], align 8
// OGCG-NEXT:   store i32 1, ptr %[[U]], align 8
// OGCG-NEXT:   %[[I_VAL:.*]] = load i32, ptr %[[U]], align 8
// OGCG-NEXT:   store float 0x3FB99999A0000000, ptr %[[U]], align 8
// OGCG-NEXT:   %[[F_VAL:.*]] = load float, ptr %[[U]], align 8
// OGCG-NEXT:   store double 1.000000e-01, ptr %[[U]], align 8
// OGCG-NEXT:   %[[D_VAL:.*]] = load double, ptr %[[U]], align 8
// OGCG-NEXT:   ret void

void f3(union U3 u) {
  u.c[2] = 0;
}

// CIR:      cir.func{{.*}} @f3(%[[ARG:.*]]: !rec_U3
// CIR-NEXT:   %[[U:.*]] = cir.alloca !rec_U3, !cir.ptr<!rec_U3>, ["u", init] {alignment = 1 : i64}
// CIR-NEXT:   cir.store{{.*}} %[[ARG]], %[[U]] : !rec_U3, !cir.ptr<!rec_U3>
// CIR-NEXT:   %[[ZERO:.*]] = cir.const #cir.int<0> : !s8i
// CIR-NEXT:   %[[IDX:.*]] = cir.const #cir.int<2> : !s32i
// CIR-NEXT:   %[[C_PTR:.*]] = cir.get_member %[[U]][0] {name = "c"} : !cir.ptr<!rec_U3> -> !cir.ptr<!cir.array<!s8i x 5>>
// CIR-NEXT:   %[[ELEM_PTR:.*]] = cir.get_element %[[C_PTR]][%[[IDX]] : !s32i] : !cir.ptr<!cir.array<!s8i x 5>> -> !cir.ptr<!s8i>
// CIR-NEXT:   cir.store{{.*}} %[[ZERO]], %[[ELEM_PTR]] : !s8i, !cir.ptr<!s8i>
// CIR-NEXT:   cir.return

// LLVM:      define{{.*}} void @f3(%union.U3 %[[ARG:.*]])
// LLVM-NEXT:   %[[U:.*]] = alloca %union.U3, i64 1, align 1
// LLVM-NEXT:   store %union.U3 %[[ARG]], ptr %[[U]], align 1
// LLVM-NEXT:   %[[ELEM_PTR:.*]] = getelementptr [5 x i8], ptr %[[U]], i32 0, i64 2
// LLVM-NEXT:   store i8 0, ptr %[[ELEM_PTR]], align 1
// LLVM-NEXT:   ret void

// OGCG:      define{{.*}} void @f3(i40 %[[ARG:.*]])
// OGCG-NEXT: entry:
// OGCG-NEXT:   %[[U:.*]] = alloca %union.U3, align 1
// OGCG-NEXT:   store i40 %[[ARG]], ptr %[[U]], align 1
// OGCG-NEXT:   %[[ARRAYIDX:.*]] = getelementptr inbounds [5 x i8], ptr %[[U]], i64 0, i64 2
// OGCG-NEXT:   store i8 0, ptr %[[ARRAYIDX]], align 1
// OGCG-NEXT:   ret void

void f5(union U4 u) {
  u.c[4] = 65;
}

// CIR:      cir.func{{.*}} @f5(%[[ARG:.*]]: !rec_U4
// CIR-NEXT:   %[[U:.*]] = cir.alloca !rec_U4, !cir.ptr<!rec_U4>, ["u", init] {alignment = 4 : i64}
// CIR-NEXT:   cir.store{{.*}} %[[ARG]], %[[U]] : !rec_U4, !cir.ptr<!rec_U4>
// CIR-NEXT:   %[[CHAR_VAL:.*]] = cir.const #cir.int<65> : !s8i
// CIR-NEXT:   %[[IDX:.*]] = cir.const #cir.int<4> : !s32i
// CIR-NEXT:   %[[C_PTR:.*]] = cir.get_member %[[U]][0] {name = "c"} : !cir.ptr<!rec_U4> -> !cir.ptr<!cir.array<!s8i x 5>>
// CIR-NEXT:   %[[ELEM_PTR:.*]] = cir.get_element %[[C_PTR]][%[[IDX]] : !s32i] : !cir.ptr<!cir.array<!s8i x 5>> -> !cir.ptr<!s8i>
// CIR-NEXT:   cir.store{{.*}} %[[CHAR_VAL]], %[[ELEM_PTR]] : !s8i, !cir.ptr<!s8i>
// CIR-NEXT:   cir.return

// LLVM:      define{{.*}} void @f5(%union.U4 %[[ARG:.*]])
// LLVM-NEXT:   %[[U:.*]] = alloca %union.U4, i64 1, align 4
// LLVM-NEXT:   store %union.U4 %[[ARG]], ptr %[[U]], align 4
// LLVM-NEXT:   %[[ELEM_PTR:.*]] = getelementptr [5 x i8], ptr %[[U]], i32 0, i64 4
// LLVM-NEXT:   store i8 65, ptr %[[ELEM_PTR]], align 4
// LLVM-NEXT:   ret void

// OGCG:      define{{.*}} void @f5(i64 %[[ARG:.*]])
// OGCG-NEXT: entry:
// OGCG-NEXT:   %[[U:.*]] = alloca %union.U4, align 4
// OGCG-NEXT:   store i64 %[[ARG]], ptr %[[U]], align 4
// OGCG-NEXT:   %[[ARRAYIDX:.*]] = getelementptr inbounds [5 x i8], ptr %[[U]], i64 0, i64 4
// OGCG-NEXT:   store i8 65, ptr %[[ARRAYIDX]], align 4
// OGCG-NEXT:   ret void
