3 ms·
I'm porting a small C compiler to 64-bit RISC-V and just learned a quirk that uint32_t is represented in-register as sign extended to 64-bit. Without Zba extens
by fuhsnn 2y ago
I'm porting a small C compiler to 64-bit RISC-V and just learned a quirk that uint32_t is represented in-register as sign extended to 64-bit. Without Zba extension the compiler has to insert a bit-clearing sequence every time the value is used for operations that cannot be agnostic in two's complement. The quirk has enough impact on benchmark scores, SiFive typedef'ed an u32 type to i32 in their Coremark repo[1]. The Coremark team later updated their rule to address this trick[2].
[1] https://github.com/sifive/benchmark-coremark/blob/4486de1f0afe9d6c1fa8dd63743e5751286f3d2f/linux64/core_portme.h#L102 https://github.com/sifive/benchmark-coremark/blob/4486de1f0a...
[2] https://github.com/riscv/riscv-isa-manual/issues/353 https://github.com/riscv/riscv-isa-manual/issues/353
- camel-cdr 2y agoInteresting, although using u32 for indices was always a problem for x86 as well, because you can't directly use it without extension in the more complex addressing modes. Why can't programmers use size_t for indices?
- brucehoult 2y agoIt's hardly a quirk when it's mentioned and explained in paragraphs three and four of the 64 bit spec. It's literally the first non-trivial [1] thing in that spec. As explained, it is the only way it is possible to use a single set of 64 bit conditional branches (and SLT/SLTU) for both 32 bit and 64 bit, signed and unsigned, values. The Coremark restriction is stupid and explicitly ARM-centric. Any sensible person would either lazily use "int" or any 64 bit type ("long", "size_t" etc) for indexes, all of which work fine on RISC-V. The only reason to use "uint32_t" is to prematurely optimise for CPUs that zero-extend 32 bit values. Furthermore, any sensible real-world software will allow per-architecture #if'd typedefs. ---- Most integer computational instructions operate on XLEN-bit values. Additional instruction variants are provided to manipulate 32-bit values in RV64I, indicated by a ‘W’ suffix to the opcode. These “*W” instructions ignore the upper 32 bits of their inputs and always produce 32-bit signed values, i.e. bits XLEN-1 through 31 are equal. The compiler and calling convention maintain an invariant that all 32-bit values are held in a sign-extended format in 64-bit registers. Even 32-bit unsigned integers extend bit 31 into bits 63 through 32. Consequently, conversion between unsigned and signed 32-bit integers is a no-op, as is conversion from a signed 32-bit integer to a signed 64-bit integer. Existing 64-bit wide SLTU and unsigned branch compares still operate correctly on unsigned 32-bit integers under this invariant. Similarly, existing 64-bit wide logical operations on 32-bit sign-extended integers preserve the sign-extension property. A few new instructions (ADD[I]W/SUBW/SxxW) are required for addition and shifts to ensure reasonable performance for 32-bit values. ---- [1] i.e. proceeded only by "This chapter describes RV64I [...] widens the integer registers and supported user address space to 64 bits"*