Skip to content

Commit

Permalink
winch: Emit unwind info in the x64 backend (bytecodealliance#7798)
Browse files Browse the repository at this point in the history
* Enable all winch tests on windows

prtest:mingw-x64

* Plumb through x64 unwind info creation

* Add the frame regs unwind info

* Emit UnwindInfo::SaveReg instructions

* Review feedback

* Comment the offset_downward_to_clobbers value
  • Loading branch information
elliottt authored Jan 25, 2024
1 parent 94b3e84 commit 0bcceda
Show file tree
Hide file tree
Showing 14 changed files with 167 additions and 53 deletions.
23 changes: 0 additions & 23 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,30 +204,7 @@ fn ignore(testsuite: &str, testname: &str, strategy: &str) -> bool {
assert!(strategy == "Cranelift" || strategy == "Winch");

// Ignore everything except the winch misc test suite.
// We ignore tests that assert for traps on windows, given
// that Winch doesn't encode unwind information for Windows, yet.
if strategy == "Winch" {
let assert_trap = [
"i32",
"i64",
"call",
"call_indirect",
"conversions",
"table_fill",
"table_init",
"table_copy",
"table_set",
"table_get",
"memory_grow",
"memory_init",
"memory_fill",
]
.contains(&testname);

if assert_trap && env::var("CARGO_CFG_TARGET_OS").unwrap().as_str() == "windows" {
return true;
}

if testsuite == "misc_testsuite" {
// The misc/call_indirect is fully supported by Winch.
if testname != "call_indirect" {
Expand Down
50 changes: 30 additions & 20 deletions cranelift/codegen/src/isa/x64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use crate::machinst::{
};
use crate::result::CodegenResult;
use crate::settings::{self as shared_settings, Flags};
use crate::{Final, MachBufferFinalized};
use alloc::{boxed::Box, vec::Vec};
use core::fmt;
use cranelift_control::ControlPlane;
Expand All @@ -27,6 +28,8 @@ mod lower;
mod pcc;
pub mod settings;

pub use inst::unwind::systemv::create_cie;

/// An X64 backend.
pub(crate) struct X64Backend {
triple: Triple,
Expand Down Expand Up @@ -118,26 +121,7 @@ impl TargetIsa for X64Backend {
result: &CompiledCode,
kind: crate::isa::unwind::UnwindInfoKind,
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
use crate::isa::unwind::UnwindInfo;
use crate::isa::unwind::UnwindInfoKind;
Ok(match kind {
UnwindInfoKind::SystemV => {
let mapper = self::inst::unwind::systemv::RegisterMapper;
Some(UnwindInfo::SystemV(
crate::isa::unwind::systemv::create_unwind_info_from_insts(
&result.buffer.unwind_info[..],
result.buffer.data().len(),
&mapper,
)?,
))
}
UnwindInfoKind::Windows => Some(UnwindInfo::WindowsX64(
crate::isa::unwind::winx64::create_unwind_info_from_insts::<
self::inst::unwind::winx64::RegisterMapper,
>(&result.buffer.unwind_info[..])?,
)),
_ => None,
})
emit_unwind_info(&result.buffer, kind)
}

#[cfg(feature = "unwind")]
Expand Down Expand Up @@ -193,6 +177,32 @@ impl TargetIsa for X64Backend {
}
}

/// Emit unwind info for an x86 target.
pub fn emit_unwind_info(
buffer: &MachBufferFinalized<Final>,
kind: crate::isa::unwind::UnwindInfoKind,
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
use crate::isa::unwind::{UnwindInfo, UnwindInfoKind};
Ok(match kind {
UnwindInfoKind::SystemV => {
let mapper = self::inst::unwind::systemv::RegisterMapper;
Some(UnwindInfo::SystemV(
crate::isa::unwind::systemv::create_unwind_info_from_insts(
&buffer.unwind_info[..],
buffer.data().len(),
&mapper,
)?,
))
}
UnwindInfoKind::Windows => Some(UnwindInfo::WindowsX64(
crate::isa::unwind::winx64::create_unwind_info_from_insts::<
self::inst::unwind::winx64::RegisterMapper,
>(&buffer.unwind_info[..])?,
)),
_ => None,
})
}

impl fmt::Display for X64Backend {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("MachBackend")
Expand Down
4 changes: 2 additions & 2 deletions cranelift/codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ pub use crate::machinst::buffer::{
};
pub use crate::machinst::{
CompiledCode, Final, MachBuffer, MachBufferFinalized, MachInst, MachInstEmit,
MachInstEmitState, MachLabel, Reg, TextSectionBuilder, VCodeConstantData, VCodeConstants,
Writable,
MachInstEmitState, MachLabel, RealReg, Reg, TextSectionBuilder, VCodeConstantData,
VCodeConstants, Writable,
};

mod alias_analysis;
Expand Down
1 change: 1 addition & 0 deletions cranelift/codegen/src/machinst/reg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ impl RealReg {
self.0.class()
}

/// The physical register number.
pub fn hw_enc(self) -> u8 {
PReg::from(self).hw_enc() as u8
}
Expand Down
48 changes: 44 additions & 4 deletions crates/winch/src/compiler.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use anyhow::Result;
use cranelift_codegen::isa::unwind::UnwindInfoKind;
use object::write::{Object, SymbolId};
use std::any::Any;
use std::mem;
Expand Down Expand Up @@ -63,6 +64,27 @@ impl Compiler {
context.allocations = allocs;
self.contexts.lock().unwrap().push(context);
}

/// Emit unwind info into the [`CompiledFunction`].
fn emit_unwind_info(
&self,
compiled_function: &mut CompiledFunction<CompiledFuncEnv>,
) -> Result<(), CompileError> {
let kind = match self.isa.triple().operating_system {
target_lexicon::OperatingSystem::Windows => UnwindInfoKind::Windows,
_ => UnwindInfoKind::SystemV,
};

if let Some(info) = self
.isa
.emit_unwind_info(&compiled_function.buffer, kind)
.map_err(|e| CompileError::Codegen(format!("{e:?}")))?
{
compiled_function.set_unwind_info(info);
}

Ok(())
}
}

impl wasmtime_environ::Compiler for Compiler {
Expand Down Expand Up @@ -98,9 +120,14 @@ impl wasmtime_environ::Compiler for Compiler {
.map_err(|e| CompileError::Codegen(format!("{e:?}")));
self.save_context(context, validator.into_allocations());
let buffer = buffer?;
let compiled_function =

let mut compiled_function =
CompiledFunction::new(buffer, CompiledFuncEnv {}, self.isa.function_alignment());

if self.isa.flags().unwind_info() {
self.emit_unwind_info(&mut compiled_function)?;
}

Ok((
WasmFunctionInfo {
start_srcloc,
Expand All @@ -123,9 +150,14 @@ impl wasmtime_environ::Compiler for Compiler {
.isa
.compile_trampoline(&ty, TrampolineKind::ArrayToWasm(func_index))
.map_err(|e| CompileError::Codegen(format!("{:?}", e)))?;
let compiled_function =

let mut compiled_function =
CompiledFunction::new(buffer, CompiledFuncEnv {}, self.isa.function_alignment());

if self.isa.flags().unwind_info() {
self.emit_unwind_info(&mut compiled_function)?;
}

Ok(Box::new(compiled_function))
}

Expand All @@ -144,9 +176,13 @@ impl wasmtime_environ::Compiler for Compiler {
.compile_trampoline(ty, TrampolineKind::NativeToWasm(func_index))
.map_err(|e| CompileError::Codegen(format!("{:?}", e)))?;

let compiled_function =
let mut compiled_function =
CompiledFunction::new(buffer, CompiledFuncEnv {}, self.isa.function_alignment());

if self.isa.flags().unwind_info() {
self.emit_unwind_info(&mut compiled_function)?;
}

Ok(Box::new(compiled_function))
}

Expand All @@ -159,9 +195,13 @@ impl wasmtime_environ::Compiler for Compiler {
.compile_trampoline(wasm_func_ty, TrampolineKind::WasmToNative)
.map_err(|e| CompileError::Codegen(format!("{:?}", e)))?;

let compiled_function =
let mut compiled_function =
CompiledFunction::new(buffer, CompiledFuncEnv {}, self.isa.function_alignment());

if self.isa.flags().unwind_info() {
self.emit_unwind_info(&mut compiled_function)?;
}

Ok(Box::new(compiled_function))
}

Expand Down
2 changes: 1 addition & 1 deletion winch/codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ target-lexicon = { workspace = true, features = ["std"] }
# mostly to have access to `PReg`s and the calling convention.
# In the next iteration we'll factor out the common bits so that they can be consumed
# by Cranelift and Winch.
cranelift-codegen = { workspace = true }
cranelift-codegen = { workspace = true, features = ["unwind"] }
regalloc2 = { workspace = true }
gimli = { workspace = true }
wasmtime-environ = { workspace = true }
Expand Down
8 changes: 8 additions & 0 deletions winch/codegen/src/isa/aarch64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,12 @@ impl TargetIsa for Aarch64 {
) -> Result<MachBufferFinalized<Final>> {
todo!()
}

fn emit_unwind_info(
&self,
_result: &MachBufferFinalized<Final>,
_kind: cranelift_codegen::isa::unwind::UnwindInfoKind,
) -> Result<Option<cranelift_codegen::isa::unwind::UnwindInfo>> {
todo!()
}
}
7 changes: 7 additions & 0 deletions winch/codegen/src/isa/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::{BuiltinFunctions, TrampolineKind};
use anyhow::{anyhow, Result};
use core::fmt::Formatter;
use cranelift_codegen::isa::unwind::{UnwindInfo, UnwindInfoKind};
use cranelift_codegen::isa::{CallConv, IsaBuilder};
use cranelift_codegen::settings;
use cranelift_codegen::{Final, MachBufferFinalized, TextSectionBuilder};
Expand Down Expand Up @@ -184,6 +185,12 @@ pub trait TargetIsa: Send + Sync {
self.triple().endianness().unwrap()
}

fn emit_unwind_info(
&self,
_result: &MachBufferFinalized<Final>,
_kind: UnwindInfoKind,
) -> Result<Option<UnwindInfo>>;

/// See `cranelift_codegen::isa::TargetIsa::create_systemv_cie`.
fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
// By default, an ISA cannot create a System V CIE.
Expand Down
15 changes: 14 additions & 1 deletion winch/codegen/src/isa/reg.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use regalloc2::PReg;
use cranelift_codegen::RealReg;
pub use regalloc2::RegClass;
use regalloc2::{PReg, VReg};

/// A newtype abstraction on top of a physical register.
//
Expand Down Expand Up @@ -71,3 +72,15 @@ impl std::fmt::Debug for Reg {
write!(f, "{}", self.0)
}
}

impl Into<VReg> for Reg {
fn into(self) -> VReg {
VReg::new(self.inner().index(), self.class())
}
}

impl Into<RealReg> for Reg {
fn into(self) -> RealReg {
Into::<VReg>::into(self).into()
}
}
6 changes: 6 additions & 0 deletions winch/codegen/src/isa/x64/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use cranelift_codegen::{
entity::EntityRef,
ir::{types, ConstantPool, ExternalName, LibCall, Opcode, TrapCode, UserExternalNameRef},
isa::{
unwind::UnwindInst,
x64::{
args::{
self, AluRmiROpcode, Amode, CmpOpcode, DivSignedness, ExtMode, FromWritableReg,
Expand Down Expand Up @@ -229,6 +230,11 @@ impl Assembler {
}
}

/// Emit an unwind instruction.
pub fn emit_unwind_inst(&mut self, inst: UnwindInst) {
self.emit(Inst::Unwind { inst })
}

/// Push register.
pub fn push_r(&mut self, reg: Reg) {
self.emit(Inst::Push64 { src: reg.into() });
Expand Down
33 changes: 32 additions & 1 deletion winch/codegen/src/isa/x64/masm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ use crate::{
masm::CalleeKind,
};
use cranelift_codegen::{
isa::x64::settings as x64_settings, settings, Final, MachBufferFinalized, MachLabel,
isa::unwind::UnwindInst, isa::x64::settings as x64_settings, settings, Final,
MachBufferFinalized, MachLabel,
};

use wasmtime_environ::{PtrSize, WasmType, WASM_PAGE_SIZE};
Expand Down Expand Up @@ -53,8 +54,25 @@ impl Masm for MacroAssembler {
let stack_pointer = rsp();

self.asm.push_r(frame_pointer);

if self.shared_flags.unwind_info() {
self.asm.emit_unwind_inst(UnwindInst::PushFrameRegs {
offset_upward_to_caller_sp: Self::ABI::arg_base_offset().try_into().unwrap(),
})
}

self.asm
.mov_rr(stack_pointer, frame_pointer, OperandSize::S64);

if self.shared_flags.unwind_info() {
self.asm.emit_unwind_inst(UnwindInst::DefineNewFrame {
offset_upward_to_caller_sp: Self::ABI::arg_base_offset().try_into().unwrap(),

// Clobbers appear directly after the RET and FP if they're present. As we just
// pushed the frame pointer, the offset to the clobbers will be `0`.
offset_downward_to_clobbers: 0,
})
}
}

fn check_stack(&mut self) {
Expand Down Expand Up @@ -107,6 +125,19 @@ impl Masm for MacroAssembler {
}
}

fn save(&mut self, clobber_offset: u32, reg: Reg, size: OperandSize) -> StackSlot {
let slot = self.push(reg, size);

if self.shared_flags.unwind_info() {
self.asm.emit_unwind_inst(UnwindInst::SaveReg {
clobber_offset,
reg: reg.into(),
});
}

slot
}

fn reserve_stack(&mut self, bytes: u32) {
if bytes == 0 {
return;
Expand Down
12 changes: 12 additions & 0 deletions winch/codegen/src/isa/x64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,4 +169,16 @@ impl TargetIsa for X64 {

Ok(masm.finalize())
}

fn emit_unwind_info(
&self,
buffer: &MachBufferFinalized<Final>,
kind: cranelift_codegen::isa::unwind::UnwindInfoKind,
) -> Result<Option<cranelift_codegen::isa::unwind::UnwindInfo>> {
Ok(cranelift_codegen::isa::x64::emit_unwind_info(buffer, kind)?)
}

fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
Some(cranelift_codegen::isa::x64::create_cie())
}
}
7 changes: 7 additions & 0 deletions winch/codegen/src/masm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -766,4 +766,11 @@ pub(crate) trait MacroAssembler {
self.free_stack(bytes);
}
}

/// Save the value of this register to the stack. By default this is the same as pushing the
/// register, however it's present in the [`MacroAssembler`] trait to ensure that it's possible
/// to add unwind info for register saves in backends.
fn save(&mut self, _off: u32, src: Reg, size: OperandSize) -> StackSlot {
self.push(src, size)
}
}
Loading

0 comments on commit 0bcceda

Please sign in to comment.