-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Add bit shifting built-ins #13001
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
base: master
Are you sure you want to change the base?
Add bit shifting built-ins #13001
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
synopsis: Add bit shifting built-ins | ||
issues: [13000] | ||
--- | ||
|
||
Added left and right bit shifting built-ins. | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4007,6 +4007,40 @@ static RegisterPrimOp primop_bitXor({ | |
.fun = prim_bitXor, | ||
}); | ||
|
||
static void prim_bitShiftLeft(EvalState & state, const PosIdx pos, Value * * args, Value & v) | ||
{ | ||
auto i1 = state.forceInt(*args[0], pos, "while evaluating the first argument passed to builtins.bitShiftLeft"); | ||
auto i2 = state.forceInt(*args[1], pos, "while evaluating the second argument passed to builtins.bitShiftLeft"); | ||
|
||
v.mkInt(i1.value << i2.value); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This contains UB if i2 is too large. Do it some other way that is safe. Maybe there's an intrinsic. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A brief look at the rust source code reveals that you have to check that i2 is in range: https://doc.rust-lang.org/stable/src/core/num/int_macros.rs.html#1222. Probably don't have to do anything else. You should add this to the checked-arithmetic library used by the other arithmetic. You also have to make sure the shift amount is positive. BUT this reveals that perhaps the correct design is a single bitShift builtin, see haskell: https://hackage.haskell.org/package/base-4.21.0.0/docs/GHC-Bits.html#v:shift There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Personally I think it would be easier and more understandable if we split the two implementations and enforce a positive shift amount. I think most of the time you know beforehand which direction you want to shift and this way we'll catch errors if that assumption is broken. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The reason I'm suggesting one of them is that it has clear and continuous behaviour over its domain: it's got less partial behaviour. If there's two, negative shift amounts are always illegal on both. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah good point. But then we might have to implement a logical right shift not the current arithmetic one. Else it might lead to a scenario where There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right. Do we want to allow shifting left into the sign bit to change the sign of a number? i dunno! but these are the questions that need to be answered in the spec. Builtins are permanent parts of derivation ABI that cannot be changed since we don't have a good language versioning story. The lix position is that builtins need:
We need to write these rules down somewhere because there's multiple sunk built-in projects that regrettably wasted a lot of people's time. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Honestly after talking with some other people about it I'm less convinced this builtin is strictly needed than before. I still think it's nice to have and would simplify things a bit, but I can see that there is no way of implementing it currently that wouldn't leak the internal integer representation, or wouldn't have some weird/unintuitive edge case behavior. So I guess the main question is if this would contain proper specified behavior, handling of edge cases and tests, would you or anyone else be open to merge it or would you rather not? |
||
} | ||
|
||
static RegisterPrimOp primop_bitShiftLeft({ | ||
.name = "__bitShiftLeft", | ||
.args = {"e1", "e2"}, | ||
.doc = R"( | ||
Return the integer *e1* shifted left by *e2*. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Insufficiently specific, what happens with negative operands? Are those invalid? What about negative shift values? I suspect the implementation here does UB with them. |
||
)", | ||
.fun = prim_bitShiftLeft, | ||
}); | ||
|
||
static void prim_bitShiftRight(EvalState & state, const PosIdx pos, Value * * args, Value & v) | ||
{ | ||
auto i1 = state.forceInt(*args[0], pos, "while evaluating the first argument passed to builtins.bitShiftRight"); | ||
auto i2 = state.forceInt(*args[1], pos, "while evaluating the second argument passed to builtins.bitShiftRight"); | ||
|
||
v.mkInt(i1.value >> i2.value); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this probably contains UB if i2 is too large |
||
} | ||
|
||
static RegisterPrimOp primop_bitShiftRight({ | ||
.name = "__bitShiftRight", | ||
.args = {"e1", "e2"}, | ||
.doc = R"( | ||
Return the integer *e1* shifted right by *e2*. | ||
)", | ||
.fun = prim_bitShiftRight, | ||
}); | ||
|
||
static void prim_lessThan(EvalState & state, const PosIdx pos, Value * * args, Value & v) | ||
{ | ||
state.forceValue(*args[0], pos); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please give an example.