Skip to content

Commit bab8a01

Browse files
committed
Implement the GC in Rust
This implements the current garbage collector in Rust. No changes were made to the GC design -- it's just ports the one implemented in code generator to Rust. The goals are: - Evaluate Rust for Motoko's RTS implementation - Make the collector easier to read, understand, modify, and extend. Currently passes the tests locally. We can't run this branch on CI yet as it needs to download rustc nightly and xargo and the domains are not allowed on the CI. I think in the final version we'll have to build rustc outselves instead of downloading. (Nightly rustc is needed as "core" distributed with rustc is not built with PIC relocation model on wam32, so we can't use it to generate a shared wasm32 library) Main changes: - New Rust crate "motoko-rts" introduced, which currently implements the garbage collector. It also has some utilities for printing the heap or individual objects, to be used when debugging. - Nix files updated to download rustc and xargo. These are used to build Rust's "core" library with PIC relocation model for wasm32. - We no longer build memset and memcpy of musl as those are provided by Rust's "core" now. The main algorithm is in `gc.rs`. Rest of the Rust files are helpers, mainly for debugging. Other changes: - I had to update lots of ic-ref-run outputs. See #1854 for the details. Remaining work and issues: - There's currently a bug somewhere that causes random failures as I move the code around. One example of this is in the last line of `gc.rs` where I have a no-op function call `dump_heap()` which when removed causes the test "life" to fail with a trap. - Figure out how to use rustc nightly (with PIC wasm32 libraries) in CI.
1 parent 102defc commit bab8a01

File tree

121 files changed

+1257
-593
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

121 files changed

+1257
-593
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
_out
44
_output
55
_build
6+
target
67

78
**/*~
89
/result*

default.nix

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,19 @@ let haskellPackages = nixpkgs.haskellPackages.override {
1919
overrides = import nix/haskell-packages.nix nixpkgs subpath;
2020
}; in
2121
let
22-
llvmBuildInputs = [
22+
rtsBuildInputs = [
2323
nixpkgs.clang_10 # for native/wasm building
2424
nixpkgs.lld_10 # for wasm building
25+
nixpkgs.rustc-nightly
26+
nixpkgs.cargo-nightly
27+
nixpkgs.xargo
2528
];
2629

27-
# When compiling natively, we want to use `clang` (which is a nixpkgs
28-
# provided wrapper that sets various include paths etc).
29-
# But for some reason it does not handle building for Wasm well, so
30-
# there we use plain clang-10. There is no stdlib there anyways.
3130
llvmEnv = ''
31+
# When compiling natively, we want to use `clang` (which is a nixpkgs
32+
# provided wrapper that sets various include paths etc).
33+
# But for some reason it does not handle building for Wasm well, so
34+
# there we use plain clang-10. There is no stdlib there anyways.
3235
export CLANG="${nixpkgs.clang_10}/bin/clang"
3336
export WASM_CLANG="clang-10"
3437
export WASM_LD=wasm-ld
@@ -126,7 +129,7 @@ rec {
126129
src = subpath ./rts;
127130
nativeBuildInputs = [ nixpkgs.makeWrapper ];
128131

129-
buildInputs = llvmBuildInputs;
132+
buildInputs = rtsBuildInputs;
130133

131134
preBuild = ''
132135
${llvmEnv}
@@ -200,7 +203,7 @@ rec {
200203
wasmtime
201204
nixpkgs.sources.esm
202205
] ++
203-
llvmBuildInputs;
206+
rtsBuildInputs;
204207

205208
checkPhase = ''
206209
patchShebangs .
@@ -329,6 +332,7 @@ rec {
329332
[ { name = "bin/FileCheck"; path = "${nixpkgs.llvm}/bin/FileCheck";} ];
330333
wabt = nixpkgs.wabt;
331334
wasmtime = nixpkgs.wasmtime;
335+
xargo = nixpkgs.xargo;
332336
wasm = nixpkgs.wasm;
333337

334338
overview-slides = stdenv.mkDerivation {

nix/default.nix

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,30 @@ let
1010
import nixpkgs_src {
1111
inherit system;
1212
overlays = [
13-
(self: super: { sources = import sourcesnix { sourcesFile = ./sources.json; pkgs = super; }; })
13+
# rust nightly
14+
(self: super: let
15+
moz_overlay = import (self.fetchzip {
16+
url = https://github.com/mozilla/nixpkgs-mozilla/archive/efda5b357451dbb0431f983cca679ae3cd9b9829.tar.gz;
17+
sha256 = "11wqrg86g3qva67vnk81ynvqyfj0zxk83cbrf0p9hsvxiwxs8469";
18+
}) self super;
19+
rust-channel = moz_overlay.rustChannelOf { date = "2020-07-22"; channel = "nightly"; };
20+
in rec {
21+
rustc-nightly = rust-channel.rust.override {
22+
targets = [ "wasm32-unknown-unknown" "wasm32-unknown-emscripten" ];
23+
extensions = ["rust-src"];
24+
};
25+
cargo-nightly = rustc-nightly;
26+
rustPlatform-nightly = pkgs.makeRustPlatform {
27+
rustc = rustc-nightly;
28+
cargo = cargo-nightly;
29+
};
30+
})
31+
32+
# add nix/sources.json
33+
(self: super: {
34+
sources = import sourcesnix { sourcesFile = ./sources.json; pkgs = super; };
35+
})
36+
1437
# Selecting the ocaml version
1538
# (self: super: { ocamlPackages = super.ocamlPackages; })
1639
(
@@ -30,10 +53,21 @@ let
3053
inherit (self) ocamlPackages;
3154
};
3255
};
33-
# wasmtime
3456
wasmtime = self.callPackage ./wasmtime.nix {};
57+
xargo = self.callPackage ./xargo.nix {};
3558
}
3659
)
60+
# nixpkgs's rustc does not include the wasm32-unknown-unknown target, so
61+
# lets add it here.
62+
# (self: super: {
63+
# rustc = super.rustc.overrideAttrs (old: {
64+
# configureFlags = self.lib.lists.forEach old.configureFlags (flag:
65+
# if self.lib.strings.hasPrefix "--target=" flag
66+
# then flag + ",wasm32-unknown-unknown,wasm32-unknown-emscripten"
67+
# else flag
68+
# );
69+
# });
70+
# })
3771
];
3872
};
3973
in

nix/xargo.nix

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# xargo is used to build motoko-rts for wasm32. We need to make a shared Wasm
2+
# library for the RTS (that's what moc-ld supports) but Rust ships wasm32
3+
# libraries (core and std) without PIC relocation model, so we use xargo to make
4+
# PIC versions of core and std.
5+
6+
{ rustPlatform-nightly, fetchFromGitHub, lib, python, cmake, llvmPackages, clang, stdenv, darwin, zlib }:
7+
8+
rustPlatform-nightly.buildRustPackage rec {
9+
name = "xargo";
10+
11+
src = fetchFromGitHub {
12+
owner = "japaric";
13+
repo = "${name}";
14+
rev = "16035a7c401262824edcb87e1401fe4b05a5ccc0";
15+
sha256 = "0m1dg7vwmmlpqp20p219gsm7zbnnii6lik6hc2vvfsdmnygf271l";
16+
fetchSubmodules = true;
17+
};
18+
19+
cargoSha256 = "0zzksgi2prgw01m6r4bqjjz902h5g5ich0h3xvb60w4sshlss891";
20+
21+
# nativeBuildInputs = [ python cmake clang ];
22+
# buildInputs = [ llvmPackages.libclang ] ++
23+
# lib.optionals stdenv.isDarwin [ darwin.apple_sdk.frameworks.Security ];
24+
# LIBCLANG_PATH = "${llvmPackages.libclang}/lib";
25+
26+
doCheck = false;
27+
# error: couldn't lock thumbv6m-panic_abort-eabi's sysroot as read-only
28+
USER = "nobody"; # for xargo tests (if we would run them)
29+
30+
meta = with lib; {
31+
description = "The sysroot manager that lets you build and customize std";
32+
homepage = "https://github.com/japaric/xargo";
33+
license = licenses.mit;
34+
maintainers = [ {
35+
email = "[email protected]";
36+
github = "osa1";
37+
githubId = 123123;
38+
name = "Ömer Sinan Ağacan";
39+
} ];
40+
platforms = platforms.unix;
41+
};
42+
}

rts/Makefile

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
SHELL:=bash -O globstar
2+
13
CLANG ?= clang-10
24
WASM_CLANG ?= clang-10
35
WASM_LD ?= wasm-ld-10
@@ -19,7 +21,7 @@ TOMMATHFILES = \
1921
MUSLFILES = \
2022
pow pow_data sin cos tan asin acos atan atan2 exp exp_data log log_data fmod \
2123
towctrans iswspace iswupper iswlower iswalpha wcschr wcslen \
22-
floor scalbn frexp strnlen memchr memset memcpy snprintf vsnprintf vfprintf \
24+
floor scalbn frexp strnlen memchr snprintf vsnprintf vfprintf \
2325
__math_oflow __math_uflow __math_xflow __math_divzero __math_invalid \
2426
__rem_pio2 __rem_pio2_large __sin __cos __tan \
2527
stubs
@@ -154,29 +156,45 @@ _build/native/tommath_%.o: %.c rts.h buf.h | _build/native
154156
_build/wasm/musl_%.o: %.c | _build/wasm
155157
$(WASM_CLANG) $(CLANG_FLAGS) $(WASM_FLAGS) $(MUSL_FLAGS) $< --output $@
156158

159+
.PHONY: _build/wasm/libmotoko_rts.a
160+
_build/wasm/libmotoko_rts.a: | _build/wasm
161+
# NB. codegen-units=1 is to make debugging easier, not strictly
162+
# necessary
163+
cd motoko-rts && \
164+
xargo rustc --target=wasm32-unknown-emscripten --release -- \
165+
-Crelocation-model=pic -Ccodegen-units=1
166+
cp motoko-rts/target/wasm32-unknown-emscripten/release/libmotoko_rts.a $@
167+
168+
.PHONY: _build/native/libmotoko_rts.a
169+
_build/native/libmotoko_rts.a: | _build/native
170+
cd motoko-rts && cargo build --release
171+
cp motoko-rts/target/release/libmotoko_rts.a $@
172+
157173
RTS_WASM_O=$(RTSFILES:%=_build/wasm/%.o)
158174
RTS_NATIVE_O=$(RTSFILES:%=_build/native/%.o)
175+
RTS_RUST_WASM_O=_build/wasm/libmotoko_rts.a
176+
RTS_RUST_NATIVE_O=_build/native/libmotoko_rts.a
159177

160178
#
161179
# The actual RTS, as we ship it with the compiler
162180
#
163181

164-
mo-rts.wasm: $(RTS_WASM_O) $(TOMMATH_WASM_O) $(MUSL_WASM_O)
182+
mo-rts.wasm: $(RTS_WASM_O) $(RTS_RUST_WASM_O) $(TOMMATH_WASM_O) $(MUSL_WASM_O)
165183
$(WASM_LD) -o $@ \
166184
--import-memory --shared --no-entry --gc-sections \
167185
--export=__wasm_call_ctors \
186+
--whole-archive \
168187
$+
169188

170189
#
171190
# A simple program to do simple tests of rts.c, using native compilation
172191
#
173192

174-
test_rts: test_rts.c $(RTS_NATIVE_O) $(TOMMATH_NATIVE_O)
193+
test_rts: test_rts.c $(RTS_NATIVE_O) $(RTS_RUST_NATIVE_O) $(TOMMATH_NATIVE_O)
175194
$(CLANG) -o $@ $+
176195

177-
test_leb128: test_leb128.c $(RTS_NATIVE_O) $(TOMMATH_NATIVE_O)
196+
test_leb128: test_leb128.c $(RTS_NATIVE_O) $(RTS_RUST_NATIVE_O) $(TOMMATH_NATIVE_O)
178197
$(CLANG) -o $@ $+
179198

180199
clean:
181-
rm -rf _build mo-rts.wasm test_rts test_leb128
182-
200+
rm -rf _build mo-rts.wasm test_rts test_leb128 motoko-rts/target

rts/motoko-rts/.cargo/config

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# NB. codegen-units=1 is not necessary, but it generates less .o files for core,
2+
# std, and compiler_builtins and makes it easier to find symbols.
3+
[build]
4+
rustflags = ["-Crelocation-model=pic", "-Ccodegen-units=1"]
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// https://github.com/rust-analyzer/rust-analyzer/blob/master/editors/code/package.json
2+
{
3+
"rust-analyzer.cargo.target": "wasm32-unknown-emscripten",
4+
5+
// This is required as `cargo check --all-targets` doesn't seem to work well
6+
// on no-std crates, it generates false "duplicate lang item" errors.
7+
"rust-analyzer.checkOnSave.allTargets": false
8+
}

rts/motoko-rts/Cargo.lock

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rts/motoko-rts/Cargo.toml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[package]
2+
name = "motoko-rts"
3+
version = "0.1.0"
4+
authors = ["Ömer Sinan Ağacan <[email protected]>"]
5+
edition = "2018"
6+
7+
[dependencies]
8+
libc = "0.2.73"
9+
10+
[lib]
11+
crate-type = ["staticlib"]
12+
13+
[profile.dev]
14+
panic = "abort"
15+
16+
[profile.release]
17+
panic = "abort"

rts/motoko-rts/src/alloc.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//! Implements allocation routines used by the generated code and the GC.
2+
3+
use core::arch::wasm32;
4+
5+
use crate::common::rts_trap_with;
6+
use crate::gc;
7+
use crate::types::*;
8+
9+
#[no_mangle]
10+
pub unsafe extern "C" fn alloc_bytes(n: Bytes<u32>) -> SkewedPtr {
11+
alloc_words(bytes_to_words(n))
12+
}
13+
14+
#[no_mangle]
15+
pub unsafe extern "C" fn alloc_words(n: Words<u32>) -> SkewedPtr {
16+
let bytes = words_to_bytes(n);
17+
// Update ALLOCATED
18+
gc::ALLOCATED.0 += bytes.0 as u64;
19+
20+
// Update heap pointer
21+
let old_hp = gc::get_hp();
22+
let new_hp = old_hp + bytes.0 as usize;
23+
gc::set_hp(new_hp);
24+
25+
// Grow memory if needed
26+
grow_memory(new_hp);
27+
28+
skew(old_hp)
29+
}
30+
31+
/// Page allocation. Ensures that the memory up to the given pointer is allocated.
32+
pub(crate) unsafe fn grow_memory(ptr: usize) {
33+
let total_pages_needed = ((ptr / 65536) + 1) as i32;
34+
let current_pages = wasm32::memory_size(0) as i32;
35+
let new_pages_needed = total_pages_needed - current_pages;
36+
if new_pages_needed > 0 {
37+
if wasm32::memory_grow(0, new_pages_needed as usize) == core::usize::MAX {
38+
rts_trap_with("Cannot grow memory\0".as_ptr());
39+
}
40+
}
41+
}

0 commit comments

Comments
 (0)