Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix bounds check for slicesx.Get #432

Merged
merged 1 commit into from
Jan 27, 2025
Merged

Fix bounds check for slicesx.Get #432

merged 1 commit into from
Jan 27, 2025

Conversation

emcfarlane
Copy link
Contributor

@emcfarlane emcfarlane commented Jan 23, 2025

This changes slicesx.Get and slicesx.GetPointer to check the idx is within the length of the slice not the capacity. This avoids accessing slice data that is outside the slice length, allowing the use of slicesx.Get as a bonds check for indexing into a slice.

Use len to ensure the slice doesn't panic on indexing.
@emcfarlane emcfarlane requested a review from mcy January 23, 2025 23:57
Copy link
Member

@jhump jhump left a comment

Choose a reason for hiding this comment

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

I think the point of this was to allow accessing any element in the underlying backing array. Though I suppose we could apply this change and then update other callers that might rely on the old behavior to pass in slice[:cap(slice)].

I'll let @mcy make the call on this one.

@emcfarlane
Copy link
Contributor Author

emcfarlane commented Jan 24, 2025

@jhump this was based on the comment here: #429 (comment)
For this to replace the current behaviour it has to be len, I'm also not sure when this would be useful otherwise as the original slice header isn't mutated to include the possible out of length value. What is a valid use case for cap?

@jhump
Copy link
Member

jhump commented Jan 24, 2025

Thinking a little more, maybe this is the right change. I guess this was really meant to elide a bounds check for accessing a slice: so instead of having to check the length yourself and then reading slice[idx] (which will emit another bounds check in the generated instructions), we can check the bounds only once inside this function and then the caller can examine the bool.

Though, now that I'm thinking about it, is the after-the-fact bool check really cheaper than the before-the-fact range check? I think they both boil down to a cmp followed by jl/jnz...

Edit: Is the bool check maybe cheaper because it doesn't have to (re)load the slice's length?

Copy link
Member

@mcy mcy left a comment

Choose a reason for hiding this comment

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

I think this is the correct change, yes.

Incidentally, I thought I had updated this file with a more efficient version of the bounds check. As Go generates this code, because the compiler is dumb, it generates two branches, even though it can be done with one. Notably, the idx < 0 bit is redundant, because Go guarantees that cap(s) < math.IntMax, and negative values compare as greater than math.IntMax when compared unsigned. When I originally wrote this, I assumed the Go compiler was not dumb and would know it could merge these branches, but I guess I gave it too much credit.

Also I can't immediately remember why I made Get not call GetPointer?

@emcfarlane
Copy link
Contributor Author

Get not call GetPointer is one less branch.

@emcfarlane emcfarlane merged commit 8199283 into main Jan 27, 2025
10 checks passed
@emcfarlane emcfarlane deleted the ed/slicesxBounds branch January 27, 2025 22:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants