-
Notifications
You must be signed in to change notification settings - Fork 396
Remove all user-space asUint/toUint in the libraries. #5194
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
Conversation
7d70ace
to
509e955
Compare
|
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.
The code in itself LGTM (modulo the minor comments).
What I'm not entirely clear about is why this is a desirable change. Could you add a comment about this in the PR description?
} | ||
|
||
newTree match { | ||
// Unless both arguments are (convertible to) bigint's, | always returns an Int |
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.
The "(convertible to)" feels confusing here. At least node seems to disallow any implicitness here:
> 1n | 1
Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions
> 1n | "1"
Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions
I assume this is specified?
Maybe also add "| always returns an Int or throws".
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.
I elaborated the comment. Primitive numbers cannot be mixed in this way, but objects with a valueOf()
method returning a bigint
can be converted to bigints.
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.
Just a typo.
/* Unless it throws, `x | y` returns either a signed 32-bit integer | ||
* (an `Int`) or a bigint. | ||
* | ||
* The only case in which it returns it bigint is when both arguments |
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.
* The only case in which it returns it bigint is when both arguments | |
* The only case in which it returns a bigint is when both arguments |
We use these in some low-level routines, notably in `RuntimeLong`. Introducing casts avoids unnecessary `AsInstanceOf`s around them.
Its implementation is shorter than many of the methods we already inline, like additions and multiplications. We do pay a little code size cost for this change. This will be particularly useful for the following commit, which will rely on folding to happen within `toDouble`, once inlined at call site.
Instead, we use `Integer.toUnsignedLong(x).toDouble`, which is semantically equivalent. The only remaining use of `x >>> 0` is in the JS-only runtime libraries, notably `RuntimeLong`. We add some optimizations to generate the best possible code on all targets. For JS with `RuntimeLong`, we need a tailored rewrite in the optimizer to get rid of a `Double` addition of `0.0 + y` when `y` is provably non-negative. This shape comes from inlining `RuntimeLong.toDouble`. For JS with `bigint`s, we directly fold `(double) <toLongUnsigned>(x)` into `x >>> 0`. Otherwise, we unnecessarily go through a `bigint` with `Number(BigInt(x >>> 0))`. For Wasm, we fold the same shape into a single operation `f64.convert_i32_u`, which we add in `WasmTransients`. Overall, this has no real impact on the JS target. However, it removes two round-trips from Wasm to JS. Previously, we needed to call a helper function to do the `x >>> 0`, then call the `$uD` helper to retrieve the result. Now, the same operations stays entirely within Wasm.
Instead, we use
Integer.toUnsignedLong(x).toDouble
, which is semantically equivalent. The only remaining use ofx >>> 0
is in the JS-only runtime libraries, notablyRuntimeLong
.We add some optimizations to generate the best possible code on all targets.
For JS with
RuntimeLong
, we need to markRuntimeLong.toDouble
as fully inline. That is fine, as its body is actually very short (shorter than most methods ofRuntimeLong
). Moreover, we need a tailored rewrite in the optimizer to get rid of aDouble
addition of0.0 + y
wheny
is provably non-negative.For JS with
bigint
s, we directly fold(double) <toLongUnsigned>(x)
intox >>> 0
. Otherwise, we unnecessarily go through abigint
withNumber(BigInt(x >>> 0))
.For Wasm, we fold the same shape into a single operation
f64.convert_i32_u
, which we add inWasmTransients
.Overall, this has no real impact on the JS target. However, it removes two round-trips from Wasm to JS. Previously, we needed to call a helper function to do the
x >>> 0
, then call the$uD
helper to retrieve the result. Now, the same operations stays entirely within Wasm.