Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/singlepass windows #2574

Merged
merged 9 commits into from
Sep 23, 2021
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ ifneq ($(ENABLE_SINGLEPASS), 0)
ifeq ($(ENABLE_SINGLEPASS), 1)
compilers += singlepass
# … otherwise, we try to check whether Singlepass works on this host.
else ifneq (, $(filter 1, $(IS_DARWIN) $(IS_LINUX)))
else ifneq (, $(filter 1, $(IS_DARWIN) $(IS_LINUX) $(IS_WINDOWS)))
ifeq ($(IS_AMD64), 1)
compilers += singlepass
endif
Expand Down
402 changes: 253 additions & 149 deletions lib/compiler-singlepass/src/codegen_x64.rs

Large diffs are not rendered by default.

38 changes: 26 additions & 12 deletions lib/compiler-singlepass/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ use loupe::MemoryUsage;
use rayon::prelude::{IntoParallelIterator, ParallelIterator};
use std::sync::Arc;
use wasmer_compiler::{
Architecture, Compilation, CompileError, CompileModuleInfo, CompiledFunction, Compiler,
CompilerConfig, FunctionBinaryReader, FunctionBody, FunctionBodyData, MiddlewareBinaryReader,
ModuleMiddleware, ModuleMiddlewareChain, ModuleTranslationState, OperatingSystem, SectionIndex,
Target, TrapInformation,
Architecture, CallingConvention, Compilation, CompileError, CompileModuleInfo,
CompiledFunction, Compiler, CompilerConfig, FunctionBinaryReader, FunctionBody,
FunctionBodyData, MiddlewareBinaryReader, ModuleMiddleware, ModuleMiddlewareChain,
ModuleTranslationState, OperatingSystem, SectionIndex, Target, TrapInformation,
};
use wasmer_types::entity::{EntityRef, PrimaryMap};
use wasmer_types::{
Expand Down Expand Up @@ -57,17 +57,24 @@ impl Compiler for SinglepassCompiler {
_module_translation: &ModuleTranslationState,
function_body_inputs: PrimaryMap<LocalFunctionIndex, FunctionBodyData<'_>>,
) -> Result<Compilation, CompileError> {
if target.triple().operating_system == OperatingSystem::Windows {
/*if target.triple().operating_system == OperatingSystem::Windows {
return Err(CompileError::UnsupportedTarget(
OperatingSystem::Windows.to_string(),
));
}
}*/
if let Architecture::X86_32(arch) = target.triple().architecture {
return Err(CompileError::UnsupportedTarget(arch.to_string()));
}
if compile_info.features.multi_value {
return Err(CompileError::UnsupportedFeature("multivalue".to_string()));
}
let calling_convention = match target.triple().default_calling_convention() {
Ok(CallingConvention::WindowsFastcall) => CallingConvention::WindowsFastcall,
Ok(CallingConvention::SystemV) => CallingConvention::SystemV,
//Ok(CallingConvention::AppleAarch64) => AppleAarch64,
_ => panic!("Unsupported Calling convention for Singlepass compiler"),
};

let memory_styles = &compile_info.memory_styles;
let table_styles = &compile_info.table_styles;
let vmoffsets = VMOffsets::new(8, &compile_info.module);
Expand All @@ -77,7 +84,12 @@ impl Compiler for SinglepassCompiler {
.collect::<Vec<_>>()
.into_par_iter_if_rayon()
.map(|i| {
gen_import_call_trampoline(&vmoffsets, i, &module.signatures[module.functions[i]])
gen_import_call_trampoline(
&vmoffsets,
i,
&module.signatures[module.functions[i]],
calling_convention,
)
})
.collect::<Vec<_>>()
.into_iter()
Expand Down Expand Up @@ -133,7 +145,7 @@ impl Compiler for SinglepassCompiler {
.values()
.collect::<Vec<_>>()
.into_par_iter_if_rayon()
.map(gen_std_trampoline)
.map(|func_type| gen_std_trampoline(&func_type, calling_convention))
.collect::<Vec<_>>()
.into_iter()
.collect::<PrimaryMap<_, _>>();
Expand All @@ -142,7 +154,9 @@ impl Compiler for SinglepassCompiler {
.imported_function_types()
.collect::<Vec<_>>()
.into_par_iter_if_rayon()
.map(|func_type| gen_std_dynamic_import_trampoline(&vmoffsets, &func_type))
.map(|func_type| {
gen_std_dynamic_import_trampoline(&vmoffsets, &func_type, calling_convention)
})
.collect::<Vec<_>>()
.into_iter()
.collect::<PrimaryMap<FunctionIndex, FunctionBody>>();
Expand Down Expand Up @@ -219,13 +233,13 @@ mod tests {
let compiler = SinglepassCompiler::new(Singlepass::default());

// Compile for win64
let win64 = Target::new(triple!("x86_64-pc-windows-msvc"), CpuFeature::for_host());
/*let win64 = Target::new(triple!("x86_64-pc-windows-msvc"), CpuFeature::for_host());
let (mut info, translation, inputs) = dummy_compilation_ingredients();
let result = compiler.compile_module(&win64, &mut info, &translation, inputs);
match result.unwrap_err() {
CompileError::UnsupportedTarget(name) => assert_eq!(name, "windows"),
error => panic!("Unexpected error: {:?}", error),
};
};*/

// Compile for 32bit Linux
let linux32 = Target::new(triple!("i686-unknown-linux-gnu"), CpuFeature::for_host());
Expand All @@ -241,7 +255,7 @@ mod tests {
let (mut info, translation, inputs) = dummy_compilation_ingredients();
let result = compiler.compile_module(&win32, &mut info, &translation, inputs);
match result.unwrap_err() {
CompileError::UnsupportedTarget(name) => assert_eq!(name, "windows"), // Windows should be checked before architecture
CompileError::UnsupportedTarget(name) => assert_eq!(name, "i686"), // Windows should be checked before architecture
error => panic!("Unexpected error: {:?}", error),
};
}
Expand Down
12 changes: 11 additions & 1 deletion lib/compiler-singlepass/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
use crate::compiler::SinglepassCompiler;
use loupe::MemoryUsage;
use std::sync::Arc;
use wasmer_compiler::{Compiler, CompilerConfig, CpuFeature, ModuleMiddleware, Target};
use wasmer_compiler::{
CallingConvention, Compiler, CompilerConfig, CpuFeature, ModuleMiddleware, Target,
};
use wasmer_types::Features;

#[derive(Debug, Clone, MemoryUsage)]
Expand All @@ -13,6 +15,8 @@ pub struct Singlepass {
pub(crate) enable_stack_check: bool,
/// The middleware chain.
pub(crate) middlewares: Vec<Arc<dyn ModuleMiddleware>>,
#[loupe(skip)]
pub(crate) calling_convention: CallingConvention,
}

impl Singlepass {
Expand All @@ -23,6 +27,12 @@ impl Singlepass {
enable_nan_canonicalization: true,
enable_stack_check: false,
middlewares: vec![],
calling_convention: match Target::default().triple().default_calling_convention() {
Ok(CallingConvention::WindowsFastcall) => CallingConvention::WindowsFastcall,
Ok(CallingConvention::SystemV) => CallingConvention::SystemV,
//Ok(CallingConvention::AppleAarch64) => AppleAarch64,
_ => panic!("Unsupported Calling convention for Singlepass"),
},
}
}

Expand Down
2 changes: 1 addition & 1 deletion lib/compiler-singlepass/src/emitter_x64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1400,7 +1400,7 @@ impl Emitter for Assembler {
}

fn emit_bkpt(&mut self) {
dynasm!(self ; int 0x3);
dynasm!(self ; int3);
}

fn emit_host_redirection(&mut self, target: GPR) {
Expand Down
73 changes: 61 additions & 12 deletions lib/compiler-singlepass/src/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use smallvec::SmallVec;
use std::cmp;
use std::collections::HashSet;
use wasmer_compiler::wasmparser::Type as WpType;
use wasmer_compiler::CallingConvention;

const NATIVE_PAGE_SIZE: usize = 4096;

Expand Down Expand Up @@ -330,6 +331,7 @@ impl Machine {
a: &mut E,
n: usize,
n_params: usize,
calling_convention: CallingConvention,
) -> Vec<Location> {
// Determine whether a local should be allocated on the stack.
fn is_local_on_stack(idx: usize) -> bool {
Expand Down Expand Up @@ -367,6 +369,11 @@ impl Machine {
// Callee-saved R15 for vmctx.
static_area_size += 8;

// For Windows ABI, save RDI and RSI
if calling_convention == CallingConvention::WindowsFastcall {
static_area_size += 8 * 2;
}

// Total size of callee saved registers.
let callee_saved_regs_size = static_area_size;

Expand Down Expand Up @@ -411,6 +418,29 @@ impl Machine {
X64Register::GPR(GPR::R15).to_index(),
));

if calling_convention == CallingConvention::WindowsFastcall {
// Save RDI
self.stack_offset.0 += 8;
a.emit_mov(
Size::S64,
Location::GPR(GPR::RDI),
Location::Memory(GPR::RBP, -(self.stack_offset.0 as i32)),
);
self.state.stack_values.push(MachineValue::PreserveRegister(
X64Register::GPR(GPR::RDI).to_index(),
));
// Save RSI
self.stack_offset.0 += 8;
a.emit_mov(
Size::S64,
Location::GPR(GPR::RSI),
Location::Memory(GPR::RBP, -(self.stack_offset.0 as i32)),
);
self.state.stack_values.push(MachineValue::PreserveRegister(
X64Register::GPR(GPR::RSI).to_index(),
));
}

// Save the offset of register save area.
self.save_area_offset = Some(MachineStackOffset(self.stack_offset.0));

Expand All @@ -432,7 +462,7 @@ impl Machine {
// Locals are allocated on the stack from higher address to lower address,
// so we won't skip the stack guard page here.
for i in 0..n_params {
let loc = Self::get_param_location(i + 1);
let loc = Self::get_param_location(i + 1, calling_convention);
match loc {
Location::GPR(_) => {
a.emit_mov(Size::S64, loc, locations[i]);
Expand All @@ -454,7 +484,7 @@ impl Machine {
// Load vmctx into R15.
a.emit_mov(
Size::S64,
Self::get_param_location(0),
Self::get_param_location(0, calling_convention),
Location::GPR(GPR::R15),
);

Expand Down Expand Up @@ -499,7 +529,12 @@ impl Machine {
locations
}

pub fn finalize_locals<E: Emitter>(&mut self, a: &mut E, locations: &[Location]) {
pub fn finalize_locals<E: Emitter>(
&mut self,
a: &mut E,
locations: &[Location],
calling_convention: CallingConvention,
) {
// Unwind stack to the "save area".
a.emit_lea(
Size::S64,
Expand All @@ -510,6 +545,11 @@ impl Machine {
Location::GPR(GPR::RSP),
);

if calling_convention == CallingConvention::WindowsFastcall {
// Restore RSI and RDI
a.emit_pop(Size::S64, Location::GPR(GPR::RSI));
a.emit_pop(Size::S64, Location::GPR(GPR::RDI));
}
// Restore R15 used by vmctx.
a.emit_pop(Size::S64, Location::GPR(GPR::R15));

Expand All @@ -521,15 +561,24 @@ impl Machine {
}
}

pub fn get_param_location(idx: usize) -> Location {
match idx {
0 => Location::GPR(GPR::RDI),
1 => Location::GPR(GPR::RSI),
2 => Location::GPR(GPR::RDX),
3 => Location::GPR(GPR::RCX),
4 => Location::GPR(GPR::R8),
5 => Location::GPR(GPR::R9),
_ => Location::Memory(GPR::RBP, (16 + (idx - 6) * 8) as i32),
pub fn get_param_location(idx: usize, calling_convention: CallingConvention) -> Location {
match calling_convention {
CallingConvention::WindowsFastcall => match idx {
0 => Location::GPR(GPR::RCX),
1 => Location::GPR(GPR::RDX),
2 => Location::GPR(GPR::R8),
3 => Location::GPR(GPR::R9),
_ => Location::Memory(GPR::RBP, (16 + 32 + (idx - 4) * 8) as i32),
},
_ => match idx {
0 => Location::GPR(GPR::RDI),
1 => Location::GPR(GPR::RSI),
2 => Location::GPR(GPR::RDX),
3 => Location::GPR(GPR::RCX),
4 => Location::GPR(GPR::R8),
5 => Location::GPR(GPR::R9),
_ => Location::Memory(GPR::RBP, (16 + (idx - 6) * 8) as i32),
},
}
}
}
Expand Down
Loading