Add definition of enum storage type#209
Conversation
riscv-elf.md
Outdated
| `enum` types use int as storage type if the value range fits into the value | ||
| range of signed int or into the value range of unsigned int. If the value | ||
| range is larger, they use long long as storage type. |
There was a problem hiding this comment.
I'd put this just below char and bool above as they all relate to integral types, rather than interspersed between float-related things (float16 and complex).
Also, is long long correct, or should it be "smallest primitive integer type" so long gets used on RV32? Using unsigned int is a bit weird potentially too, would have to see what other ABIs do here.
There was a problem hiding this comment.
I tested both on ARM 32bit and riscv64:
GCC uses 32 bit storage with 32bit alignment if the enum values are either
- in [0, 2^32-1] or
- in [-2^31, 2^31-1]
Otherwise a 64 bit storage with 64bit alignment is chosen.
If the values do not fit into a 64bit representation this results in a "warning: enumeration values exceed range of largest integer".
I don't have an RV32 compiler but I would assume that GCC does the same as on ARM 32bit.
So long long seems to be correct.
Concerning the placement of the paragraph I have no preferences. Shall I resubmit with the same text? Or will you relocate when merging?
For testing on riscv32 you could use this program:
#include <stdio.h>
#include <limits.h>
enum e1 {
A1 = 0,
B1 = 0x7FFFFFFF,
};
enum e2 {
A2 = 0,
B2 = 0xFFFFFFFF,
};
enum e3 {
A3 = -0x80000000,
B3 = 0x7FFFFFFF,
};
enum e4 {
A4 = 0,
B4 = 0xFFFFFFFF,
};
enum e5 {
A5 = 0,
B5 = 0xFFFFFFFFFFFFFFFF,
};
enum e6 {
A6 = -0x8000000000000000,
B6 = 0x7FFFFFFFFFFFFFFF,
};
enum e7 {
A7 = 0,
B7 = 0xFFFFFFFFFFFFFFFF,
};
enum e8 {
A8 = -0x8000000000000000,
B8 = 0xFFFFFFFFFFFFFFFF,
};
int main()
{
printf("e1: %zu, %zu\n", sizeof(enum e1), __alignof__(enum e1));
printf("e2: %zu, %zu\n", sizeof(enum e2), __alignof__(enum e2));
printf("e3: %zu, %zu\n", sizeof(enum e3), __alignof__(enum e3));
printf("e4: %zu, %zu\n", sizeof(enum e4), __alignof__(enum e4));
printf("e5: %zu, %zu\n", sizeof(enum e5), __alignof__(enum e5));
printf("e6: %zu, %zu\n", sizeof(enum e6), __alignof__(enum e6));
printf("e7: %zu, %zu\n", sizeof(enum e7), __alignof__(enum e7));
printf("e8: %zu, %zu\n", sizeof(enum e8), __alignof__(enum e8));
return 0;
}
There was a problem hiding this comment.
Oops, I meant long on RV64, as that's sufficient for 64 bits. I realise it'll be a 64-bit type, but that doesn't tell you if it uses long or long long when the two have the same representation (which matters for printf format strings, C++ name mangling, overloaded functions, ...).
Using Clang is easiest, one compiler does everything. But godbolt.org has RV32+RV64 Clang+GCC.
There was a problem hiding this comment.
On riscv64 %lx is required for printing. On armv7 only %llx delivers the correct output with GCC while %lx and %x are accepted but the output seems to be an address.
So the 64 bit types should be long on riscv64 and long long on riscv32.
There was a problem hiding this comment.
Ok, so it is indeed the lowest rank integer type whose range is large enough to represent it.
There was a problem hiding this comment.
@jrtc27 I pushed a revised path some time ago. Could you, please, review.
Some APIs use enum types in their definitions. The C specification does not define the size of the integer type used for storing and passing enum type variables. Provide a definition matching GCC's default behavior. Signed-off-by: Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
| `enum` types use `int` as storage type if the value range fits into the value | ||
| range of `signed int` or into the value range of `unsigned int`. If the value | ||
| range is larger, the enum values use an 64bit integer storage type: | ||
|
|
||
| * `long` for an LP64 calling convention, | ||
| * `long long` for an ILP32 calling convention. |
There was a problem hiding this comment.
This needs to actually specify the signedness of the types explicitly, not just that it uses an int's worth of space, as it affects things like C++ overloading. That is, it needs to be clearly:
- If it fits in an int, use int
- Else, if it fits in an unsigned int, use unsigned int
- Else, if it fits in a long (long), use long (long)
- Else use unsigned long (long)
(assuming that the long (long) case mirrors the int case in its choice of signedness)
|
LGTM, @jrtc27 feel free to merge that once you are OK. |
Some APIs use enum types in their definitions. The C specification does not
define the size of the integer type used for storing and passing enum type
variables.
Provide a definition matching GCC's default behavior.
Signed-off-by: Heinrich Schuchardt heinrich.schuchardt@canonical.com