add riscv64 backend for cranelift. (#4271)

Add a RISC-V 64 (`riscv64`, RV64GC) backend.

Co-authored-by: yuyang <756445638@qq.com>
Co-authored-by: Chris Fallin <chris@cfallin.org>
Co-authored-by: Afonso Bordado <afonsobordado@az8.co>
This commit is contained in:
yuyang-ok
2022-09-28 08:30:31 +08:00
committed by GitHub
parent 9715d91c50
commit cdecc858b4
182 changed files with 21024 additions and 36 deletions

View File

@@ -240,6 +240,7 @@ pub trait Compiler: Send + Sync {
Arm(_) => Architecture::Arm,
Aarch64(_) => Architecture::Aarch64,
S390x => Architecture::S390x,
Riscv64(_) => Architecture::Riscv64,
architecture => {
anyhow::bail!("target architecture {:?} is unsupported", architecture,);
}

View File

@@ -189,7 +189,9 @@ cfg_if::cfg_if! {
} else if #[cfg(target_arch = "s390x")] {
// currently `global_asm!` isn't stable on s390x so this is an external
// assembler file built with the `build.rs`.
} else {
} else if #[cfg(target_arch = "riscv64")] {
mod riscv64;
}else {
compile_error!("fibers are not supported on this CPU architecture");
}
}

View File

@@ -0,0 +1,157 @@
// A WORD OF CAUTION
//
// This entire file basically needs to be kept in sync with itself. It's not
// really possible to modify just one bit of this file without understanding
// all the other bits. Documentation tries to reference various bits here and
// there but try to make sure to read over everything before tweaking things!
use wasmtime_asm_macros::asm_func;
// fn(top_of_stack(rdi): *mut u8)
asm_func!(
"wasmtime_fiber_switch",
"
// See https://github.com/rust-lang/rust/issues/80608.
.attribute arch, \"rv64gc\"
// We're switching to arbitrary code somewhere else, so pessimistically
// assume that all callee-save register are clobbered. This means we need
// to save/restore all of them.
//
// Note that this order for saving is important since we use CFI directives
// below to point to where all the saved registers are.
sd ra,-0x8(sp)
sd fp,-0x10(sp)
sd s1,-0x18(sp)
sd s2,-0x20(sp)
sd s3,-0x28(sp)
sd s4,-0x30(sp)
sd s5,-0x38(sp)
sd s6,-0x40(sp)
sd s7,-0x48(sp)
sd s8,-0x50(sp)
sd s9,-0x58(sp)
sd s10,-0x60(sp)
sd s11,-0x68(sp)
fsd fs0,-0x70(sp)
fsd fs1,-0x78(sp)
fsd fs2,-0x80(sp)
fsd fs3,-0x88(sp)
fsd fs4,-0x90(sp)
fsd fs5,-0x98(sp)
fsd fs6,-0xa0(sp)
fsd fs7,-0xa8(sp)
fsd fs8,-0xb0(sp)
fsd fs9,-0xb8(sp)
fsd fs10,-0xc0(sp)
fsd fs11,-0xc8(sp)
addi sp , sp , -0xd0
ld t0 ,-0x10(a0)
sd sp ,-0x10(a0)
// Swap stacks and restore all our callee-saved registers
mv sp,t0
fld fs11,0x8(sp)
fld fs10,0x10(sp)
fld fs9,0x18(sp)
fld fs8,0x20(sp)
fld fs7,0x28(sp)
fld fs6,0x30(sp)
fld fs5,0x38(sp)
fld fs4,0x40(sp)
fld fs3,0x48(sp)
fld fs2,0x50(sp)
fld fs1,0x58(sp)
fld fs0,0x60(sp)
ld s11,0x68(sp)
ld s10,0x70(sp)
ld s9,0x78(sp)
ld s8,0x80(sp)
ld s7,0x88(sp)
ld s6,0x90(sp)
ld s5,0x98(sp)
ld s4,0xa0(sp)
ld s3,0xa8(sp)
ld s2,0xb0(sp)
ld s1,0xb8(sp)
ld fp,0xc0(sp)
ld ra,0xc8(sp)
addi sp , sp , 0xd0
jr ra
",
);
// fn(
// top_of_stack(a0): *mut u8,
// entry_point(a1): extern fn(*mut u8, *mut u8),
// entry_arg0(a2): *mut u8,
// )
#[rustfmt::skip]
asm_func!(
"wasmtime_fiber_init",
"
lla t0,wasmtime_fiber_start
sd t0,-0x18(a0) // ra,first should be wasmtime_fiber_start.
sd a0,-0x20(a0) // fp pointer.
sd a1,-0x28(a0) // entry_point will load to s1.
sd a2,-0x30(a0) // entry_arg0 will load to s2.
//
addi t0,a0,-0xe0
sd t0,-0x10(a0)
ret
",
);
asm_func!(
"wasmtime_fiber_start",
"
.cfi_startproc simple
.cfi_def_cfa_offset 0
.cfi_escape 0x0f, /* DW_CFA_def_cfa_expression */ \
5, /* the byte length of this expression */ \
0x52, /* DW_OP_reg2 (sp) */ \
0x06, /* DW_OP_deref */ \
0x08, 0xd0 , /* DW_OP_const1u 0xc8 */ \
0x22 /* DW_OP_plus */
.cfi_rel_offset ra,-0x8
.cfi_rel_offset fp,-0x10
.cfi_rel_offset s1,-0x18
.cfi_rel_offset s2,-0x20
.cfi_rel_offset s3,-0x28
.cfi_rel_offset s4,-0x30
.cfi_rel_offset s5,-0x38
.cfi_rel_offset s6,-0x40
.cfi_rel_offset s7,-0x48
.cfi_rel_offset s8,-0x50
.cfi_rel_offset s9,-0x58
.cfi_rel_offset s10,-0x60
.cfi_rel_offset s11,-0x68
.cfi_rel_offset fs0,-0x70
.cfi_rel_offset fs1,-0x78
.cfi_rel_offset fs2,-0x80
.cfi_rel_offset fs3,-0x88
.cfi_rel_offset fs4,-0x90
.cfi_rel_offset fs5,-0x98
.cfi_rel_offset fs6,-0xa0
.cfi_rel_offset fs7,-0xa8
.cfi_rel_offset fs8,-0xb0
.cfi_rel_offset fs9,-0xb8
.cfi_rel_offset fs10,-0xc0
.cfi_rel_offset fs11,-0xc8
mv a0,s2
mv a1,fp
jalr s1
// .4byte 0 will cause panic.
// for safety just like x86_64.rs.
.4byte 0
.cfi_endproc
",
);

View File

@@ -30,8 +30,8 @@ wasmi = "0.11.0"
# We rely on precompiled v8 binaries, but rusty-v8 doesn't have a precompiled
# binary for MinGW which is built on our CI. It does have one for Windows-msvc,
# though, so we could use that if we wanted. For now though just simplify a bit
# and don't depend on this on Windows. The same applies on s390x.
[target.'cfg(not(any(windows, target_arch = "s390x")))'.dependencies]
# and don't depend on this on Windows. The same applies on s390x and riscv.
[target.'cfg(not(any(windows, target_arch = "s390x", target_arch = "riscv64")))'.dependencies]
v8 = "0.44.3"
[dev-dependencies]

View File

@@ -31,7 +31,7 @@ use std::time::{Duration, Instant};
use wasmtime::*;
use wasmtime_wast::WastContext;
#[cfg(not(any(windows, target_arch = "s390x")))]
#[cfg(not(any(windows, target_arch = "s390x", target_arch = "riscv64")))]
mod diff_v8;
static CNT: AtomicUsize = AtomicUsize::new(0);

View File

@@ -24,9 +24,9 @@ pub fn build(
#[cfg(not(feature = "fuzz-spec-interpreter"))]
"spec" => return Ok(None),
#[cfg(not(any(windows, target_arch = "s390x")))]
#[cfg(not(any(windows, target_arch = "s390x", target_arch = "riscv64")))]
"v8" => Box::new(crate::oracles::diff_v8::V8Engine::new(config)),
#[cfg(any(windows, target_arch = "s390x"))]
#[cfg(any(windows, target_arch = "s390x", target_arch = "riscv64"))]
"v8" => return Ok(None),
_ => panic!("unknown engine {name}"),

View File

@@ -97,6 +97,7 @@ fn ensure_supported_elf_format(bytes: &[u8]) -> Result<Endianness, Error> {
EM_AARCH64 => (),
EM_X86_64 => (),
EM_S390 => (),
EM_RISCV => (),
machine => {
bail!("Unsupported ELF target machine: {:x}", machine);
}

View File

@@ -51,6 +51,9 @@ cfg_if::cfg_if! {
} else if #[cfg(target_arch = "s390x")] {
#[macro_use]
mod s390x;
}else if #[cfg(target_arch = "riscv64")] {
#[macro_use]
mod riscv64;
} else {
compile_error!("unsupported architecture");
}

View File

@@ -0,0 +1,117 @@
use wasmtime_asm_macros::asm_func;
#[rustfmt::skip]
asm_func!(
"host_to_wasm_trampoline",
r#"
.cfi_startproc
// Load the pointer to `VMRuntimeLimits` in `t0`.
ld t0, 8(a1)
// Check to see if callee is a core `VMContext` (MAGIC == "core"). NB:
// we do not support big-endian riscv64 so the magic value is always
// little-endian encoded.
li t1,0x65726f63
lwu t3,0(a0)
bne t3,t1,ne
mv t1,sp
j over
ne:
li t1,-1
over:
// Store the last Wasm SP into the `last_wasm_entry_sp` in the limits, if this
// was core Wasm, otherwise store an invalid sentinal value.
sd t1,40(t0)
ld t0,16(a1)
jr t0
.cfi_endproc
"#
);
#[cfg(test)]
mod host_to_wasm_trampoline_offsets_tests {
use wasmtime_environ::{Module, PtrSize, VMOffsets};
#[test]
fn test() {
let module = Module::new();
let offsets = VMOffsets::new(std::mem::size_of::<*mut u8>() as u8, &module);
assert_eq!(8, offsets.vmctx_runtime_limits());
assert_eq!(40, offsets.ptr.vmruntime_limits_last_wasm_entry_sp());
assert_eq!(16, offsets.vmctx_callee());
assert_eq!(0x65726f63, u32::from_le_bytes(*b"core"));
}
}
#[rustfmt::skip]
asm_func!(
"wasm_to_host_trampoline",
"
.cfi_startproc simple
// Load the pointer to `VMRuntimeLimits` in `t0`.
ld t0,8(a1)
// Store the last Wasm FP into the `last_wasm_exit_fp` in the limits.
sd fp,24(t0)
// Store the last Wasm PC into the `last_wasm_exit_pc` in the limits.
sd ra,32(t0)
// Tail call to the actual host function.
//
// This *must* be a tail call so that we do not push to the stack and mess
// up the offsets of stack arguments (if any).
ld t0, 8(a0)
jr t0
.cfi_endproc
",
);
#[cfg(test)]
mod wasm_to_host_trampoline_offsets_tests {
use crate::VMHostFuncContext;
use memoffset::offset_of;
use wasmtime_environ::{Module, PtrSize, VMOffsets};
#[test]
fn test() {
let module = Module::new();
let offsets = VMOffsets::new(std::mem::size_of::<*mut u8>() as u8, &module);
assert_eq!(8, offsets.vmctx_runtime_limits());
assert_eq!(24, offsets.ptr.vmruntime_limits_last_wasm_exit_fp());
assert_eq!(32, offsets.ptr.vmruntime_limits_last_wasm_exit_pc());
assert_eq!(8, offset_of!(VMHostFuncContext, host_func));
}
}
#[rustfmt::skip]
macro_rules! wasm_to_libcall_trampoline {
($libcall:ident ; $libcall_impl:ident) => {
wasmtime_asm_macros::asm_func!(
stringify!($libcall),
"
.cfi_startproc
// Load the pointer to `VMRuntimeLimits` in `t0`.
ld t0, 8(a0)
// Store the last Wasm FP into the `last_wasm_exit_fp` in the limits.
sd fp, 24(t0)
// Store the last Wasm PC into the `last_wasm_exit_pc` in the limits.
sd ra, 32(t0)
// Tail call to the actual implementation of this libcall.
j ", wasmtime_asm_macros::asm_sym!(stringify!($libcall_impl)), "
.cfi_endproc
"
);
};
}

View File

@@ -42,6 +42,9 @@ cfg_if! {
} else if #[cfg(target_arch = "s390x")] {
mod s390x;
use s390x as arch;
} else if #[cfg(target_arch = "riscv64")] {
mod riscv64;
use riscv64 as arch;
} else {
compile_error!("unsupported architecture");
}

View File

@@ -0,0 +1,21 @@
//
pub unsafe fn get_next_older_pc_from_fp(fp: usize) -> usize {
*(fp as *mut usize).offset(1)
}
// And the current frame pointer points to the next older frame pointer.
pub const NEXT_OLDER_FP_FROM_FP_OFFSET: usize = 0;
pub fn reached_entry_sp(fp: usize, first_wasm_sp: usize) -> bool {
// Calls in riscv64 push two i64s (old FP and return PC) so our entry SP is
// two i64s above the first Wasm FP.
fp == first_wasm_sp - 16
}
pub fn assert_entry_sp_is_aligned(sp: usize) {
assert_eq!(sp % 16, 0, "stack should always be aligned to 16");
}
pub fn assert_fp_is_aligned(fp: usize) {
assert_eq!(fp % 16, 0, "stack should always be aligned to 16");
}

View File

@@ -228,7 +228,14 @@ unsafe fn get_pc_and_fp(cx: *mut libc::c_void, _signum: libc::c_int) -> (*const
cx.uc_mcontext.mc_rip as *const u8,
cx.uc_mcontext.mc_rbp as usize,
)
} else {
} else if #[cfg(all(target_os = "linux", target_arch = "riscv64"))] {
let cx = &*(cx as *const libc::ucontext_t);
(
cx.uc_mcontext.__gregs[libc::REG_PC] as *const u8,
cx.uc_mcontext.__gregs[libc::REG_S0] as usize,
)
}
else {
compile_error!("unsupported platform");
}
}

View File

@@ -490,6 +490,17 @@ impl Engine {
}
}
#[cfg(target_arch = "riscv64")]
{
enabled = match flag {
// make sure `test_isa_flags_mismatch` test pass.
"not_a_flag" => None,
// due to `is_riscv64_feature_detected` is not stable.
// we cannot use it.
_ => Some(true),
}
}
#[cfg(target_arch = "x86_64")]
{
enabled = match flag {