最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c - Discrepancy of `unsigned long` size between llvm and gcc in riscv32 - Stack Overflow

programmeradmin0浏览0评论

I do not know if this is a bug or intended (I'm missing a flag). But in gcc unsigned long expands to uint32_t while in llvm it doesn't.

#include <stdint.h>

void test (uint32_t i);

void test (unsigned long j);

int main (void) {}

riscv64-elf-gcc -march=rv32imac_zicsr -mabi=ilp32 -msmall-data-limit=8 -o out main.c success compile

clang -c -target riscv32-none-elf -march=rv32imaczicsr -mabi=ilp32 -msmall-data-limit=8 -o out main.c:

main.c:5:6: error: conflicting types for 'test'
    5 | void test (unsigned long j);
      |      ^
main.c:3:6: note: previous declaration is here
    3 | void test (uint32_t i);
      |      ^
1 error generated.

I can see gcc is correct according to:

In both RV32 and RV64 C compilers, the C type int is 32 bits wide. longs and pointers, on the other hand, are both as wide as a integer register, so in RV32, both are 32 bits wide, while in RV64, both are 64 bits wide.

Am I missing a flag? Thank you.

clangd version 19.1.7

I do not know if this is a bug or intended (I'm missing a flag). But in gcc unsigned long expands to uint32_t while in llvm it doesn't.

#include <stdint.h>

void test (uint32_t i);

void test (unsigned long j);

int main (void) {}

riscv64-elf-gcc -march=rv32imac_zicsr -mabi=ilp32 -msmall-data-limit=8 -o out main.c success compile

clang -c -target riscv32-none-elf -march=rv32imaczicsr -mabi=ilp32 -msmall-data-limit=8 -o out main.c:

main.c:5:6: error: conflicting types for 'test'
    5 | void test (unsigned long j);
      |      ^
main.c:3:6: note: previous declaration is here
    3 | void test (uint32_t i);
      |      ^
1 error generated.

I can see gcc is correct according to:

In both RV32 and RV64 C compilers, the C type int is 32 bits wide. longs and pointers, on the other hand, are both as wide as a integer register, so in RV32, both are 32 bits wide, while in RV64, both are 64 bits wide.

Am I missing a flag? Thank you.

clangd version 19.1.7

Share Improve this question edited 7 hours ago Jonathan Leffler 754k145 gold badges946 silver badges1.3k bronze badges asked 10 hours ago Anton AAnton A 1937 bronze badges 1
  • This is exactly the reason why types like uint32_t were introduced. – Gerhardh Commented 6 hours ago
Add a comment  | 

2 Answers 2

Reset to default 3

I do not know if this is a bug or intended (I'm missing a flag). But in gcc unsigned long expands to uint32_t while in llvm it doesn't.

It's a feature, not a bug. The C language specifications allow leeway for implementations to choose the size and representation of the standard integer types, such as unsigned long, within limits. This allows selection of types that are natural for the execution environment. It follows that the explicit-width types will correspond to different standard types in some implementations than they do in others.

In implementations for 64-bit environments, it is common for unsigned long to have 64 value bits and no padding, though 64-bit Windows is a notable exception. In implementations for 32-bit environments, it is typical for unsigned long to have 32 value bits and no padding, which often is the same as unsigned int in those environments.

Am I missing a flag?

It appears not.

Some C compilers do have flags that affect the sizes of the standard integer types. That may or may not also affect the definition of any particular explicit-width integer type.

To the best of my knowledge, GCC always uses data-type sizes consistent with a specific ABI, but for some architectures, it offers a choice of ABI. The -mabi=ilp32 flag you show in the question is its mechanism for specifying target ABI explicitly, so you are not missing it. Available ABI choices vary with the target architecture.

Clang accepts many of the same options as GCC, but not all of them. It does not seem to document -mabi as an accepted option. Your experiment seems to show it accepting the option, but that does not mean it elicits analogous behavior from Clang. Nor does Clang seem to document any other option for the purpose.

Note also that

  • You're not altogether free to choose ABI as you like. If you depend on any external libraries -- probably including the C standard library -- then they must all be built for the same ABI.

  • Data type sizes are not the end of the story. The C rules for compatible integer types are not based on size or representation. unsigned long and unsigned int are not compatible types by C's definition, even if they have identical size and representation. Choosing an ABI that features 32-bit longs does not necessarily get you uint32_t being the same as unsigned long.


Overall, portable code cannot freely intermingle the standard integer types with the explicit-width integer types. It cannot safely assume that any given explicit-width type is the same as any particular standard integer type. It can convert between one and the other, but it must do so carefully and intentionally to ensure that data are not lost or corrupted.

The only requirement in the C standard regarding the size of unsigned long is that it be large enough to hold values in the range 0 to 232-1.

There is no problem with unsigned long being either a 32 bit type or a 64 bit type, and as you've seen which one it is differs by compiler.

If you need an integer type that's a fixed size, then use one of the fixed size types.

发布评论

评论列表(0)

  1. 暂无评论