<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; }
```

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">