diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 970a32ae5..5d029d1cd 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -54,6 +54,9 @@ jobs: - target: powerpc64le-unknown-linux-gnu os: ubuntu-latest rust: nightly + - target: sbf-solana-solana + os: ubuntu-latest + rust: nightly - target: thumbv6m-none-eabi os: ubuntu-latest rust: nightly @@ -97,6 +100,7 @@ jobs: run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} shell: bash - run: rustup target add ${{ matrix.target }} + if: matrix.target != 'sbf-solana-solana' - run: rustup component add llvm-tools-preview - name: Download compiler-rt reference sources run: | diff --git a/build.rs b/build.rs index 47c8b4ffe..e5bcad17b 100644 --- a/build.rs +++ b/build.rs @@ -43,6 +43,7 @@ fn main() { || target.contains("i686") || target.contains("aarch64") || target.contains("bpf") + || target.contains("sbf") { println!("cargo:rustc-cfg=feature=\"mem-unaligned\""); } @@ -272,6 +273,7 @@ mod c { let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap(); let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap(); let target_vendor = env::var("CARGO_CFG_TARGET_VENDOR").unwrap(); + let target_feature = env::var("CARGO_CFG_TARGET_FEATURE").unwrap_or_default(); let mut consider_float_intrinsics = true; let cfg = &mut cc::Build::new(); @@ -625,6 +627,23 @@ mod c { sources.extend(&[("__emutls_get_address", "emutls.c")]); } + if target_os == "solana" { + cfg.define("__ELF__", None); + // Use the static-syscall target feature to detect if we're + // compiling for sbfv2, in which case set the corresponding clang + // cpu flag. + if target_feature.contains("static-syscalls") { + cfg.flag("-mcpu=sbfv2"); + } + // Remove the implementations that fail to build. + // This list should shrink to zero + sources.remove(&[ + "__int_util", // Unsupported architecture error + "__mulvdi3", // Unsupported signed division + "__mulvsi3", // Unsupported signed division + ]); + } + // When compiling the C code we require the user to tell us where the // source code is, and this is largely done so when we're compiling as // part of rust-lang/rust we can use the same llvm-project repository as diff --git a/ci/docker/sbf-solana-solana/Dockerfile b/ci/docker/sbf-solana-solana/Dockerfile new file mode 100644 index 000000000..d60cc649f --- /dev/null +++ b/ci/docker/sbf-solana-solana/Dockerfile @@ -0,0 +1,23 @@ +FROM ubuntu:20.04 +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + curl \ + gcc libc6-dev ca-certificates + +ENV RUSTUP_INIT_SKIP_PATH_CHECK="yes" +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y -v --no-modify-path +RUN cp ${HOME}/.cargo/bin/* /usr/local/bin/ + +RUN cargo install --git https://github.com/solana-labs/cargo-run-solana-tests.git \ + --rev df2f642924aee7bbd2566017b3d71cb0c389b015 \ + --bin cargo-run-solana-tests --root /usr/local + +RUN mkdir -p /tmp/.cache/solana/v1.38/platform-tools +RUN curl -L -o platform-tools-linux-x86_64.tar.bz2 https://github.com/solana-labs/platform-tools/releases/download/v1.38/platform-tools-linux-x86_64.tar.bz2 +RUN tar -xjf platform-tools-linux-x86_64.tar.bz2 --strip-components 1 -C /tmp/.cache/solana/v1.38/platform-tools +RUN rustup toolchain link solana /tmp/.cache/solana/v1.38/platform-tools/rust +RUN cp -R ${HOME}/.rustup /tmp/ + +ENV CARGO_TARGET_SBF_SOLANA_SOLANA_RUNNER="cargo-run-solana-tests --heap-size 104857600" +ENV CC="/tmp/.cache/solana/v1.38/platform-tools/llvm/bin/clang" +ENV RUSTUP_TOOLCHAIN="solana" diff --git a/examples/intrinsics.rs b/examples/intrinsics.rs index 54b703dfb..88b1a2705 100644 --- a/examples/intrinsics.rs +++ b/examples/intrinsics.rs @@ -16,7 +16,12 @@ extern crate panic_handler; -#[cfg(all(not(thumb), not(windows), not(target_arch = "wasm32")))] +#[cfg(all( + not(thumb), + not(windows), + not(target_arch = "wasm32"), + not(target_os = "solana") +))] #[link(name = "c")] extern "C" {} diff --git a/src/lib.rs b/src/lib.rs index 40564178a..98305751a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ #![cfg_attr(not(feature = "no-asm"), feature(global_asm))] #![feature(cfg_target_has_atomic)] #![feature(compiler_builtins)] -#![feature(core_ffi_c)] +#![cfg_attr(not(target_os = "solana"), feature(core_ffi_c))] #![feature(core_intrinsics)] #![feature(inline_const)] #![feature(lang_items)] @@ -78,4 +78,12 @@ pub mod x86; #[cfg(target_arch = "x86_64")] pub mod x86_64; +#[cfg(all(target_os = "solana", target_feature = "static-syscalls"))] +#[cfg_attr(not(feature = "mangled-names"), no_mangle)] +#[linkage = "weak"] +pub unsafe extern "C" fn abort() -> ! { + let syscall: extern "C" fn() -> ! = core::mem::transmute(3069975057u64); // murmur32 hash of "abort" + syscall() +} + pub mod probestack; diff --git a/src/mem/mod.rs b/src/mem/mod.rs index d0ff50158..c22c3a7ef 100644 --- a/src/mem/mod.rs +++ b/src/mem/mod.rs @@ -8,17 +8,22 @@ type c_int = i16; #[cfg(not(target_pointer_width = "16"))] type c_int = i32; +#[cfg(not(target_os = "solana"))] use core::intrinsics::{atomic_load_unordered, atomic_store_unordered, exact_div}; +#[cfg(not(target_os = "solana"))] use core::mem; +#[cfg(not(target_os = "solana"))] use core::ops::{BitOr, Shl}; // memcpy/memmove/memset have optimized implementations on some architectures +#[cfg(not(target_os = "solana"))] #[cfg_attr( all(not(feature = "no-asm"), target_arch = "x86_64"), path = "x86_64.rs" )] mod impls; +#[cfg(not(target_os = "solana"))] intrinsics! { #[mem_builtin] pub unsafe extern "C" fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 { @@ -62,6 +67,7 @@ intrinsics! { } // `bytes` must be a multiple of `mem::size_of::()` +#[cfg(not(target_os = "solana"))] #[cfg_attr(not(target_has_atomic_load_store = "8"), allow(dead_code))] fn memcpy_element_unordered_atomic(dest: *mut T, src: *const T, bytes: usize) { unsafe { @@ -75,6 +81,7 @@ fn memcpy_element_unordered_atomic(dest: *mut T, src: *const T, bytes: } // `bytes` must be a multiple of `mem::size_of::()` +#[cfg(not(target_os = "solana"))] #[cfg_attr(not(target_has_atomic_load_store = "8"), allow(dead_code))] fn memmove_element_unordered_atomic(dest: *mut T, src: *const T, bytes: usize) { unsafe { @@ -98,6 +105,7 @@ fn memmove_element_unordered_atomic(dest: *mut T, src: *const T, bytes: } // `T` must be a primitive integer type, and `bytes` must be a multiple of `mem::size_of::()` +#[cfg(not(target_os = "solana"))] #[cfg_attr(not(target_has_atomic_load_store = "8"), allow(dead_code))] fn memset_element_unordered_atomic(s: *mut T, c: u8, bytes: usize) where @@ -124,6 +132,7 @@ where } } +#[cfg(not(target_os = "solana"))] intrinsics! { #[cfg(target_has_atomic_load_store = "8")] pub unsafe extern "C" fn __llvm_memcpy_element_unordered_atomic_1(dest: *mut u8, src: *const u8, bytes: usize) -> () { @@ -188,3 +197,204 @@ intrinsics! { memset_element_unordered_atomic(s, c, bytes); } } + +// MEM functions have been rewritten to copy 8 byte chunks. No +// compensation for alignment is made here with the requirement that +// the underlying hardware supports unaligned loads/stores. If the +// number of store operations is greater than 8 the memory operation +// is performed in the run-time system instead, by calling the +// corresponding "C" function. + +#[cfg(all(target_os = "solana", not(target_feature = "static-syscalls")))] +mod syscalls { + extern "C" { + pub fn sol_memcpy_(dest: *mut u8, src: *const u8, n: u64); + pub fn sol_memmove_(dest: *mut u8, src: *const u8, n: u64); + pub fn sol_memset_(s: *mut u8, c: u8, n: u64); + pub fn sol_memcmp_(s1: *const u8, s2: *const u8, n: u64, result: *mut i32); + } +} + +#[cfg(all(target_os = "solana", target_feature = "static-syscalls"))] +mod syscalls { + pub(crate) fn sol_memcpy_(dest: *mut u8, src: *const u8, n: u64) { + let syscall: extern "C" fn(*mut u8, *const u8, u64) = + unsafe { core::mem::transmute(1904002211u64) }; // murmur32 hash of "sol_memcpy_" + syscall(dest, src, n) + } + + pub(crate) fn sol_memmove_(dest: *mut u8, src: *const u8, n: u64) { + let syscall: extern "C" fn(*mut u8, *const u8, u64) = + unsafe { core::mem::transmute(1128493560u64) }; // murmur32 hash of "sol_memmove_" + syscall(dest, src, n) + } + + pub(crate) fn sol_memcmp_(dest: *const u8, src: *const u8, n: u64, result: *mut i32) { + let syscall: extern "C" fn(*const u8, *const u8, u64, *mut i32) = + unsafe { core::mem::transmute(1608310321u64) }; // murmur32 hash of "sol_memcmp_" + syscall(dest, src, n, result) + } + + pub(crate) fn sol_memset_(dest: *mut u8, c: u8, n: u64) { + let syscall: extern "C" fn(*mut u8, u8, u64) = + unsafe { core::mem::transmute(930151202u64) }; // murmur32 hash of "sol_memset_" + syscall(dest, c, n) + } +} + +#[cfg(target_os = "solana")] +use self::syscalls::*; + +#[cfg(target_os = "solana")] +const NSTORE_THRESHOLD: usize = 15; + +#[cfg(target_os = "solana")] +#[cfg_attr( + all(feature = "mem-unaligned", not(feature = "mangled-names")), + no_mangle +)] +#[inline] +pub unsafe extern "C" fn memcpy(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 { + let chunks = (n / 8) as isize; + let nstore = n - (7 * chunks) as usize; + if nstore > NSTORE_THRESHOLD { + sol_memcpy_(dest, src, n as u64); + return dest; + } + let mut i: isize = 0; + if chunks != 0 { + let dest_64 = dest as *mut _ as *mut u64; + let src_64 = src as *const _ as *const u64; + while i < chunks { + *dest_64.offset(i) = *src_64.offset(i); + i += 1; + } + i *= 8; + } + while i < n as isize { + *dest.offset(i) = *src.offset(i); + i += 1; + } + dest +} + +#[cfg(target_os = "solana")] +#[cfg_attr( + all(feature = "mem-unaligned", not(feature = "mangled-names")), + no_mangle +)] +#[inline] +pub unsafe extern "C" fn memmove(dest: *mut u8, src: *const u8, n: usize) -> *mut u8 { + let chunks = (n / 8) as isize; + let nstore = n - (7 * chunks) as usize; + if nstore > NSTORE_THRESHOLD { + sol_memmove_(dest, src, n as u64); + return dest; + } + if src < dest as *const u8 { + // copy from end + let mut i = n as isize; + while i > chunks * 8 { + i -= 1; + *dest.offset(i) = *src.offset(i); + } + i = chunks; + if i > 0 { + let dest_64 = dest as *mut _ as *mut u64; + let src_64 = src as *const _ as *const u64; + while i > 0 { + i -= 1; + *dest_64.offset(i) = *src_64.offset(i); + } + } + } else { + // copy from beginning + let mut i: isize = 0; + if chunks != 0 { + let dest_64 = dest as *mut _ as *mut u64; + let src_64 = src as *const _ as *const u64; + while i < chunks { + *dest_64.offset(i) = *src_64.offset(i); + i += 1; + } + i *= 8; + } + while i < n as isize { + *dest.offset(i) = *src.offset(i); + i += 1; + } + } + dest +} + +#[cfg(target_os = "solana")] +#[cfg_attr( + all(feature = "mem-unaligned", not(feature = "mangled-names")), + no_mangle +)] +#[inline] +pub unsafe extern "C" fn memset(s: *mut u8, c: c_int, n: usize) -> *mut u8 { + let chunks = (n / 8) as isize; + let nstore = n - (7 * chunks) as usize; + if nstore > NSTORE_THRESHOLD { + sol_memset_(s, c as u8, n as u64); + return s; + } + let mut i: isize = 0; + if chunks != 0 { + let mut c_64 = c as u64 & 0xFF as u64; + c_64 |= c_64 << 8; + c_64 |= c_64 << 16; + c_64 |= c_64 << 32; + let s_64 = s as *mut _ as *mut u64; + while i < chunks { + *s_64.offset(i) = c_64; + i += 1; + } + i *= 8; + } + while i < n as isize { + *s.offset(i) = c as u8; + i += 1; + } + s +} + +#[cfg(target_os = "solana")] +#[cfg_attr( + all(feature = "mem-unaligned", not(feature = "mangled-names")), + no_mangle +)] +#[inline] +pub unsafe extern "C" fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32 { + let chunks = (n / 8) as isize; + let nstore = n - (7 * chunks) as usize; + if nstore > NSTORE_THRESHOLD { + let mut result = 0; + sol_memcmp_(s1, s2, n as u64, &mut result as *mut i32); + return result; + } + let mut i: isize = 0; + if chunks != 0 { + let s1_64 = s1 as *const _ as *const u64; + let s2_64 = s2 as *const _ as *const u64; + while i < chunks { + let a = *s1_64.offset(i); + let b = *s2_64.offset(i); + if a != b { + break; + } + i += 1; + } + i *= 8; + } + while i < n as isize { + let a = *s1.offset(i); + let b = *s2.offset(i); + if a != b { + return a as i32 - b as i32; + } + i += 1; + } + 0 +} diff --git a/testcrate/tests/conv.rs b/testcrate/tests/conv.rs index 5cff01202..daec9e05e 100644 --- a/testcrate/tests/conv.rs +++ b/testcrate/tests/conv.rs @@ -56,7 +56,8 @@ macro_rules! i_to_f { if f0 != f1 && !cfg!(any( target_arch = "x86", target_arch = "powerpc", - target_arch = "powerpc64" + target_arch = "powerpc64", + target_family = "solana" )) { panic!( "{}({}): std: {}, builtins: {}", diff --git a/testcrate/tests/div_rem.rs b/testcrate/tests/div_rem.rs index de3bd9bee..5debc3e7e 100644 --- a/testcrate/tests/div_rem.rs +++ b/testcrate/tests/div_rem.rs @@ -132,7 +132,10 @@ macro_rules! float { }; } -#[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] +#[cfg(not(any( + all(target_arch = "x86", not(target_feature = "sse")), + target_family = "solana" +)))] #[test] fn float_div() { use compiler_builtins::float::{ diff --git a/testcrate/tests/mem.rs b/testcrate/tests/mem.rs index 5099d69ed..59ae0bc5d 100644 --- a/testcrate/tests/mem.rs +++ b/testcrate/tests/mem.rs @@ -37,6 +37,7 @@ fn memcpy_10() { } } +#[cfg(not(target_os = "solana"))] #[test] fn memcpy_big() { // Make the arrays cross 3 pages @@ -163,6 +164,7 @@ fn memmove_forward_misaligned_nonaligned_start() { } } +#[cfg(not(target_os = "solana"))] #[test] fn memmove_forward_misaligned_aligned_start() { let mut arr = gen_arr::<32>(); diff --git a/testcrate/tests/misc.rs b/testcrate/tests/misc.rs index cdc37e2a0..053acd485 100644 --- a/testcrate/tests/misc.rs +++ b/testcrate/tests/misc.rs @@ -133,7 +133,10 @@ macro_rules! pow { }; } -#[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))] +#[cfg(not(any( + all(target_arch = "x86", not(target_feature = "sse")), + target_os = "solana" +)))] #[test] fn float_pow() { use compiler_builtins::float::pow::{__powidf2, __powisf2}; @@ -143,4 +146,4 @@ fn float_pow() { f32, 1e-4, __powisf2; f64, 1e-12, __powidf2; ); -} +} \ No newline at end of file