diff --git a/.appveyor.yml b/.appveyor.yml index 4b94ce48915..8aaf485d69d 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -18,24 +18,22 @@ environment: cache: - 'C:\Users\appveyor\.cargo' - target - - wapm-cli-target install: - # # Install LLVM - # - mkdir C:\projects\deps - # - cd C:\projects\deps - # - appveyor DownloadFile https://prereleases.llvm.org/win-snapshots/LLVM-8.0.0-r351033-win64.exe -FileName llvm.exe - # - 7z x llvm.exe -oC:\projects\deps\llvm - # # - set "PATH=%PATH%;C:\projects\deps\llvm\bin" - # - set "LLD_LINK=C:\projects\deps\llvm\bin\lld-link.exe" - # - set "LLVM_SYS_80_PREFIX=C:\projects\deps\llvm" - # - cd "%APPVEYOR_BUILD_FOLDER%" + # Install LLVM + - mkdir C:\projects\deps + - cd C:\projects\deps + - appveyor DownloadFile https://github.com/wasmerio/windows-llvm-build/releases/download/v8.0.0/llvm-8.0.0-install.zip -FileName llvm-8.0.0-install.zip + - 7z x llvm-8.0.0-install.zip + - C:\projects\deps\llvm-8.0.0-install\bin\llvm-config.exe --version + - set "LLVM_SYS_80_PREFIX=C:\projects\deps\llvm-8.0.0-install" + - cd "%APPVEYOR_BUILD_FOLDER%" # Install Rust # uncomment these lines if the cache is cleared, or if we must re-install rust for some reason # - appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe # - rustup-init.exe -yv --default-host %target% - - set PATH=%PATH%;C:\\Libraries\\llvm-5.0.0\\bin;%USERPROFILE%\.cargo\bin + - set PATH=%PATH%;%USERPROFILE%\.cargo\bin - rustup default stable-%target% - rustup update - rustc -vV @@ -52,16 +50,7 @@ build_script: - cargo build --release --verbose - git submodule init - git submodule update - # Cache wapm cli target in dir above to prevent breaking git submodule on windows - - if not exist wapm-cli-target mkdir wapm-cli-target - - move wapm-cli-target wapm-cli - - cd wapm-cli - - rename wapm-cli-target target - - cd .. - - cargo build --release --manifest-path wapm-cli/Cargo.toml --features "telemetry update-notifications" - - cd wapm-cli - - cd .. - - xcopy wapm-cli\target wapm-cli-target\ /i /y + - if %APPVEYOR_REPO_BRANCH%==master cargo build --release --manifest-path wapm-cli/Cargo.toml --features "telemetry update-notifications" - cargo build --release --manifest-path lib/runtime-c-api/Cargo.toml test_script: diff --git a/CHANGELOG.md b/CHANGELOG.md index ba484084ee8..113f7b82492 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All PRs to the Wasmer repository must add to this file. Blocks of changes will separated by version increments. ## **[Unreleased]** +- [#598](https://github.com/wasmerio/wasmer/pull/598) LLVM Backend is now supported in Windows - [#599](https://github.com/wasmerio/wasmer/pull/599) Fix llvm backend failures in fat spec tests and simd_binaryen spec test. - [#579](https://github.com/wasmerio/wasmer/pull/579) Fix bug in caching with LLVM and Singlepass backends. Add `default-backend-singlepass`, `default-backend-llvm`, and `default-backend-cranelift` features to `wasmer-runtime` diff --git a/Cargo.lock b/Cargo.lock index c3af0bd1bb8..ca701633ca8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -565,19 +565,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "inkwell" version = "0.1.0" -source = "git+https://github.com/wasmerio/inkwell?branch=llvm8-0#f1775622b6d88fd30006b377042726854c9e0193" +source = "git+https://github.com/wasmerio/inkwell?branch=llvm8-0#4c5359e507b8a3439c9e2c7fff5c336224069638" dependencies = [ "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "enum-methods 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "inkwell_internal_macros 0.1.0 (git+https://github.com/wasmerio/inkwell?branch=llvm8-0)", "libc 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)", "llvm-sys 80.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "inkwell_internal_macros" version = "0.1.0" -source = "git+https://github.com/wasmerio/inkwell?branch=llvm8-0#f1775622b6d88fd30006b377042726854c9e0193" +source = "git+https://github.com/wasmerio/inkwell?branch=llvm8-0#4c5359e507b8a3439c9e2c7fff5c336224069638" dependencies = [ "cargo_toml 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1522,6 +1523,7 @@ dependencies = [ "wabt 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "wasmer-runtime-core 0.5.7", "wasmparser 0.34.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/examples/particle-repel-simd.wasm b/examples/particle-repel-simd.wasm new file mode 100755 index 00000000000..ecc781c450c Binary files /dev/null and b/examples/particle-repel-simd.wasm differ diff --git a/examples/particle-repel.wasm b/examples/particle-repel.wasm new file mode 100755 index 00000000000..7acadf73a6c Binary files /dev/null and b/examples/particle-repel.wasm differ diff --git a/lib/llvm-backend/Cargo.toml b/lib/llvm-backend/Cargo.toml index 30d125917a6..64c3475fb4c 100644 --- a/lib/llvm-backend/Cargo.toml +++ b/lib/llvm-backend/Cargo.toml @@ -1,13 +1,12 @@ [package] name = "wasmer-llvm-backend" version = "0.5.7" -authors = ["Lachlan Sneff "] +authors = ["The Wasmer Engineering Team "] edition = "2018" readme = "README.md" [dependencies] wasmer-runtime-core = { path = "../runtime-core", version = "0.5.7" } -inkwell = { git = "https://github.com/wasmerio/inkwell", branch = "llvm8-0", features = ["llvm8-0"] } wasmparser = "0.34.0" hashbrown = "0.1.8" smallvec = "0.6.8" @@ -16,6 +15,15 @@ libc = "0.2.49" nix = "0.14.0" capstone = { version = "0.5.0", optional = true } +[dependencies.inkwell] +git = "https://github.com/wasmerio/inkwell" +branch = "llvm8-0" +default-features = false +features = ["llvm8-0", "target-x86"] + +[target.'cfg(windows)'.dependencies] +winapi = { version = "0.3", features = ["memoryapi"] } + [build-dependencies] cc = "1.0" lazy_static = "1.2.0" diff --git a/lib/llvm-backend/cpp/object_loader.cpp b/lib/llvm-backend/cpp/object_loader.cpp index 3f2078c84f2..5323505781c 100644 --- a/lib/llvm-backend/cpp/object_loader.cpp +++ b/lib/llvm-backend/cpp/object_loader.cpp @@ -75,16 +75,24 @@ struct MemoryManager : llvm::RuntimeDyld::MemoryManager { } virtual void registerEHFrames(uint8_t* addr, uint64_t LoadAddr, size_t size) override { + // We don't know yet how to do this on Windows, so we hide this on compilation + // so we can compile and pass spectests on unix systems + #ifndef _WIN32 eh_frame_ptr = addr; eh_frame_size = size; eh_frames_registered = true; callbacks.visit_fde(addr, size, __register_frame); + #endif } virtual void deregisterEHFrames() override { + // We don't know yet how to do this on Windows, so we hide this on compilation + // so we can compile and pass spectests on unix systems + #ifndef _WIN32 if (eh_frames_registered) { callbacks.visit_fde(eh_frame_ptr, eh_frame_size, __deregister_frame); } + #endif } virtual bool finalizeMemory(std::string *ErrMsg = nullptr) override { diff --git a/lib/llvm-backend/src/backend.rs b/lib/llvm-backend/src/backend.rs index 7a8c6c64ac8..ff8c281df9a 100644 --- a/lib/llvm-backend/src/backend.rs +++ b/lib/llvm-backend/src/backend.rs @@ -1,14 +1,12 @@ use crate::intrinsics::Intrinsics; +use crate::structs::{Callbacks, LLVMModule, LLVMResult, MemProtect}; use inkwell::{ memory_buffer::MemoryBuffer, module::Module, targets::{CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine}, OptimizationLevel, }; -use libc::{ - c_char, mmap, mprotect, munmap, MAP_ANON, MAP_PRIVATE, PROT_EXEC, PROT_NONE, PROT_READ, - PROT_WRITE, -}; +use libc::c_char; use std::{ any::Any, ffi::{c_void, CString}, @@ -31,42 +29,6 @@ use wasmer_runtime_core::{ vm, vmcalls, }; -#[repr(C)] -struct LLVMModule { - _private: [u8; 0], -} - -#[allow(non_camel_case_types, dead_code)] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[repr(C)] -enum MemProtect { - NONE, - READ, - READ_WRITE, - READ_EXECUTE, -} - -#[allow(non_camel_case_types, dead_code)] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[repr(C)] -enum LLVMResult { - OK, - ALLOCATE_FAILURE, - PROTECT_FAILURE, - DEALLOC_FAILURE, - OBJECT_LOAD_FAILURE, -} - -#[repr(C)] -struct Callbacks { - alloc_memory: extern "C" fn(usize, MemProtect, &mut *mut u8, &mut usize) -> LLVMResult, - protect_memory: extern "C" fn(*mut u8, usize, MemProtect) -> LLVMResult, - dealloc_memory: extern "C" fn(*mut u8, usize) -> LLVMResult, - - lookup_vm_symbol: extern "C" fn(*const c_char, usize) -> *const vm::Func, - visit_fde: extern "C" fn(*mut u8, usize, extern "C" fn(*mut u8)), -} - extern "C" { fn module_load( mem_ptr: *const u8, @@ -99,69 +61,21 @@ extern "C" { } fn get_callbacks() -> Callbacks { - fn round_up_to_page_size(size: usize) -> usize { - (size + (4096 - 1)) & !(4096 - 1) - } - extern "C" fn alloc_memory( size: usize, protect: MemProtect, ptr_out: &mut *mut u8, size_out: &mut usize, ) -> LLVMResult { - let size = round_up_to_page_size(size); - let ptr = unsafe { - mmap( - ptr::null_mut(), - size, - match protect { - MemProtect::NONE => PROT_NONE, - MemProtect::READ => PROT_READ, - MemProtect::READ_WRITE => PROT_READ | PROT_WRITE, - MemProtect::READ_EXECUTE => PROT_READ | PROT_EXEC, - }, - MAP_PRIVATE | MAP_ANON, - -1, - 0, - ) - }; - if ptr as isize == -1 { - return LLVMResult::ALLOCATE_FAILURE; - } - *ptr_out = ptr as _; - *size_out = size; - LLVMResult::OK + unsafe { crate::platform::alloc_memory(size, protect, ptr_out, size_out) } } extern "C" fn protect_memory(ptr: *mut u8, size: usize, protect: MemProtect) -> LLVMResult { - let res = unsafe { - mprotect( - ptr as _, - round_up_to_page_size(size), - match protect { - MemProtect::NONE => PROT_NONE, - MemProtect::READ => PROT_READ, - MemProtect::READ_WRITE => PROT_READ | PROT_WRITE, - MemProtect::READ_EXECUTE => PROT_READ | PROT_EXEC, - }, - ) - }; - - if res == 0 { - LLVMResult::OK - } else { - LLVMResult::PROTECT_FAILURE - } + unsafe { crate::platform::protect_memory(ptr, size, protect) } } extern "C" fn dealloc_memory(ptr: *mut u8, size: usize) -> LLVMResult { - let res = unsafe { munmap(ptr as _, round_up_to_page_size(size)) }; - - if res == 0 { - LLVMResult::OK - } else { - LLVMResult::DEALLOC_FAILURE - } + unsafe { crate::platform::dealloc_memory(ptr, size) } } extern "C" fn lookup_vm_symbol(name_ptr: *const c_char, length: usize) -> *const vm::Func { diff --git a/lib/llvm-backend/src/lib.rs b/lib/llvm-backend/src/lib.rs index 0c6d00006d8..9d7a7acf5d0 100644 --- a/lib/llvm-backend/src/lib.rs +++ b/lib/llvm-backend/src/lib.rs @@ -7,6 +7,7 @@ mod intrinsics; mod platform; mod read_info; mod state; +mod structs; mod trampolines; pub use code::LLVMFunctionCodeGenerator as FunctionCodeGenerator; diff --git a/lib/llvm-backend/src/platform/common.rs b/lib/llvm-backend/src/platform/common.rs new file mode 100644 index 00000000000..7fcd8493e85 --- /dev/null +++ b/lib/llvm-backend/src/platform/common.rs @@ -0,0 +1,3 @@ +pub fn round_up_to_page_size(size: usize) -> usize { + (size + (4096 - 1)) & !(4096 - 1) +} diff --git a/lib/llvm-backend/src/platform/mod.rs b/lib/llvm-backend/src/platform/mod.rs index 01b81b022e2..b61d0b57e75 100644 --- a/lib/llvm-backend/src/platform/mod.rs +++ b/lib/llvm-backend/src/platform/mod.rs @@ -1,7 +1,14 @@ +mod common; + #[cfg(unix)] mod unix; #[cfg(unix)] pub use self::unix::*; #[cfg(target_family = "windows")] -compile_error!("windows not yet supported for the llvm-based compiler backend"); +mod win; +#[cfg(target_family = "windows")] +pub use self::win::*; + +#[cfg(not(any(unix, target_family = "windows")))] +compile_error!("Your system is not yet supported for the llvm-based compiler backend"); diff --git a/lib/llvm-backend/src/platform/unix.rs b/lib/llvm-backend/src/platform/unix.rs index d16384179c1..a07afa13042 100644 --- a/lib/llvm-backend/src/platform/unix.rs +++ b/lib/llvm-backend/src/platform/unix.rs @@ -1,5 +1,11 @@ -use libc::{c_void, siginfo_t}; +use super::common::round_up_to_page_size; +use crate::structs::{LLVMResult, MemProtect}; +use libc::{ + c_void, mmap, mprotect, munmap, siginfo_t, MAP_ANON, MAP_PRIVATE, PROT_EXEC, PROT_NONE, + PROT_READ, PROT_WRITE, +}; use nix::sys::signal::{sigaction, SaFlags, SigAction, SigHandler, SigSet, SIGBUS, SIGSEGV}; +use std::ptr; /// `__register_frame` and `__deregister_frame` on macos take a single fde as an /// argument, so we need to parse the fde table here. @@ -68,3 +74,60 @@ extern "C" fn signal_trap_handler( throw_trap(2); } } + +pub unsafe fn alloc_memory( + size: usize, + protect: MemProtect, + ptr_out: &mut *mut u8, + size_out: &mut usize, +) -> LLVMResult { + let size = round_up_to_page_size(size); + let ptr = mmap( + ptr::null_mut(), + size, + match protect { + MemProtect::NONE => PROT_NONE, + MemProtect::READ => PROT_READ, + MemProtect::READ_WRITE => PROT_READ | PROT_WRITE, + MemProtect::READ_EXECUTE => PROT_READ | PROT_EXEC, + }, + MAP_PRIVATE | MAP_ANON, + -1, + 0, + ); + if ptr as isize == -1 { + return LLVMResult::ALLOCATE_FAILURE; + } + *ptr_out = ptr as _; + *size_out = size; + LLVMResult::OK +} + +pub unsafe fn protect_memory(ptr: *mut u8, size: usize, protect: MemProtect) -> LLVMResult { + let res = mprotect( + ptr as _, + round_up_to_page_size(size), + match protect { + MemProtect::NONE => PROT_NONE, + MemProtect::READ => PROT_READ, + MemProtect::READ_WRITE => PROT_READ | PROT_WRITE, + MemProtect::READ_EXECUTE => PROT_READ | PROT_EXEC, + }, + ); + + if res == 0 { + LLVMResult::OK + } else { + LLVMResult::PROTECT_FAILURE + } +} + +pub unsafe fn dealloc_memory(ptr: *mut u8, size: usize) -> LLVMResult { + let res = munmap(ptr as _, round_up_to_page_size(size)); + + if res == 0 { + LLVMResult::OK + } else { + LLVMResult::DEALLOC_FAILURE + } +} diff --git a/lib/llvm-backend/src/platform/win.rs b/lib/llvm-backend/src/platform/win.rs new file mode 100644 index 00000000000..88315fd24a8 --- /dev/null +++ b/lib/llvm-backend/src/platform/win.rs @@ -0,0 +1,80 @@ +use super::common::round_up_to_page_size; +use crate::structs::{LLVMResult, MemProtect}; +use std::ptr; + +use winapi::um::memoryapi::{VirtualAlloc, VirtualFree}; +use winapi::um::winnt::{ + MEM_COMMIT, MEM_DECOMMIT, MEM_RESERVE, PAGE_EXECUTE_READ, PAGE_NOACCESS, PAGE_READONLY, + PAGE_READWRITE, +}; + +pub unsafe fn visit_fde(_addr: *mut u8, _size: usize, _visitor: extern "C" fn(*mut u8)) { + // Do nothing on Windows +} + +pub unsafe fn install_signal_handler() { + // Do nothing on Windows +} + +pub unsafe fn alloc_memory( + size: usize, + protect: MemProtect, + ptr_out: &mut *mut u8, + size_out: &mut usize, +) -> LLVMResult { + let size = round_up_to_page_size(size); + let flags = if protect == MemProtect::NONE { + MEM_RESERVE + } else { + MEM_RESERVE | MEM_COMMIT + }; + let ptr = VirtualAlloc( + ptr::null_mut(), + size, + flags, + memprotect_to_protect_const(protect), + ); + + if ptr.is_null() { + return LLVMResult::ALLOCATE_FAILURE; + } + + *ptr_out = ptr as _; + *size_out = size; + LLVMResult::OK +} + +pub unsafe fn protect_memory(ptr: *mut u8, size: usize, protect: MemProtect) -> LLVMResult { + let size = round_up_to_page_size(size); + let ptr = VirtualAlloc( + ptr as _, + size, + MEM_COMMIT, + memprotect_to_protect_const(protect), + ); + + if ptr.is_null() { + LLVMResult::PROTECT_FAILURE + } else { + LLVMResult::OK + } +} + +pub unsafe fn dealloc_memory(ptr: *mut u8, size: usize) -> LLVMResult { + let success = VirtualFree(ptr as _, size, MEM_DECOMMIT); + // If the function succeeds, the return value is nonzero. + if success == 1 { + LLVMResult::OK + } else { + LLVMResult::DEALLOC_FAILURE + } +} + +fn memprotect_to_protect_const(protect: MemProtect) -> u32 { + match protect { + MemProtect::NONE => PAGE_NOACCESS, + MemProtect::READ => PAGE_READONLY, + MemProtect::READ_WRITE => PAGE_READWRITE, + MemProtect::READ_EXECUTE => PAGE_EXECUTE_READ, + } +} diff --git a/lib/llvm-backend/src/structs.rs b/lib/llvm-backend/src/structs.rs new file mode 100644 index 00000000000..0aa2b6e9cb6 --- /dev/null +++ b/lib/llvm-backend/src/structs.rs @@ -0,0 +1,39 @@ +use libc::c_char; + +use wasmer_runtime_core::vm; + +#[repr(C)] +pub struct LLVMModule { + _private: [u8; 0], +} + +#[allow(non_camel_case_types, dead_code)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[repr(C)] +pub enum MemProtect { + NONE, + READ, + READ_WRITE, + READ_EXECUTE, +} + +#[allow(non_camel_case_types, dead_code)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[repr(C)] +pub enum LLVMResult { + OK, + ALLOCATE_FAILURE, + PROTECT_FAILURE, + DEALLOC_FAILURE, + OBJECT_LOAD_FAILURE, +} + +#[repr(C)] +pub struct Callbacks { + pub alloc_memory: extern "C" fn(usize, MemProtect, &mut *mut u8, &mut usize) -> LLVMResult, + pub protect_memory: extern "C" fn(*mut u8, usize, MemProtect) -> LLVMResult, + pub dealloc_memory: extern "C" fn(*mut u8, usize) -> LLVMResult, + + pub lookup_vm_symbol: extern "C" fn(*const c_char, usize) -> *const vm::Func, + pub visit_fde: extern "C" fn(*mut u8, usize, extern "C" fn(*mut u8)), +} diff --git a/lib/runtime/Cargo.toml b/lib/runtime/Cargo.toml index e8e02afd01e..b4ac5c9d5a3 100644 --- a/lib/runtime/Cargo.toml +++ b/lib/runtime/Cargo.toml @@ -27,7 +27,7 @@ tempfile = "3.0.7" criterion = "0.2" wabt = "0.9.0" -[target.'cfg(not(windows))'.dependencies.wasmer-llvm-backend] +[dependencies.wasmer-llvm-backend] path = "../llvm-backend" optional = true