Are there any tutorials at all on how to use ASM.js's Foreign Function Interface (FFI)? The specification lacks examples, and seems to be very underspecified. :(
Note that the FFI is a crucial component of ASM.js, as everything in the browser is external to ASM.js (DOM/any strings/any objects/etc). It seems weird that there's so much hype around ASM.js, yet there's very little written about the FFI or how to use it. Any project that uses ASM.js is going to need to interact with it.
Also, are there any projects besides Emscripten that use ASM.js, or is the push to put ASM.js into these browsers due purely to Emscripten?
There is almost nothing to the FFI interface: Basically, the asm FFI can only have asm types, as you would expect. Then you can just call normal JS methods that were imported. So stuff like
function asmFunc(import) {
'use asm';
var externFunc = import.externFunc;
function asmFunc() {
externFunc(123, -0.5);
}
return asmFunc;
}
Essentially, a complete executable example of a simple ASM.js program that involves a foreign function, including processing return values. (I imagine if it's this simple, then return values are handled using the same annotations as ASM.js functions?)
While I understand that specs don't necessarily have to have examples, I believe it would benefit the community to have one in the ASM.js spec.
Thanks for clarifying here, though! I now understand.
Yeah, ffis are just calls like any other, with the usual annotations and types on parameters and return values, etc. They just happen to be to functions on the outside of the asm module.
Ah, I wasn't aware there was an actual repo associated with ASM.js. The example doesn't show how you call into the module with `consoleDotLog` defined, though. I know that sounds like a triviality, but complete examples are really useful to cut through any possible ambiguity!
I would definitely lobby to get something smaller and concise into the spec under the "Putting it All Together" heading! You could modify the existing example in the spec to include an external function definition and call.
FFI calls are just like normal calls: they need a coercion on them.
i = ffiFunc() | 0; // returns an int
d = +ffiFunc(); // returns a double
This is identical for FFI and non-FFI calls. A difference though is that asm functions have specific types (an asm function returns either nothing, an int, or a double). But an FFI is just any old JS function. So you could not write the above 2 lines together with an asm function, only an FFI can get an int one time and a double another time from the same function.
Understood. If `ffiFunc()` returns an empty array at a double call site, does ASM.js just accept it as `0`? (`+[]` is `0`). Or are there runtime checks that verify that the return value is a numeric type?
btw, I hope these questions make it clearer why I grumped a bit about the specification, and result in a more robust spec!
From that example, the list of return types from operators (section 8), and the graph of value types (section 2.1), it is simple to figure out, if not a little foreign at first.
The example doesn't use the FFI, though. And I understand the graph of value types and the restrictions that ASM.js imposes on ASM.js code, but there's no clear linkage between that and the FFI.
Some other FFIs require you to encode type information in some manner to uphold the guarantees of the language. It's not clear from scanning the specification if that is the case here, as the FFI is only briefly mentioned in passing.
Finally, ASM.js doesn't validate the return expression of external functions (it does for ASM.js code [0]). How does it deal with foreign functions that return values? What type does the return value take? Or does there need to be an annotation at the call site?
I hope this means that Microsoft has given up on pushing JavaScript to developers and we will get a proper C#/F#+XAML to whatever-legacy-highly-compatible-language (HTML, CSS, JS) toolchain.
I am getting increasingly annoyed by the limitations of asm.js, in particular the "allocate all the memory you'll ever need up front", which requires compiling in your own malloc, and makes it hard not to massively waste memory by overallocating a huge buffer.
Therefore I greatly prefer optimisations which speed up 'asm.js-like code', rather than the all-or-nothing optimisation one gets from firefox.
asm.js now allows the heap to be resized (by replacing the heap's ArrayBuffer with a newer, bigger ArrayBuffer that was either copied or produced via ES7-proposed ArrayBuffer.transfer [1]). Heap resizing currently has to be enabled in Emscripten (by passing -s ALLOW_MEMORY_GROWTH=1), but may become the default in the future. The main asm.js spec page hasn't been updated yet, but the extension was discussed publicly with comments from Microsoft engineers [2].
That is nice to hear. I don't keep enough track to see that this was being discussed, but it does deal with the only major issue I had with C++ -> asm.js code conversion, particularly for mobile.
Coming from the compiler side of things, you're sacrificing in usability to make it possible for the JIT compiler to optimize the code further. If you allocate all memory ahead-of-time, the GC never needs to operate on that region, which gives you substantial performance improvements.
This is definitely a trade-off, but it's my understanding that asm.js makes more sense as a target for compilers to emit code for, rather than for hand-writing. Would you hand write an entire application in x86 assembly? Probably not...
dlmalloc is cross compiled for you. I might be mincing words, but you make it sound like you have to bring your own allocator. Not so.
> makes it hard not to massively waste memory by overallocating a huge buffer.
Non issue. there are compiler flags `-s TOTAL_MEMORY=X` and `-s TOTAL_STACK=X`. Of course, you have to play around with these values otherwise you might not have enough space. I'm working on memory profiling tools to visualize the state of the heap in emscriptenized code [0] and getting them merged upstream. [1] The docs could be better on those flags.
> Therefore I greatly prefer optimisations which speed up 'asm.js-like code', rather than the all-or-nothing optimisation one gets from firefox.
Why must they be mutually exclusive? It's a straw man to paint it like we can only have one or the other but not both, and therefor we must choose. All-or-nothing is also an incorrect categorization; Firefox's JIT makes the same optimizations for non ASM code after it warms up.
> dlmalloc is cross compiled for you. I might be mincing words, but you make it sound like you have to bring your own allocator. Not so.
Wait, so I don't have to include an allocator in every asm.js project? How do I avoid doing that?
> Non issue. there are compiler flags `-s TOTAL_MEMORY=X` and `-s TOTAL_STACK=X`. Of course, you have to play around with these values otherwise you might not have enough space.
How does that make my complaint a non-issue? I shouldn't have to, in this day and age, manually tune the maximum amount of memory my program will use up front. That what my OS is for, I ask for more memory when I want it. What happens when some user decides to use my app in a sensible, but more memory hungry way, and hits so arbitrary limit I had to set for mobile users?
Of course, someone else replied this second point is now invalid, as asm.js is gaining the ability to grow the heap.
Note that the FFI is a crucial component of ASM.js, as everything in the browser is external to ASM.js (DOM/any strings/any objects/etc). It seems weird that there's so much hype around ASM.js, yet there's very little written about the FFI or how to use it. Any project that uses ASM.js is going to need to interact with it.
Also, are there any projects besides Emscripten that use ASM.js, or is the push to put ASM.js into these browsers due purely to Emscripten?