From c3f18a306e7a00769eeb570eef3078892fd28ddd Mon Sep 17 00:00:00 2001 From: Ben Lewis Date: Thu, 27 Aug 2020 20:35:15 +1200 Subject: [PATCH 1/2] Use libffi to call jitted functions. --- Cargo.lock | 139 ++++++++++++++++++++++++++++++++++++++++ jit/Cargo.toml | 1 + jit/src/instructions.rs | 20 +++++- jit/src/lib.rs | 37 ++++++----- tests/snippets/jit.py | 3 + 5 files changed, 184 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 881852168..dd70824fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,6 +6,12 @@ version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +[[package]] +name = "abort_on_panic" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955f37ac58af2416bac687c8ab66a4ccba282229bd7422a28d2281a5e66a6116" + [[package]] name = "adler" version = "0.2.3" @@ -117,6 +123,30 @@ dependencies = [ "serde", ] +[[package]] +name = "bindgen" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1c85344eb535a31b62f0af37be84441ba9e7f0f4111eb0530f43d15e513fe57" +dependencies = [ + "bitflags", + "cexpr", + "cfg-if", + "clang-sys", + "clap", + "env_logger", + "lazy_static 1.4.0", + "lazycell", + "log", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "which", +] + [[package]] name = "bit-set" version = "0.5.2" @@ -234,6 +264,15 @@ version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66120af515773fb005778dc07c261bd201ec8ce50bd6e7144c927753fe013381" +[[package]] +name = "cexpr" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fce5b5fb86b0c57c20c834c1b412fd09c77c8a59b9473f86272709e78874cd1d" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "0.1.10" @@ -253,6 +292,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "clang-sys" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81de550971c976f176130da4b2978d3b524eaa0fd9ac31f3ceb5ae1231fb4853" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" version = "2.33.3" @@ -722,6 +772,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + [[package]] name = "hashbrown" version = "0.8.2" @@ -868,6 +924,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "lexical-core" version = "0.7.4" @@ -887,6 +949,39 @@ version = "0.2.76" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3" +[[package]] +name = "libffi" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c18efe55925cc7f83bf60a61394696a734ae90e668d1f2bbd954354416fec6f2" +dependencies = [ + "abort_on_panic", + "libc", + "libffi-sys", +] + +[[package]] +name = "libffi-sys" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f00e48ce437c5741a4da3b51738498343b5158c37bfa02bcb969efcc44e4e06" +dependencies = [ + "bindgen", + "cc", + "make-cmd", + "pkg-config", +] + +[[package]] +name = "libloading" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" +dependencies = [ + "cc", + "winapi", +] + [[package]] name = "libz-sys" version = "1.1.0" @@ -932,6 +1027,12 @@ dependencies = [ "libc", ] +[[package]] +name = "make-cmd" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8ca8afbe8af1785e09636acb5a41e08a765f5f0340568716c18a8700ba3c0d3" + [[package]] name = "maplit" version = "1.0.2" @@ -999,6 +1100,16 @@ dependencies = [ "void", ] +[[package]] +name = "nom" +version = "4.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" +dependencies = [ + "memchr", + "version_check", +] + [[package]] name = "num-bigint" version = "0.3.0" @@ -1195,6 +1306,12 @@ dependencies = [ "proc-macro-hack", ] +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + [[package]] name = "petgraph" version = "0.5.1" @@ -1539,6 +1656,7 @@ dependencies = [ "cranelift", "cranelift-module", "cranelift-simplejit", + "libffi", "num-traits", "rustpython-bytecode", "thiserror", @@ -1810,6 +1928,12 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "shlex" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" + [[package]] name = "siphasher" version = "0.3.3" @@ -2204,6 +2328,12 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +[[package]] +name = "version_check" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" + [[package]] name = "void" version = "1.0.2" @@ -2298,6 +2428,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "which" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724" +dependencies = [ + "libc", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/jit/Cargo.toml b/jit/Cargo.toml index c6d529cde..3f33538fe 100644 --- a/jit/Cargo.toml +++ b/jit/Cargo.toml @@ -12,5 +12,6 @@ cranelift = "0.66.0" cranelift-module = "0.66.0" cranelift-simplejit = "0.66.0" num-traits = "0.2" +libffi = "0.9.0" rustpython-bytecode = { path = "../bytecode", version = "0.1.1" } thiserror = "1.0" diff --git a/jit/src/instructions.rs b/jit/src/instructions.rs index 4ef117ce1..1c3f12aa0 100644 --- a/jit/src/instructions.rs +++ b/jit/src/instructions.rs @@ -10,6 +10,16 @@ pub struct JitSig { pub ret: Option, } +impl JitSig { + pub fn to_cif(&self) -> libffi::middle::Cif { + let ret = match self.ret { + Some(ref ty) => ty.to_libffi(), + None => libffi::middle::Type::void(), + }; + libffi::middle::Cif::new(Vec::new(), ret) + } +} + #[derive(Clone, PartialEq)] pub enum JitType { Int, @@ -23,6 +33,13 @@ impl JitType { Self::Float => types::F64, } } + + fn to_libffi(&self) -> libffi::middle::Type { + match self { + Self::Int => libffi::middle::Type::i64(), + Self::Float => libffi::middle::Type::f64(), + } + } } #[derive(Clone)] @@ -132,8 +149,9 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> { Ok(()) } Instruction::BinaryOperation { op, .. } => { - let a = self.stack.pop().ok_or(JitCompileError::BadBytecode)?; + // the rhs is popped off first let b = self.stack.pop().ok_or(JitCompileError::BadBytecode)?; + let a = self.stack.pop().ok_or(JitCompileError::BadBytecode)?; match (a.ty, b.ty) { (JitType::Int, JitType::Int) => match op { BinaryOperator::Add => { diff --git a/jit/src/lib.rs b/jit/src/lib.rs index 26c0a24fb..c1e254490 100644 --- a/jit/src/lib.rs +++ b/jit/src/lib.rs @@ -1,5 +1,4 @@ use std::fmt; -use std::mem; use cranelift::prelude::*; use cranelift_module::{Backend, FuncId, Linkage, Module, ModuleError}; @@ -98,20 +97,13 @@ pub struct CompiledCode { impl CompiledCode { pub fn invoke(&self) -> Option { - match self.sig.ret { - Some(JitType::Int) => { - let func = unsafe { mem::transmute::<_, fn() -> i64>(self.code) }; - Some(AbiValue::Int(func())) - } - Some(JitType::Float) => { - let func = unsafe { mem::transmute::<_, fn() -> f64>(self.code) }; - Some(AbiValue::Float(func())) - } - None => { - let func = unsafe { mem::transmute::<_, fn()>(self.code) }; - func(); - None - } + let cif = self.sig.to_cif(); + unsafe { + let value = cif.call::( + libffi::middle::CodePtr::from_ptr(self.code as *const _), + &[], + ); + self.sig.ret.as_ref().map(|ty| value.to_typed(ty)) } } } @@ -121,6 +113,21 @@ pub enum AbiValue { Int(i64), } +union UnTypedAbiValue { + float: f64, + int: i64, + _void: (), +} + +impl UnTypedAbiValue { + unsafe fn to_typed(&self, ty: &JitType) -> AbiValue { + match ty { + JitType::Int => AbiValue::Int(self.int), + JitType::Float => AbiValue::Float(self.float), + } + } +} + unsafe impl Send for CompiledCode {} unsafe impl Sync for CompiledCode {} diff --git a/tests/snippets/jit.py b/tests/snippets/jit.py index f7a02f221..2a8a65903 100644 --- a/tests/snippets/jit.py +++ b/tests/snippets/jit.py @@ -3,6 +3,7 @@ def foo(): a = 5 return 10 + a + def bar(): a = 1e6 return a / 5.0 @@ -12,9 +13,11 @@ def tests(): assert foo() == 15 assert bar() == 2e5 + tests() if hasattr(foo, "__jit__"): print("Has jit") foo.__jit__() + bar.__jit__() tests() From fca23276e0a62276883ef353d07c756ca2f102e1 Mon Sep 17 00:00:00 2001 From: Ben Lewis Date: Fri, 28 Aug 2020 06:23:09 +1200 Subject: [PATCH 2/2] Add documentation to README.md, and install required dependencies on ci. --- .github/workflows/ci.yaml | 8 ++++++++ README.md | 24 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index d2743a465..e0cb0469d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -20,8 +20,12 @@ jobs: - uses: actions/checkout@master - name: Set up the Windows environment run: | + choco install llvm powershell.exe scripts/symlinks-to-hardlinks.ps1 if: runner.os == 'Windows' + - name: Set up the Mac environment + run: brew install autoconf automake libtool + if: runner.os == 'macOS' - name: Cache cargo dependencies uses: actions/cache@v2 with: @@ -52,8 +56,12 @@ jobs: - uses: actions/checkout@master - name: Set up the Windows environment run: | + choco install llvm powershell.exe scripts/symlinks-to-hardlinks.ps1 if: runner.os == 'Windows' + - name: Set up the Mac environment + run: brew install autoconf automake libtool + if: runner.os == 'macOS' - name: Cache cargo dependencies uses: actions/cache@v2 with: diff --git a/README.md b/README.md index f5a45c601..cd743819c 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,30 @@ cargo build --release --target wasm32-wasi --features="freeze-stdlib" > Note: we use the `freeze-stdlib` to include the standard library inside the binary. +### JIT(Just in time) compiler + +RustPython has an **very** experimental JIT compiler that compile python functions into native code. + +#### Building + +By default the JIT compiler isn't enabled, it's enabled with the `jit` cargo feature. + + $ cargo run --features jit + +This requires autoconf, automake, libtool, and clang to be installed. + +#### Using + +To compile a function, call `__jit__()` on it. + +```python +def foo(): + a = 5 + return 10 + a + +foo.__jit__() # this will compile foo to native code and subsequent calls will execute that native code +assert foo() == 15 +``` ## Embedding RustPython into your Rust Applications