-
Notifications
You must be signed in to change notification settings - Fork 163
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Clarity on rules for bitfields in fp+int structs eligible for the FP calling convention #99
Comments
There first seems fine to me. It's quirky to define,nbut given that's what
is currently generated is efficient I don't see a reason to change things.
Th second seems like a bug, but one that's unlikely to manifest in a
meaning context. I vote we match GCC here, even if it's kind of silly to
write down.
…On Tue, Jul 2, 2019, 4:50 PM Alex Bradbury ***@***.***> wrote:
There is a mention for bitfields, but it's still slightly under-documented.
The behaviour I'm observing from GCC is that:
- struct s1 { float f; long long int j : 32; } and similar are passed
as FPR+GPR on ILP32D (should be clarified in the doc that this is still
allowable, despite long long int being 64 bits.
- A struct with a float + any number of zero-width bitfields is passed
in an FPR, but passed according to the integer calling convention if there
is e.g. a zero-width bitfield + a int or non-zero-width bitfield
Can someone please confirm the above are expected behaviour, suitable for
documentation in the psabi-doc?
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
<#99>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAKRUXXG7CS4CUPKQUR7HOTP5NTNFANCNFSM4H44TREA>
.
|
…convention This patch reflects the observed behaviour from GCC, as discussed in issue riscv-non-isa#99.
…convention This patch reflects the observed behaviour from GCC, as discussed in issue riscv-non-isa#99. Fixes riscv-non-isa#99.
…convention This patch reflects the observed behaviour from GCC, as discussed in issue riscv-non-isa#99. Fixes riscv-non-isa#99.
…convention This patch reflects the observed behaviour from GCC, as discussed in issue riscv-non-isa#99. Fixes riscv-non-isa#99.
…convention This patch reflects the observed behaviour from GCC, as discussed in issue riscv-non-isa#99. Fixes riscv-non-isa#99.
I submitted #100 which documents the current behaviour. I think a better solution would probably be to always ignore zero-width bitfields, which seems consistent with how packed and aligned attributes don't affect a struct's potential eligibility for being passed by the floating-point calling convention. |
If a struct has only one non-zero width field, then the gcc front end gives the struct the mode of that one field as this improves optimization. The gcc backend isn't checking for this case, so a struct with any number of zero width fields and exactly one non-zero width FP field gets treated as if it has only one FP field. This happens in riscv_get_arg_info when we call riscv_pass_mode_in_fpr_p which checks only the argument mode, but don't bother to check if it is a structure and if so how many fields are in it. If a struct has more than 2 fields, then it is excluded as a candidate for passing in regs, even if one of those fields is a zero-size field, as we aren't checking for this case. This is a separate independent test than the one above. This happens in riscv_flatten_aggregate_field, which just counts the number of fields and doesn't exclude the zero-size ones. This is used by riscv_pass_aggregate_in_fpr_pair_p and riscv_pass_aggregate_in_fpr_and_gpr_p, so a zero size field plus two non-zero size fields prevents both the 2-fp reg passing case and the 1-fp reg+1gp reg passing case. |
Hi Jim, @rofirrim reports that there seems to be inconsistency between C/C++ mode riscv64-linux-gcc (tested on 9.1.0), even when
For C the struct is passed in a0 and a1, while for C++ it's passed in fa0 and a0. Can you please clarify which behaviour we should consider correct? |
The fundamental problem as mentioned before is that the RISC-V backend isn't checking for and handling zero-length fields. Looking at this problem, I see that the C++ front end is deliberately removing all zero-length fields after doing layout, i.e. after assigning bit offsets to each field, as zero-length fields are not needed after this point. The C front end does not do this. That means in the RISC-V backend, we are seeing two slightly different types, one with zero-length fields for C and one without for C++, which means we get different calling convention behavior for C and C++ code. Using extern "C" doesn't have any effect on this, as the struct still goes through the C++ front end. The C++ front end code in question is the remove_zero_width_bit_fields function in gcc/cp/class.c. This was added in December 1999. Comments in the email suggest it is related to the new abi class layout support. Different ABI support for C and C++ is a problem, and should be fixed, but unfortunately we have two bad choices here now. We can try to revert the 20 year old C++ front end change, maybe conditionally for RISC-V, which is likely to be hard and raise a lot of objections. Or we can fix the RISC-V backend to ignore zero-length fields, which means an ABI change for the C compiler, which is easy to implement but may be hard for others to handle, especially if something important like glibc is affected. Something I can try doing is instrumenting gcc and building OpenEmbedded to see if the ABI change will actually trigger on real code. This could take a few days. |
Closely related to this, given a testcase like this
the C and C++ compiler will pass them differently too. So this issue exists for both the case where we use two fp regs, and the case where we use one int reg and one fp reg. |
I did an initial round of testing. I modified gcc to abort if it sees a struct that would be passed differently in C and C++. But since the C++ front end removes the zero-length bit-fields, this only works for the C compiler. I built newlib/elf and glibc/linux toolchains and ran the gcc testsuites. The only thing that triggered was 2 tests in our ABI compatibility testsuite, which is expected. Unfortunately, while we have support for testing one C compiler against another, and one C++ compiler against another, we don't have support for testing the C compiler against the C++ compiler. That looks like an oversight that should be fixed, though I doubt that I will have time to try to fix it myself. I also did an open-embedded build, dropping in my gcc patch, and the image built without error. I built a core-image-lsb-sdk image that includes a native compiler, so quite a bit of stuff gets built, and it was all built successfully. I booted the image in qemu and verified that the system compiler called abort when fed the testcases. I don't know if there is a better open-embedded image for testing that will build more stuff. At the moment, it looks like we can change the ABI and it won't have much affect. I'd modify the gcc RISC-V port to give a warning when it sees a struct whose ABI changed. I can add an option to choose whether people want the old ABI or the new ABI, and make it default to the new ABI. I'd need a nice name for the option if anyone wants to suggest one. Or we could just start adding numbers for different ABI versions, and have an option to chose the ABI version by number. The C++ front end supports a -fabi-version=X option to specify which C++ ABI version is supported. We could add a -mabi-version=X option for the RISC-V backend. Or we could add an option specific to this problem, something about zero-length bitfields. I've got a riscv-gnu-toolchain regression build running which will test the default list of 26 or so multilibs but I'm not expecting a problem. And I will check to see if there is a bigger open-embedded image I can build to test more stuff. |
I just noticed that my second testcase is identical to the first one. It was meant to be a testcase with two float fields instead of one float field and one int field. I've filed an FSF GCC bug report for the ABI change I haven't found any real code effected by the change as yet. One of the tests I did was an open-embedded "world" build, not everything builds because not everything has RISC-V support yet, but I did get over 30,000 tasks run before it failed, and no hits on a abort I added to check for code affected by the change. |
I just committed the psABI fix for zero-length bitfields. Do we still need clarification for non-zero-length bitfields? The docs say I think the issue here is that with "long long int i : 32;" compiling for rv32, the question is whether the length is 64 because of the type or the length is 32 because of the bit-field size. I would assume the length is 32 because it is a bit-field. Gcc appears to agree with this interpretation. I don't see how the length can be 64 because it is a bit-field, but we could add some extra text to try to clarify this. |
Clang rejects bitfields that are wider than the type in C and truncates them for C++ (emitting a warning). I think the original point of this issue has been resolve (many thanks to @jim-wilson for resolving the issue on the GCC side). It's not clear if we need further clarification about larger-than-type bitfields in the psABI, but if we do it's probably a separate issue. I'm happy to close this one - any views on the larger-than-type bitfields question? |
There is a mention for bitfields, but it's still slightly under-documented.
The behaviour I'm observing from GCC is that:
struct s1 { float f; long long int j : 32; }
and similar are passed as FPR+GPR on ILP32D (should be clarified in the doc that this is still allowable, despitelong long int
being 64 bits.Can someone please confirm the above are expected behaviour, suitable for documentation in the psabi-doc?
The text was updated successfully, but these errors were encountered: