Skip to content

Fix left shift operator undefined behavior in integer_ops#2627

Open
joselopeqti wants to merge 2 commits intoKhronosGroup:mainfrom
joselopeqti:fix_left_shift_undefined_behavior
Open

Fix left shift operator undefined behavior in integer_ops#2627
joselopeqti wants to merge 2 commits intoKhronosGroup:mainfrom
joselopeqti:fix_left_shift_undefined_behavior

Conversation

@joselopeqti
Copy link
Collaborator

As per the C++ Programming Reference page n0: 151, section: 7.6.7 Shift operators
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/n5001.pdf

The operands shall be prvalues of integral or unscoped enumeration type and integral promotions are performed. The type of the result is that of the promoted left operand. The behavior is undefined if the right operand is negative, or greater than or equal to the width of the promoted left operand.

Copy link
Contributor

@bashbaug bashbaug left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Darn, I remember trying hard to avoid undefined behavior when we first did these tests (#1232), but I guess we missed this case.

Out of curiosity, how did you find this?

Comment on lines +38 to +43
cl_ulong mask = 0;
if (offset < 64)
mask = (count < 64) ? ((1ULL << count) - 1) << offset : ~0ULL;
cl_ulong shifted = 0;
if (offset < 64) shifted = (insert << offset);
cl_ulong result = (shifted & mask) | (base & ~mask);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we do something like this? A bit more compact and easier to understand (IMHO):

Suggested change
cl_ulong mask = 0;
if (offset < 64)
mask = (count < 64) ? ((1ULL << count) - 1) << offset : ~0ULL;
cl_ulong shifted = 0;
if (offset < 64) shifted = (insert << offset);
cl_ulong result = (shifted & mask) | (base & ~mask);
cl_ulong result = base;
if (offset < 64)
{
cl_ulong mask = (count < 64) ? ((1ULL << count) - 1) << offset : ~0ULL;
result = ((insert << offset) & mask) | (base & ~mask);
}

I also considered this version with just the ternary operator also, but I think the version above with the single if statement is easier to understand.

    cl_ulong mask =
        (count < 64) && (offset < 64) ? ((1ULL << count) - 1) << offset : ~0ULL;
    cl_ulong result =
        (offset < 64) ? ((insert << offset) & mask) | (base & ~mask) : base;

@svenvh
Copy link
Member

svenvh commented Mar 10, 2026

Out of curiosity, how did you find this?

On current main, I can reproduce the issue locally by compiling the CTS with -fsanitize=undefined.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants