// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 2
// RUN: %clang_cc1 -triple loongarch64 -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-LA64
// RUN: %clang_cc1 -triple loongarch32 -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-LA32

struct bfloat1 {
  __bf16 a;
};

// CHECK-LABEL: define dso_local bfloat @h1
// CHECK-SAME: (bfloat noundef [[A:%.*]]) #[[ATTR0:[0-9]+]] {
// CHECK-NEXT:  entry:
// CHECK-NEXT:    [[RETVAL:%.*]] = alloca [[STRUCT_BFLOAT1:%.*]], align 2
// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-NEXT:    store bfloat [[A]], ptr [[A_ADDR]], align 2
// CHECK-NEXT:    [[TMP0:%.*]] = load bfloat, ptr [[A_ADDR]], align 2
// CHECK-NEXT:    [[A1:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT1]], ptr [[RETVAL]], i32 0, i32 0
// CHECK-NEXT:    store bfloat [[TMP0]], ptr [[A1]], align 2
// CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds nuw { bfloat }, ptr [[RETVAL]], i32 0, i32 0
// CHECK-NEXT:    [[TMP2:%.*]] = load bfloat, ptr [[TMP1]], align 2
// CHECK-NEXT:    ret bfloat [[TMP2]]
//
struct bfloat1 h1(__bf16 a) {
  struct bfloat1 x;
  x.a = a;
  return x;
}

struct bfloat2 {
  __bf16 a;
  __bf16 b;
};

// CHECK-LABEL: define dso_local { bfloat, bfloat } @h2
// CHECK-SAME: (bfloat noundef [[A:%.*]], bfloat noundef [[B:%.*]]) #[[ATTR0]] {
// CHECK-NEXT:  entry:
// CHECK-NEXT:    [[RETVAL:%.*]] = alloca [[STRUCT_BFLOAT2:%.*]], align 2
// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-NEXT:    [[B_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-NEXT:    store bfloat [[A]], ptr [[A_ADDR]], align 2
// CHECK-NEXT:    store bfloat [[B]], ptr [[B_ADDR]], align 2
// CHECK-NEXT:    [[TMP0:%.*]] = load bfloat, ptr [[A_ADDR]], align 2
// CHECK-NEXT:    [[A1:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT2]], ptr [[RETVAL]], i32 0, i32 0
// CHECK-NEXT:    store bfloat [[TMP0]], ptr [[A1]], align 2
// CHECK-NEXT:    [[TMP1:%.*]] = load bfloat, ptr [[B_ADDR]], align 2
// CHECK-NEXT:    [[B2:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT2]], ptr [[RETVAL]], i32 0, i32 1
// CHECK-NEXT:    store bfloat [[TMP1]], ptr [[B2]], align 2
// CHECK-NEXT:    [[TMP2:%.*]] = getelementptr inbounds nuw { bfloat, bfloat }, ptr [[RETVAL]], i32 0, i32 0
// CHECK-NEXT:    [[TMP3:%.*]] = load bfloat, ptr [[TMP2]], align 2
// CHECK-NEXT:    [[TMP4:%.*]] = getelementptr inbounds nuw { bfloat, bfloat }, ptr [[RETVAL]], i32 0, i32 1
// CHECK-NEXT:    [[TMP5:%.*]] = load bfloat, ptr [[TMP4]], align 2
// CHECK-NEXT:    [[TMP6:%.*]] = insertvalue { bfloat, bfloat } poison, bfloat [[TMP3]], 0
// CHECK-NEXT:    [[TMP7:%.*]] = insertvalue { bfloat, bfloat } [[TMP6]], bfloat [[TMP5]], 1
// CHECK-NEXT:    ret { bfloat, bfloat } [[TMP7]]
//
struct bfloat2 h2(__bf16 a, __bf16 b) {
  struct bfloat2 x;
  x.a = a;
  x.b = b;
  return x;
}

struct bfloat3 {
  __bf16 a;
  __bf16 b;
  __bf16 c;
};

// CHECK-LA64-LABEL: define dso_local i64 @h3
// CHECK-LA64-SAME: (bfloat noundef [[A:%.*]], bfloat noundef [[B:%.*]], bfloat noundef [[C:%.*]]) #[[ATTR0]] {
// CHECK-LA64-NEXT:  entry:
// CHECK-LA64-NEXT:    [[RETVAL:%.*]] = alloca [[STRUCT_BFLOAT3:%.*]], align 2
// CHECK-LA64-NEXT:    [[A_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA64-NEXT:    [[B_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA64-NEXT:    [[C_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA64-NEXT:    [[RETVAL_COERCE:%.*]] = alloca i64, align 8
// CHECK-LA64-NEXT:    store bfloat [[A]], ptr [[A_ADDR]], align 2
// CHECK-LA64-NEXT:    store bfloat [[B]], ptr [[B_ADDR]], align 2
// CHECK-LA64-NEXT:    store bfloat [[C]], ptr [[C_ADDR]], align 2
// CHECK-LA64-NEXT:    [[TMP0:%.*]] = load bfloat, ptr [[A_ADDR]], align 2
// CHECK-LA64-NEXT:    [[A1:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT3]], ptr [[RETVAL]], i32 0, i32 0
// CHECK-LA64-NEXT:    store bfloat [[TMP0]], ptr [[A1]], align 2
// CHECK-LA64-NEXT:    [[TMP1:%.*]] = load bfloat, ptr [[B_ADDR]], align 2
// CHECK-LA64-NEXT:    [[B2:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT3]], ptr [[RETVAL]], i32 0, i32 1
// CHECK-LA64-NEXT:    store bfloat [[TMP1]], ptr [[B2]], align 2
// CHECK-LA64-NEXT:    [[TMP2:%.*]] = load bfloat, ptr [[C_ADDR]], align 2
// CHECK-LA64-NEXT:    [[C3:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT3]], ptr [[RETVAL]], i32 0, i32 2
// CHECK-LA64-NEXT:    store bfloat [[TMP2]], ptr [[C3]], align 2
// CHECK-LA64-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[RETVAL_COERCE]], ptr align 2 [[RETVAL]], i64 6, i1 false)
// CHECK-LA64-NEXT:    [[TMP3:%.*]] = load i64, ptr [[RETVAL_COERCE]], align 8
// CHECK-LA64-NEXT:    ret i64 [[TMP3]]
//
// CHECK-LA32-LABEL: define dso_local [2 x i32] @h3
// CHECK-LA32-SAME: (bfloat noundef [[A:%.*]], bfloat noundef [[B:%.*]], bfloat noundef [[C:%.*]]) #[[ATTR0]] {
// CHECK-LA32-NEXT:  entry:
// CHECK-LA32-NEXT:    [[RETVAL:%.*]] = alloca [[STRUCT_BFLOAT3:%.*]], align 2
// CHECK-LA32-NEXT:    [[A_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA32-NEXT:    [[B_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA32-NEXT:    [[C_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA32-NEXT:    [[RETVAL_COERCE:%.*]] = alloca [2 x i32], align 4
// CHECK-LA32-NEXT:    store bfloat [[A]], ptr [[A_ADDR]], align 2
// CHECK-LA32-NEXT:    store bfloat [[B]], ptr [[B_ADDR]], align 2
// CHECK-LA32-NEXT:    store bfloat [[C]], ptr [[C_ADDR]], align 2
// CHECK-LA32-NEXT:    [[TMP0:%.*]] = load bfloat, ptr [[A_ADDR]], align 2
// CHECK-LA32-NEXT:    [[A1:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT3]], ptr [[RETVAL]], i32 0, i32 0
// CHECK-LA32-NEXT:    store bfloat [[TMP0]], ptr [[A1]], align 2
// CHECK-LA32-NEXT:    [[TMP1:%.*]] = load bfloat, ptr [[B_ADDR]], align 2
// CHECK-LA32-NEXT:    [[B2:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT3]], ptr [[RETVAL]], i32 0, i32 1
// CHECK-LA32-NEXT:    store bfloat [[TMP1]], ptr [[B2]], align 2
// CHECK-LA32-NEXT:    [[TMP2:%.*]] = load bfloat, ptr [[C_ADDR]], align 2
// CHECK-LA32-NEXT:    [[C3:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT3]], ptr [[RETVAL]], i32 0, i32 2
// CHECK-LA32-NEXT:    store bfloat [[TMP2]], ptr [[C3]], align 2
// CHECK-LA32-NEXT:    call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[RETVAL_COERCE]], ptr align 2 [[RETVAL]], i32 6, i1 false)
// CHECK-LA32-NEXT:    [[TMP3:%.*]] = load [2 x i32], ptr [[RETVAL_COERCE]], align 4
// CHECK-LA32-NEXT:    ret [2 x i32] [[TMP3]]
//
struct bfloat3 h3(__bf16 a, __bf16 b, __bf16 c) {
  struct bfloat3 x;
  x.a = a;
  x.b = b;
  x.c = c;
  return x;
}

struct bfloat4 {
  __bf16 a;
  __bf16 b;
  __bf16 c;
  __bf16 d;
};

// CHECK-LA64-LABEL: define dso_local i64 @h4
// CHECK-LA64-SAME: (bfloat noundef [[A:%.*]], bfloat noundef [[B:%.*]], bfloat noundef [[C:%.*]], bfloat noundef [[D:%.*]]) #[[ATTR0]] {
// CHECK-LA64-NEXT:  entry:
// CHECK-LA64-NEXT:    [[RETVAL:%.*]] = alloca [[STRUCT_BFLOAT4:%.*]], align 2
// CHECK-LA64-NEXT:    [[A_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA64-NEXT:    [[B_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA64-NEXT:    [[C_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA64-NEXT:    [[D_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA64-NEXT:    store bfloat [[A]], ptr [[A_ADDR]], align 2
// CHECK-LA64-NEXT:    store bfloat [[B]], ptr [[B_ADDR]], align 2
// CHECK-LA64-NEXT:    store bfloat [[C]], ptr [[C_ADDR]], align 2
// CHECK-LA64-NEXT:    store bfloat [[D]], ptr [[D_ADDR]], align 2
// CHECK-LA64-NEXT:    [[TMP0:%.*]] = load bfloat, ptr [[A_ADDR]], align 2
// CHECK-LA64-NEXT:    [[A1:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT4]], ptr [[RETVAL]], i32 0, i32 0
// CHECK-LA64-NEXT:    store bfloat [[TMP0]], ptr [[A1]], align 2
// CHECK-LA64-NEXT:    [[TMP1:%.*]] = load bfloat, ptr [[B_ADDR]], align 2
// CHECK-LA64-NEXT:    [[B2:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT4]], ptr [[RETVAL]], i32 0, i32 1
// CHECK-LA64-NEXT:    store bfloat [[TMP1]], ptr [[B2]], align 2
// CHECK-LA64-NEXT:    [[TMP2:%.*]] = load bfloat, ptr [[C_ADDR]], align 2
// CHECK-LA64-NEXT:    [[C3:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT4]], ptr [[RETVAL]], i32 0, i32 2
// CHECK-LA64-NEXT:    store bfloat [[TMP2]], ptr [[C3]], align 2
// CHECK-LA64-NEXT:    [[TMP3:%.*]] = load bfloat, ptr [[D_ADDR]], align 2
// CHECK-LA64-NEXT:    [[D4:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT4]], ptr [[RETVAL]], i32 0, i32 3
// CHECK-LA64-NEXT:    store bfloat [[TMP3]], ptr [[D4]], align 2
// CHECK-LA64-NEXT:    [[TMP4:%.*]] = load i64, ptr [[RETVAL]], align 2
// CHECK-LA64-NEXT:    ret i64 [[TMP4]]
//
// CHECK-LA32-LABEL: define dso_local [2 x i32] @h4
// CHECK-LA32-SAME: (bfloat noundef [[A:%.*]], bfloat noundef [[B:%.*]], bfloat noundef [[C:%.*]], bfloat noundef [[D:%.*]]) #[[ATTR0]] {
// CHECK-LA32-NEXT:  entry:
// CHECK-LA32-NEXT:    [[RETVAL:%.*]] = alloca [[STRUCT_BFLOAT4:%.*]], align 2
// CHECK-LA32-NEXT:    [[A_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA32-NEXT:    [[B_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA32-NEXT:    [[C_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA32-NEXT:    [[D_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA32-NEXT:    store bfloat [[A]], ptr [[A_ADDR]], align 2
// CHECK-LA32-NEXT:    store bfloat [[B]], ptr [[B_ADDR]], align 2
// CHECK-LA32-NEXT:    store bfloat [[C]], ptr [[C_ADDR]], align 2
// CHECK-LA32-NEXT:    store bfloat [[D]], ptr [[D_ADDR]], align 2
// CHECK-LA32-NEXT:    [[TMP0:%.*]] = load bfloat, ptr [[A_ADDR]], align 2
// CHECK-LA32-NEXT:    [[A1:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT4]], ptr [[RETVAL]], i32 0, i32 0
// CHECK-LA32-NEXT:    store bfloat [[TMP0]], ptr [[A1]], align 2
// CHECK-LA32-NEXT:    [[TMP1:%.*]] = load bfloat, ptr [[B_ADDR]], align 2
// CHECK-LA32-NEXT:    [[B2:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT4]], ptr [[RETVAL]], i32 0, i32 1
// CHECK-LA32-NEXT:    store bfloat [[TMP1]], ptr [[B2]], align 2
// CHECK-LA32-NEXT:    [[TMP2:%.*]] = load bfloat, ptr [[C_ADDR]], align 2
// CHECK-LA32-NEXT:    [[C3:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT4]], ptr [[RETVAL]], i32 0, i32 2
// CHECK-LA32-NEXT:    store bfloat [[TMP2]], ptr [[C3]], align 2
// CHECK-LA32-NEXT:    [[TMP3:%.*]] = load bfloat, ptr [[D_ADDR]], align 2
// CHECK-LA32-NEXT:    [[D4:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT4]], ptr [[RETVAL]], i32 0, i32 3
// CHECK-LA32-NEXT:    store bfloat [[TMP3]], ptr [[D4]], align 2
// CHECK-LA32-NEXT:    [[TMP4:%.*]] = load [2 x i32], ptr [[RETVAL]], align 2
// CHECK-LA32-NEXT:    ret [2 x i32] [[TMP4]]
//
struct bfloat4 h4(__bf16 a, __bf16 b, __bf16 c, __bf16 d) {
  struct bfloat4 x;
  x.a = a;
  x.b = b;
  x.c = c;
  x.d = d;
  return x;
}

struct floatbfloat {
  float a;
  __bf16 b;
};

// CHECK-LABEL: define dso_local { float, bfloat } @fh
// CHECK-SAME: (float noundef [[A:%.*]], bfloat noundef [[B:%.*]]) #[[ATTR0]] {
// CHECK-NEXT:  entry:
// CHECK-NEXT:    [[RETVAL:%.*]] = alloca [[STRUCT_FLOATBFLOAT:%.*]], align 4
// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca float, align 4
// CHECK-NEXT:    [[B_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-NEXT:    store float [[A]], ptr [[A_ADDR]], align 4
// CHECK-NEXT:    store bfloat [[B]], ptr [[B_ADDR]], align 2
// CHECK-NEXT:    [[TMP0:%.*]] = load float, ptr [[A_ADDR]], align 4
// CHECK-NEXT:    [[A1:%.*]] = getelementptr inbounds nuw [[STRUCT_FLOATBFLOAT]], ptr [[RETVAL]], i32 0, i32 0
// CHECK-NEXT:    store float [[TMP0]], ptr [[A1]], align 4
// CHECK-NEXT:    [[TMP1:%.*]] = load bfloat, ptr [[B_ADDR]], align 2
// CHECK-NEXT:    [[B2:%.*]] = getelementptr inbounds nuw [[STRUCT_FLOATBFLOAT]], ptr [[RETVAL]], i32 0, i32 1
// CHECK-NEXT:    store bfloat [[TMP1]], ptr [[B2]], align 4
// CHECK-NEXT:    [[TMP2:%.*]] = getelementptr inbounds nuw { float, bfloat }, ptr [[RETVAL]], i32 0, i32 0
// CHECK-NEXT:    [[TMP3:%.*]] = load float, ptr [[TMP2]], align 4
// CHECK-NEXT:    [[TMP4:%.*]] = getelementptr inbounds nuw { float, bfloat }, ptr [[RETVAL]], i32 0, i32 1
// CHECK-NEXT:    [[TMP5:%.*]] = load bfloat, ptr [[TMP4]], align 4
// CHECK-NEXT:    [[TMP6:%.*]] = insertvalue { float, bfloat } poison, float [[TMP3]], 0
// CHECK-NEXT:    [[TMP7:%.*]] = insertvalue { float, bfloat } [[TMP6]], bfloat [[TMP5]], 1
// CHECK-NEXT:    ret { float, bfloat } [[TMP7]]
//
struct floatbfloat fh(float a, __bf16 b) {
  struct floatbfloat x;
  x.a = a;
  x.b = b;
  return x;
}

struct floatbfloat2 {
  float a;
  __bf16 b;
  __bf16 c;
};

// CHECK-LA64-LABEL: define dso_local i64 @fh2
// CHECK-LA64-SAME: (float noundef [[A:%.*]], bfloat noundef [[B:%.*]], bfloat noundef [[C:%.*]]) #[[ATTR0]] {
// CHECK-LA64-NEXT:  entry:
// CHECK-LA64-NEXT:    [[RETVAL:%.*]] = alloca [[STRUCT_FLOATBFLOAT2:%.*]], align 4
// CHECK-LA64-NEXT:    [[A_ADDR:%.*]] = alloca float, align 4
// CHECK-LA64-NEXT:    [[B_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA64-NEXT:    [[C_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA64-NEXT:    store float [[A]], ptr [[A_ADDR]], align 4
// CHECK-LA64-NEXT:    store bfloat [[B]], ptr [[B_ADDR]], align 2
// CHECK-LA64-NEXT:    store bfloat [[C]], ptr [[C_ADDR]], align 2
// CHECK-LA64-NEXT:    [[TMP0:%.*]] = load float, ptr [[A_ADDR]], align 4
// CHECK-LA64-NEXT:    [[A1:%.*]] = getelementptr inbounds nuw [[STRUCT_FLOATBFLOAT2]], ptr [[RETVAL]], i32 0, i32 0
// CHECK-LA64-NEXT:    store float [[TMP0]], ptr [[A1]], align 4
// CHECK-LA64-NEXT:    [[TMP1:%.*]] = load bfloat, ptr [[B_ADDR]], align 2
// CHECK-LA64-NEXT:    [[B2:%.*]] = getelementptr inbounds nuw [[STRUCT_FLOATBFLOAT2]], ptr [[RETVAL]], i32 0, i32 1
// CHECK-LA64-NEXT:    store bfloat [[TMP1]], ptr [[B2]], align 4
// CHECK-LA64-NEXT:    [[TMP2:%.*]] = load bfloat, ptr [[C_ADDR]], align 2
// CHECK-LA64-NEXT:    [[C3:%.*]] = getelementptr inbounds nuw [[STRUCT_FLOATBFLOAT2]], ptr [[RETVAL]], i32 0, i32 2
// CHECK-LA64-NEXT:    store bfloat [[TMP2]], ptr [[C3]], align 2
// CHECK-LA64-NEXT:    [[TMP3:%.*]] = load i64, ptr [[RETVAL]], align 4
// CHECK-LA64-NEXT:    ret i64 [[TMP3]]
//
// CHECK-LA32-LABEL: define dso_local [2 x i32] @fh2
// CHECK-LA32-SAME: (float noundef [[A:%.*]], bfloat noundef [[B:%.*]], bfloat noundef [[C:%.*]]) #[[ATTR0]] {
// CHECK-LA32-NEXT:  entry:
// CHECK-LA32-NEXT:    [[RETVAL:%.*]] = alloca [[STRUCT_FLOATBFLOAT2:%.*]], align 4
// CHECK-LA32-NEXT:    [[A_ADDR:%.*]] = alloca float, align 4
// CHECK-LA32-NEXT:    [[B_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA32-NEXT:    [[C_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA32-NEXT:    store float [[A]], ptr [[A_ADDR]], align 4
// CHECK-LA32-NEXT:    store bfloat [[B]], ptr [[B_ADDR]], align 2
// CHECK-LA32-NEXT:    store bfloat [[C]], ptr [[C_ADDR]], align 2
// CHECK-LA32-NEXT:    [[TMP0:%.*]] = load float, ptr [[A_ADDR]], align 4
// CHECK-LA32-NEXT:    [[A1:%.*]] = getelementptr inbounds nuw [[STRUCT_FLOATBFLOAT2]], ptr [[RETVAL]], i32 0, i32 0
// CHECK-LA32-NEXT:    store float [[TMP0]], ptr [[A1]], align 4
// CHECK-LA32-NEXT:    [[TMP1:%.*]] = load bfloat, ptr [[B_ADDR]], align 2
// CHECK-LA32-NEXT:    [[B2:%.*]] = getelementptr inbounds nuw [[STRUCT_FLOATBFLOAT2]], ptr [[RETVAL]], i32 0, i32 1
// CHECK-LA32-NEXT:    store bfloat [[TMP1]], ptr [[B2]], align 4
// CHECK-LA32-NEXT:    [[TMP2:%.*]] = load bfloat, ptr [[C_ADDR]], align 2
// CHECK-LA32-NEXT:    [[C3:%.*]] = getelementptr inbounds nuw [[STRUCT_FLOATBFLOAT2]], ptr [[RETVAL]], i32 0, i32 2
// CHECK-LA32-NEXT:    store bfloat [[TMP2]], ptr [[C3]], align 2
// CHECK-LA32-NEXT:    [[TMP3:%.*]] = load [2 x i32], ptr [[RETVAL]], align 4
// CHECK-LA32-NEXT:    ret [2 x i32] [[TMP3]]
//
struct floatbfloat2 fh2(float a, __bf16 b, __bf16 c) {
  struct floatbfloat2 x;
  x.a = a;
  x.b = b;
  x.c = c;
  return x;
}

struct bfloatfloat {
  __bf16 a;
  float b;
};

// CHECK-LABEL: define dso_local { bfloat, float } @hf
// CHECK-SAME: (bfloat noundef [[A:%.*]], float noundef [[B:%.*]]) #[[ATTR0]] {
// CHECK-NEXT:  entry:
// CHECK-NEXT:    [[RETVAL:%.*]] = alloca [[STRUCT_BFLOATFLOAT:%.*]], align 4
// CHECK-NEXT:    [[A_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-NEXT:    [[B_ADDR:%.*]] = alloca float, align 4
// CHECK-NEXT:    store bfloat [[A]], ptr [[A_ADDR]], align 2
// CHECK-NEXT:    store float [[B]], ptr [[B_ADDR]], align 4
// CHECK-NEXT:    [[TMP0:%.*]] = load bfloat, ptr [[A_ADDR]], align 2
// CHECK-NEXT:    [[A1:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOATFLOAT]], ptr [[RETVAL]], i32 0, i32 0
// CHECK-NEXT:    store bfloat [[TMP0]], ptr [[A1]], align 4
// CHECK-NEXT:    [[TMP1:%.*]] = load float, ptr [[B_ADDR]], align 4
// CHECK-NEXT:    [[B2:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOATFLOAT]], ptr [[RETVAL]], i32 0, i32 1
// CHECK-NEXT:    store float [[TMP1]], ptr [[B2]], align 4
// CHECK-NEXT:    [[TMP2:%.*]] = getelementptr inbounds nuw { bfloat, float }, ptr [[RETVAL]], i32 0, i32 0
// CHECK-NEXT:    [[TMP3:%.*]] = load bfloat, ptr [[TMP2]], align 4
// CHECK-NEXT:    [[TMP4:%.*]] = getelementptr inbounds nuw { bfloat, float }, ptr [[RETVAL]], i32 0, i32 1
// CHECK-NEXT:    [[TMP5:%.*]] = load float, ptr [[TMP4]], align 4
// CHECK-NEXT:    [[TMP6:%.*]] = insertvalue { bfloat, float } poison, bfloat [[TMP3]], 0
// CHECK-NEXT:    [[TMP7:%.*]] = insertvalue { bfloat, float } [[TMP6]], float [[TMP5]], 1
// CHECK-NEXT:    ret { bfloat, float } [[TMP7]]
//
struct bfloatfloat hf(__bf16 a, float b) {
  struct bfloatfloat x;
  x.a = a;
  x.b = b;
  return x;
}

struct bfloat2float {
  __bf16 a;
  __bf16 b;
  float c;
};

// CHECK-LA64-LABEL: define dso_local i64 @h2f
// CHECK-LA64-SAME: (bfloat noundef [[A:%.*]], bfloat noundef [[B:%.*]], float noundef [[C:%.*]]) #[[ATTR0]] {
// CHECK-LA64-NEXT:  entry:
// CHECK-LA64-NEXT:    [[RETVAL:%.*]] = alloca [[STRUCT_BFLOAT2FLOAT:%.*]], align 4
// CHECK-LA64-NEXT:    [[A_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA64-NEXT:    [[B_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA64-NEXT:    [[C_ADDR:%.*]] = alloca float, align 4
// CHECK-LA64-NEXT:    store bfloat [[A]], ptr [[A_ADDR]], align 2
// CHECK-LA64-NEXT:    store bfloat [[B]], ptr [[B_ADDR]], align 2
// CHECK-LA64-NEXT:    store float [[C]], ptr [[C_ADDR]], align 4
// CHECK-LA64-NEXT:    [[TMP0:%.*]] = load bfloat, ptr [[A_ADDR]], align 2
// CHECK-LA64-NEXT:    [[A1:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT2FLOAT]], ptr [[RETVAL]], i32 0, i32 0
// CHECK-LA64-NEXT:    store bfloat [[TMP0]], ptr [[A1]], align 4
// CHECK-LA64-NEXT:    [[TMP1:%.*]] = load bfloat, ptr [[B_ADDR]], align 2
// CHECK-LA64-NEXT:    [[B2:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT2FLOAT]], ptr [[RETVAL]], i32 0, i32 1
// CHECK-LA64-NEXT:    store bfloat [[TMP1]], ptr [[B2]], align 2
// CHECK-LA64-NEXT:    [[TMP2:%.*]] = load float, ptr [[C_ADDR]], align 4
// CHECK-LA64-NEXT:    [[C3:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT2FLOAT]], ptr [[RETVAL]], i32 0, i32 2
// CHECK-LA64-NEXT:    store float [[TMP2]], ptr [[C3]], align 4
// CHECK-LA64-NEXT:    [[TMP3:%.*]] = load i64, ptr [[RETVAL]], align 4
// CHECK-LA64-NEXT:    ret i64 [[TMP3]]
//
// CHECK-LA32-LABEL: define dso_local [2 x i32] @h2f
// CHECK-LA32-SAME: (bfloat noundef [[A:%.*]], bfloat noundef [[B:%.*]], float noundef [[C:%.*]]) #[[ATTR0]] {
// CHECK-LA32-NEXT:  entry:
// CHECK-LA32-NEXT:    [[RETVAL:%.*]] = alloca [[STRUCT_BFLOAT2FLOAT:%.*]], align 4
// CHECK-LA32-NEXT:    [[A_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA32-NEXT:    [[B_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA32-NEXT:    [[C_ADDR:%.*]] = alloca float, align 4
// CHECK-LA32-NEXT:    store bfloat [[A]], ptr [[A_ADDR]], align 2
// CHECK-LA32-NEXT:    store bfloat [[B]], ptr [[B_ADDR]], align 2
// CHECK-LA32-NEXT:    store float [[C]], ptr [[C_ADDR]], align 4
// CHECK-LA32-NEXT:    [[TMP0:%.*]] = load bfloat, ptr [[A_ADDR]], align 2
// CHECK-LA32-NEXT:    [[A1:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT2FLOAT]], ptr [[RETVAL]], i32 0, i32 0
// CHECK-LA32-NEXT:    store bfloat [[TMP0]], ptr [[A1]], align 4
// CHECK-LA32-NEXT:    [[TMP1:%.*]] = load bfloat, ptr [[B_ADDR]], align 2
// CHECK-LA32-NEXT:    [[B2:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT2FLOAT]], ptr [[RETVAL]], i32 0, i32 1
// CHECK-LA32-NEXT:    store bfloat [[TMP1]], ptr [[B2]], align 2
// CHECK-LA32-NEXT:    [[TMP2:%.*]] = load float, ptr [[C_ADDR]], align 4
// CHECK-LA32-NEXT:    [[C3:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT2FLOAT]], ptr [[RETVAL]], i32 0, i32 2
// CHECK-LA32-NEXT:    store float [[TMP2]], ptr [[C3]], align 4
// CHECK-LA32-NEXT:    [[TMP3:%.*]] = load [2 x i32], ptr [[RETVAL]], align 4
// CHECK-LA32-NEXT:    ret [2 x i32] [[TMP3]]
//
struct bfloat2float h2f(__bf16 a, __bf16 b, float c) {
  struct bfloat2float x;
  x.a = a;
  x.b = b;
  x.c = c;
  return x;
}

struct floatbfloat3 {
  float a;
  __bf16 b;
  __bf16 c;
  __bf16 d;
};

// CHECK-LA64-LABEL: define dso_local [2 x i64] @fh3
// CHECK-LA64-SAME: (float noundef [[A:%.*]], bfloat noundef [[B:%.*]], bfloat noundef [[C:%.*]], bfloat noundef [[D:%.*]]) #[[ATTR0]] {
// CHECK-LA64-NEXT:  entry:
// CHECK-LA64-NEXT:    [[RETVAL:%.*]] = alloca [[STRUCT_FLOATBFLOAT3:%.*]], align 4
// CHECK-LA64-NEXT:    [[A_ADDR:%.*]] = alloca float, align 4
// CHECK-LA64-NEXT:    [[B_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA64-NEXT:    [[C_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA64-NEXT:    [[D_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA64-NEXT:    [[RETVAL_COERCE:%.*]] = alloca [2 x i64], align 8
// CHECK-LA64-NEXT:    store float [[A]], ptr [[A_ADDR]], align 4
// CHECK-LA64-NEXT:    store bfloat [[B]], ptr [[B_ADDR]], align 2
// CHECK-LA64-NEXT:    store bfloat [[C]], ptr [[C_ADDR]], align 2
// CHECK-LA64-NEXT:    store bfloat [[D]], ptr [[D_ADDR]], align 2
// CHECK-LA64-NEXT:    [[TMP0:%.*]] = load float, ptr [[A_ADDR]], align 4
// CHECK-LA64-NEXT:    [[A1:%.*]] = getelementptr inbounds nuw [[STRUCT_FLOATBFLOAT3]], ptr [[RETVAL]], i32 0, i32 0
// CHECK-LA64-NEXT:    store float [[TMP0]], ptr [[A1]], align 4
// CHECK-LA64-NEXT:    [[TMP1:%.*]] = load bfloat, ptr [[B_ADDR]], align 2
// CHECK-LA64-NEXT:    [[B2:%.*]] = getelementptr inbounds nuw [[STRUCT_FLOATBFLOAT3]], ptr [[RETVAL]], i32 0, i32 1
// CHECK-LA64-NEXT:    store bfloat [[TMP1]], ptr [[B2]], align 4
// CHECK-LA64-NEXT:    [[TMP2:%.*]] = load bfloat, ptr [[C_ADDR]], align 2
// CHECK-LA64-NEXT:    [[C3:%.*]] = getelementptr inbounds nuw [[STRUCT_FLOATBFLOAT3]], ptr [[RETVAL]], i32 0, i32 2
// CHECK-LA64-NEXT:    store bfloat [[TMP2]], ptr [[C3]], align 2
// CHECK-LA64-NEXT:    [[TMP3:%.*]] = load bfloat, ptr [[D_ADDR]], align 2
// CHECK-LA64-NEXT:    [[D4:%.*]] = getelementptr inbounds nuw [[STRUCT_FLOATBFLOAT3]], ptr [[RETVAL]], i32 0, i32 3
// CHECK-LA64-NEXT:    store bfloat [[TMP3]], ptr [[D4]], align 4
// CHECK-LA64-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[RETVAL_COERCE]], ptr align 4 [[RETVAL]], i64 12, i1 false)
// CHECK-LA64-NEXT:    [[TMP4:%.*]] = load [2 x i64], ptr [[RETVAL_COERCE]], align 8
// CHECK-LA64-NEXT:    ret [2 x i64] [[TMP4]]
//
// CHECK-LA32-LABEL: define dso_local void @fh3
// CHECK-LA32-SAME: (ptr dead_on_unwind noalias writable sret([[STRUCT_FLOATBFLOAT3:%.*]]) align 4 [[AGG_RESULT:%.*]], float noundef [[A:%.*]], bfloat noundef [[B:%.*]], bfloat noundef [[C:%.*]], bfloat noundef [[D:%.*]]) #[[ATTR0]] {
// CHECK-LA32-NEXT:  entry:
// CHECK-LA32-NEXT:    [[RESULT_PTR:%.*]] = alloca ptr, align 4
// CHECK-LA32-NEXT:    [[A_ADDR:%.*]] = alloca float, align 4
// CHECK-LA32-NEXT:    [[B_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA32-NEXT:    [[C_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA32-NEXT:    [[D_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA32-NEXT:    store ptr [[AGG_RESULT]], ptr [[RESULT_PTR]], align 4
// CHECK-LA32-NEXT:    store float [[A]], ptr [[A_ADDR]], align 4
// CHECK-LA32-NEXT:    store bfloat [[B]], ptr [[B_ADDR]], align 2
// CHECK-LA32-NEXT:    store bfloat [[C]], ptr [[C_ADDR]], align 2
// CHECK-LA32-NEXT:    store bfloat [[D]], ptr [[D_ADDR]], align 2
// CHECK-LA32-NEXT:    [[TMP0:%.*]] = load float, ptr [[A_ADDR]], align 4
// CHECK-LA32-NEXT:    [[A1:%.*]] = getelementptr inbounds nuw [[STRUCT_FLOATBFLOAT3]], ptr [[AGG_RESULT]], i32 0, i32 0
// CHECK-LA32-NEXT:    store float [[TMP0]], ptr [[A1]], align 4
// CHECK-LA32-NEXT:    [[TMP1:%.*]] = load bfloat, ptr [[B_ADDR]], align 2
// CHECK-LA32-NEXT:    [[B2:%.*]] = getelementptr inbounds nuw [[STRUCT_FLOATBFLOAT3]], ptr [[AGG_RESULT]], i32 0, i32 1
// CHECK-LA32-NEXT:    store bfloat [[TMP1]], ptr [[B2]], align 4
// CHECK-LA32-NEXT:    [[TMP2:%.*]] = load bfloat, ptr [[C_ADDR]], align 2
// CHECK-LA32-NEXT:    [[C3:%.*]] = getelementptr inbounds nuw [[STRUCT_FLOATBFLOAT3]], ptr [[AGG_RESULT]], i32 0, i32 2
// CHECK-LA32-NEXT:    store bfloat [[TMP2]], ptr [[C3]], align 2
// CHECK-LA32-NEXT:    [[TMP3:%.*]] = load bfloat, ptr [[D_ADDR]], align 2
// CHECK-LA32-NEXT:    [[D4:%.*]] = getelementptr inbounds nuw [[STRUCT_FLOATBFLOAT3]], ptr [[AGG_RESULT]], i32 0, i32 3
// CHECK-LA32-NEXT:    store bfloat [[TMP3]], ptr [[D4]], align 4
// CHECK-LA32-NEXT:    ret void
//
struct floatbfloat3 fh3(float a, __bf16 b, __bf16 c, __bf16 d) {
  struct floatbfloat3 x;
  x.a = a;
  x.b = b;
  x.c = c;
  x.d = d;
  return x;
}

struct bfloat5 {
  __bf16 a;
  __bf16 b;
  __bf16 c;
  __bf16 d;
  __bf16 e;
};

// CHECK-LA64-LABEL: define dso_local [2 x i64] @h5
// CHECK-LA64-SAME: (bfloat noundef [[A:%.*]], bfloat noundef [[B:%.*]], bfloat noundef [[C:%.*]], bfloat noundef [[D:%.*]], bfloat noundef [[E:%.*]]) #[[ATTR0]] {
// CHECK-LA64-NEXT:  entry:
// CHECK-LA64-NEXT:    [[RETVAL:%.*]] = alloca [[STRUCT_BFLOAT5:%.*]], align 2
// CHECK-LA64-NEXT:    [[A_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA64-NEXT:    [[B_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA64-NEXT:    [[C_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA64-NEXT:    [[D_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA64-NEXT:    [[E_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA64-NEXT:    [[RETVAL_COERCE:%.*]] = alloca [2 x i64], align 8
// CHECK-LA64-NEXT:    store bfloat [[A]], ptr [[A_ADDR]], align 2
// CHECK-LA64-NEXT:    store bfloat [[B]], ptr [[B_ADDR]], align 2
// CHECK-LA64-NEXT:    store bfloat [[C]], ptr [[C_ADDR]], align 2
// CHECK-LA64-NEXT:    store bfloat [[D]], ptr [[D_ADDR]], align 2
// CHECK-LA64-NEXT:    store bfloat [[E]], ptr [[E_ADDR]], align 2
// CHECK-LA64-NEXT:    [[TMP0:%.*]] = load bfloat, ptr [[A_ADDR]], align 2
// CHECK-LA64-NEXT:    [[A1:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT5]], ptr [[RETVAL]], i32 0, i32 0
// CHECK-LA64-NEXT:    store bfloat [[TMP0]], ptr [[A1]], align 2
// CHECK-LA64-NEXT:    [[TMP1:%.*]] = load bfloat, ptr [[B_ADDR]], align 2
// CHECK-LA64-NEXT:    [[B2:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT5]], ptr [[RETVAL]], i32 0, i32 1
// CHECK-LA64-NEXT:    store bfloat [[TMP1]], ptr [[B2]], align 2
// CHECK-LA64-NEXT:    [[TMP2:%.*]] = load bfloat, ptr [[C_ADDR]], align 2
// CHECK-LA64-NEXT:    [[C3:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT5]], ptr [[RETVAL]], i32 0, i32 2
// CHECK-LA64-NEXT:    store bfloat [[TMP2]], ptr [[C3]], align 2
// CHECK-LA64-NEXT:    [[TMP3:%.*]] = load bfloat, ptr [[D_ADDR]], align 2
// CHECK-LA64-NEXT:    [[D4:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT5]], ptr [[RETVAL]], i32 0, i32 3
// CHECK-LA64-NEXT:    store bfloat [[TMP3]], ptr [[D4]], align 2
// CHECK-LA64-NEXT:    [[TMP4:%.*]] = load bfloat, ptr [[E_ADDR]], align 2
// CHECK-LA64-NEXT:    [[E5:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT5]], ptr [[RETVAL]], i32 0, i32 4
// CHECK-LA64-NEXT:    store bfloat [[TMP4]], ptr [[E5]], align 2
// CHECK-LA64-NEXT:    call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[RETVAL_COERCE]], ptr align 2 [[RETVAL]], i64 10, i1 false)
// CHECK-LA64-NEXT:    [[TMP5:%.*]] = load [2 x i64], ptr [[RETVAL_COERCE]], align 8
// CHECK-LA64-NEXT:    ret [2 x i64] [[TMP5]]
//
// CHECK-LA32-LABEL: define dso_local void @h5
// CHECK-LA32-SAME: (ptr dead_on_unwind noalias writable sret([[STRUCT_BFLOAT5:%.*]]) align 2 [[AGG_RESULT:%.*]], bfloat noundef [[A:%.*]], bfloat noundef [[B:%.*]], bfloat noundef [[C:%.*]], bfloat noundef [[D:%.*]], bfloat noundef [[E:%.*]]) #[[ATTR0]] {
// CHECK-LA32-NEXT:  entry:
// CHECK-LA32-NEXT:    [[RESULT_PTR:%.*]] = alloca ptr, align 4
// CHECK-LA32-NEXT:    [[A_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA32-NEXT:    [[B_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA32-NEXT:    [[C_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA32-NEXT:    [[D_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA32-NEXT:    [[E_ADDR:%.*]] = alloca bfloat, align 2
// CHECK-LA32-NEXT:    store ptr [[AGG_RESULT]], ptr [[RESULT_PTR]], align 4
// CHECK-LA32-NEXT:    store bfloat [[A]], ptr [[A_ADDR]], align 2
// CHECK-LA32-NEXT:    store bfloat [[B]], ptr [[B_ADDR]], align 2
// CHECK-LA32-NEXT:    store bfloat [[C]], ptr [[C_ADDR]], align 2
// CHECK-LA32-NEXT:    store bfloat [[D]], ptr [[D_ADDR]], align 2
// CHECK-LA32-NEXT:    store bfloat [[E]], ptr [[E_ADDR]], align 2
// CHECK-LA32-NEXT:    [[TMP0:%.*]] = load bfloat, ptr [[A_ADDR]], align 2
// CHECK-LA32-NEXT:    [[A1:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT5]], ptr [[AGG_RESULT]], i32 0, i32 0
// CHECK-LA32-NEXT:    store bfloat [[TMP0]], ptr [[A1]], align 2
// CHECK-LA32-NEXT:    [[TMP1:%.*]] = load bfloat, ptr [[B_ADDR]], align 2
// CHECK-LA32-NEXT:    [[B2:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT5]], ptr [[AGG_RESULT]], i32 0, i32 1
// CHECK-LA32-NEXT:    store bfloat [[TMP1]], ptr [[B2]], align 2
// CHECK-LA32-NEXT:    [[TMP2:%.*]] = load bfloat, ptr [[C_ADDR]], align 2
// CHECK-LA32-NEXT:    [[C3:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT5]], ptr [[AGG_RESULT]], i32 0, i32 2
// CHECK-LA32-NEXT:    store bfloat [[TMP2]], ptr [[C3]], align 2
// CHECK-LA32-NEXT:    [[TMP3:%.*]] = load bfloat, ptr [[D_ADDR]], align 2
// CHECK-LA32-NEXT:    [[D4:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT5]], ptr [[AGG_RESULT]], i32 0, i32 3
// CHECK-LA32-NEXT:    store bfloat [[TMP3]], ptr [[D4]], align 2
// CHECK-LA32-NEXT:    [[TMP4:%.*]] = load bfloat, ptr [[E_ADDR]], align 2
// CHECK-LA32-NEXT:    [[E5:%.*]] = getelementptr inbounds nuw [[STRUCT_BFLOAT5]], ptr [[AGG_RESULT]], i32 0, i32 4
// CHECK-LA32-NEXT:    store bfloat [[TMP4]], ptr [[E5]], align 2
// CHECK-LA32-NEXT:    ret void
//
struct bfloat5 h5(__bf16 a, __bf16 b, __bf16 c, __bf16 d, __bf16 e) {
  struct bfloat5 x;
  x.a = a;
  x.b = b;
  x.c = c;
  x.d = d;
  x.e = e;
  return x;
}
