Remove finalize_* from the Backend trait

Instead let the `finish` method perform finalization
This commit is contained in:
bjorn3
2020-09-30 14:20:39 +02:00
parent 59f95083b1
commit 405b9e2875
4 changed files with 142 additions and 164 deletions

View File

@@ -103,30 +103,6 @@ where
contents: &ModuleContents<Self>,
) -> ModuleResult<Self::CompiledData>;
/// Perform all outstanding relocations on the given function. This requires all `Local`
/// and `Export` entities referenced to be defined.
///
/// This method is not relevant for `Backend` implementations that do not provide
/// `Backend::FinalizedFunction`.
fn finalize_function(
&mut self,
id: FuncId,
func: &Self::CompiledFunction,
contents: &ModuleContents<Self>,
);
/// Perform all outstanding relocations on the given data object. This requires all
/// `Local` and `Export` entities referenced to be defined.
///
/// This method is not relevant for `Backend` implementations that do not provide
/// `Backend::FinalizedData`.
fn finalize_data(
&mut self,
id: DataId,
data: &Self::CompiledData,
contents: &ModuleContents<Self>,
);
/// Consume this `Backend` and return a result. Some implementations may
/// provide additional functionality through this result.
fn finish(

View File

@@ -213,14 +213,14 @@ pub struct DataDeclaration {
}
/// A data object belonging to a `Module`.
struct ModuleData<B>
pub struct ModuleData<B>
where
B: Backend,
{
/// The data object declaration.
decl: DataDeclaration,
pub decl: DataDeclaration,
/// The "compiled" artifact, once it's available.
compiled: Option<B::CompiledData>,
pub compiled: Option<B::CompiledData>,
}
impl<B> ModuleData<B>
@@ -282,11 +282,21 @@ where
}
}
/// Get the `ModuleFunction` for the given function.
pub fn get_function_info(&self, func_id: FuncId) -> &ModuleFunction<B> {
&self.functions[func_id]
}
/// Get the `FunctionDeclaration` for the function named by `name`.
pub fn get_function_decl(&self, name: &ir::ExternalName) -> &FunctionDeclaration {
&self.functions[self.get_function_id(name)].decl
}
/// Get the `ModuleData` for the given data object.
pub fn get_data_info(&self, data_id: DataId) -> &ModuleData<B> {
&self.data_objects[data_id]
}
/// Get the `DataDeclaration` for the data object named by `name`.
pub fn get_data_decl(&self, name: &ir::ExternalName) -> &DataDeclaration {
&self.data_objects[self.get_data_id(name)].decl
@@ -647,29 +657,7 @@ where
/// Consume the module and return the resulting `Product`. Some `Backend`
/// implementations may provide additional functionality available after
/// a `Module` is complete.
pub fn finish(mut self) -> B::Product {
for func in self.functions_to_finalize.drain(..) {
let info = &self.contents.functions[func];
debug_assert!(info.decl.linkage.is_definable());
self.backend.finalize_function(
func,
info.compiled
.as_ref()
.expect("function must be compiled before it can be finalized"),
&self.contents,
);
}
for data in self.data_objects_to_finalize.drain(..) {
let info = &self.contents.data_objects[data];
debug_assert!(info.decl.linkage.is_definable());
self.backend.finalize_data(
data,
info.compiled
.as_ref()
.expect("data object must be compiled before it can be finalized"),
&self.contents,
);
}
pub fn finish(self) -> B::Product {
self.backend.finish(self.names, self.contents)
}
}

View File

@@ -400,24 +400,6 @@ impl Backend for ObjectBackend {
Ok(ObjectCompiledData)
}
fn finalize_function(
&mut self,
_id: FuncId,
_func: &ObjectCompiledFunction,
_contents: &ModuleContents<Self>,
) {
// Nothing to do.
}
fn finalize_data(
&mut self,
_id: DataId,
_data: &ObjectCompiledData,
_contents: &ModuleContents<Self>,
) {
// Nothing to do.
}
fn finish(
mut self,
_names: HashMap<String, FuncOrDataId>,

View File

@@ -124,6 +124,8 @@ pub struct SimpleJITBackend {
symbols: HashMap<String, *const u8>,
libcall_names: Box<dyn Fn(ir::LibCall) -> String>,
memory: SimpleJITMemoryHandle,
functions_to_finalize: Vec<FuncId>,
data_objects_to_finalize: Vec<DataId>,
}
/// A record of a relocation to perform.
@@ -261,6 +263,100 @@ impl SimpleJITBackend {
let _ = writeln!(map_file, "{:x} {:x} {}", ptr as usize, size, name);
}
}
fn finalize_function(
&mut self,
_id: FuncId,
func: &SimpleJITCompiledFunction,
contents: &ModuleContents<Self>,
) {
use std::ptr::write_unaligned;
for &RelocRecord {
reloc,
offset,
ref name,
addend,
} in &func.relocs
{
let ptr = func.code;
debug_assert!((offset as usize) < func.size);
let at = unsafe { ptr.offset(offset as isize) };
let base = self.get_definition(contents, name);
// TODO: Handle overflow.
let what = unsafe { base.offset(addend as isize) };
match reloc {
Reloc::Abs4 => {
// TODO: Handle overflow.
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
unsafe {
write_unaligned(at as *mut u32, what as u32)
};
}
Reloc::Abs8 => {
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
unsafe {
write_unaligned(at as *mut u64, what as u64)
};
}
Reloc::X86PCRel4 | Reloc::X86CallPCRel4 => {
// TODO: Handle overflow.
let pcrel = ((what as isize) - (at as isize)) as i32;
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
unsafe {
write_unaligned(at as *mut i32, pcrel)
};
}
Reloc::X86GOTPCRel4 | Reloc::X86CallPLTRel4 => panic!("unexpected PIC relocation"),
_ => unimplemented!(),
}
}
}
fn finalize_data(
&mut self,
_id: DataId,
data: &SimpleJITCompiledData,
contents: &ModuleContents<Self>,
) {
use std::ptr::write_unaligned;
for &RelocRecord {
reloc,
offset,
ref name,
addend,
} in &data.relocs
{
let ptr = data.storage;
debug_assert!((offset as usize) < data.size);
let at = unsafe { ptr.offset(offset as isize) };
let base = self.get_definition(contents, name);
// TODO: Handle overflow.
let what = unsafe { base.offset(addend as isize) };
match reloc {
Reloc::Abs4 => {
// TODO: Handle overflow.
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
unsafe {
write_unaligned(at as *mut u32, what as u32)
};
}
Reloc::Abs8 => {
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
unsafe {
write_unaligned(at as *mut u64, what as u64)
};
}
Reloc::X86PCRel4
| Reloc::X86CallPCRel4
| Reloc::X86GOTPCRel4
| Reloc::X86CallPLTRel4 => panic!("unexpected text relocation in data"),
_ => unimplemented!(),
}
}
}
}
impl<'simple_jit_backend> Backend for SimpleJITBackend {
@@ -292,6 +388,8 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
symbols: builder.symbols,
libcall_names: builder.libcall_names,
memory,
functions_to_finalize: Vec::new(),
data_objects_to_finalize: Vec::new(),
}
}
@@ -318,7 +416,7 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
fn define_function<TS>(
&mut self,
_id: FuncId,
id: FuncId,
name: &str,
ctx: &cranelift_codegen::Context,
_contents: &ModuleContents<Self>,
@@ -328,6 +426,7 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
where
TS: TrapSink,
{
self.functions_to_finalize.push(id);
let size = code_size as usize;
let ptr = self
.memory
@@ -358,11 +457,12 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
fn define_function_bytes(
&mut self,
_id: FuncId,
id: FuncId,
name: &str,
bytes: &[u8],
_contents: &ModuleContents<Self>,
) -> ModuleResult<Self::CompiledFunction> {
self.functions_to_finalize.push(id);
let size = bytes.len();
let ptr = self
.memory
@@ -385,7 +485,7 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
fn define_data(
&mut self,
_id: DataId,
id: DataId,
_name: &str,
writable: bool,
tls: bool,
@@ -395,6 +495,8 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
) -> ModuleResult<Self::CompiledData> {
assert!(!tls, "SimpleJIT doesn't yet support TLS");
self.data_objects_to_finalize.push(id);
let &DataDescription {
ref init,
ref function_decls,
@@ -460,99 +562,6 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
})
}
fn finalize_function(
&mut self,
_id: FuncId,
func: &Self::CompiledFunction,
contents: &ModuleContents<Self>,
) {
use std::ptr::write_unaligned;
for &RelocRecord {
reloc,
offset,
ref name,
addend,
} in &func.relocs
{
let ptr = func.code;
debug_assert!((offset as usize) < func.size);
let at = unsafe { ptr.offset(offset as isize) };
let base = self.get_definition(contents, name);
// TODO: Handle overflow.
let what = unsafe { base.offset(addend as isize) };
match reloc {
Reloc::Abs4 => {
// TODO: Handle overflow.
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
unsafe {
write_unaligned(at as *mut u32, what as u32)
};
}
Reloc::Abs8 => {
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
unsafe {
write_unaligned(at as *mut u64, what as u64)
};
}
Reloc::X86PCRel4 | Reloc::X86CallPCRel4 => {
// TODO: Handle overflow.
let pcrel = ((what as isize) - (at as isize)) as i32;
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
unsafe {
write_unaligned(at as *mut i32, pcrel)
};
}
Reloc::X86GOTPCRel4 | Reloc::X86CallPLTRel4 => panic!("unexpected PIC relocation"),
_ => unimplemented!(),
}
}
}
fn finalize_data(
&mut self,
_id: DataId,
data: &Self::CompiledData,
contents: &ModuleContents<Self>,
) {
use std::ptr::write_unaligned;
for &RelocRecord {
reloc,
offset,
ref name,
addend,
} in &data.relocs
{
let ptr = data.storage;
debug_assert!((offset as usize) < data.size);
let at = unsafe { ptr.offset(offset as isize) };
let base = self.get_definition(contents, name);
// TODO: Handle overflow.
let what = unsafe { base.offset(addend as isize) };
match reloc {
Reloc::Abs4 => {
// TODO: Handle overflow.
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
unsafe {
write_unaligned(at as *mut u32, what as u32)
};
}
Reloc::Abs8 => {
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
unsafe {
write_unaligned(at as *mut u64, what as u64)
};
}
Reloc::X86PCRel4
| Reloc::X86CallPCRel4
| Reloc::X86GOTPCRel4
| Reloc::X86CallPLTRel4 => panic!("unexpected text relocation in data"),
_ => unimplemented!(),
}
}
}
/// SimpleJIT emits code and data into memory as it processes them. This
/// method performs no additional processing, but returns a handle which
/// allows freeing the allocated memory. Otherwise said memory is leaked
@@ -565,6 +574,29 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
names: HashMap<String, FuncOrDataId>,
contents: ModuleContents<Self>,
) -> Self::Product {
for func in std::mem::take(&mut self.functions_to_finalize) {
let info = contents.get_function_info(func);
debug_assert!(info.decl.linkage.is_definable());
self.finalize_function(
func,
info.compiled
.as_ref()
.expect("function must be compiled before it can be finalized"),
&contents,
);
}
for data in std::mem::take(&mut self.data_objects_to_finalize) {
let info = contents.get_data_info(data);
debug_assert!(info.decl.linkage.is_definable());
self.finalize_data(
data,
info.compiled
.as_ref()
.expect("data object must be compiled before it can be finalized"),
&contents,
);
}
// Now that we're done patching, prepare the memory for execution!
self.memory.readonly.set_readonly();
self.memory.code.set_readable_and_executable();