Skip to content

Commit 445358a

Browse files
committed
Implement extensible syscall interface for wasm
1 parent a18dea9 commit 445358a

File tree

9 files changed

+367
-196
lines changed

9 files changed

+367
-196
lines changed

src/etc/wasm32-shim.js

+84-65
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,76 @@ let m = new WebAssembly.Module(buffer);
2828

2929
let memory = null;
3030

31+
function viewstruct(data, fields) {
32+
return new Uint32Array(memory.buffer).subarray(data/4, data/4 + fields);
33+
}
34+
3135
function copystr(a, b) {
32-
if (memory === null) {
33-
return null
34-
}
35-
let view = new Uint8Array(memory.buffer).slice(a, a + b);
36+
let view = new Uint8Array(memory.buffer).subarray(a, a + b);
3637
return String.fromCharCode.apply(null, view);
3738
}
3839

40+
function syscall_write([fd, ptr, len]) {
41+
let s = copystr(ptr, len);
42+
switch (fd) {
43+
case 1: process.stdout.write(s); break;
44+
case 2: process.stderr.write(s); break;
45+
}
46+
}
47+
48+
function syscall_exit([code]) {
49+
process.exit(code);
50+
}
51+
52+
function syscall_args(params) {
53+
let [ptr, len] = params;
54+
55+
// Calculate total required buffer size
56+
let totalLen = -1;
57+
for (let i = 2; i < process.argv.length; ++i) {
58+
totalLen += Buffer.byteLength(process.argv[i]) + 1;
59+
}
60+
if (totalLen < 0) { totalLen = 0; }
61+
params[2] = totalLen;
62+
63+
// If buffer is large enough, copy data
64+
if (len >= totalLen) {
65+
let view = new Uint8Array(memory.buffer);
66+
for (let i = 2; i < process.argv.length; ++i) {
67+
let value = process.argv[i];
68+
Buffer.from(value).copy(view, ptr);
69+
ptr += Buffer.byteLength(process.argv[i]) + 1;
70+
}
71+
}
72+
}
73+
74+
function syscall_getenv(params) {
75+
let [keyPtr, keyLen, valuePtr, valueLen] = params;
76+
77+
let key = copystr(keyPtr, keyLen);
78+
let value = process.env[key];
79+
80+
if (value == null) {
81+
params[4] = 0xFFFFFFFF;
82+
} else {
83+
let view = new Uint8Array(memory.buffer);
84+
let totalLen = Buffer.byteLength(value);
85+
params[4] = totalLen;
86+
if (valueLen >= totalLen) {
87+
Buffer.from(value).copy(view, valuePtr);
88+
}
89+
}
90+
}
91+
92+
function syscall_time(params) {
93+
let t = Date.now();
94+
let secs = Math.floor(t / 1000);
95+
let millis = t % 1000;
96+
params[1] = Math.floor(secs / 0x100000000);
97+
params[2] = secs % 0x100000000;
98+
params[3] = Math.floor(millis * 1000000);
99+
}
100+
39101
let imports = {};
40102
imports.env = {
41103
// These are generated by LLVM itself for various intrinsic calls. Hopefully
@@ -48,68 +110,25 @@ imports.env = {
48110
log10: Math.log10,
49111
log10f: Math.log10,
50112

51-
// These are called in src/libstd/sys/wasm/stdio.rs and are used when
52-
// debugging is enabled.
53-
rust_wasm_write_stdout: function(a, b) {
54-
let s = copystr(a, b);
55-
if (s !== null) {
56-
process.stdout.write(s);
57-
}
58-
},
59-
rust_wasm_write_stderr: function(a, b) {
60-
let s = copystr(a, b);
61-
if (s !== null) {
62-
process.stderr.write(s);
63-
}
64-
},
65-
66-
// These are called in src/libstd/sys/wasm/args.rs and are used when
67-
// debugging is enabled.
68-
rust_wasm_args_count: function() {
69-
if (memory === null)
70-
return 0;
71-
return process.argv.length - 2;
72-
},
73-
rust_wasm_args_arg_size: function(i) {
74-
return Buffer.byteLength(process.argv[i + 2]);
75-
},
76-
rust_wasm_args_arg_fill: function(idx, ptr) {
77-
let arg = process.argv[idx + 2];
78-
let view = new Uint8Array(memory.buffer);
79-
Buffer.from(arg).copy(view, ptr);
80-
},
81-
82-
// These are called in src/libstd/sys/wasm/os.rs and are used when
83-
// debugging is enabled.
84-
rust_wasm_getenv_len: function(a, b) {
85-
let key = copystr(a, b);
86-
if (key === null) {
87-
return -1;
113+
rust_wasm_syscall: function(index, data) {
114+
switch (index) {
115+
case 1: syscall_write(viewstruct(data, 3)); return true;
116+
case 2: syscall_exit(viewstruct(data, 1)); return true;
117+
case 3: syscall_args(viewstruct(data, 3)); return true;
118+
case 4: syscall_getenv(viewstruct(data, 5)); return true;
119+
case 6: syscall_time(viewstruct(data, 4)); return true;
120+
default:
121+
console.log("Unsupported syscall: " + index);
122+
return false;
88123
}
89-
if (!(key in process.env)) {
90-
return -1;
91-
}
92-
return Buffer.byteLength(process.env[key]);
93-
},
94-
rust_wasm_getenv_data: function(a, b, ptr) {
95-
let key = copystr(a, b);
96-
let value = process.env[key];
97-
let view = new Uint8Array(memory.buffer);
98-
Buffer.from(value).copy(view, ptr);
99-
},
100-
};
101-
102-
let module_imports = WebAssembly.Module.imports(m);
103-
104-
for (var i = 0; i < module_imports.length; i++) {
105-
let imp = module_imports[i];
106-
if (imp.module != 'env') {
107-
continue
108124
}
109-
if (imp.name == 'memory' && imp.kind == 'memory') {
110-
memory = new WebAssembly.Memory({initial: 20});
111-
imports.env.memory = memory;
112-
}
113-
}
125+
};
114126

115127
let instance = new WebAssembly.Instance(m, imports);
128+
memory = instance.exports.memory;
129+
try {
130+
instance.exports.main();
131+
} catch (e) {
132+
console.error(e);
133+
process.exit(101);
134+
}

src/librustc_trans/back/write.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,7 @@ pub struct CodegenContext {
337337
binaryen_linker: bool,
338338
debuginfo: config::DebugInfoLevel,
339339
wasm_import_memory: bool,
340+
wasm_auto_run: bool,
340341

341342
// Number of cgus excluding the allocator/metadata modules
342343
pub total_cgus: usize,
@@ -807,7 +808,7 @@ fn binaryen_assemble(cgcx: &CodegenContext,
807808
if cgcx.debuginfo != config::NoDebugInfo {
808809
options.debuginfo(true);
809810
}
810-
if cgcx.crate_types.contains(&config::CrateTypeExecutable) {
811+
if cgcx.wasm_auto_run && cgcx.crate_types.contains(&config::CrateTypeExecutable) {
811812
options.start("main");
812813
}
813814
options.stack(1024 * 1024);
@@ -1391,6 +1392,9 @@ fn start_executing_work(tcx: TyCtxt,
13911392
let wasm_import_memory =
13921393
attr::contains_name(&tcx.hir.krate().attrs, "wasm_import_memory");
13931394

1395+
// Disable wasm auto-run when building tests
1396+
let wasm_auto_run = !sess.opts.test;
1397+
13941398
let cgcx = CodegenContext {
13951399
crate_types: sess.crate_types.borrow().clone(),
13961400
each_linked_rlib_for_lto,
@@ -1428,7 +1432,8 @@ fn start_executing_work(tcx: TyCtxt,
14281432
target_pointer_width: tcx.sess.target.target.target_pointer_width.clone(),
14291433
binaryen_linker: tcx.sess.linker_flavor() == LinkerFlavor::Binaryen,
14301434
debuginfo: tcx.sess.opts.debuginfo,
1431-
wasm_import_memory: wasm_import_memory,
1435+
wasm_import_memory,
1436+
wasm_auto_run,
14321437
};
14331438

14341439
// This is the "main loop" of parallel work happening for parallel codegen.

src/libstd/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,4 @@ jemalloc = ["alloc_jemalloc"]
4848
force_alloc_system = []
4949
panic-unwind = ["panic_unwind"]
5050
profiler = ["profiler_builtins"]
51+
wasm_syscall = []

src/libstd/sys/wasm/args.rs

+5-33
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010

1111
use ffi::OsString;
1212
use marker::PhantomData;
13-
use mem;
1413
use vec;
14+
use sys::ArgsSysCall;
1515

1616
pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
1717
// On wasm these should always be null, so there's nothing for us to do here
@@ -21,38 +21,10 @@ pub unsafe fn cleanup() {
2121
}
2222

2323
pub fn args() -> Args {
24-
// When the runtime debugging is enabled we'll link to some extra runtime
25-
// functions to actually implement this. These are for now just implemented
26-
// in a node.js script but they're off by default as they're sort of weird
27-
// in a web-wasm world.
28-
if !super::DEBUG {
29-
return Args {
30-
iter: Vec::new().into_iter(),
31-
_dont_send_or_sync_me: PhantomData,
32-
}
33-
}
34-
35-
// You'll find the definitions of these in `src/etc/wasm32-shim.js`. These
36-
// are just meant for debugging and should not be relied on.
37-
extern {
38-
fn rust_wasm_args_count() -> usize;
39-
fn rust_wasm_args_arg_size(a: usize) -> usize;
40-
fn rust_wasm_args_arg_fill(a: usize, ptr: *mut u8);
41-
}
42-
43-
unsafe {
44-
let cnt = rust_wasm_args_count();
45-
let mut v = Vec::with_capacity(cnt);
46-
for i in 0..cnt {
47-
let n = rust_wasm_args_arg_size(i);
48-
let mut data = vec![0; n];
49-
rust_wasm_args_arg_fill(i, data.as_mut_ptr());
50-
v.push(mem::transmute::<Vec<u8>, OsString>(data));
51-
}
52-
Args {
53-
iter: v.into_iter(),
54-
_dont_send_or_sync_me: PhantomData,
55-
}
24+
let v = ArgsSysCall::perform();
25+
Args {
26+
iter: v.into_iter(),
27+
_dont_send_or_sync_me: PhantomData,
5628
}
5729
}
5830

0 commit comments

Comments
 (0)