-
Notifications
You must be signed in to change notification settings - Fork 13k
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
std: Add a new wasm32-unknown-unknown target #45905
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT | ||
// file at the top-level directory of this distribution and at | ||
// http://rust-lang.org/COPYRIGHT. | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
|
||
// This is a small "shim" program which is used when wasm32 unit tests are run | ||
// in this repository. This program is intended to be run in node.js and will | ||
// load a wasm module into memory, instantiate it with a set of imports, and | ||
// then run it. | ||
// | ||
// There's a bunch of helper functions defined here in `imports.env`, but note | ||
// that most of them aren't actually needed to execute most programs. Many of | ||
// these are just intended for completeness or debugging. Hopefully over time | ||
// nothing here is needed for completeness. | ||
|
||
const fs = require('fs'); | ||
const process = require('process'); | ||
const buffer = fs.readFileSync(process.argv[2]); | ||
|
||
Error.stackTraceLimit = 20; | ||
|
||
let m = new WebAssembly.Module(buffer); | ||
|
||
let memory = null; | ||
|
||
function copystr(a, b) { | ||
if (memory === null) { | ||
return null | ||
} | ||
let view = new Uint8Array(memory.buffer).slice(a, a + b); | ||
return String.fromCharCode.apply(null, view); | ||
} | ||
|
||
let imports = {}; | ||
imports.env = { | ||
// These are generated by LLVM itself for various intrinsic calls. Hopefully | ||
// one day this is not necessary and something will automatically do this. | ||
fmod: function(x, y) { return x % y; }, | ||
exp2: function(x) { return Math.pow(2, x); }, | ||
exp2f: function(x) { return Math.pow(2, x); }, | ||
ldexp: function(x, y) { return x * Math.pow(2, y); }, | ||
ldexpf: function(x, y) { return x * Math.pow(2, y); }, | ||
log10: function(x) { return Math.log10(x); }, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Btw - no need to wrap these, you can just do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice! I'll try to get that done in a follow-up commit. |
||
log10f: function(x) { return Math.log10(x); }, | ||
|
||
// These are called in src/libstd/sys/wasm/stdio.rs and are used when | ||
// debugging is enabled. | ||
rust_wasm_write_stdout: function(a, b) { | ||
let s = copystr(a, b); | ||
if (s !== null) { | ||
process.stdout.write(s); | ||
} | ||
}, | ||
rust_wasm_write_stderr: function(a, b) { | ||
let s = copystr(a, b); | ||
if (s !== null) { | ||
process.stderr.write(s); | ||
} | ||
}, | ||
|
||
// These are called in src/libstd/sys/wasm/args.rs and are used when | ||
// debugging is enabled. | ||
rust_wasm_args_count: function() { | ||
if (memory === null) | ||
return 0; | ||
return process.argv.length - 2; | ||
}, | ||
rust_wasm_args_arg_size: function(i) { | ||
return process.argv[i + 2].length; | ||
}, | ||
rust_wasm_args_arg_fill: function(idx, ptr) { | ||
let arg = process.argv[idx + 2]; | ||
let view = new Uint8Array(memory.buffer); | ||
for (var i = 0; i < arg.length; i++) { | ||
view[ptr + i] = arg.charCodeAt(i); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here about strings, but I guess too late now that it's merged? @alexcrichton There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @RReverser yes I will try to fix these when testing later. This doesn't matter for correctness and it's just internal debugging, it's not massively critical this is working. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, even better could be to just have UTF-16 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again though the intent here is to not go crazy, if we're dealing with utf-16 then let's just delete this code. I just used it for debugging and it shouldn't affect the standard library in any way. This isn't a node target, it's a wasm target and there's no strings in wasm yet in the abi There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure wasm will ever have strings in the ABI when there is |
||
} | ||
}, | ||
|
||
// These are called in src/libstd/sys/wasm/os.rs and are used when | ||
// debugging is enabled. | ||
rust_wasm_getenv_len: function(a, b) { | ||
let key = copystr(a, b); | ||
if (key === null) { | ||
return -1; | ||
} | ||
if (!(key in process.env)) { | ||
return -1; | ||
} | ||
return process.env[key].length; | ||
}, | ||
rust_wasm_getenv_data: function(a, b, ptr) { | ||
let key = copystr(a, b); | ||
let value = process.env[key]; | ||
let view = new Uint8Array(memory.buffer); | ||
for (var i = 0; i < value.length; i++) { | ||
view[ptr + i] = value.charCodeAt(i); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FWIW this won't work correctly for Unicode chars. Given that this is for Node.js, you might want to do something like |
||
} | ||
}, | ||
}; | ||
|
||
let module_imports = WebAssembly.Module.imports(m); | ||
|
||
for (var i = 0; i < module_imports.length; i++) { | ||
let imp = module_imports[i]; | ||
if (imp.module != 'env') { | ||
continue | ||
} | ||
if (imp.name == 'memory' && imp.kind == 'memory') { | ||
memory = new WebAssembly.Memory({initial: 20}); | ||
imports.env.memory = memory; | ||
} | ||
} | ||
|
||
let instance = new WebAssembly.Instance(m, imports); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason not to implement these on Rust side? (then they would be "automatic" for any consumer)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean manually emulating the operation as if they were soft floats? I'm pretty sure that this would be much much slower than calling from wasm to js, executing a native float op, and returning the result to the wasm executable. Also not sure how WASM compilation works but it seems like it is possible to inline that function when the JS runtime JIT compiles it...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It wouldn't need soft float; it would just be a call to
fmod
in libm.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not because JS and WASM use distinct compilers and runtimes, so every call outside is actually somewhat expensive (just like calls between JS and C++ runtimes).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've discussed this a bit with @nagisa , @rkruppe and @sunfishcode on IRC.
So it seems we'd have to implement the functions ourselves.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be still worth benchmarking both approaches. It might very well turn out to be still faster than calls to JS side just to modulo two numbers, and even if it's on par, it gives benefit of not forcing each consumer to include these "intrinsics" in their JS consumers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I'm assuming that one day we can just natively depend on the JS implementation here without implementing our own. Right now though it just requires an opt-in from everyone using it :(
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which is why I'm suggesting just including soft emulation of these functions on Rust side. Might be not too bad (and operators that are slow to emulate like
fmod
are not used too frequently anyway).