// 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 // 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 // LLVM: %union.U2 = type { double } // OGCG: %union.U2 = type { double } union U3 { char c[5]; int i; } __attribute__((packed)); // CIR: !rec_U3 = !cir.record, !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, !s32i, !cir.array}> // 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 : !cir.ptr // 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, !cir.ptr>, ["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, ["__retval"] {alignment = 4 : i64} // CIR-NEXT: %[[U:.*]] = cir.alloca !rec_U1, !cir.ptr, ["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 -> !cir.ptr // CIR-NEXT: cir.store{{.*}} %[[I]], %[[N]] : !s32i, !cir.ptr // CIR-NEXT: %[[N2:.*]] = cir.get_member %[[U]][0] {name = "n"} : !cir.ptr -> !cir.ptr // CIR-NEXT: %[[VAL:.*]] = cir.load{{.*}} %[[N2]] : !cir.ptr, !s32i // CIR-NEXT: cir.store{{.*}} %[[VAL]], %[[RETVAL_ADDR]] : !s32i, !cir.ptr // CIR-NEXT: %[[RET:.*]] = cir.load{{.*}} %[[RETVAL_ADDR]] : !cir.ptr, !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, ["u", init] {alignment = 8 : i64} // CIR-NEXT: cir.store{{.*}} %[[ARG]], %[[U]] : !rec_U2, !cir.ptr // CIR-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i // CIR-NEXT: %[[ZERO_CHAR:.*]] = cir.cast(integral, %[[ZERO]] : !s32i), !s8i // CIR-NEXT: %[[B_PTR:.*]] = cir.get_member %[[U]][0] {name = "b"} : !cir.ptr -> !cir.ptr // CIR-NEXT: cir.store{{.*}} %[[ZERO_CHAR]], %[[B_PTR]] : !s8i, !cir.ptr // CIR-NEXT: %[[B_PTR2:.*]] = cir.get_member %[[U]][0] {name = "b"} : !cir.ptr -> !cir.ptr // CIR-NEXT: %[[B_VAL:.*]] = cir.load{{.*}} %[[B_PTR2]] : !cir.ptr, !s8i // CIR-NEXT: %[[ONE:.*]] = cir.const #cir.int<1> : !s32i // CIR-NEXT: %[[I_PTR:.*]] = cir.get_member %[[U]][2] {name = "i"} : !cir.ptr -> !cir.ptr // CIR-NEXT: cir.store{{.*}} %[[ONE]], %[[I_PTR]] : !s32i, !cir.ptr // CIR-NEXT: %[[I_PTR2:.*]] = cir.get_member %[[U]][2] {name = "i"} : !cir.ptr -> !cir.ptr // CIR-NEXT: %[[I_VAL:.*]] = cir.load{{.*}} %[[I_PTR2]] : !cir.ptr, !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 -> !cir.ptr // CIR-NEXT: cir.store{{.*}} %[[FLOAT_VAL]], %[[F_PTR]] : !cir.float, !cir.ptr // CIR-NEXT: %[[F_PTR2:.*]] = cir.get_member %[[U]][3] {name = "f"} : !cir.ptr -> !cir.ptr // CIR-NEXT: %[[F_VAL:.*]] = cir.load{{.*}} %[[F_PTR2]] : !cir.ptr, !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 -> !cir.ptr // CIR-NEXT: cir.store{{.*}} %[[DOUBLE_VAL]], %[[D_PTR]] : !cir.double, !cir.ptr // CIR-NEXT: %[[D_PTR2:.*]] = cir.get_member %[[U]][4] {name = "d"} : !cir.ptr -> !cir.ptr // CIR-NEXT: %[[D_VAL:.*]] = cir.load{{.*}} %[[D_PTR2]] : !cir.ptr, !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, ["u", init] {alignment = 1 : i64} // CIR-NEXT: cir.store{{.*}} %[[ARG]], %[[U]] : !rec_U3, !cir.ptr // CIR-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i // CIR-NEXT: %[[ZERO_CHAR:.*]] = cir.cast(integral, %[[ZERO]] : !s32i), !s8i // CIR-NEXT: %[[IDX:.*]] = cir.const #cir.int<2> : !s32i // CIR-NEXT: %[[C_PTR:.*]] = cir.get_member %[[U]][0] {name = "c"} : !cir.ptr -> !cir.ptr> // CIR-NEXT: %[[C_DECAY:.*]] = cir.cast(array_to_ptrdecay, %[[C_PTR]] : !cir.ptr>), !cir.ptr // CIR-NEXT: %[[ELEM_PTR:.*]] = cir.ptr_stride(%[[C_DECAY]] : !cir.ptr, %[[IDX]] : !s32i), !cir.ptr // CIR-NEXT: cir.store{{.*}} %[[ZERO_CHAR]], %[[ELEM_PTR]] : !s8i, !cir.ptr // 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: %[[C_PTR:.*]] = getelementptr i8, ptr %[[U]], i32 0 // LLVM-NEXT: %[[ELEM_PTR:.*]] = getelementptr i8, ptr %[[C_PTR]], 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, ["u", init] {alignment = 4 : i64} // CIR-NEXT: cir.store{{.*}} %[[ARG]], %[[U]] : !rec_U4, !cir.ptr // CIR-NEXT: %[[CHAR_VAL:.*]] = cir.const #cir.int<65> : !s32i // CIR-NEXT: %[[CHAR_CAST:.*]] = cir.cast(integral, %[[CHAR_VAL]] : !s32i), !s8i // CIR-NEXT: %[[IDX:.*]] = cir.const #cir.int<4> : !s32i // CIR-NEXT: %[[C_PTR:.*]] = cir.get_member %[[U]][0] {name = "c"} : !cir.ptr -> !cir.ptr> // CIR-NEXT: %[[C_DECAY:.*]] = cir.cast(array_to_ptrdecay, %[[C_PTR]] : !cir.ptr>), !cir.ptr // CIR-NEXT: %[[ELEM_PTR:.*]] = cir.ptr_stride(%[[C_DECAY]] : !cir.ptr, %[[IDX]] : !s32i), !cir.ptr // CIR-NEXT: cir.store{{.*}} %[[CHAR_CAST]], %[[ELEM_PTR]] : !s8i, !cir.ptr // 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: %[[C_PTR:.*]] = getelementptr i8, ptr %[[U]], i32 0 // LLVM-NEXT: %[[ELEM_PTR:.*]] = getelementptr i8, ptr %[[C_PTR]], 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