<table border="1" cellspacing="0" cellpadding="8">
    <tr>
        <th>Issue</th>
        <td>
            <a href=https://github.com/llvm/llvm-project/issues/59306>59306</a>
        </td>
    </tr>

    <tr>
        <th>Summary</th>
        <td>
            [Clang (all versions][x86-64 backend] Incorrect handling of uint64_t bitfields
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
      </td>
    </tr>

    <tr>
      <th>Assignees</th>
      <td>
      </td>
    </tr>

    <tr>
      <th>Reporter</th>
      <td>
          g0blue
      </td>
    </tr>
</table>

<pre>
    # Overview

**TL;DR:** See godbolt link: https://godbolt.org/z/GhWdrnv5s

# Details

There seems to be an "optimization" for bitfield members that are <= 32bits but have a 64-bit data type.  Specifically, reading the value returns a signed int32, not an unsigned int64.  Most of the time this does not cause a problem, but when shifting the bitfield value directly the result can meet or exceed 32 bits and the result is incorrect.

## Reproduction

Compile and run the below Example Code

## Example Code

```cpp
#include <stdint.h>
#include <stdio.h>
#include <inttypes.h>

static constexpr uint32_t NUM_BITS = 27;

struct foo
{
    foo() { Fields.data = 0;}
    union Fields
    {
        struct Bits
 {
            uint64_t                 FlagValue      : 1;
 uint64_t                 Reserved_0     : 5;
            uint64_t Value          : NUM_BITS;
        } Bits;

        uint64_t data;
    } Fields;

    enum VALUE
    {
 VALUE_BIT_SHIFT  = 6,
    };

    void SetValue( const uint64_t value )
    {
        Fields.Bits.Value = value >> VALUE_BIT_SHIFT;
 }

    // MSVC will note that GetValue and GetValue2 are identical and the
    // linker will remove GetValue and make both calls from main() call
    // GetValue2. All versions of MSVC work correctly.
    //
 // All versions of clang fail
    // GCC versions >= 10.1 fail (older versions work correctly)
    uint64_t GetValue( void ) const 
    {
 // This function may return INCORRECT results when:
        // (32 - VALUE_BIT_SHIFT) <= NUM_BITS <= 32!!
        //
        // In these error cases a 32-bit register is left shifted
        // and x86-64 HW will sign extend the result.
        // So, we could lose MSBs, or we could incorrectly get a sign-extended result
        //
        // For Clang - it's even in the IR!!
        //   %8 = trunc i64 %7 to i32, !dbg !144
        //   %9 = shl i32 %8, 6, !dbg !146
        //   %10 = sext i32 %9 to i64, !dbg !147
        return (Fields.Bits.Value << VALUE_BIT_SHIFT);
    }

    uint64_t GetValue2( void ) const 
 {
        // This version correctly left shifts a 64-bit register
 return (static_cast<uint64_t>(Fields.Bits.Value) << VALUE_BIT_SHIFT);
 }
};

int main()
{
    foo f;
    uint64_t start = ((uint64_t(3) << (NUM_BITS-2)) - 1) << foo::VALUE_BIT_SHIFT;
 f.SetValue(start);
        
    uint64_t val = f.GetValue();
 uint64_t val2 = f.GetValue2();

    printf("Start: 0x%" PRIx64 "\n", start);
    printf("Get:   0x%" PRIx64 "\n", val);
 printf("Get2:  0x%" PRIx64 "\n", val2);
    printf("%s", (val == val2 && val == start) ? "Correct Results" : "Bug detected!!");

 return int(val != val2 || val != start);
}
```
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJyMV91y4rgSfhpx0wVly_yYCy6CCbOp2tk9FbKzlynZboPOyBIlySTZpz8l2cY2mNlDuUKQuj_179cyM4YfJeKGLLZksZuwyp6U3hyDVFQ4SVX-tSE0gj8vqC8cP0iwI8FT85e65-13Em13ryRqfsMBEY4qT5WwILj8SaInOFl7Nl5kT-i-2Z0pfSR0_w-h-2-nv3MtLwszxI9gh5ZxMVh-O6FGMIilAasgRWASCKXqbHnJ_2GWK0kohUJpSLktOIocSixT1AbsiVlgGoFECYl2ENGUWwNpZeHELggMlvNpyi3kzDKwX2ecARzOmPGCZ0yIL0IT0MhyLo9gTwgXJioEjbbS0gADH80cuLQRdbJSWWdfJbuN5XwG8F0ZC6rwGJaXCPbEDeQKjVfJWGWcNWetUoGlQ3I2fpxQgjnxwrbnXz2sDcm5xsyKL7-n0VTCYUkoES0oDfiZIeYQUfB-M5n3JbkBLjOlHcbsJhUuG6941iqvMh_i3naiyjMX6OF0JWvDUKgPeP5k5VkgJCrHEcCH28ugfrLz-arAZSaq3KfO2JxLOzuR6PnRtnq4y6V1iTV9Af_XWGZ5BpmSxuLnWUPl0_hu4Y-_vr9vX94O4GqGrki0HerpKrNQKNWsrpptAPCrNCZ0DWS1hb3LlZn56nJYgYNa7TrxSnIlG7FudYDoPs2RW25bqTsRj-bL7d3C7Wcv2PGHrxj_cT0aXr16rPaKBvUF8_fgqrbo1MYO7h3SarTB7EVxTVa72plhZO_gXOAGBzrFJlr3qiirEn48_f7X81go_Yaz5P3w28v-DXxCloQmA_QR2IviORzQet8IjeuK6YysW5HQ9S8S2BSC83lWx8id3mhGzyR6vrWvc_taMD18T6zw_fAjgQ8uhCMRrNnuW2Oo7872B_UkyHOU1vFaSwR3gI7AUdeQGkt1wSFeyX4ipMqewLGjgUKrEkrGZVPzbvUO9GrEDJ6EgAtqw5U0jg5rB5T-CQ0Nia_ZrX4bhRrsFiETTB6hYHzk3CTpRH2QdxAGs9BLA6GxEjnqTmRoxyCf11x_65WBrwvvta-Hsew3lrw5si8q6ZkUSvbVTBB4-SP58_X1OXlrKNl4yneTc1A9DQyhcURhelcpjmzq-dYjrmbeERq6ZwRu9IgXz-YGAbVWGjJm0I25iPopqfHIjUXtJofAwtazCfNRKFcvn_FyupzDb3_XJeWGIuCnxcEcmo2qH5Sbgh8ImapEDkIZhO-HrXGrSncb1wkmvuCItpnJ0_oUzJsz_u8A7JWGxNfUFLgldGUALyiB12Pu5fVxQP0_i9h3ttWVzIAv525p5S4uvL4gEBrm6dF9hfP5Y5S1RzEn4fQ8rFNe3iIsHyOEQQ2Bn7bFWHs7lvNblNUQpalNQuMx1kpIlIxU4C1L3zLWXQfRxy10x579NmratWvUXiWa7kLXlmoD1PlUT_33jBlLoqQ1y9HDiL_X1vq1y52_dwOES9sjyPELAxSD6F1DZSzT1mfRa8dXY2kc9SwjNG77fkrdIXQNUwh7Eu5SEj2R6OnhjClmvQHnz73Nqc_DvY0XJryFxaxHjQPdvii9kaU3wt0BZ82lLfw2PXh7oicIPglduMv-f15fPn1vUbJI3PXflfSo2X2cb-hR4F9wLkwMUG4gqMf4Vwj6K0sIXZhGlNC4iWFzI3CtuiR0Cb3l1jUgkRsDNKmL313Q3NRwdjirCKXb6gg5WswcLddURelIiJuG4NK2BtCwM2CVkFUCveW72HYl397dJ_kmytfRmk1wEy5XQRDMV-t4ctrM8_WaFutVuorjNYspwyAMsiDIgrTIY8YmfEMDSkMa0DAOozCeBcVivqTFOmR0hTQsyDzAknExE-JSuvfICTemws1iHQXLiWApCtO-0eqNE5qm1dGQeSC4saZTs9wK_-5bUzyhMevdJ8hiRxbbZmalLPuJMieLHby0IwZOTObCvYupoqvr9p3MTCotNjfvvtyeqnSWqZLQvTOj-ZqetfovZpbQvXfFELr33vwvAAD__2CQY04">