asm-js / validator

A reference validator for asm.js.
Apache License 2.0
1.78k stars 148 forks source link

ffi underspecified #38

Closed kg closed 10 years ago

kg commented 11 years ago

As a compiler author, I'd really like to see the details of the FFI specified so I can understand what kind of interactions are possible across the FFI boundary between normal JS and asm.js. My compiler doesn't use a virtual heap like emscripten does so at present it's hard to tell whether I could use asm.js at all, even if my individual functions are expressible in asm.js's subset of the language (and would benefit from the resulting performance improvements).

A few sample questions: Is it possible for JS objects to cross the FFI boundary? If no, what happens when they cross it? A runtime error? Is it possible to load properties from JS objects inside of asm.js? (Guessing no) What about property stores?

As a baseline, I could probably do everything I want with the ability for JS objects to cross the FFI boundary, even if they end up as opaque void * style data that is simply passed back out to other FFI functions (and stored in locals). For example, this would let me generate asm.js code for many methods of C# structs and classes that take a 'this' reference as an opaque JS object, and for the relatively rare cases where JS object interactions are needed, I could call getter/setter functions through the FFI, passing the this-reference in.

Being able to read/write ordinary properties of JS objects would be a huge boon too, but isn't necessary if I can bridge with the FFI. Even constrained access to properties (i.e. no prototype chain support, for example) would be great.

kripken commented 11 years ago

Is it possible for JS objects to cross the FFI boundary?

No, in asm.js code there are just ints and doubles basically. No JS objects at all. That's how it is possible to avoid all possible causes of bailouts. So likely for a compiler that generates JS objects like yours, asm.js isn't directly relevant, except perhaps for library code.

(This doesn't mean C# can't use asm.js though, an AOT might generate C or LLVM IR and be compiled to asm.js.)

kg commented 11 years ago

I was assuming it could operate at the function level where as long as a function only interacts with ints and floats and things that live in typed arrays, it can "use asm". It sounds like the entire application has to "use asm", then?

EDIT: I don't understand how JS objects crossing the FFI causes bailouts. I can understand how interacting with JS objects would cause bailouts. Why can't they have the semantics of void *? Is it because the GC rooting can cause bailouts?

kripken commented 11 years ago

You can do "use asm" in a function scope, but lots of little asms won't be a good idea probably, the ffi path is not fast.

JS objects crossing ffi don't cause bailouts. JS objects in general cause bailouts. If you don't know at every point that no variable is a JS object, then you need to check a lot more stuff and possibly deoptimize. asm code avoids that.

kripken commented 11 years ago

Btw, all of this is still a work in progress, don't take anything i said as a final answer. But what I said is true of the prototype implementation so far.

curiousdannii commented 11 years ago

In the example you have this. It would be good to explain why the sign must be forced etc.

        log(x|0);     // call into FFI -- must force the sign
        log(y);       // call into FFI -- already know it's a double
kripken commented 11 years ago

The assumption in asm.js variables is that they are 32-bit values, neither signed nor unsigned. This makes it very easy to optimize them. But it also means that when the sign matters - like when calling an ffi - you must specify the sign.

Put another way, inside asm.js, variables have no sign, operations determine the sign (again, to make it easy to optimize). When going outside of asm.js, you need to specify the sign because a concrete value is being sent into JS, not just 32 bits of uncertain interpretation.

ghost commented 10 years ago

I think FFIs are fully specified now (it is well-defined exactly what you can pass to them and get from them).