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

int32, int64, int operations #107

Closed
bobzhang opened this issue Feb 18, 2016 · 14 comments · Fixed by #226
Closed

int32, int64, int operations #107

bobzhang opened this issue Feb 18, 2016 · 14 comments · Fixed by #226

Comments

@bobzhang
Copy link
Member

we need make sure int32 int64 semantics correct for sure.

for int (ocaml int is 31/63 bits), do we really need wrap those int everywhere, like

a+b --> a+b|0, the chance of overflow in ocaml often result in incorrect programs

@bobzhang
Copy link
Member Author

here is a nice summary how different languages handle overflow:
https://blog.lowsnr.net/2013/11/07/on-integer-overflow/

@bobzhang
Copy link
Member Author

here is my proposal:

  1. For Int32, Int64 it behaves exactly the same as ocaml
    for example Int32.max_int + 1 will get Int32.min_int
  2. For int, it will behave the same as signed int 32 except that when overflow or underflow, the behavior is undefined
  3. Integer literal will be signed int 32, 0xffffff will be -1
  4. signed int64 will be representation using ocaml records
type t = {lo : int (* signed 32 bit *) ; high: int} (* which will be [lo, hi] in js *)

Obj.tag (x : Int64.t) will be different from ocaml, in ocaml it is 255, in js it would be like other normal records, 0

@alainfrisch
Copy link

I've never seen correct code which relies on the overflow semantics of plain ints. If it can simplify the generated code to drop strict compatibility with the OCaml semantics, this seems a very reasonable choice to me.

@copy
Copy link
Contributor

copy commented Feb 20, 2016

Int32.max_int + 1 will get -1

You mean Int32.min_value?

Integer literal will be signed int 32, 0xffffff will be -1

What is the reason for this? I don't see an issue with a signed representation and it would make use from JavaScript easier.

  1. For int, it will behave the same as signed int 32 except that when overflow or underflow, the behavior is undefined

I think this is reasonable to keep the code uncluttered, although browsers are able to generate better code (integer operations) if the range of numbers is restricted to integers.

Note that for 32 bit multiplication you need to generate Math.imul, since the result doesn't fit in JavaScript's doubles.

64 bit multiplication and division are rather complicated, have a look here: https://closure-library.googlecode.com/git-history/docs/local_closure_goog_math_long.js.source.html and here: https://github.com/kripken/emscripten/blob/master/src/fastLong.js

@bobzhang
Copy link
Member Author

You mean Int32.min_value?

Yes, sorry for the typo

What is the reason for this? I don't see an issue with a signed representation and it would make use from JavaScript easier.

since 0xffffffff is -1 as a signed integer, maybe we can piggy back Nativeint.t as unsigned int support, I am not sure this is a good idea.

Math.imul will be generated for Int32.t, for int, if you do multiplication and it is overflowed, it's undefined behavior

Thanks for the link to Google Closure Library

@bobzhang
Copy link
Member Author

about Division_by_zero exception, what do you think?
My proposal:
follow js behavior for int , return Infinity ( or call it undefined behavior)
for Int32.t we follow the ocaml semantics which raise exception Division_by_zero (does people actually depend on such semantics? is it okay to make it undefined behavior?)

@copy
Copy link
Contributor

copy commented Feb 20, 2016

about Division_by_zero exception, what do you think?
My proposal:
follow js behavior for int , return Infinity ( or call it undefined behavior)

I am strongly opposed to this. If people want floating point semantics, they can use Float. Once you introduce Infinity, you also need to be very careful about not producing NaN (0 * Infinity, Infinity / Infinity, etc.). And yes, I also think people are using Division_by_zero (in a long formula with multiple divisions, for example).

since 0xffffffff is -1 as a signed integer, maybe we can piggy back Nativeint.t as unsigned int support, I am not sure this is a good idea.

I don't see the need for unsigned integers in the standard library. After all, OCaml (and many other languages) also don't have them.

for int, if you do multiplication and it is overflowed, it's undefined behavior

I am also opposed to this, I don't think we should introduce undefined behaviour in OCaml.

@bobzhang
Copy link
Member Author

but if you want int behave exactly as c like int, you can always use Int32.t. I would imagine people use int (js context) mostly just like plain numbers. Having int behave like js number would allow people write some low level js code in ocaml, for example, if I want implement polyfill Math.imul, I would really like it behave like js number

@copy
Copy link
Contributor

copy commented Feb 20, 2016

If you want a number to behave like JavaScript's numbers, you can use Float. When a developer uses integers they expect integer semantics (not float semantics), for example x == x being true for all x.

@bobzhang
Copy link
Member Author

Note that I did take a look at signed int in the c standard, it seems that signed int overflow is undefined behavior, so techinally I think what we are doing here is correct.
It still makes sense to use Math.imul for int mul operation (mostly due to performance issues).
We can use generate raw * operation for Nativeint, Nativeint.t will behave similar to js numbers, so that we can write some low level arithmetic code in ocaml.

@copy
Copy link
Contributor

copy commented Feb 23, 2016

Note that I did take a look at signed int in the c standard, it seems that signed int overflow is undefined behavior, so techinally I think what we are doing here is correct.

I wasn't aware of that. I'd still prefer to keep numbers in the integer range, at least for performance (V8 tries to keep numbers in 32 bit registers and deoptimizes if they overflow). Can we make this a compiler flag?

We can use generate raw * operation for Nativeint, Nativeint.t will behave similar to js numbers, so that we can write some low level arithmetic code in ocaml.

Why not use Float? JavaScript's numbers are floats.

@bobzhang
Copy link
Member Author

if I remember correctly, v8 will optimize 31 ints(same as ocaml), which means you can get deoptimized even if it is a purely 32 bit number

@bobzhang bobzhang added this to the alpha-release milestone Mar 11, 2016
@bobzhang
Copy link
Member Author

bobzhang commented Apr 8, 2016

@copy we will add |0 for (int, Int32.t) addition : )

@copy
Copy link
Contributor

copy commented Apr 8, 2016

Nice!

EduardoRFS added a commit to EduardoRFS/bucklescript that referenced this issue Apr 5, 2021
Co-authored-by: EduardoRFS <theeduardorfs@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants