Skip to content

Switch to new BigInt implementation #997

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

Merged
merged 4 commits into from
Apr 2, 2025
Merged

Switch to new BigInt implementation #997

merged 4 commits into from
Apr 2, 2025

Conversation

saghul
Copy link
Contributor

@saghul saghul commented Mar 24, 2025

No description provided.

@saghul
Copy link
Contributor Author

saghul commented Mar 24, 2025

Well, this is a messy one... we had already axed BigFloat, BigDecimal, use math and qjscalc. In addition some of our changes conflict so I'm pretty much surgically applying each hunk by hand.

Opening as draft to signal it's WIP.

@saghul
Copy link
Contributor Author

saghul commented Mar 30, 2025

Only major thing left is Math.sumPrecise. I hope to crack that one tonight...

@bnoordhuis
Copy link
Contributor

Ah, you might want to leave that one alone for now (and keep libbf a little longer) because you can't get the necessary precision with plain doubles (or even long double), not even when switching to something like Kahan summation.

I did some work on it a while ago (basically a bespoke floating point adder with ample precision) but I haven't had time to finish it.

@saghul
Copy link
Contributor Author

saghul commented Mar 30, 2025

The internal functions seem to suffice (at a cursory look, very la the last night though!) except they cannot represent NaN and Infinity so some checks would be necessary and maybe to keep state as the spec describes. I'll give it a shot and see if it can work 😅

@chqrlie
Copy link
Collaborator

chqrlie commented Mar 30, 2025

@saghul: Math.sumPrecise() is a non trivial task, a brute force approach works in trivial cases, but to achieve the expected precision efficiently requires a very specific implementation. I know Fabrice has something in the works, but if you want to tackle this, expect some surprises...

@saghul
Copy link
Contributor Author

saghul commented Mar 31, 2025

Ah FML, I see what you guys meant now. The new internal functions only work for ints, not floats, so the addition doesn't work.

I'll look into keeping libbf around a bit longer then.

@chqrlie
Copy link
Collaborator

chqrlie commented Mar 31, 2025

Ah FML, I see what you guys meant now. The new internal functions only work for ints, not floats, so the addition doesn't work.

Not sure what you mean. Math.sumPrecise() needs to keep track of precision loss due to cancellation and adding numbers that have very different magnitude. What functions are you referring to?

I'll look into keeping libbf around a bit longer then.

This function can be implemented with regular IEEE doubles, but the algorithm is subtle. Keeping libbf for this sole purpose seems overkill.

@saghul
Copy link
Contributor Author

saghul commented Apr 1, 2025

Alright, I think I got it all tidy up. This was very painful to do.

In order to implement Math.sumPrecise I introduced xsum, a reference implementation linked in the spec. Test262 passes locally with it and it's much smaller than libbf so even if we end up swapping it out I think it's a reasonable compromise for now.

Let's see what the CI has to say.

@bnoordhuis can you give it a read-through when you get a chance? You can likely skip the large chunks of contiguous code that are directly applied from bellard/ but I had to make some tweaks here and there.

@saghul saghul force-pushed the new-bigint branch 2 times, most recently from fb929f3 to 10dc80f Compare April 1, 2025 08:03
@saghul
Copy link
Contributor Author

saghul commented Apr 1, 2025

 lld-link : error : undefined symbol: __udivti3 [D:\a\quickjs\quickjs\build\point.vcxproj]

Looks like we need to link with clang RT, at a cursory search... something relates to int128 operations.

@saghul
Copy link
Contributor Author

saghul commented Apr 1, 2025

 lld-link : error : undefined symbol: __udivti3 [D:\a\quickjs\quickjs\build\point.vcxproj]

Looks like we need to link with clang RT, at a cursory search... something relates to int128 operations.

Ah I think we can fix it by using the same trick as we did in libbf and use 32 bits for the limbs on Windows.

saghul added 2 commits April 1, 2025 22:43
Manually port the following commits from bellard/quickjs

bellard/quickjs@61e8b94
bellard/quickjs@ee4cd4d
bellard/quickjs@6de8885
bellard/quickjs@5a16c0c
bellard/quickjs@6d6893b
bellard/quickjs@131408f

Co-authored-by: Fabrice Bellard <[email protected]>

Since libbf is gone, incorporate xsum as the backing for the
Math.sumPrecise implementation.

Changes made to xsum:

- Removed printf debug statements since many contained type errors
- Removed unneeded debug functions
- Added incldue guards
Copy link
Contributor

@bnoordhuis bnoordhuis left a comment

Choose a reason for hiding this comment

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

LGTM, good job. A couple of observations:

  • such a missed opportunity to name it JS_TAG_LITTLE_BIG_INT

  • for another PR: maybe remove support for 32 bits limbs (or vice versa, 64 bits limbs); simplifies the code a great deal in some places. Likely doesn't affect real world performance much because hardly anyone uses real big BigInts

  • fwiw, firefox/spidermonkey uses xsum_small_accumulator in Math.sumPrecise. xsum_large_accumulator takes up over 8k stack space, which is maybe a little much.

Comment on lines +10405 to 10413
ret = false;
for(i = p->len - 1; i >= 0; i--) {
if (p->tab[i] != 0) {
ret = true;
break;
}
}
JS_FreeValue(ctx, val);
return ret;
Copy link
Contributor

Choose a reason for hiding this comment

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

Not wrong but can be simplified to:

Suggested change
ret = false;
for(i = p->len - 1; i >= 0; i--) {
if (p->tab[i] != 0) {
ret = true;
break;
}
}
JS_FreeValue(ctx, val);
return ret;
for(i = p->len - 1; i >= 0; i--)
if (p->tab[i])
break;
JS_FreeValue(ctx, val);
return i >= 0;

If it's verbatim from bellard then just keep it as-is.

@saghul
Copy link
Contributor Author

saghul commented Apr 2, 2025

Thanks for the insight Ben!

I'll look into those in follow-up PRs since this one is already quite big 😅

@saghul saghul merged commit ca1964e into master Apr 2, 2025
124 checks passed
@saghul saghul deleted the new-bigint branch April 2, 2025 20:35
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.

4 participants