OS: Windows 11 (x64), macOS 13.6 (arm64)
PB Version: 6.02, 6.03b9
PB Backend: C / ASM (inconsistency)
PB Architecture: x64, arm64
Details
Given a 64-bit target platform, bit-shift operations with the C-backend sometimes operate in 32-bit mode and other times 64-bit. Meanwhile, the ASM-backend seems to always sign-extend the value-to-shift to 64-bits and operate in 64-bit mode.
SHL (<<) and SHR (>>) operations vary in their application of mode.
Details – SHL (<<)
The C-backend's left-shift is inconsistent with the ASM-backend's behaviour when all of:
- The result type is smaller than the machine word.
- The shift-count type is smaller than the machine word.
Example:
Code: Select all
If Not OpenConsole() : DebuggerError("Failed to open console!") : End : EndIf
Define x.l = -2013265920
Define count_long.l = 32
Define count_quad.q = 32
Define x_shl_inline.l = x << 32 ; C 64-bit | ASM 64-bit
Define x_shl_by_long.l = x << count_long ; C 32-bit | ASM 64-bit
Define x_shl_by_quad.l = x << count_quad ; C 64-bit | ASM 64-bit
PrintN("x: " + x)
PrintN("x << inline : " + x_shl_inline)
PrintN("x << long : " + x_shl_by_long)
PrintN("x << quad : " + x_shl_by_quad)
Input()
Code: Select all
x: -2013265920
x << inline : 0
x << long : 0
x << quad : 0
Code: Select all
x: -2013265920
x << inline : 0
x << long : -2013265920
x << quad : 0
Produced C in this case:
Code: Select all
// Define x_shl_by_long.l = x << count_long
v_x_shl_by_long=(v_x<<v_count_long);
Code: Select all
// Define x_shl_by_long.l = x << count_long
v_x_shl_by_long=((quad)v_x<<v_count_long);
Details – SHR (>>)
The C-backend's right-shift is inconsistent with the ASM-backend's behaviour when any of:
- The value to be shifted is of a type smaller than the machine word.
Example:
Code: Select all
If Not OpenConsole() : DebuggerError("Failed to open console!") : End : EndIf
Define x.l = -2013265920
Define count_long.l = 32
Define count_quad.q = 32
Define x_shr_inline.l = x >> 32 ; C 32-bit | ASM 64-bit
Define x_shr_by_long.l = x >> count_long ; C 32-bit | ASM 64-bit
Define x_shr_by_quad.l = x >> count_quad ; C 32-bit | ASM 64-bit
PrintN("x: " + x)
PrintN("x >> inline : " + x_shr_inline)
PrintN("x >> long : " + x_shr_by_long)
PrintN("x >> quad : " + x_shr_by_quad)
Input()
Code: Select all
x: -2013265920
x >> inline : -1
x >> long : -1
x >> quad : -1
Code: Select all
x: -2013265920
x >> inline : -2013265920
x >> long : -2013265920
x >> quad : -2013265920
Produced C in this case:
Code: Select all
// Define x_shr_inline.l = x >> 32
v_x_shr_inline=(v_x>>(int)32);
// Define x_shr_by_long.l = x >> count_long
v_x_shr_by_long=(v_x>>v_count_long);
// Define x_shr_by_quad.l = x >> count_quad
v_x_shr_by_quad=(v_x>>v_count_quad);
Code: Select all
// Define x_shr_inline.l = x >> 32
v_x_shr_inline=((quad)v_x>>(int)32);
// Define x_shr_by_long.l = x >> count_long
v_x_shr_by_long=((quad)v_x>>v_count_long);
// Define x_shr_by_quad.l = x >> count_quad
v_x_shr_by_quad=((quad)v_x>>v_count_quad);
Expectation
C and ASM backends should probably agree here.
It's difficult to say which backend's approach should be preferred.
The C-backend's approach of favouring 32-bit in some cases allows for performance gains, whereas the ASM-backend's approach is much more predictable.
If the backends are not to agree in this case, documentation of this as potentially undefined behaviour would be appreciated.