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

    <tr>
        <th>Summary</th>
        <td>
            clang and GCC disagree about whether union containing empty struct can be HFA
        </td>
    </tr>

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

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

    <tr>
      <th>Reporter</th>
      <td>
          eleanor-arm
      </td>
    </tr>
</table>

<pre>
    clang and GCC disagree about whether this union type is an HFA, and so whether it should be passed in floating-point or integer registers

**empty_struct.cpp**
```
struct empty {};
union maybe_hfa {
  float a;
  empty b;
};

float bar(float a, maybe_hfa b) { return b.a; }
```
![HFA](https://github.com/user-attachments/assets/c0dc2065-0f4c-45ba-9b5e-547e6ffdae6c)


The ABI isn't totally clear about what the right answer is here, but it seems more consistent to always treat empty as a type with size and alignment of 1 in all contexts in C++, so I think GCC's behaviour is more likely to be correct.

Note that the meaning of an empty struct varies between C and C++:

In C, it is technically undefined behaviour, but GCC defines it as having size zero as an extension, and I think clang tries to match that: https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Empty-Structures.html
In C++, an empty struct (or class, or union) is well-defined, and the Itanium C++ ABI says it has a size and alignment of 1 byte.

This is LLVM's codegen, for both AArch32 and AArch64, which shows it passing the maybe_hfa argument in s1, since it considers it to be an HFA:

```
clang --target=arm--none-eabi -march=armv8-a -O1 -c empty_struct.cpp -o - -S -mfloat-abi=hard
        .text
        .syntax unified
 .eabi_attribute 67, "2.09"      @ Tag_conformance
 .eabi_attribute 6, 14   @ Tag_CPU_arch
        .eabi_attribute 7, 65   @ Tag_CPU_arch_profile
        .eabi_attribute 8, 1    @ Tag_ARM_ISA_use
 .eabi_attribute 9, 2    @ Tag_THUMB_ISA_use
        .fpu crypto-neon-fp-armv8
        .eabi_attribute 12, 3   @ Tag_Advanced_SIMD_arch
        .eabi_attribute 36, 1   @ Tag_FP_HP_extension
        .eabi_attribute 42, 1   @ Tag_MPextension_use
        .eabi_attribute 34, 1   @ Tag_CPU_unaligned_access
        .eabi_attribute 68, 3   @ Tag_Virtualization_use
        .eabi_attribute 17, 1   @ Tag_ABI_PCS_GOT_use
        .eabi_attribute 20, 1   @ Tag_ABI_FP_denormal
        .eabi_attribute 21, 0   @ Tag_ABI_FP_exceptions
        .eabi_attribute 23, 3   @ Tag_ABI_FP_number_model
        .eabi_attribute 24, 1   @ Tag_ABI_align_needed
        .eabi_attribute 25, 1   @ Tag_ABI_align_preserved
        .eabi_attribute 28, 1   @ Tag_ABI_VFP_args
        .eabi_attribute 38, 1   @ Tag_ABI_FP_16bit_format
        .eabi_attribute 18, 4   @ Tag_ABI_PCS_wchar_t
        .eabi_attribute 26, 2   @ Tag_ABI_enum_size
        .eabi_attribute 14, 0   @ Tag_ABI_PCS_R9_use
 .file   "empty_struct.cpp"
        .globl  _Z3barf9maybe_hfa @ -- Begin function _Z3barf9maybe_hfa
        .p2align 2
        .type   _Z3barf9maybe_hfa,%function
        .code   32 @ @_Z3barf9maybe_hfa
_Z3barf9maybe_hfa:
 .fnstart
@ %bb.0:                                @ %entry
 vmov.f32        s0, s1
        bx      lr
.Lfunc_end0:
        .size _Z3barf9maybe_hfa, .Lfunc_end0-_Z3barf9maybe_hfa
        .cantunwind
 .fnend
                                        @ -- End function
        .ident  "clang version 19.0.0git (git@github.com:llvm/llvm-project.git 79ce70b8033815b6abd3a9a5cc2335de70f1aaab)"
        .section ".note.GNU-stack","",%progbits
        .addrsig
 .eabi_attribute 30, 1   @ Tag_ABI_optimization_goals
```
```
clang --target=aarch64--none-elf -march=armv8-a -O1 -c empty_struct.cpp -o - -S
 .text
        .file   "empty_struct.cpp"
        .globl _Z3barf9maybe_hfa               // -- Begin function _Z3barf9maybe_hfa
 .p2align        2
        .type _Z3barf9maybe_hfa,@function
_Z3barf9maybe_hfa:                      // @_Z3barf9maybe_hfa
// %bb.0:                               // %entry
 fmov    s0, s1
        ret
.Lfunc_end0:
        .size _Z3barf9maybe_hfa, .Lfunc_end0-_Z3barf9maybe_hfa
 // -- End function
        .ident  "clang version 19.0.0git (git@github.com:llvm/llvm-project.git 79ce70b8033815b6abd3a9a5cc2335de70f1aaab)"
        .section ".note.GNU-stack","",@progbits
        .addrsig
```
For comparison, this is gcc's output with the same options, showing that is passes the argument in r0/w0:

```
arm-none-eabi-gcc -march=armv8-a -mfpu=fp-armv8 -c empty_struct.cpp -O1 -o - -S -mfloat-abi=hard
 .arch armv8-a
        .fpu fp-armv8
        .eabi_attribute 28, 1
 .eabi_attribute 20, 1
        .eabi_attribute 21, 1
 .eabi_attribute 23, 3
        .eabi_attribute 24, 1
 .eabi_attribute 25, 1
        .eabi_attribute 26, 1
 .eabi_attribute 30, 1
        .eabi_attribute 34, 1
 .eabi_attribute 18, 4
        .file   "empty_struct.cpp"
 .text
        .align  2
        .global _Z3barf9maybe_hfa
 .syntax unified
        .arm
        .type   _Z3barf9maybe_hfa, %function
_Z3barf9maybe_hfa:
        .fnstart
.LFB0:
        @ args = 0, pretend = 0, frame = 0
        @ frame_needed = 0, uses_anonymous_args = 0
        @ link register save eliminated.
 vmov    s0, r0
        bx      lr
        .cantunwind
 .fnend
        .size   _Z3barf9maybe_hfa, .-_Z3barf9maybe_hfa
 .ident  "GCC: (fsf-trunk.3697) 15.0.0 20240514 (experimental)"
```
```
aarch64-none-elf-gcc -march=armv8-a -c -c empty_struct.cpp -O1 -o - -S
        .arch armv8-a
        .file "empty_struct.cpp"
        .text
        .align  2
        .global _Z3barf9maybe_hfa
        .type   _Z3barf9maybe_hfa, %function
_Z3barf9maybe_hfa:
.LFB0:
        .cfi_startproc
 fmov    s0, w0
        ret
        .cfi_endproc
.LFE0:
 .size   _Z3barf9maybe_hfa, .-_Z3barf9maybe_hfa
        .ident  "GCC: (fsf-trunk.3697) 15.0.0 20240514 (experimental)"

```

</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJzcWUuPozrT_jX0pgQCA7ksepFOT2Zamjmndeay-DbIQAH-Bmxkm6Qzv_6VTUinCelkjubdvFGUC7iurnp4CqhSrOSI90784MSPd7TTlZD3WCPlQrpUNnepyPf3WU15CZTn8HG9hpwpWkpEoKnoNOwq1BVK0BVT0HEmOOh9i8AUUA6fNiuHrK2sEse1TIOqRFfnkCK0VCnMgXEoakE146XbCsY1CAmMayxRgsSSKY1SOf6j468On8S8sWn1PlFadpn2srbtjx5WzPzD2_7tF4GVAGf-4MwfnfChP9d73tB9iklVUHvanoDeLaDHpXDQkB6PnCrqP3uZlEqHLAZ5sj7RnzpkaYyARN1JDqlnDIDRNOW6QwInfjDZjB8dsqi0bpUTrhyyccimZLrqUi8TjUM2nULpUq1pVjXItXLIxiTY_sj8PCP-LHb9IsrcKE6pu0xjdONojrOiyCnOMocs3yTZfn6rEFYPT8AUd8hcgxaa1vUeshqpPBYC1aArBMnKSgPlame2WkGFEk3waaftziM2ChohETLBldlXbjQCrXd0r0BLpMMmUQW0L6cd0xUo9gttLdGaldyEB6KAwNQOrWujTuOLVub_2iEP9r02hfdkypP_NPXrkLmCFCu6ZaKz_llXavYT671xIzV-SYmZ9k5T8JfQCHqIsUHKGS-NecoPzh7Ka0slQ2NC7xA59MU_uBO-SeuT9XNtssIUaMwqzjKb2I7nWDCO-aurQwptD9qTyghSBWYBL_vs_EIpbNo44ItGrpjgQwcOWej7WVs3tYCG6qyyoTnhCkallWVeyTtPyNIhG8Fr45PIVH_KjbzAI_1vh2w-mDS4X20aOonKq3RTn0Y67Mg4ZQ5ZCGncUsqcFrIHEtMiTMEO69o9pGMIxezBk6acdc2g2BaoMhXENFS2ci7VS7rX6L2tb6aMqc-ff3yxBZKJHEu0mSuEhFToClYrmVUhsQrt71lkzu8qllUGz3bWsoEzsxu2So7tTmXZWfuMgwpsVTKeoRGwTZCjtNJ9_R2A822xjBCh30TX1VSWqJ3wkcrGdbng6CJNGbgNlVnVH98uXAru3wG4GYzxElwBLrhfwW0sULk0ZU74WFGZD3DXvzzTW6NDas81fTG7VTAc1nvGfkK1liztNMJsbuJ1CCGev3QI6WWdyIdvtEwywQshG8ozvCRvxIPoRGb9_D2x0b31ZiRnzc7iCbmklaJgNb4vv7B2T31d_fMlefq6Sjp1ydelkSGnMt8-ff_yMJIaDBZtB5nct1q4HAV3i9a1u_W-XwExRsJTv_KtyV-efH368nhDZsLZENqgYfOcfHpOXhHjXfGIjMW_PB9FJ8IcW4_G4mZfOm6bFPOEZhkq9b6O2WKcgx9M6o7W7BfVN3kRzMderB6ekuf11-Tj39-uixN_SnzznOTITTnXV8QtBvjn4viSYWsiuJIAEp4VQa-Ad02KMmlEjtd8ONsHo8LuQsIRcxwDwFg-vizfSlQot1dVLKZU_Ng8J1SWVzIQTspunpNgljKdWFAZ49W4BqyKaKIGdllFZXJFnMyGbj8VR941ibnyXLEdTVWAsf3P8hRgDE6ZRYScM10yMlHWIq0Bkv8LUyqL5QmbjXxwXXjA0tDsjmemws7XjdS1xG4mjM1YQjZhxiFrh8SD-pGQuaICQEisM07kX7B-fni4DIJXcKWpPOyKVUPiNPV8w1uuvA6rkWu5P6jbNmLrFSEZlijb0ip463j60n_Xsj_ufTYBJshz_9WzIUhLOKbyAidi7rW8Z5Trju8Yz18DRz5qpVsCdl34wHO4sCMsN4TEVFbPJbYoDYBDsPR8zy-Z5WUl007kn0wY4aqut2bQMF9uK8X_G6ZsVs-XGc79dOGH4SKI0xlN85AuaZxlJAzjHOd-EVBKzeRzXroK-5p0CPG40Oh9_Ou7qzTNfpq1prDI8CNupShTpsf4QPNcKlZeuDKHk4AtWs2a4ZpRClqr6fHrGveilg4O_Ksufpt-DV5PkKzfR4BzABjVhuX2v4EIRyg4vCYRYRIPIv9t9U2194UC7p28jBTDglsx4HX9KQoUjdjCxe6XqP_rbf-6G_97zRr5tzTrqL02ZhgUTUslU_30qg_zmZ0z5wpEp9tO9zcFzKClaIMgDrzJbGMldv0QRu1obe8xKbv0dBKTvkM2O__9QcvMVcexyi2zbKK3m6LtnPBx4O_TjW4A4Nqs5RnNcNA7MS3cNiEcaNUFJDxQ11sI6kUVPfu8iV9eUhHf4sXsPRXhLYGE73pxYID_AnGnkPqAkVOgTCdQedA0OUUfdcrmdvoFZ_zrHTp1jPaUVXmfNw_nCGcumYaRgxM-gs16K1Ejz18PFNL0YP_3TNaePAwVryKdQpVQLvi-EZ1KTgycKagZ_3m8CwyKbhGwZg3jVGPunRC6VyiX_vtE7hj_TXSrx_gLWfcuYvsJbn9cr801yiGLQhWulh3_6YWz5dwhSwhig-JAfBL5cRCZRfjSomQGqWh9CsLvM5OBiAw85AJeZdcQ6qwIL6OS6ZWbuMkfa5g_3AjTJe9lBUtsY7RSZJNkYedfIAtvdCDPXzV4nzcfTmz9y6oa1P_54pqqqbv8PsyX4ZLe4X0wJ0G4JHEQ31X3i2Xsz2fL2SIoZnHsx8tlMYtn6YLgLFyEQXHH7q3RebAMSBgHsRdHabgIaJAvF3ExjyMn8rGhrPYMP_GELO-YUh3eL5ezKLqraYq1sk-nDrTHOBo_3sl7y2fSrlRO5NdMafWqQTNd423PrPqHPpngmjJ7O__NXemMckgRPm1Wd52s79955jJBshyysaEoh2z6aLb35D8BAAD__0fjCpo">