-
Notifications
You must be signed in to change notification settings - Fork 8k
arch: riscv: Fix warning when C++ is enabled #88734
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
Conversation
When compiling with C++ enabled (CONFIG_CPP), add an unused member to prevent an empty struct; this makes the struct size the same for both C and C++. Fixes the following warnings: In file included from include/zephyr/drivers/gpio.h:22: In file included from include/zephyr/tracing/tracing.h:9: In file included from include/zephyr/kernel.h:17: In file included from include/zephyr/kernel_includes.h:32: In file included from include/zephyr/kernel_structs.h:29: In file included from include/zephyr/arch/structs.h:29: include/zephyr/arch/riscv/structs.h:11:1: error: empty struct has size 0 in C, size 1 in C++ [-Werror,-Wextern-c-compat] 11 | struct _cpu_arch { | ^ In file included from include/zephyr/drivers/gpio.h:22: In file included from include/zephyr/tracing/tracing.h:9: In file included from include/zephyr/kernel.h:17: In file included from include/zephyr/kernel_includes.h:36: In file included from include/zephyr/arch/cpu.h:25: In file included from include/zephyr/arch/riscv/arch.h:18: include/zephyr/arch/riscv/thread.h:68:1: error: empty struct has size 0 in C, size 1 in C++ [-Werror,-Wextern-c-compat] 68 | struct _thread_arch { | ^ Signed-off-by: Tom Hughes <[email protected]>
Hi, thanks for this PR! Unfortunately, I can't seem to reproduce this warning using the following command:
Could you perhaps create an issue with information on how to reproduce this warning? |
There are two things in my setup that are different than the way you're testing:
It should be reproducible if you use LLVM and make sure that the relevant configs are disabled that make Here's an example of what the failure looks like in CI when building with LLVM in another part of the code: #87088 (comment) |
Right. This is a configuration that, as far as I’m aware, is unsupported by Zephyr out of the box.
Unfortunately, I can’t access the link without signing in, but I assume you had to at least add a CMake script in This is something we’re not testing right now in CI, and I’d rather we add this configuration to our suite as well and make it much more easily accessible for maintainers to test and use. |
LLVM is officially supported and tested with CI: #85542. For example, see #87088 (comment). However, the CI is currently only building for
https://crrev.com/c/6349236 is publicly accessible. No sign in is required to view it. I'll see if I can create an upstream change to cover this path in CI. At the very least, it should be possible to demonstrate the size issue with gcc using |
@fkokosinski I added another commit that adds a
For reference, here's another change in the code where we did the same thing: #87088 (review) and #88778. |
#if defined(CONFIG_CPP) && !defined(CONFIG_FPU_SHARING) && !defined(CONFIG_USERSPACE) && \ | ||
!defined(CONFIG_PMP_STACK_GUARD) | ||
/* Empty struct has size 0 in C, size 1 in C++. Force them to be the same. */ | ||
uint8_t unused_cpp_size_compatibility; | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we should have something like the following defined somewhere
#ifdef __cplusplus
#define CPP_DUMMY_STRUCT_MEMBER int __dummy__
#else
#define CPP_DUMMY_STRUCT_MEMBER
#endif
and used in structs that can be empty
struct _thread_arch {
#ifdef CONFIG_FPU_SHARING
struct z_riscv_fp_context saved_fp_context;
bool fpu_recently_used;
uint8_t exception_depth;
#endif
#ifdef CONFIG_USERSPACE
unsigned long priv_stack_start;
unsigned long u_mode_pmpaddr_regs[CONFIG_PMP_SLOTS];
unsigned long u_mode_pmpcfg_regs[CONFIG_PMP_SLOTS / sizeof(unsigned long)];
unsigned int u_mode_pmp_domain_offset;
unsigned int u_mode_pmp_end_index;
unsigned int u_mode_pmp_update_nr;
#endif
#ifdef CONFIG_PMP_STACK_GUARD
unsigned int m_mode_pmp_end_index;
unsigned long m_mode_pmpaddr_regs[PMP_M_MODE_SLOTS];
unsigned long m_mode_pmpcfg_regs[PMP_M_MODE_SLOTS / sizeof(unsigned long)];
#endif
CPP_DUMMY_STRUCT_MEMBER;
}
so that the code looks cleaner, at the expense of an extra int
for every such struct when compiled for cpp, regardless of whether or not they are actually empty.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the idea of the dummy struct member, but I think you got the condition backwards? Plus, the dummy member should probably be a char
instead.
We could avoid the dummy member this way:
#ifdef __cplusplus
#define C_DUMMY_STRUCT_MEMBER
#else
#define C_DUMMY_STRUCT_MEMBER char __dummy__
#endif
struct foobar {
#ifdef CONFIG_FOO
int foo;
#endif
#ifdef CONFIG_BAR
int bar;
#endif
#if !(CONFIG_FOO || CONFIG_BAR)
C_DUMMY_STRUCT_MEMBER;
#endif
};
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about I implement something like this in a followup PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but I think you got the condition backwards?
Not really, I think we are talking about different idea - I'm proposing an alternative where we always have a dummy variable in every struct that might be empty when CONFIG_CPP=y
#ifdef __cplusplus
#define CPP_DUMMY_STRUCT_MEMBER char __dummy__ <- make sure that a struct can never be empty when CONFIG_CPP=y
#else
#define CPP_DUMMY_STRUCT_MEMBER
#endif
and whenever we have a struct that can be empty due to Kconfigs, simply append CPP_DUMMY_STRUCT_MEMBER
at the end to avoid more preprocessor macros like these:
#if !(CONFIG_FOO || CONFIG_BAR)
CPP_DUMMY_STRUCT_MEMBER;
#endif
Thanks! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably a RFC to streamline how we would deal with possibly empty struct in the future, but +1 to this PR
Using clang to build for RISC-V with these configs: CONFIG_CPP=y CONFIG_USERSPACE=n CONFIG_SMP=n CONFIG_FPU_SHARING=n results in the following warnings: include/zephyr/arch/riscv/structs.h:11:1: error: empty struct has size 0 in C, size 1 in C++ [-Werror,-Wextern-c-compat] 11 | struct _cpu_arch { | ^ include/zephyr/arch/riscv/thread.h:68:1: error: empty struct has size 0 in C, size 1 in C++ [-Werror,-Wextern-c-compat] 68 | struct _thread_arch { | ^ This commit adds a test with those configs to verify compilation succeeds. Although gcc doesn't emit these warnings, we can test with gcc as well with a BUILD_ASSERT in include/zephyr/arch/riscv/thread.h and include/zephyr/arch/riscv/structs.h. Signed-off-by: Tom Hughes <[email protected]>
65ccd3a
to
ff2cdb8
Compare
- CONFIG_STD_CPP2B=y | ||
# Verify struct _cpu_arch struct _thread_arch are compatible with C++ when | ||
# specific configs are enabled. | ||
cpp.main.empty_struct_size: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test is arch agnostic, but the configs required to trigger the empty struct are architecture dependent. We could put an arch_allow: riscv
on the test to make that clear. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue of empty struct can happen in all archs under different configurations, but keeping the test updated to track all possible scenarios seems tedious, I wonder if we should just drop the test and resort to PR review / static code analysis (can sonarqube detect possibly empty C structs?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
keeping the test updated to track all possible scenarios seems tedious
I agree, but I don't think we need to track all possible scenarios. We can add cases as we find ones that people care about. I don't think the empty struct issue is going to be caught in PR review; it's too easy to miss.
|
This pull request has been marked as stale because it has been open (more than) 60 days with no activity. Remove the stale label or add a comment saying that you would like to have the label removed otherwise this pull request will automatically be closed in 14 days. Note, that you can always re-open a closed pull request at any time. |
Please remove stale label and re-open the PR. |
I can't seem to re-open this, so I created #96943. |
Test moved from this PR to #96944 |
When compiling with C++ enabled (CONFIG_CPP), add an unused member to
prevent an empty struct; this makes the struct size the same for both C
and C++.
Fixes the following warnings:
In file included from include/zephyr/drivers/gpio.h:22:
In file included from include/zephyr/tracing/tracing.h:9:
In file included from include/zephyr/kernel.h:17:
In file included from include/zephyr/kernel_includes.h:32:
In file included from include/zephyr/kernel_structs.h:29:
In file included from include/zephyr/arch/structs.h:29:
include/zephyr/arch/riscv/structs.h:11:1: error: empty struct has size 0
in C, size 1 in C++ [-Werror,-Wextern-c-compat]
11 | struct _cpu_arch {
| ^
In file included from include/zephyr/drivers/gpio.h:22:
In file included from include/zephyr/tracing/tracing.h:9:
In file included from include/zephyr/kernel.h:17:
In file included from include/zephyr/kernel_includes.h:36:
In file included from include/zephyr/arch/cpu.h:25:
In file included from include/zephyr/arch/riscv/arch.h:18:
include/zephyr/arch/riscv/thread.h:68:1: error: empty struct has size 0
in C, size 1 in C++ [-Werror,-Wextern-c-compat]
68 | struct _thread_arch {
| ^