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

Compile OCaml with Melange #164

Open
1 of 8 tasks
EduardoRFS opened this issue Apr 7, 2021 · 10 comments
Open
1 of 8 tasks

Compile OCaml with Melange #164

EduardoRFS opened this issue Apr 7, 2021 · 10 comments
Assignees

Comments

@EduardoRFS
Copy link
Collaborator

EduardoRFS commented Apr 7, 2021

I'm opening this issue because hopefully we can compile OCaml with Melange soon. And this is a good signal that we support OCaml.

Known issues

  • Bytes is incomplete
  • Weak is not supported
  • Nativeint is not supported
  • bsc file.ml ignores file.mli leading to weak types
    solved by adding -bs-read-cmi
  • symtable.ml uses Obj.new_block and Obj.set_field
  • in_channel != Stdlib.in_channel
  • out_channel != Stdlib.out_channel
  • implement file system hack from JSOO
@EduardoRFS
Copy link
Collaborator Author

After a lot of hacks, ALMOST

$ /usr/bin/node driver/main.js 
/home/eduardo/reason/ocaml/_esy/ocaml-development/store/b/melange-ed5e2880/default/lib/js/caml_external_polyfill.js:16
    throw new Error(s + " not polyfilled by ReScript yet\n")
    ^

Error: caml_weak_create not polyfilled by ReScript yet

@TheSpyder
Copy link
Contributor

Some of this will require dropping IE11 support, but that's probably ok.

Weak is not supported

This can likely be done with a combination of WeakMap, WeakSet and WeakRef.

Note that the MDN WeakRef docs states Safari is not supported but it does work (I've filed a bug).

Nativeint is not supported

There was previously a nativeint implementation but it wasn't correct. It should be possible to implement it using Bigint, but instead of attempting this it was removed.
rescript-lang/rescript-compiler#4440

Looking through other ReScript issues that mention nativeint, one interesting complication is likely to be pattern matching:

let f = function 
  | 1n
  | 2n -> 3n
  | x -> Nativeint.add 3n x

So this may involve more than just implementing the basic nativeint functions.

@EduardoRFS
Copy link
Collaborator Author

@TheSpyder I don't think we need to use BigInt for Nativeint as our Nativeint is 32 bits and that can be encoded in OCaml easily, my guess is that it can behave exactly like Int.

And it will behave exactly like OCaml 32bits platforms, my guess is that we can even generate binaries for it. It would be also cool to have support to Nativeint as 64bits but that is a long shot. But also it's one my dream to make the OCaml backend able to compile from 32bits -> 64bits and vice versa, so the Nativeint as 64bits may be not needed.

WeakMap seems to be supported by IE11 so we can probably use it and it seems enough to encode the module Weak in OCaml

@TheSpyder
Copy link
Contributor

TheSpyder commented Apr 7, 2021

yeah reverting the removal will restore 32 bit nativeint support.

JS isn’t a 32 bit platform though, if there’s no advantage over Int why would anyone want to use it. Bigint gives us 64 bit support.

@EduardoRFS
Copy link
Collaborator Author

@TheSpyder

JS isn’t a 32 bit platform though, if there’s no advantage over Int why would anyone want to use it.

Because Nativeint is portable, that's the same reason why people would use Nativeint in native OCaml. Nativeint will always describe the natural support for integers on a platform.

And JS is a 32bit platform, the only kind of integers supported naturally by JS is 32bits.

@TheSpyder
Copy link
Contributor

JS is a 32bit platform, the only kind of integers supported naturally by JS is 32bits.

That’s not true with bigint, it was added to the primitive data types, and arguably it’s not true at all - JS integers are 64 bit and the maximum value is an obscene 2^1024 (although the largest "safe integer" is 2^53 - 1, anything bigger is an approximation).

I think it's safe to say it is supported "naturally", whatever that means, even if some older devices don't implement bigint (a self-solving problem).

@EduardoRFS
Copy link
Collaborator Author

EduardoRFS commented Apr 8, 2021

@TheSpyder BigInt is not a type of integer in the low level sense of it.

JS integers are 64 bit

JS integers are not 64bit, number is a float of 64bits like in OCaml. That's why 2^53-1 is the safe integer, because that's how much a float can safely encode.

Remember typeof in JS doesn't give you the type of something in memory, it only gives a semantic type, for example a function is actually described as an object per ECMA262 with a [[Call]] symbol. A number is a 64bits float, but because of the bitwise OP spec, which describes operations on numbers by first casting them to 32bits integers, basically every JS engine can treat numbers as 32bits integers.

https://tc39.es/ecma262/#sec-numberbitwiseop

So the range of numbers which JS can process the fastest, is only 32bits integers, and I believe that as soon as you cross the 32bits range for integers you get a performance penalty, that's also why the Int module in JS is 32 and in OCaml 31 or 63.

That's also why in asm.js all ints are 32bits and encoded as n | 0 because an engine can really quickly understand what this meant, that's also the case for Melange. And if you look on the generated assembly for V8 you will be able to notice that.

Why does it matter?

If you're writing a compiler for a target that matters, as you can do all sorts of tricks based on the supported Nativeint. This is what OCaml does on itself for example, and why Nativeint is used.

@TheSpyder
Copy link
Contributor

You're right, I forgot the math operations on numbers truncate to 32 bit (I hit this in my project at work). I realise n | 0 is used all over generated code to truncate to 32 bits. But bigint supports, as far as I can tell, every number operator. Including bitwise without truncation.
https://tc39.es/ecma262/#sec-bigintbitwiseop

I certainly haven't tested whether bigint is good enough to encode Nativeint at 64 bit. But my expectation is that it can.

@TheSpyder
Copy link
Contributor

also, having 500n in OCaml compile to 500n in JS instead of 500 | 0 will add to the "cool readable output" factor.

@EduardoRFS
Copy link
Collaborator Author

@TheSpyder n | 0 is also a performance feature. And it actually works, engines can easily optimize it, is an old trick from asm.js time.

Encoding Nativeint as 64bit wouldn't be helpful to achieve codegen,

The problem of bigint is the platform requirement, it cannot be easily transpiled so adding a dependency on it is really bad. But also the syntax conflict is truly a shame, 500n in Reason means something completely different ;/

@EduardoRFS EduardoRFS self-assigned this Sep 7, 2021
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

No branches or pull requests

2 participants