/* { dg-require-effective-target int32plus } */

#ifdef __UINT32_TYPE__
typedef __UINT32_TYPE__ uint32_t;
#else
typedef __UINT32_TYPE__ unsigned;
#endif

struct bitfield {
    unsigned char f0 : 7;
    unsigned char : 1;
    unsigned char f1 : 7;
    unsigned char : 1;
    unsigned char f2 : 7;
    unsigned char : 1;
    unsigned char f3 : 7;
};

struct ok {
    unsigned char f0;
    unsigned char f1;
    unsigned char f2;
    unsigned char f3;
};

union bf_or_uint32 {
    struct ok inval;
    struct bitfield bfval;
};

__attribute__((noinline, noclone)) uint32_t
partial_read_le32(union bf_or_uint32 in) {
    return in.bfval.f0 | (in.bfval.f1 << 8) | (in.bfval.f2 << 16) | (in.bfval.f3 << 24);
}

__attribute__((noinline, noclone)) uint32_t
partial_read_be32(union bf_or_uint32 in) {
    return in.bfval.f3 | (in.bfval.f2 << 8) | (in.bfval.f1 << 16) | (in.bfval.f0 << 24);
}

__attribute__((noinline, noclone)) uint32_t
fake_read_le32(char *x, char *y) {
    unsigned char c0, c1, c2, c3;

    c0 = x[0];
    c1 = x[1];
    *y = 1;
    c2 = x[2];
    c3 = x[3];
    return c0 | c1 << 8 | c2 << 16 | c3 << 24;
}

__attribute__((noinline, noclone)) uint32_t
fake_read_be32(char *x, char *y) {
    unsigned char c0, c1, c2, c3;

    c0 = x[0];
    c1 = x[1];
    *y = 1;
    c2 = x[2];
    c3 = x[3];
    return c3 | c2 << 8 | c1 << 16 | c0 << 24;
}

__attribute__((noinline, noclone)) uint32_t
incorrect_read_le32(char *x, char *y) {
    unsigned char c0, c1, c2, c3;

    c0 = x[0];
    c1 = x[1];
    c2 = x[2];
    c3 = x[3];
    *y = 1;
    return c0 | c1 << 8 | c2 << 16 | c3 << 24;
}

__attribute__((noinline, noclone)) uint32_t
incorrect_read_be32(char *x, char *y) {
    unsigned char c0, c1, c2, c3;

    c0 = x[0];
    c1 = x[1];
    c2 = x[2];
    c3 = x[3];
    *y = 1;
    return c3 | c2 << 8 | c1 << 16 | c0 << 24;
}

int main() {
    union bf_or_uint32 bfin;
    uint32_t out;
    char cin[] = {0x83, 0x85, 0x87, 0x89};

    if (sizeof(uint32_t) * __CHAR_BIT__ != 32)
        return 0;
    bfin.inval = (struct ok){0x83, 0x85, 0x87, 0x89};
    out = partial_read_le32(bfin);
    /* Test what bswap would do if its check are not strict enough instead of
     what is the expected result as there is too many possible results with
     bitfields.  */
    if (out == 0x89878583)
        __builtin_abort();
    bfin.inval = (struct ok){0x83, 0x85, 0x87, 0x89};
    out = partial_read_be32(bfin);
    /* Test what bswap would do if its check are not strict enough instead of
     what is the expected result as there is too many possible results with
     bitfields.  */
    if (out == 0x83858789)
        __builtin_abort();
    out = fake_read_le32(cin, &cin[2]);
    if (out != 0x89018583)
        __builtin_abort();
    cin[2] = 0x87;
    out = fake_read_be32(cin, &cin[2]);
    if (out != 0x83850189)
        __builtin_abort();
    cin[2] = 0x87;
    out = incorrect_read_le32(cin, &cin[2]);
    if (out != 0x89878583)
        __builtin_abort();
    cin[2] = 0x87;
    out = incorrect_read_be32(cin, &cin[2]);
    if (out != 0x83858789)
        __builtin_abort();
    return 0;
}
