Skip to content

Instantly share code, notes, and snippets.

@abrown
Created May 4, 2023 15:38
Show Gist options
  • Save abrown/9a94aaa164ad1a8d29d5e86b737bbdc6 to your computer and use it in GitHub Desktop.
Save abrown/9a94aaa164ad1a8d29d5e86b737bbdc6 to your computer and use it in GitHub Desktop.
How to start working on `wasm32-wasi-threads`

After merging rust-lang/compiler-team#574 I got side-tracked on other work, but here is a description of how I configured my rustc environment in order to start adding and testing the new wasm32-wasi-threads target. Don't be surprised if I forgot a step or left some TODOs in place — there was some trial-and-error involved in getting to this point!

  • First, in order to get things working locally, I build a local copy of a WASI sysroot from wasi-libc: make && make THREAD_MODEL=posix (sysroot/share should now contain files for both targets--wasm32-wasi and wasm32-wasi-threads)

  • Then, in my rust project, I point my config.toml to look at the local WASI sysroot I just built; I do this for both targets (the existing one and the one we're about to add):

    [target.wasm32-wasi]
    wasi-root = ".../wasi-libc/sysroot"
    [target.wasm32-wasi-threads]
    wasi-root = ".../wasi-libc/sysroot"
  • Add compiler/rustc_target/src/spec/wasm32_wasi_threads.rs:

    use super::crt_objects::{self, LinkSelfContainedDefault};
    use super::{wasm_base, Cc, LinkerFlavor, Target};
    
    pub fn target() -> Target {
        let mut options = wasm_base::options();
    
        options.os = "wasi".into();
        // options.add_pre_link_args(LinkerFlavor::WasmLld(Cc::Yes), &["--target=wasm32-wasi-threads"]); // TODO is this right? we do pass this target to clang...
        options.add_pre_link_args(LinkerFlavor::WasmLld(Cc::No), &["--import-memory", "--export-memory,", "--shared-memory"]);
        options.add_pre_link_args(LinkerFlavor::WasmLld(Cc::Yes), &["--target=wasm32-wasi-threads", "-Wl,--import-memory", "-Wl,--export-memory,", "-Wl,--shared-memory"]);
    
        options.pre_link_objects_self_contained = crt_objects::pre_wasi_self_contained();
        options.post_link_objects_self_contained = crt_objects::post_wasi_self_contained();
    
        // FIXME: Figure out cases in which WASM needs to link with a native toolchain.
        options.link_self_contained = LinkSelfContainedDefault::True;
    
        // Right now this is a bit of a workaround but we're currently saying that
        // the target by default has a static crt which we're taking as a signal
        // for "use the bundled crt". If that's turned off then the system's crt
        // will be used, but this means that default usage of this target doesn't
        // need an external compiler but it's still interoperable with an external
        // compiler if configured correctly.
        options.crt_static_default = true;
        options.crt_static_respected = true;
    
        // Allow `+crt-static` to create a "cdylib" output which is just a wasm file
        // without a main function.
        options.crt_static_allows_dylibs = true;
    
        // WASI's `sys::args::init` function ignores its arguments; instead,
        // `args::args()` makes the WASI API calls itself.
        options.main_needs_argc_argv = false;
    
        // And, WASI mangles the name of "main" to distinguish between different
        // signatures.
        options.entry_name = "__main_void".into();
    
        // TODO
        options.singlethread = false;
    
        Target {
            llvm_target: "wasm32-wasi".into(), // TODO: is this right? LLVM doesn't know this target...
            pointer_width: 32,
            data_layout: "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20".into(),
            arch: "wasm32".into(),
            options,
        }
    }
  • Make some minor changes to get rustc to recognize the new wasm32-wasi-threads target and find the correct sysroot:

    diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
    index 0d86a3032a6..5823ba6d9c6 100644
    --- a/compiler/rustc_target/src/spec/mod.rs
    +++ b/compiler/rustc_target/src/spec/mod.rs
    @@ -1175,6 +1175,7 @@ fn $module() {
            ("wasm32-unknown-emscripten", wasm32_unknown_emscripten),
            ("wasm32-unknown-unknown", wasm32_unknown_unknown),
            ("wasm32-wasi", wasm32_wasi),
    +    ("wasm32-wasi-threads", wasm32_wasi_threads),
            ("wasm64-unknown-unknown", wasm64_unknown_unknown),
    
            ("thumbv6m-none-eabi", thumbv6m_none_eabi),
    diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml
    index 598a4bf9282..16d3d242c4c 100644
    --- a/library/std/Cargo.toml
    +++ b/library/std/Cargo.toml
    @@ -48,6 +48,9 @@ hermit-abi = { version = "0.3.0", features = ['rustc-dep-of-std'] }
        [target.wasm32-wasi.dependencies]
        wasi = { version = "0.11.0", features = ['rustc-dep-of-std'], default-features = false }
    
    +[target.wasm32-wasi-threads.dependencies]
    +wasi = { version = "0.11.0", features = ['rustc-dep-of-std'], default-features = false }
    +
        [features]
        backtrace = [
        "gimli-symbolize",
    diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs
    index 8b80dfc0f9b..cadff3c5730 100644
    --- a/src/bootstrap/compile.rs
    +++ b/src/bootstrap/compile.rs
    @@ -278,13 +278,14 @@ fn copy_self_contained_objects(
                let libunwind_path = copy_llvm_libunwind(builder, target, &libdir_self_contained);
                target_deps.push((libunwind_path, DependencyType::TargetSelfContained));
            }
    -    } else if target.ends_with("-wasi") {
    +    } else if target.contains("-wasi") {
            let srcdir = builder
                .wasi_root(target)
                .unwrap_or_else(|| {
                panic!("Target {:?} does not have a \"wasi-root\" key", target.triple)
                })
    -            .join("lib/wasm32-wasi");
    +            .join("lib")
    +            .join(target.to_string());
            for &obj in &["libc.a", "crt1-command.o", "crt1-reactor.o"] {
                copy_and_stamp(
                builder,
    diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
    index f4abdf1cc57..8765e971317 100644
    --- a/src/bootstrap/lib.rs
    +++ b/src/bootstrap/lib.rs
    @@ -126,7 +126,7 @@ pub unsafe fn setup(_build: &mut crate::Build) {}
            (Some(Mode::Std), "backtrace_in_libstd", None),
            /* Extra values not defined in the built-in targets yet, but used in std */
            (Some(Mode::Std), "target_env", Some(&["libnx"])),
    -    // (Some(Mode::Std), "target_os", Some(&[])),
    +    (Some(Mode::Std), "target_os", Some(&["wasm32-wasi-threads"])),
            (Some(Mode::Std), "target_arch", Some(&["asmjs", "spirv", "nvptx", "xtensa"])),
            /* Extra names used by dependencies */
            // FIXME: Used by serde_json, but we should not be triggering on external dependencies.
  • Build the stage 1 rustc compiler: ./x.py build --stage 1 --target wasm32-wasi-threads

  • Create a /tmp/threads.rs test file:

    fn main() {
        let mut join = vec![];
        for i in 0..10 {
            join.push(std::thread::spawn(move || {
                println!("thread {}", i);
            }));
        }
        for j in join {
            j.join().unwrap();
        }
    }
  • Try (and surely fail) to build this file with the new target: rustc +stage1 -v --target=wasm32-wasi-threads threads.rs

  • This is the point that I stopped at: std::thread::spawn still needs to be implemented and I believe the place to start at is in library/std/src/sys/wasi/thread.rs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment