Browse Source

Remove all Sink traits

pull/3680/head
bjorn3 3 years ago
parent
commit
f0e821b9e0
  1. 79
      cranelift/codegen/src/binemit/memorysink.rs
  2. 4
      cranelift/codegen/src/binemit/mod.rs
  3. 2
      cranelift/codegen/src/machinst/buffer.rs
  4. 53
      cranelift/jit/src/backend.rs
  5. 11
      cranelift/jit/src/compiled_blob.rs
  6. 20
      cranelift/module/src/data_context.rs
  7. 2
      cranelift/module/src/lib.rs
  8. 19
      cranelift/module/src/module.rs
  9. 51
      cranelift/object/src/backend.rs
  10. 42
      cranelift/src/compile.rs
  11. 120
      cranelift/src/disasm.rs
  12. 54
      cranelift/src/wasm.rs
  13. 231
      crates/cranelift/src/compiler.rs

79
cranelift/codegen/src/binemit/memorysink.rs

@ -1,79 +0,0 @@
//! Code sink that writes binary machine code into contiguous memory.
//!
//! The `CodeSink` trait is the most general way of extracting binary machine code from Cranelift,
//! and it is implemented by things like the `test binemit` file test driver to generate
//! hexadecimal machine code. The `CodeSink` has some undesirable performance properties because of
//! the dual abstraction: `TargetIsa` is a trait object implemented by each supported ISA, so it
//! can't have any generic functions that could be specialized for each `CodeSink` implementation.
//! This results in many virtual function callbacks (one per `put*` call) when
//! `TargetIsa::emit_inst()` is used.
//!
//! The `MemoryCodeSink` type fixes the performance problem because it is a type known to
//! `TargetIsa` so it can specialize its machine code generation for the type. The trade-off is
//! that a `MemoryCodeSink` will always write binary machine code to raw memory. It forwards any
//! relocations to a `RelocSink` trait object. Relocations are less frequent than the
//! `CodeSink::put*` methods, so the performance impact of the virtual callbacks is less severe.
use super::{Addend, CodeOffset, Reloc};
use crate::binemit::stack_map::StackMap;
use crate::ir::{ExternalName, SourceLoc, TrapCode};
/// A trait for receiving relocations for code that is emitted directly into memory.
pub trait RelocSink {
/// Add a relocation referencing an external symbol at the current offset.
fn reloc_external(
&mut self,
_: CodeOffset,
_: SourceLoc,
_: Reloc,
_: &ExternalName,
_: Addend,
);
}
/// A trait for receiving trap codes and offsets.
///
/// If you don't need information about possible traps, you can use the
/// [`NullTrapSink`](NullTrapSink) implementation.
pub trait TrapSink {
/// Add trap information for a specific offset.
fn trap(&mut self, _: CodeOffset, _: SourceLoc, _: TrapCode);
}
/// A `RelocSink` implementation that does nothing, which is convenient when
/// compiling code that does not relocate anything.
#[derive(Default)]
pub struct NullRelocSink {}
impl RelocSink for NullRelocSink {
fn reloc_external(
&mut self,
_: CodeOffset,
_: SourceLoc,
_: Reloc,
_: &ExternalName,
_: Addend,
) {
}
}
/// A `TrapSink` implementation that does nothing, which is convenient when
/// compiling code that does not rely on trapping semantics.
#[derive(Default)]
pub struct NullTrapSink {}
impl TrapSink for NullTrapSink {
fn trap(&mut self, _offset: CodeOffset, _srcloc: SourceLoc, _code: TrapCode) {}
}
/// A trait for emitting stack maps.
pub trait StackMapSink {
/// Output a bitmap of the stack representing the live reference variables at this code offset.
fn add_stack_map(&mut self, _: CodeOffset, _: StackMap);
}
/// Placeholder StackMapSink that does nothing.
pub struct NullStackMapSink {}
impl StackMapSink for NullStackMapSink {
fn add_stack_map(&mut self, _: CodeOffset, _: StackMap) {}
}

4
cranelift/codegen/src/binemit/mod.rs

@ -3,12 +3,8 @@
//! The `binemit` module contains code for translating Cranelift's intermediate representation into //! The `binemit` module contains code for translating Cranelift's intermediate representation into
//! binary machine code. //! binary machine code.
mod memorysink;
mod stack_map; mod stack_map;
pub use self::memorysink::{
NullRelocSink, NullStackMapSink, NullTrapSink, RelocSink, StackMapSink, TrapSink,
};
pub use self::stack_map::StackMap; pub use self::stack_map::StackMap;
use core::fmt; use core::fmt;
#[cfg(feature = "enable-serde")] #[cfg(feature = "enable-serde")]

2
cranelift/codegen/src/machinst/buffer.rs

@ -1499,6 +1499,7 @@ struct MachLabelFixup<I: VCodeInst> {
} }
/// A relocation resulting from a compilation. /// A relocation resulting from a compilation.
#[derive(Clone, Debug)]
pub struct MachReloc { pub struct MachReloc {
/// The offset at which the relocation applies, *relative to the /// The offset at which the relocation applies, *relative to the
/// containing section*. /// containing section*.
@ -1514,6 +1515,7 @@ pub struct MachReloc {
} }
/// A trap record resulting from a compilation. /// A trap record resulting from a compilation.
#[derive(Clone, Debug)]
pub struct MachTrap { pub struct MachTrap {
/// The offset at which the trap instruction occurs, *relative to the /// The offset at which the trap instruction occurs, *relative to the
/// containing section*. /// containing section*.

53
cranelift/jit/src/backend.rs

@ -5,13 +5,13 @@ use cranelift_codegen::isa::TargetIsa;
use cranelift_codegen::settings::Configurable; use cranelift_codegen::settings::Configurable;
use cranelift_codegen::{self, ir, settings, MachReloc}; use cranelift_codegen::{self, ir, settings, MachReloc};
use cranelift_codegen::{ use cranelift_codegen::{
binemit::{Addend, CodeInfo, CodeOffset, Reloc, RelocSink}, binemit::{CodeInfo, Reloc},
CodegenError, CodegenError,
}; };
use cranelift_entity::SecondaryMap; use cranelift_entity::SecondaryMap;
use cranelift_module::{ use cranelift_module::{
DataContext, DataDescription, DataId, FuncId, Init, Linkage, Module, ModuleCompiledFunction, DataContext, DataDescription, DataId, FuncId, Init, Linkage, Module, ModuleCompiledFunction,
ModuleDeclarations, ModuleError, ModuleResult, RelocRecord, ModuleDeclarations, ModuleError, ModuleResult,
}; };
use log::info; use log::info;
use std::collections::HashMap; use std::collections::HashMap;
@ -671,25 +671,17 @@ impl Module for JITModule {
.allocate(size, EXECUTABLE_DATA_ALIGNMENT) .allocate(size, EXECUTABLE_DATA_ALIGNMENT)
.expect("TODO: handle OOM etc."); .expect("TODO: handle OOM etc.");
let mut reloc_sink = JITRelocSink::default();
unsafe { ctx.emit_to_memory(ptr) }; unsafe { ctx.emit_to_memory(ptr) };
for &MachReloc { let relocs = ctx
offset, .mach_compile_result
srcloc, .as_ref()
kind, .unwrap()
ref name, .buffer
addend, .relocs()
} in ctx.mach_compile_result.as_ref().unwrap().buffer.relocs() .to_vec();
{
reloc_sink.reloc_external(offset, srcloc, kind, name, addend);
}
self.record_function_for_perf(ptr, size, &decl.name); self.record_function_for_perf(ptr, size, &decl.name);
self.compiled_functions[id] = Some(CompiledBlob { self.compiled_functions[id] = Some(CompiledBlob { ptr, size, relocs });
ptr,
size,
relocs: reloc_sink.relocs,
});
if self.isa.flags().is_pic() { if self.isa.flags().is_pic() {
self.pending_got_updates.push(GotUpdate { self.pending_got_updates.push(GotUpdate {
@ -729,7 +721,7 @@ impl Module for JITModule {
&mut self, &mut self,
id: FuncId, id: FuncId,
bytes: &[u8], bytes: &[u8],
relocs: &[RelocRecord], relocs: &[MachReloc],
) -> ModuleResult<ModuleCompiledFunction> { ) -> ModuleResult<ModuleCompiledFunction> {
info!("defining function {} with bytes", id); info!("defining function {} with bytes", id);
let total_size: u32 = match bytes.len().try_into() { let total_size: u32 = match bytes.len().try_into() {
@ -896,26 +888,3 @@ fn lookup_with_dlsym(name: &str) -> Option<*const u8> {
None None
} }
} }
#[derive(Default)]
struct JITRelocSink {
relocs: Vec<RelocRecord>,
}
impl RelocSink for JITRelocSink {
fn reloc_external(
&mut self,
offset: CodeOffset,
_srcloc: ir::SourceLoc,
reloc: Reloc,
name: &ir::ExternalName,
addend: Addend,
) {
self.relocs.push(RelocRecord {
offset,
reloc,
name: name.clone(),
addend,
});
}
}

11
cranelift/jit/src/compiled_blob.rs

@ -1,13 +1,13 @@
use cranelift_codegen::binemit::Reloc; use cranelift_codegen::binemit::Reloc;
use cranelift_codegen::ir::ExternalName; use cranelift_codegen::ir::ExternalName;
use cranelift_module::RelocRecord; use cranelift_codegen::MachReloc;
use std::convert::TryFrom; use std::convert::TryFrom;
#[derive(Clone)] #[derive(Clone)]
pub(crate) struct CompiledBlob { pub(crate) struct CompiledBlob {
pub(crate) ptr: *mut u8, pub(crate) ptr: *mut u8,
pub(crate) size: usize, pub(crate) size: usize,
pub(crate) relocs: Vec<RelocRecord>, pub(crate) relocs: Vec<MachReloc>,
} }
impl CompiledBlob { impl CompiledBlob {
@ -19,16 +19,17 @@ impl CompiledBlob {
) { ) {
use std::ptr::write_unaligned; use std::ptr::write_unaligned;
for &RelocRecord { for &MachReloc {
reloc, kind,
offset, offset,
srcloc: _,
ref name, ref name,
addend, addend,
} in &self.relocs } in &self.relocs
{ {
debug_assert!((offset as usize) < self.size); debug_assert!((offset as usize) < self.size);
let at = unsafe { self.ptr.offset(isize::try_from(offset).unwrap()) }; let at = unsafe { self.ptr.offset(isize::try_from(offset).unwrap()) };
match reloc { match kind {
Reloc::Abs4 => { Reloc::Abs4 => {
let base = get_address(name); let base = get_address(name);
let what = unsafe { base.offset(isize::try_from(addend).unwrap()) }; let what = unsafe { base.offset(isize::try_from(addend).unwrap()) };

20
cranelift/module/src/data_context.rs

@ -2,14 +2,13 @@
use cranelift_codegen::binemit::{Addend, CodeOffset, Reloc}; use cranelift_codegen::binemit::{Addend, CodeOffset, Reloc};
use cranelift_codegen::entity::PrimaryMap; use cranelift_codegen::entity::PrimaryMap;
use cranelift_codegen::ir; use cranelift_codegen::ir::{self, SourceLoc};
use cranelift_codegen::MachReloc;
use std::borrow::ToOwned; use std::borrow::ToOwned;
use std::boxed::Box; use std::boxed::Box;
use std::string::String; use std::string::String;
use std::vec::Vec; use std::vec::Vec;
use crate::RelocRecord;
/// This specifies how data is to be initialized. /// This specifies how data is to be initialized.
#[derive(PartialEq, Eq, Debug)] #[derive(PartialEq, Eq, Debug)]
pub enum Init { pub enum Init {
@ -59,25 +58,24 @@ pub struct DataDescription {
impl DataDescription { impl DataDescription {
/// An iterator over all relocations of the data object. /// An iterator over all relocations of the data object.
pub fn all_relocs<'a>( pub fn all_relocs<'a>(&'a self, pointer_reloc: Reloc) -> impl Iterator<Item = MachReloc> + 'a {
&'a self,
pointer_reloc: Reloc,
) -> impl Iterator<Item = RelocRecord> + 'a {
let func_relocs = self let func_relocs = self
.function_relocs .function_relocs
.iter() .iter()
.map(move |&(offset, id)| RelocRecord { .map(move |&(offset, id)| MachReloc {
reloc: pointer_reloc, kind: pointer_reloc,
offset, offset,
srcloc: SourceLoc::default(),
name: self.function_decls[id].clone(), name: self.function_decls[id].clone(),
addend: 0, addend: 0,
}); });
let data_relocs = self let data_relocs = self
.data_relocs .data_relocs
.iter() .iter()
.map(move |&(offset, id, addend)| RelocRecord { .map(move |&(offset, id, addend)| MachReloc {
reloc: pointer_reloc, kind: pointer_reloc,
offset, offset,
srcloc: SourceLoc::default(),
name: self.data_decls[id].clone(), name: self.data_decls[id].clone(),
addend, addend,
}); });

2
cranelift/module/src/lib.rs

@ -43,7 +43,7 @@ mod traps;
pub use crate::data_context::{DataContext, DataDescription, Init}; pub use crate::data_context::{DataContext, DataDescription, Init};
pub use crate::module::{ pub use crate::module::{
DataId, FuncId, FuncOrDataId, Linkage, Module, ModuleCompiledFunction, ModuleDeclarations, DataId, FuncId, FuncOrDataId, Linkage, Module, ModuleCompiledFunction, ModuleDeclarations,
ModuleError, ModuleResult, RelocRecord, ModuleError, ModuleResult,
}; };
pub use crate::traps::TrapSite; pub use crate::traps::TrapSite;

19
cranelift/module/src/module.rs

@ -7,8 +7,8 @@
use super::HashMap; use super::HashMap;
use crate::data_context::DataContext; use crate::data_context::DataContext;
use cranelift_codegen::binemit;
use cranelift_codegen::entity::{entity_impl, PrimaryMap}; use cranelift_codegen::entity::{entity_impl, PrimaryMap};
use cranelift_codegen::{binemit, MachReloc};
use cranelift_codegen::{ir, isa, CodegenError, Context}; use cranelift_codegen::{ir, isa, CodegenError, Context};
use std::borrow::ToOwned; use std::borrow::ToOwned;
use std::string::String; use std::string::String;
@ -416,19 +416,6 @@ pub struct ModuleCompiledFunction {
pub size: binemit::CodeOffset, pub size: binemit::CodeOffset,
} }
/// A record of a relocation to perform.
#[derive(Clone)]
pub struct RelocRecord {
/// Where in the generated code this relocation is to be applied.
pub offset: binemit::CodeOffset,
/// The kind of relocation this represents.
pub reloc: binemit::Reloc,
/// What symbol we're relocating against.
pub name: ir::ExternalName,
/// The offset to add to the relocation.
pub addend: binemit::Addend,
}
/// A `Module` is a utility for collecting functions and data objects, and linking them together. /// A `Module` is a utility for collecting functions and data objects, and linking them together.
pub trait Module { pub trait Module {
/// Return the `TargetIsa` to compile for. /// Return the `TargetIsa` to compile for.
@ -567,7 +554,7 @@ pub trait Module {
&mut self, &mut self,
func: FuncId, func: FuncId,
bytes: &[u8], bytes: &[u8],
relocs: &[RelocRecord], relocs: &[MachReloc],
) -> ModuleResult<ModuleCompiledFunction>; ) -> ModuleResult<ModuleCompiledFunction>;
/// Define a data object, producing the data contents from the given `DataContext`. /// Define a data object, producing the data contents from the given `DataContext`.
@ -662,7 +649,7 @@ impl<M: Module> Module for &mut M {
&mut self, &mut self,
func: FuncId, func: FuncId,
bytes: &[u8], bytes: &[u8],
relocs: &[RelocRecord], relocs: &[MachReloc],
) -> ModuleResult<ModuleCompiledFunction> { ) -> ModuleResult<ModuleCompiledFunction> {
(**self).define_function_bytes(func, bytes, relocs) (**self).define_function_bytes(func, bytes, relocs)
} }

51
cranelift/object/src/backend.rs

@ -5,12 +5,12 @@ use cranelift_codegen::entity::SecondaryMap;
use cranelift_codegen::isa::TargetIsa; use cranelift_codegen::isa::TargetIsa;
use cranelift_codegen::{self, ir, MachReloc}; use cranelift_codegen::{self, ir, MachReloc};
use cranelift_codegen::{ use cranelift_codegen::{
binemit::{Addend, CodeOffset, Reloc, RelocSink}, binemit::{Addend, CodeOffset, Reloc},
CodegenError, CodegenError,
}; };
use cranelift_module::{ use cranelift_module::{
DataContext, DataDescription, DataId, FuncId, Init, Linkage, Module, ModuleCompiledFunction, DataContext, DataDescription, DataId, FuncId, Init, Linkage, Module, ModuleCompiledFunction,
ModuleDeclarations, ModuleError, ModuleResult, RelocRecord, ModuleDeclarations, ModuleError, ModuleResult,
}; };
use log::info; use log::info;
use object::write::{ use object::write::{
@ -310,29 +310,21 @@ impl Module for ObjectModule {
) -> ModuleResult<ModuleCompiledFunction> { ) -> ModuleResult<ModuleCompiledFunction> {
info!("defining function {}: {}", func_id, ctx.func.display()); info!("defining function {}: {}", func_id, ctx.func.display());
let mut code: Vec<u8> = Vec::new(); let mut code: Vec<u8> = Vec::new();
let mut reloc_sink = ObjectRelocSink::default();
ctx.compile_and_emit(self.isa(), &mut code)?; ctx.compile_and_emit(self.isa(), &mut code)?;
for &MachReloc { self.define_function_bytes(
offset, func_id,
srcloc, &code,
kind, ctx.mach_compile_result.as_ref().unwrap().buffer.relocs(),
ref name, )
addend,
} in ctx.mach_compile_result.as_ref().unwrap().buffer.relocs()
{
reloc_sink.reloc_external(offset, srcloc, kind, name, addend);
}
self.define_function_bytes(func_id, &code, &reloc_sink.relocs)
} }
fn define_function_bytes( fn define_function_bytes(
&mut self, &mut self,
func_id: FuncId, func_id: FuncId,
bytes: &[u8], bytes: &[u8],
relocs: &[RelocRecord], relocs: &[MachReloc],
) -> ModuleResult<ModuleCompiledFunction> { ) -> ModuleResult<ModuleCompiledFunction> {
info!("defining function {} with bytes", func_id); info!("defining function {} with bytes", func_id);
let total_size: u32 = match bytes.len().try_into() { let total_size: u32 = match bytes.len().try_into() {
@ -563,9 +555,9 @@ impl ObjectModule {
} }
} }
fn process_reloc(&self, record: &RelocRecord) -> ObjectRelocRecord { fn process_reloc(&self, record: &MachReloc) -> ObjectRelocRecord {
let mut addend = record.addend; let mut addend = record.addend;
let (kind, encoding, size) = match record.reloc { let (kind, encoding, size) = match record.kind {
Reloc::Abs4 => (RelocationKind::Absolute, RelocationEncoding::Generic, 32), Reloc::Abs4 => (RelocationKind::Absolute, RelocationEncoding::Generic, 32),
Reloc::Abs8 => (RelocationKind::Absolute, RelocationEncoding::Generic, 64), Reloc::Abs8 => (RelocationKind::Absolute, RelocationEncoding::Generic, 64),
Reloc::X86PCRel4 => (RelocationKind::Relative, RelocationEncoding::Generic, 32), Reloc::X86PCRel4 => (RelocationKind::Relative, RelocationEncoding::Generic, 32),
@ -710,26 +702,3 @@ struct ObjectRelocRecord {
size: u8, size: u8,
addend: Addend, addend: Addend,
} }
#[derive(Default)]
struct ObjectRelocSink {
relocs: Vec<RelocRecord>,
}
impl RelocSink for ObjectRelocSink {
fn reloc_external(
&mut self,
offset: CodeOffset,
_srcloc: ir::SourceLoc,
reloc: Reloc,
name: &ir::ExternalName,
addend: Addend,
) {
self.relocs.push(RelocRecord {
offset,
reloc,
addend,
name: name.clone(),
})
}
}

42
cranelift/src/compile.rs

@ -1,13 +1,12 @@
//! CLI tool to read Cranelift IR files and compile them into native code. //! CLI tool to read Cranelift IR files and compile them into native code.
use crate::disasm::{print_all, PrintRelocs, PrintStackMaps, PrintTraps}; use crate::disasm::print_all;
use crate::utils::{parse_sets_and_triple, read_to_string}; use crate::utils::{parse_sets_and_triple, read_to_string};
use anyhow::{Context as _, Result}; use anyhow::{Context as _, Result};
use cranelift_codegen::binemit::{RelocSink, StackMapSink, TrapSink};
use cranelift_codegen::print_errors::pretty_error; use cranelift_codegen::print_errors::pretty_error;
use cranelift_codegen::settings::FlagsOrIsa; use cranelift_codegen::settings::FlagsOrIsa;
use cranelift_codegen::timing;
use cranelift_codegen::Context; use cranelift_codegen::Context;
use cranelift_codegen::{timing, MachReloc, MachStackMap, MachTrap};
use cranelift_reader::{parse_test, ParseOptions}; use cranelift_reader::{parse_test, ParseOptions};
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
@ -69,10 +68,6 @@ fn handle_module(options: &Options, path: &Path, name: &str, fisa: FlagsOrIsa) -
}; };
for (func, _) in test_file.functions { for (func, _) in test_file.functions {
let mut relocs = PrintRelocs::new(options.print);
let mut traps = PrintTraps::new(options.print);
let mut stack_maps = PrintStackMaps::new(options.print);
if let Some(isa) = isa { if let Some(isa) = isa {
let mut context = Context::new(); let mut context = Context::new();
context.func = func; context.func = func;
@ -84,32 +79,6 @@ fn handle_module(options: &Options, path: &Path, name: &str, fisa: FlagsOrIsa) -
.map_err(|err| anyhow::anyhow!("{}", pretty_error(&context.func, err)))?; .map_err(|err| anyhow::anyhow!("{}", pretty_error(&context.func, err)))?;
let result = context.mach_compile_result.as_ref().unwrap(); let result = context.mach_compile_result.as_ref().unwrap();
let code_info = result.code_info(); let code_info = result.code_info();
for &MachReloc {
offset,
srcloc,
kind,
ref name,
addend,
} in result.buffer.relocs()
{
relocs.reloc_external(offset, srcloc, kind, name, addend);
}
for &MachTrap {
offset,
srcloc,
code,
} in result.buffer.traps()
{
traps.trap(offset, srcloc, code);
}
for &MachStackMap {
offset_end,
ref stack_map,
..
} in result.buffer.stack_maps()
{
stack_maps.add_stack_map(offset_end, stack_map.clone());
}
if options.print { if options.print {
println!("{}", context.func.display()); println!("{}", context.func.display());
@ -120,9 +89,10 @@ fn handle_module(options: &Options, path: &Path, name: &str, fisa: FlagsOrIsa) -
isa, isa,
&mem, &mem,
code_info.total_size, code_info.total_size,
&relocs, options.print,
&traps, result.buffer.relocs(),
&stack_maps, result.buffer.traps(),
result.buffer.stack_maps(),
)?; )?;
} }
} }

120
cranelift/src/disasm.rs

@ -1,85 +1,53 @@
use anyhow::Result; use anyhow::Result;
use cfg_if::cfg_if; use cfg_if::cfg_if;
use cranelift_codegen::isa::TargetIsa; use cranelift_codegen::isa::TargetIsa;
use cranelift_codegen::{binemit, ir}; use cranelift_codegen::{MachReloc, MachStackMap, MachTrap};
use std::fmt::Write; use std::fmt::Write;
pub struct PrintRelocs { pub fn print_relocs(relocs: &[MachReloc]) -> String {
pub flag_print: bool, let mut text = String::new();
pub text: String, for &MachReloc {
} kind,
offset,
impl PrintRelocs { srcloc: _,
pub fn new(flag_print: bool) -> Self { ref name,
Self { addend,
flag_print, } in relocs
text: String::new(), {
}
}
}
impl binemit::RelocSink for PrintRelocs {
fn reloc_external(
&mut self,
where_: binemit::CodeOffset,
_srcloc: ir::SourceLoc,
r: binemit::Reloc,
name: &ir::ExternalName,
addend: binemit::Addend,
) {
if self.flag_print {
writeln!( writeln!(
&mut self.text, text,
"reloc_external: {} {} {} at {}", "reloc_external: {} {} {} at {}",
r, name, addend, where_ kind, name, addend, offset
) )
.unwrap(); .unwrap();
} }
} text
}
pub struct PrintTraps {
pub flag_print: bool,
pub text: String,
}
impl PrintTraps {
pub fn new(flag_print: bool) -> Self {
Self {
flag_print,
text: String::new(),
}
}
}
impl binemit::TrapSink for PrintTraps {
fn trap(&mut self, offset: binemit::CodeOffset, _srcloc: ir::SourceLoc, code: ir::TrapCode) {
if self.flag_print {
writeln!(&mut self.text, "trap: {} at {}", code, offset).unwrap();
}
}
}
pub struct PrintStackMaps {
pub flag_print: bool,
pub text: String,
} }
impl PrintStackMaps { pub fn print_traps(traps: &[MachTrap]) -> String {
pub fn new(flag_print: bool) -> Self { let mut text = String::new();
Self { for &MachTrap {
flag_print, offset,
text: String::new(), srcloc: _,
} code,
} } in traps
{
writeln!(text, "trap: {} at {}", code, offset).unwrap();
}
text
} }
impl binemit::StackMapSink for PrintStackMaps { pub fn print_stack_maps(traps: &[MachStackMap]) -> String {
fn add_stack_map(&mut self, offset: binemit::CodeOffset, _: binemit::StackMap) { let mut text = String::new();
if self.flag_print { for &MachStackMap {
writeln!(&mut self.text, "add_stack_map at {}", offset).unwrap(); offset,
} offset_end: _,
} stack_map: _,
} in traps
{
writeln!(text, "add_stack_map at {}", offset).unwrap();
}
text
} }
cfg_if! { cfg_if! {
@ -193,13 +161,21 @@ pub fn print_all(
isa: &dyn TargetIsa, isa: &dyn TargetIsa,
mem: &[u8], mem: &[u8],
code_size: u32, code_size: u32,
relocs: &PrintRelocs, print: bool,
traps: &PrintTraps, relocs: &[MachReloc],
stack_maps: &PrintStackMaps, traps: &[MachTrap],
stack_maps: &[MachStackMap],
) -> Result<()> { ) -> Result<()> {
print_bytes(&mem); print_bytes(&mem);
print_disassembly(isa, &mem[0..code_size as usize])?; print_disassembly(isa, &mem[0..code_size as usize])?;
println!("\n{}\n{}\n{}", &relocs.text, &traps.text, &stack_maps.text); if print {
println!(
"\n{}\n{}\n{}",
print_relocs(relocs),
print_traps(traps),
print_stack_maps(stack_maps)
);
}
Ok(()) Ok(())
} }

54
cranelift/src/wasm.rs

@ -7,15 +7,14 @@
allow(clippy::too_many_arguments, clippy::cognitive_complexity) allow(clippy::too_many_arguments, clippy::cognitive_complexity)
)] )]
use crate::disasm::{print_all, PrintRelocs, PrintStackMaps, PrintTraps}; use crate::disasm::print_all;
use crate::utils::parse_sets_and_triple; use crate::utils::parse_sets_and_triple;
use anyhow::{Context as _, Result}; use anyhow::{Context as _, Result};
use cranelift_codegen::binemit::{RelocSink, StackMapSink, TrapSink};
use cranelift_codegen::ir::DisplayFunctionAnnotations; use cranelift_codegen::ir::DisplayFunctionAnnotations;
use cranelift_codegen::print_errors::{pretty_error, pretty_verifier_error}; use cranelift_codegen::print_errors::{pretty_error, pretty_verifier_error};
use cranelift_codegen::settings::FlagsOrIsa; use cranelift_codegen::settings::FlagsOrIsa;
use cranelift_codegen::timing;
use cranelift_codegen::Context; use cranelift_codegen::Context;
use cranelift_codegen::{timing, MachReloc, MachStackMap, MachTrap};
use cranelift_entity::EntityRef; use cranelift_entity::EntityRef;
use cranelift_wasm::{translate_module, DummyEnvironment, FuncIndex, ReturnMode}; use cranelift_wasm::{translate_module, DummyEnvironment, FuncIndex, ReturnMode};
use std::io::Read; use std::io::Read;
@ -259,45 +258,17 @@ fn handle_module(options: &Options, path: &Path, name: &str, fisa: FlagsOrIsa) -
let mut saved_size = None; let mut saved_size = None;
let func_index = num_func_imports + def_index.index(); let func_index = num_func_imports + def_index.index();
let mut mem = vec![]; let mut mem = vec![];
let mut relocs = PrintRelocs::new(options.print); let (relocs, traps, stack_maps) = if options.check_translation {
let mut traps = PrintTraps::new(options.print);
let mut stack_maps = PrintStackMaps::new(options.print);
if options.check_translation {
if let Err(errors) = context.verify(fisa) { if let Err(errors) = context.verify(fisa) {
anyhow::bail!("{}", pretty_verifier_error(&context.func, None, errors)); anyhow::bail!("{}", pretty_verifier_error(&context.func, None, errors));
} }
(vec![], vec![], vec![])
} else { } else {
context context
.compile_and_emit(isa, &mut mem) .compile_and_emit(isa, &mut mem)
.map_err(|err| anyhow::anyhow!("{}", pretty_error(&context.func, err)))?; .map_err(|err| anyhow::anyhow!("{}", pretty_error(&context.func, err)))?;
let result = context.mach_compile_result.as_ref().unwrap(); let result = context.mach_compile_result.as_ref().unwrap();
let code_info = result.code_info(); let code_info = result.code_info();
for &MachReloc {
offset,
srcloc,
kind,
ref name,
addend,
} in result.buffer.relocs()
{
relocs.reloc_external(offset, srcloc, kind, name, addend);
}
for &MachTrap {
offset,
srcloc,
code,
} in result.buffer.traps()
{
traps.trap(offset, srcloc, code);
}
for &MachStackMap {
offset_end,
ref stack_map,
..
} in result.buffer.stack_maps()
{
stack_maps.add_stack_map(offset_end, stack_map.clone());
}
if options.print_size { if options.print_size {
println!( println!(
@ -315,7 +286,12 @@ fn handle_module(options: &Options, path: &Path, name: &str, fisa: FlagsOrIsa) -
if options.disasm { if options.disasm {
saved_size = Some(code_info.total_size); saved_size = Some(code_info.total_size);
} }
} (
result.buffer.relocs().to_vec(),
result.buffer.traps().to_vec(),
result.buffer.stack_maps().to_vec(),
)
};
if options.print { if options.print {
vprintln!(options.verbose, ""); vprintln!(options.verbose, "");
@ -351,7 +327,15 @@ fn handle_module(options: &Options, path: &Path, name: &str, fisa: FlagsOrIsa) -
} }
if let Some(total_size) = saved_size { if let Some(total_size) = saved_size {
print_all(isa, &mem, total_size, &relocs, &traps, &stack_maps)?; print_all(
isa,
&mem,
total_size,
options.print,
&relocs,
&traps,
&stack_maps,
)?;
} }
context.clear(); context.clear();

231
crates/cranelift/src/compiler.rs

@ -7,11 +7,10 @@ use crate::{
CompiledFunction, FunctionAddressMap, Relocation, RelocationTarget, CompiledFunction, FunctionAddressMap, Relocation, RelocationTarget,
}; };
use anyhow::{Context as _, Result}; use anyhow::{Context as _, Result};
use cranelift_codegen::binemit::{RelocSink as _, StackMapSink as _, TrapSink as _};
use cranelift_codegen::ir::{self, ExternalName, InstBuilder, MemFlags}; use cranelift_codegen::ir::{self, ExternalName, InstBuilder, MemFlags};
use cranelift_codegen::isa::TargetIsa; use cranelift_codegen::isa::TargetIsa;
use cranelift_codegen::print_errors::pretty_error; use cranelift_codegen::print_errors::pretty_error;
use cranelift_codegen::{binemit, Context}; use cranelift_codegen::Context;
use cranelift_codegen::{settings, MachReloc, MachTrap}; use cranelift_codegen::{settings, MachReloc, MachTrap};
use cranelift_codegen::{MachSrcLoc, MachStackMap}; use cranelift_codegen::{MachSrcLoc, MachStackMap};
use cranelift_entity::{EntityRef, PrimaryMap}; use cranelift_entity::{EntityRef, PrimaryMap};
@ -167,40 +166,85 @@ impl wasmtime_environ::Compiler for Compiler {
self.save_translator(func_translator); self.save_translator(func_translator);
let mut code_buf: Vec<u8> = Vec::new(); let mut code_buf: Vec<u8> = Vec::new();
let mut reloc_sink = RelocSink::new();
let mut trap_sink = TrapSink::new();
let mut stack_map_sink = StackMapSink::default();
context context
.compile_and_emit(isa, &mut code_buf) .compile_and_emit(isa, &mut code_buf)
.map_err(|error| CompileError::Codegen(pretty_error(&context.func, error)))?; .map_err(|error| CompileError::Codegen(pretty_error(&context.func, error)))?;
let result = context.mach_compile_result.as_ref().unwrap(); let result = context.mach_compile_result.as_ref().unwrap();
let mut func_relocs = Vec::new();
for &MachReloc { for &MachReloc {
offset, offset,
srcloc, srcloc: _,
kind, kind,
ref name, ref name,
addend, addend,
} in result.buffer.relocs() } in result.buffer.relocs()
{ {
reloc_sink.reloc_external(offset, srcloc, kind, name, addend); let reloc_target = if let ExternalName::User { namespace, index } = *name {
debug_assert_eq!(namespace, 0);
RelocationTarget::UserFunc(FuncIndex::from_u32(index))
} else if let ExternalName::LibCall(libcall) = *name {
RelocationTarget::LibCall(libcall)
} else {
panic!("unrecognized external name")
};
func_relocs.push(Relocation {
reloc: kind,
reloc_target,
offset,
addend,
});
} }
let mut traps = Vec::new();
for &MachTrap { for &MachTrap {
offset, offset,
srcloc, srcloc: _,
code, code,
} in result.buffer.traps() } in result.buffer.traps()
{ {
trap_sink.trap(offset, srcloc, code); traps.push(TrapInformation {
code_offset: offset,
trap_code: match code {
ir::TrapCode::StackOverflow => TrapCode::StackOverflow,
ir::TrapCode::HeapOutOfBounds => TrapCode::HeapOutOfBounds,
ir::TrapCode::HeapMisaligned => TrapCode::HeapMisaligned,
ir::TrapCode::TableOutOfBounds => TrapCode::TableOutOfBounds,
ir::TrapCode::IndirectCallToNull => TrapCode::IndirectCallToNull,
ir::TrapCode::BadSignature => TrapCode::BadSignature,
ir::TrapCode::IntegerOverflow => TrapCode::IntegerOverflow,
ir::TrapCode::IntegerDivisionByZero => TrapCode::IntegerDivisionByZero,
ir::TrapCode::BadConversionToInteger => TrapCode::BadConversionToInteger,
ir::TrapCode::UnreachableCodeReached => TrapCode::UnreachableCodeReached,
ir::TrapCode::Interrupt => TrapCode::Interrupt,
// these should never be emitted by wasmtime-cranelift
ir::TrapCode::User(_) => unreachable!(),
},
});
} }
// This is converting from Cranelift's representation of a stack map to
// Wasmtime's representation. They happen to align today but that may
// not always be true in the future.
let mut stack_maps = Vec::new();
for &MachStackMap { for &MachStackMap {
offset_end, offset_end,
ref stack_map, ref stack_map,
.. ..
} in result.buffer.stack_maps() } in result.buffer.stack_maps()
{ {
stack_map_sink.add_stack_map(offset_end, stack_map.clone()); let stack_map = wasmtime_environ::StackMap::new(
stack_map.mapped_words(),
stack_map.as_slice().iter().map(|a| a.0),
);
stack_maps.push(StackMapInformation {
code_offset: offset_end,
stack_map,
});
} }
stack_maps.sort_unstable_by_key(|info| info.code_offset);
let unwind_info = context let unwind_info = context
.create_unwind_info(isa) .create_unwind_info(isa)
@ -229,14 +273,14 @@ impl wasmtime_environ::Compiler for Compiler {
let length = u32::try_from(code_buf.len()).unwrap(); let length = u32::try_from(code_buf.len()).unwrap();
Ok(Box::new(CompiledFunction { Ok(Box::new(CompiledFunction {
body: code_buf, body: code_buf,
relocations: reloc_sink.func_relocs, relocations: func_relocs,
value_labels_ranges: ranges.unwrap_or(Default::default()), value_labels_ranges: ranges.unwrap_or(Default::default()),
stack_slots: context.func.stack_slots, stack_slots: context.func.stack_slots,
unwind_info, unwind_info,
traps: trap_sink.traps, traps,
info: FunctionInfo { info: FunctionInfo {
start_srcloc: address_transform.start_srcloc, start_srcloc: address_transform.start_srcloc,
stack_maps: stack_map_sink.finish(), stack_maps,
start: 0, start: 0,
length, length,
}, },
@ -557,14 +601,14 @@ impl Compiler {
isa: &dyn TargetIsa, isa: &dyn TargetIsa,
) -> Result<CompiledFunction, CompileError> { ) -> Result<CompiledFunction, CompileError> {
let mut code_buf = Vec::new(); let mut code_buf = Vec::new();
let mut reloc_sink = TrampolineRelocSink::default(); let mut relocs = Vec::new();
context context
.compile_and_emit(isa, &mut code_buf) .compile_and_emit(isa, &mut code_buf)
.map_err(|error| CompileError::Codegen(pretty_error(&context.func, error)))?; .map_err(|error| CompileError::Codegen(pretty_error(&context.func, error)))?;
for &MachReloc { for &MachReloc {
offset, offset,
srcloc, srcloc: _,
kind, kind,
ref name, ref name,
addend, addend,
@ -575,7 +619,17 @@ impl Compiler {
.buffer .buffer
.relocs() .relocs()
{ {
reloc_sink.reloc_external(offset, srcloc, kind, name, addend); let reloc_target = if let ir::ExternalName::LibCall(libcall) = *name {
RelocationTarget::LibCall(libcall)
} else {
panic!("unrecognized external name")
};
relocs.push(Relocation {
reloc: kind,
reloc_target,
offset,
addend,
});
} }
let unwind_info = context let unwind_info = context
@ -585,7 +639,7 @@ impl Compiler {
Ok(CompiledFunction { Ok(CompiledFunction {
body: code_buf, body: code_buf,
unwind_info, unwind_info,
relocations: reloc_sink.relocs, relocations: relocs,
stack_slots: Default::default(), stack_slots: Default::default(),
value_labels_ranges: Default::default(), value_labels_ranges: Default::default(),
info: Default::default(), info: Default::default(),
@ -657,146 +711,3 @@ fn collect_address_maps(
} }
} }
} }
/// Implementation of a relocation sink that just saves all the information for later
struct RelocSink {
/// Relocations recorded for the function.
func_relocs: Vec<Relocation>,
}
impl binemit::RelocSink for RelocSink {
fn reloc_external(
&mut self,
offset: binemit::CodeOffset,
_srcloc: ir::SourceLoc,
reloc: binemit::Reloc,
name: &ExternalName,
addend: binemit::Addend,
) {
let reloc_target = if let ExternalName::User { namespace, index } = *name {
debug_assert_eq!(namespace, 0);
RelocationTarget::UserFunc(FuncIndex::from_u32(index))
} else if let ExternalName::LibCall(libcall) = *name {
RelocationTarget::LibCall(libcall)
} else {
panic!("unrecognized external name")
};
self.func_relocs.push(Relocation {
reloc,
reloc_target,
offset,
addend,
});
}
}
impl RelocSink {
/// Return a new `RelocSink` instance.
fn new() -> Self {
Self {
func_relocs: Vec::new(),
}
}
}
/// Implementation of a trap sink that simply stores all trap info in-memory
#[derive(Default)]
struct TrapSink {
/// The in-memory vector of trap info
traps: Vec<TrapInformation>,
}
impl TrapSink {
/// Create a new `TrapSink`
fn new() -> Self {
Self::default()
}
}
impl binemit::TrapSink for TrapSink {
fn trap(
&mut self,
code_offset: binemit::CodeOffset,
_source_loc: ir::SourceLoc,
trap_code: ir::TrapCode,
) {
self.traps.push(TrapInformation {
code_offset,
trap_code: match trap_code {
ir::TrapCode::StackOverflow => TrapCode::StackOverflow,
ir::TrapCode::HeapOutOfBounds => TrapCode::HeapOutOfBounds,
ir::TrapCode::HeapMisaligned => TrapCode::HeapMisaligned,
ir::TrapCode::TableOutOfBounds => TrapCode::TableOutOfBounds,
ir::TrapCode::IndirectCallToNull => TrapCode::IndirectCallToNull,
ir::TrapCode::BadSignature => TrapCode::BadSignature,
ir::TrapCode::IntegerOverflow => TrapCode::IntegerOverflow,
ir::TrapCode::IntegerDivisionByZero => TrapCode::IntegerDivisionByZero,
ir::TrapCode::BadConversionToInteger => TrapCode::BadConversionToInteger,
ir::TrapCode::UnreachableCodeReached => TrapCode::UnreachableCodeReached,
ir::TrapCode::Interrupt => TrapCode::Interrupt,
// these should never be emitted by wasmtime-cranelift
ir::TrapCode::User(_) => unreachable!(),
},
});
}
}
#[derive(Default)]
struct StackMapSink {
infos: Vec<StackMapInformation>,
}
impl binemit::StackMapSink for StackMapSink {
fn add_stack_map(&mut self, code_offset: binemit::CodeOffset, stack_map: binemit::StackMap) {
// This is converting from Cranelift's representation of a stack map to
// Wasmtime's representation. They happen to align today but that may
// not always be true in the future.
let stack_map = wasmtime_environ::StackMap::new(
stack_map.mapped_words(),
stack_map.as_slice().iter().map(|a| a.0),
);
self.infos.push(StackMapInformation {
code_offset,
stack_map,
});
}
}
impl StackMapSink {
fn finish(mut self) -> Vec<StackMapInformation> {
self.infos.sort_unstable_by_key(|info| info.code_offset);
self.infos
}
}
/// We don't expect trampoline compilation to produce many relocations, so
/// this `RelocSink` just asserts that it doesn't recieve most of them, but
/// handles libcall ones.
#[derive(Default)]
struct TrampolineRelocSink {
relocs: Vec<Relocation>,
}
impl binemit::RelocSink for TrampolineRelocSink {
fn reloc_external(
&mut self,
offset: binemit::CodeOffset,
_srcloc: ir::SourceLoc,
reloc: binemit::Reloc,
name: &ir::ExternalName,
addend: binemit::Addend,
) {
let reloc_target = if let ir::ExternalName::LibCall(libcall) = *name {
RelocationTarget::LibCall(libcall)
} else {
panic!("unrecognized external name")
};
self.relocs.push(Relocation {
reloc,
reloc_target,
offset,
addend,
});
}
}

Loading…
Cancel
Save