[cranelift] Rejigger the compile API (#4540)
* Move `emit_to_memory` to `MachCompileResult` This small refactoring makes it clearer to me that emitting to memory doesn't require anything else from the compilation `Context`. While it's a trivial change, it's a small public API change that shouldn't cause too much trouble, and doesn't seem RFC-worthy. Happy to hear different opinions about this, though! * hide the MachCompileResult behind a method * Add a `CompileError` wrapper type that references a `Function` * Rename MachCompileResult to CompiledCode * Additionally remove the last unsafe API in cranelift-codegen
This commit is contained in:
@@ -10,7 +10,6 @@
|
|||||||
//! single ISA instance.
|
//! single ISA instance.
|
||||||
|
|
||||||
use crate::alias_analysis::AliasAnalysis;
|
use crate::alias_analysis::AliasAnalysis;
|
||||||
use crate::binemit::CodeInfo;
|
|
||||||
use crate::dce::do_dce;
|
use crate::dce::do_dce;
|
||||||
use crate::dominator_tree::DominatorTree;
|
use crate::dominator_tree::DominatorTree;
|
||||||
use crate::flowgraph::ControlFlowGraph;
|
use crate::flowgraph::ControlFlowGraph;
|
||||||
@@ -19,16 +18,16 @@ use crate::isa::TargetIsa;
|
|||||||
use crate::legalizer::simple_legalize;
|
use crate::legalizer::simple_legalize;
|
||||||
use crate::licm::do_licm;
|
use crate::licm::do_licm;
|
||||||
use crate::loop_analysis::LoopAnalysis;
|
use crate::loop_analysis::LoopAnalysis;
|
||||||
use crate::machinst::MachCompileResult;
|
use crate::machinst::CompiledCode;
|
||||||
use crate::nan_canonicalization::do_nan_canonicalization;
|
use crate::nan_canonicalization::do_nan_canonicalization;
|
||||||
use crate::remove_constant_phis::do_remove_constant_phis;
|
use crate::remove_constant_phis::do_remove_constant_phis;
|
||||||
use crate::result::CodegenResult;
|
use crate::result::{CodegenResult, CompileResult};
|
||||||
use crate::settings::{FlagsOrIsa, OptLevel};
|
use crate::settings::{FlagsOrIsa, OptLevel};
|
||||||
use crate::simple_gvn::do_simple_gvn;
|
use crate::simple_gvn::do_simple_gvn;
|
||||||
use crate::simple_preopt::do_preopt;
|
use crate::simple_preopt::do_preopt;
|
||||||
use crate::timing;
|
|
||||||
use crate::unreachable_code::eliminate_unreachable_code;
|
use crate::unreachable_code::eliminate_unreachable_code;
|
||||||
use crate::verifier::{verify_context, VerifierErrors, VerifierResult};
|
use crate::verifier::{verify_context, VerifierErrors, VerifierResult};
|
||||||
|
use crate::{timing, CompileError};
|
||||||
#[cfg(feature = "souper-harvest")]
|
#[cfg(feature = "souper-harvest")]
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
@@ -51,9 +50,9 @@ pub struct Context {
|
|||||||
pub loop_analysis: LoopAnalysis,
|
pub loop_analysis: LoopAnalysis,
|
||||||
|
|
||||||
/// Result of MachBackend compilation, if computed.
|
/// Result of MachBackend compilation, if computed.
|
||||||
pub mach_compile_result: Option<MachCompileResult>,
|
compiled_code: Option<CompiledCode>,
|
||||||
|
|
||||||
/// Flag: do we want a disassembly with the MachCompileResult?
|
/// Flag: do we want a disassembly with the CompiledCode?
|
||||||
pub want_disasm: bool,
|
pub want_disasm: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +75,7 @@ impl Context {
|
|||||||
cfg: ControlFlowGraph::new(),
|
cfg: ControlFlowGraph::new(),
|
||||||
domtree: DominatorTree::new(),
|
domtree: DominatorTree::new(),
|
||||||
loop_analysis: LoopAnalysis::new(),
|
loop_analysis: LoopAnalysis::new(),
|
||||||
mach_compile_result: None,
|
compiled_code: None,
|
||||||
want_disasm: false,
|
want_disasm: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,10 +86,16 @@ impl Context {
|
|||||||
self.cfg.clear();
|
self.cfg.clear();
|
||||||
self.domtree.clear();
|
self.domtree.clear();
|
||||||
self.loop_analysis.clear();
|
self.loop_analysis.clear();
|
||||||
self.mach_compile_result = None;
|
self.compiled_code = None;
|
||||||
self.want_disasm = false;
|
self.want_disasm = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the compilation result for this function, available after any `compile` function
|
||||||
|
/// has been called.
|
||||||
|
pub fn compiled_code(&self) -> Option<&CompiledCode> {
|
||||||
|
self.compiled_code.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
/// Set the flag to request a disassembly when compiling with a
|
/// Set the flag to request a disassembly when compiling with a
|
||||||
/// `MachBackend` backend.
|
/// `MachBackend` backend.
|
||||||
pub fn set_disasm(&mut self, val: bool) {
|
pub fn set_disasm(&mut self, val: bool) {
|
||||||
@@ -102,7 +107,7 @@ impl Context {
|
|||||||
/// Run the function through all the passes necessary to generate code for the target ISA
|
/// Run the function through all the passes necessary to generate code for the target ISA
|
||||||
/// represented by `isa`, as well as the final step of emitting machine code into a
|
/// represented by `isa`, as well as the final step of emitting machine code into a
|
||||||
/// `Vec<u8>`. The machine code is not relocated. Instead, any relocations can be obtained
|
/// `Vec<u8>`. The machine code is not relocated. Instead, any relocations can be obtained
|
||||||
/// from `mach_compile_result`.
|
/// from `compiled_code()`.
|
||||||
///
|
///
|
||||||
/// This function calls `compile` and `emit_to_memory`, taking care to resize `mem` as
|
/// This function calls `compile` and `emit_to_memory`, taking care to resize `mem` as
|
||||||
/// needed, so it provides a safe interface.
|
/// needed, so it provides a safe interface.
|
||||||
@@ -112,13 +117,13 @@ impl Context {
|
|||||||
&mut self,
|
&mut self,
|
||||||
isa: &dyn TargetIsa,
|
isa: &dyn TargetIsa,
|
||||||
mem: &mut Vec<u8>,
|
mem: &mut Vec<u8>,
|
||||||
) -> CodegenResult<()> {
|
) -> CompileResult<&CompiledCode> {
|
||||||
let info = self.compile(isa)?;
|
let compiled_code = self.compile(isa)?;
|
||||||
|
let code_info = compiled_code.code_info();
|
||||||
let old_len = mem.len();
|
let old_len = mem.len();
|
||||||
mem.resize(old_len + info.total_size as usize, 0);
|
mem.resize(old_len + code_info.total_size as usize, 0);
|
||||||
let new_info = unsafe { self.emit_to_memory(mem.as_mut_ptr().add(old_len)) };
|
mem[old_len..].copy_from_slice(compiled_code.code_buffer());
|
||||||
debug_assert!(new_info == info);
|
Ok(compiled_code)
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compile the function.
|
/// Compile the function.
|
||||||
@@ -128,86 +133,66 @@ impl Context {
|
|||||||
/// code sink.
|
/// code sink.
|
||||||
///
|
///
|
||||||
/// Returns information about the function's code and read-only data.
|
/// Returns information about the function's code and read-only data.
|
||||||
pub fn compile(&mut self, isa: &dyn TargetIsa) -> CodegenResult<CodeInfo> {
|
pub fn compile(&mut self, isa: &dyn TargetIsa) -> CompileResult<&CompiledCode> {
|
||||||
let _tt = timing::compile();
|
let _tt = timing::compile();
|
||||||
self.verify_if(isa)?;
|
|
||||||
|
|
||||||
let opt_level = isa.flags().opt_level();
|
let mut inner = || {
|
||||||
log::trace!(
|
self.verify_if(isa)?;
|
||||||
"Compiling (opt level {:?}):\n{}",
|
|
||||||
opt_level,
|
|
||||||
self.func.display()
|
|
||||||
);
|
|
||||||
|
|
||||||
self.compute_cfg();
|
let opt_level = isa.flags().opt_level();
|
||||||
if opt_level != OptLevel::None {
|
log::trace!(
|
||||||
self.preopt(isa)?;
|
"Compiling (opt level {:?}):\n{}",
|
||||||
}
|
opt_level,
|
||||||
if isa.flags().enable_nan_canonicalization() {
|
self.func.display()
|
||||||
self.canonicalize_nans(isa)?;
|
);
|
||||||
}
|
|
||||||
|
self.compute_cfg();
|
||||||
|
if opt_level != OptLevel::None {
|
||||||
|
self.preopt(isa)?;
|
||||||
|
}
|
||||||
|
if isa.flags().enable_nan_canonicalization() {
|
||||||
|
self.canonicalize_nans(isa)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.legalize(isa)?;
|
||||||
|
if opt_level != OptLevel::None {
|
||||||
|
self.compute_domtree();
|
||||||
|
self.compute_loop_analysis();
|
||||||
|
self.licm(isa)?;
|
||||||
|
self.simple_gvn(isa)?;
|
||||||
|
}
|
||||||
|
|
||||||
self.legalize(isa)?;
|
|
||||||
if opt_level != OptLevel::None {
|
|
||||||
self.compute_domtree();
|
self.compute_domtree();
|
||||||
self.compute_loop_analysis();
|
self.eliminate_unreachable_code(isa)?;
|
||||||
self.licm(isa)?;
|
if opt_level != OptLevel::None {
|
||||||
self.simple_gvn(isa)?;
|
self.dce(isa)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.compute_domtree();
|
self.remove_constant_phis(isa)?;
|
||||||
self.eliminate_unreachable_code(isa)?;
|
|
||||||
if opt_level != OptLevel::None {
|
|
||||||
self.dce(isa)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.remove_constant_phis(isa)?;
|
if opt_level != OptLevel::None && isa.flags().enable_alias_analysis() {
|
||||||
|
self.replace_redundant_loads()?;
|
||||||
|
self.simple_gvn(isa)?;
|
||||||
|
}
|
||||||
|
|
||||||
if opt_level != OptLevel::None && isa.flags().enable_alias_analysis() {
|
let result = isa.compile_function(&self.func, self.want_disasm)?;
|
||||||
self.replace_redundant_loads()?;
|
self.compiled_code = Some(result);
|
||||||
self.simple_gvn(isa)?;
|
Ok(())
|
||||||
}
|
};
|
||||||
|
|
||||||
let result = isa.compile_function(&self.func, self.want_disasm)?;
|
inner()
|
||||||
let info = result.code_info();
|
.map(|_| self.compiled_code.as_ref().unwrap())
|
||||||
self.mach_compile_result = Some(result);
|
.map_err(|error| CompileError {
|
||||||
Ok(info)
|
inner: error,
|
||||||
}
|
func: &self.func,
|
||||||
|
})
|
||||||
/// Emit machine code directly into raw memory.
|
|
||||||
///
|
|
||||||
/// Write all of the function's machine code to the memory at `mem`. The size of the machine
|
|
||||||
/// code is returned by `compile` above.
|
|
||||||
///
|
|
||||||
/// The machine code is not relocated.
|
|
||||||
/// Instead, any relocations can be obtained from `mach_compile_result`.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// This function is unsafe since it does not perform bounds checking on the memory buffer,
|
|
||||||
/// and it can't guarantee that the `mem` pointer is valid.
|
|
||||||
///
|
|
||||||
/// Returns information about the emitted code and data.
|
|
||||||
#[deny(unsafe_op_in_unsafe_fn)]
|
|
||||||
pub unsafe fn emit_to_memory(&self, mem: *mut u8) -> CodeInfo {
|
|
||||||
let _tt = timing::binemit();
|
|
||||||
let result = self
|
|
||||||
.mach_compile_result
|
|
||||||
.as_ref()
|
|
||||||
.expect("only using mach backend now");
|
|
||||||
let info = result.code_info();
|
|
||||||
|
|
||||||
let mem = unsafe { std::slice::from_raw_parts_mut(mem, info.total_size as usize) };
|
|
||||||
mem.copy_from_slice(result.buffer.data());
|
|
||||||
|
|
||||||
info
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If available, return information about the code layout in the
|
/// If available, return information about the code layout in the
|
||||||
/// final machine code: the offsets (in bytes) of each basic-block
|
/// final machine code: the offsets (in bytes) of each basic-block
|
||||||
/// start, and all basic-block edges.
|
/// start, and all basic-block edges.
|
||||||
pub fn get_code_bb_layout(&self) -> Option<(Vec<usize>, Vec<(usize, usize)>)> {
|
pub fn get_code_bb_layout(&self) -> Option<(Vec<usize>, Vec<(usize, usize)>)> {
|
||||||
if let Some(result) = self.mach_compile_result.as_ref() {
|
if let Some(result) = self.compiled_code.as_ref() {
|
||||||
Some((
|
Some((
|
||||||
result.bb_starts.iter().map(|&off| off as usize).collect(),
|
result.bb_starts.iter().map(|&off| off as usize).collect(),
|
||||||
result
|
result
|
||||||
@@ -230,7 +215,7 @@ impl Context {
|
|||||||
isa: &dyn TargetIsa,
|
isa: &dyn TargetIsa,
|
||||||
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
|
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
|
||||||
let unwind_info_kind = isa.unwind_info_kind();
|
let unwind_info_kind = isa.unwind_info_kind();
|
||||||
let result = self.mach_compile_result.as_ref().unwrap();
|
let result = self.compiled_code.as_ref().unwrap();
|
||||||
isa.emit_unwind_info(result, unwind_info_kind)
|
isa.emit_unwind_info(result, unwind_info_kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use crate::isa::aarch64::settings as aarch64_settings;
|
|||||||
use crate::isa::unwind::systemv;
|
use crate::isa::unwind::systemv;
|
||||||
use crate::isa::{Builder as IsaBuilder, TargetIsa};
|
use crate::isa::{Builder as IsaBuilder, TargetIsa};
|
||||||
use crate::machinst::{
|
use crate::machinst::{
|
||||||
compile, MachCompileResult, MachTextSectionBuilder, Reg, TextSectionBuilder, VCode,
|
compile, CompiledCode, MachTextSectionBuilder, Reg, TextSectionBuilder, VCode,
|
||||||
};
|
};
|
||||||
use crate::result::CodegenResult;
|
use crate::result::CodegenResult;
|
||||||
use crate::settings as shared_settings;
|
use crate::settings as shared_settings;
|
||||||
@@ -65,11 +65,7 @@ impl AArch64Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TargetIsa for AArch64Backend {
|
impl TargetIsa for AArch64Backend {
|
||||||
fn compile_function(
|
fn compile_function(&self, func: &Function, want_disasm: bool) -> CodegenResult<CompiledCode> {
|
||||||
&self,
|
|
||||||
func: &Function,
|
|
||||||
want_disasm: bool,
|
|
||||||
) -> CodegenResult<MachCompileResult> {
|
|
||||||
let flags = self.flags();
|
let flags = self.flags();
|
||||||
let (vcode, regalloc_result) = self.compile_vcode(func, flags.clone())?;
|
let (vcode, regalloc_result) = self.compile_vcode(func, flags.clone())?;
|
||||||
|
|
||||||
@@ -84,7 +80,7 @@ impl TargetIsa for AArch64Backend {
|
|||||||
log::debug!("disassembly:\n{}", disasm);
|
log::debug!("disassembly:\n{}", disasm);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(MachCompileResult {
|
Ok(CompiledCode {
|
||||||
buffer,
|
buffer,
|
||||||
frame_size,
|
frame_size,
|
||||||
disasm: emit_result.disasm,
|
disasm: emit_result.disasm,
|
||||||
@@ -125,7 +121,7 @@ impl TargetIsa for AArch64Backend {
|
|||||||
#[cfg(feature = "unwind")]
|
#[cfg(feature = "unwind")]
|
||||||
fn emit_unwind_info(
|
fn emit_unwind_info(
|
||||||
&self,
|
&self,
|
||||||
result: &MachCompileResult,
|
result: &CompiledCode,
|
||||||
kind: crate::machinst::UnwindInfoKind,
|
kind: crate::machinst::UnwindInfoKind,
|
||||||
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
|
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
|
||||||
use crate::isa::unwind::UnwindInfo;
|
use crate::isa::unwind::UnwindInfo;
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ use crate::flowgraph;
|
|||||||
use crate::ir::{self, Function};
|
use crate::ir::{self, Function};
|
||||||
#[cfg(feature = "unwind")]
|
#[cfg(feature = "unwind")]
|
||||||
use crate::isa::unwind::systemv::RegisterMappingError;
|
use crate::isa::unwind::systemv::RegisterMappingError;
|
||||||
use crate::machinst::{MachCompileResult, TextSectionBuilder, UnwindInfoKind};
|
use crate::machinst::{CompiledCode, TextSectionBuilder, UnwindInfoKind};
|
||||||
use crate::settings;
|
use crate::settings;
|
||||||
use crate::settings::SetResult;
|
use crate::settings::SetResult;
|
||||||
use crate::CodegenResult;
|
use crate::CodegenResult;
|
||||||
@@ -230,11 +230,7 @@ pub trait TargetIsa: fmt::Display + Send + Sync {
|
|||||||
fn dynamic_vector_bytes(&self, dynamic_ty: ir::Type) -> u32;
|
fn dynamic_vector_bytes(&self, dynamic_ty: ir::Type) -> u32;
|
||||||
|
|
||||||
/// Compile the given function.
|
/// Compile the given function.
|
||||||
fn compile_function(
|
fn compile_function(&self, func: &Function, want_disasm: bool) -> CodegenResult<CompiledCode>;
|
||||||
&self,
|
|
||||||
func: &Function,
|
|
||||||
want_disasm: bool,
|
|
||||||
) -> CodegenResult<MachCompileResult>;
|
|
||||||
|
|
||||||
#[cfg(feature = "unwind")]
|
#[cfg(feature = "unwind")]
|
||||||
/// Map a regalloc::Reg to its corresponding DWARF register.
|
/// Map a regalloc::Reg to its corresponding DWARF register.
|
||||||
@@ -254,7 +250,7 @@ pub trait TargetIsa: fmt::Display + Send + Sync {
|
|||||||
#[cfg(feature = "unwind")]
|
#[cfg(feature = "unwind")]
|
||||||
fn emit_unwind_info(
|
fn emit_unwind_info(
|
||||||
&self,
|
&self,
|
||||||
result: &MachCompileResult,
|
result: &CompiledCode,
|
||||||
kind: UnwindInfoKind,
|
kind: UnwindInfoKind,
|
||||||
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>>;
|
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>>;
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use crate::isa::s390x::settings as s390x_settings;
|
|||||||
use crate::isa::unwind::systemv::RegisterMappingError;
|
use crate::isa::unwind::systemv::RegisterMappingError;
|
||||||
use crate::isa::{Builder as IsaBuilder, TargetIsa};
|
use crate::isa::{Builder as IsaBuilder, TargetIsa};
|
||||||
use crate::machinst::{
|
use crate::machinst::{
|
||||||
compile, MachCompileResult, MachTextSectionBuilder, Reg, TextSectionBuilder, VCode,
|
compile, CompiledCode, MachTextSectionBuilder, Reg, TextSectionBuilder, VCode,
|
||||||
};
|
};
|
||||||
use crate::result::CodegenResult;
|
use crate::result::CodegenResult;
|
||||||
use crate::settings as shared_settings;
|
use crate::settings as shared_settings;
|
||||||
@@ -64,11 +64,7 @@ impl S390xBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TargetIsa for S390xBackend {
|
impl TargetIsa for S390xBackend {
|
||||||
fn compile_function(
|
fn compile_function(&self, func: &Function, want_disasm: bool) -> CodegenResult<CompiledCode> {
|
||||||
&self,
|
|
||||||
func: &Function,
|
|
||||||
want_disasm: bool,
|
|
||||||
) -> CodegenResult<MachCompileResult> {
|
|
||||||
let flags = self.flags();
|
let flags = self.flags();
|
||||||
let (vcode, regalloc_result) = self.compile_vcode(func, flags.clone())?;
|
let (vcode, regalloc_result) = self.compile_vcode(func, flags.clone())?;
|
||||||
|
|
||||||
@@ -83,7 +79,7 @@ impl TargetIsa for S390xBackend {
|
|||||||
log::debug!("disassembly:\n{}", disasm);
|
log::debug!("disassembly:\n{}", disasm);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(MachCompileResult {
|
Ok(CompiledCode {
|
||||||
buffer,
|
buffer,
|
||||||
frame_size,
|
frame_size,
|
||||||
disasm: emit_result.disasm,
|
disasm: emit_result.disasm,
|
||||||
@@ -127,7 +123,7 @@ impl TargetIsa for S390xBackend {
|
|||||||
#[cfg(feature = "unwind")]
|
#[cfg(feature = "unwind")]
|
||||||
fn emit_unwind_info(
|
fn emit_unwind_info(
|
||||||
&self,
|
&self,
|
||||||
result: &MachCompileResult,
|
result: &CompiledCode,
|
||||||
kind: crate::machinst::UnwindInfoKind,
|
kind: crate::machinst::UnwindInfoKind,
|
||||||
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
|
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
|
||||||
use crate::isa::unwind::UnwindInfo;
|
use crate::isa::unwind::UnwindInfo;
|
||||||
|
|||||||
@@ -9,9 +9,7 @@ use crate::isa::unwind::systemv;
|
|||||||
use crate::isa::x64::{inst::regs::create_reg_env_systemv, settings as x64_settings};
|
use crate::isa::x64::{inst::regs::create_reg_env_systemv, settings as x64_settings};
|
||||||
use crate::isa::Builder as IsaBuilder;
|
use crate::isa::Builder as IsaBuilder;
|
||||||
use crate::machinst::Reg;
|
use crate::machinst::Reg;
|
||||||
use crate::machinst::{
|
use crate::machinst::{compile, CompiledCode, MachTextSectionBuilder, TextSectionBuilder, VCode};
|
||||||
compile, MachCompileResult, MachTextSectionBuilder, TextSectionBuilder, VCode,
|
|
||||||
};
|
|
||||||
use crate::result::{CodegenError, CodegenResult};
|
use crate::result::{CodegenError, CodegenResult};
|
||||||
use crate::settings::{self as shared_settings, Flags};
|
use crate::settings::{self as shared_settings, Flags};
|
||||||
use alloc::{boxed::Box, vec::Vec};
|
use alloc::{boxed::Box, vec::Vec};
|
||||||
@@ -59,11 +57,7 @@ impl X64Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TargetIsa for X64Backend {
|
impl TargetIsa for X64Backend {
|
||||||
fn compile_function(
|
fn compile_function(&self, func: &Function, want_disasm: bool) -> CodegenResult<CompiledCode> {
|
||||||
&self,
|
|
||||||
func: &Function,
|
|
||||||
want_disasm: bool,
|
|
||||||
) -> CodegenResult<MachCompileResult> {
|
|
||||||
let flags = self.flags();
|
let flags = self.flags();
|
||||||
let (vcode, regalloc_result) = self.compile_vcode(func, flags.clone())?;
|
let (vcode, regalloc_result) = self.compile_vcode(func, flags.clone())?;
|
||||||
|
|
||||||
@@ -78,7 +72,7 @@ impl TargetIsa for X64Backend {
|
|||||||
log::trace!("disassembly:\n{}", disasm);
|
log::trace!("disassembly:\n{}", disasm);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(MachCompileResult {
|
Ok(CompiledCode {
|
||||||
buffer,
|
buffer,
|
||||||
frame_size,
|
frame_size,
|
||||||
disasm: emit_result.disasm,
|
disasm: emit_result.disasm,
|
||||||
@@ -119,7 +113,7 @@ impl TargetIsa for X64Backend {
|
|||||||
#[cfg(feature = "unwind")]
|
#[cfg(feature = "unwind")]
|
||||||
fn emit_unwind_info(
|
fn emit_unwind_info(
|
||||||
&self,
|
&self,
|
||||||
result: &MachCompileResult,
|
result: &CompiledCode,
|
||||||
kind: crate::machinst::UnwindInfoKind,
|
kind: crate::machinst::UnwindInfoKind,
|
||||||
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
|
) -> CodegenResult<Option<crate::isa::unwind::UnwindInfo>> {
|
||||||
use crate::isa::unwind::UnwindInfo;
|
use crate::isa::unwind::UnwindInfo;
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ mod value_label;
|
|||||||
#[cfg(feature = "souper-harvest")]
|
#[cfg(feature = "souper-harvest")]
|
||||||
mod souper_harvest;
|
mod souper_harvest;
|
||||||
|
|
||||||
pub use crate::result::{CodegenError, CodegenResult};
|
pub use crate::result::{CodegenError, CodegenResult, CompileError};
|
||||||
|
|
||||||
/// Even when trace logging is disabled, the trace macro has a significant performance cost so we
|
/// Even when trace logging is disabled, the trace macro has a significant performance cost so we
|
||||||
/// disable it by default.
|
/// disable it by default.
|
||||||
|
|||||||
@@ -272,7 +272,7 @@ pub trait MachInstEmitState<I: MachInst>: Default + Clone + Debug {
|
|||||||
|
|
||||||
/// The result of a `MachBackend::compile_function()` call. Contains machine
|
/// The result of a `MachBackend::compile_function()` call. Contains machine
|
||||||
/// code (as bytes) and a disassembly, if requested.
|
/// code (as bytes) and a disassembly, if requested.
|
||||||
pub struct MachCompileResult {
|
pub struct CompiledCode {
|
||||||
/// Machine code.
|
/// Machine code.
|
||||||
pub buffer: MachBufferFinalized,
|
pub buffer: MachBufferFinalized,
|
||||||
/// Size of stack frame, in bytes.
|
/// Size of stack frame, in bytes.
|
||||||
@@ -299,13 +299,18 @@ pub struct MachCompileResult {
|
|||||||
pub bb_edges: Vec<(CodeOffset, CodeOffset)>,
|
pub bb_edges: Vec<(CodeOffset, CodeOffset)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MachCompileResult {
|
impl CompiledCode {
|
||||||
/// Get a `CodeInfo` describing section sizes from this compilation result.
|
/// Get a `CodeInfo` describing section sizes from this compilation result.
|
||||||
pub fn code_info(&self) -> CodeInfo {
|
pub fn code_info(&self) -> CodeInfo {
|
||||||
CodeInfo {
|
CodeInfo {
|
||||||
total_size: self.buffer.total_size(),
|
total_size: self.buffer.total_size(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a reference to the machine code generated for this function compilation.
|
||||||
|
pub fn code_buffer(&self) -> &[u8] {
|
||||||
|
self.buffer.data()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An object that can be used to create the text section of an executable.
|
/// An object that can be used to create the text section of an executable.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use regalloc2::checker::CheckerErrors;
|
use regalloc2::checker::CheckerErrors;
|
||||||
|
|
||||||
use crate::verifier::VerifierErrors;
|
use crate::{ir::Function, verifier::VerifierErrors};
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
|
|
||||||
/// A compilation error.
|
/// A compilation error.
|
||||||
@@ -81,3 +81,22 @@ impl From<VerifierErrors> for CodegenError {
|
|||||||
CodegenError::Verifier { 0: source }
|
CodegenError::Verifier { 0: source }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Compilation error, with the accompanying function to help printing it.
|
||||||
|
pub struct CompileError<'a> {
|
||||||
|
/// Underlying `CodegenError` that triggered the error.
|
||||||
|
pub inner: CodegenError,
|
||||||
|
/// Function we tried to compile, for display purposes.
|
||||||
|
pub func: &'a Function,
|
||||||
|
}
|
||||||
|
|
||||||
|
// By default, have `CompileError` be displayed as the internal error, and let consumers care if
|
||||||
|
// they want to use the func field for adding details.
|
||||||
|
impl<'a> core::fmt::Debug for CompileError<'a> {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
self.inner.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A convenient alias for a `Result` that uses `CompileError` as the error type.
|
||||||
|
pub type CompileResult<'a, T> = Result<T, CompileError<'a>>;
|
||||||
|
|||||||
@@ -68,7 +68,6 @@ define_passes! {
|
|||||||
|
|
||||||
regalloc: "Register allocation",
|
regalloc: "Register allocation",
|
||||||
regalloc_checker: "Register allocation symbolic verification",
|
regalloc_checker: "Register allocation symbolic verification",
|
||||||
binemit: "Binary machine code emission",
|
|
||||||
layout_renumber: "Layout full renumbering",
|
layout_renumber: "Layout full renumbering",
|
||||||
|
|
||||||
canonicalize_nans: "Canonicalization of NaNs",
|
canonicalize_nans: "Canonicalization of NaNs",
|
||||||
|
|||||||
@@ -241,12 +241,10 @@ fn compile(function: Function, isa: &dyn TargetIsa) -> Result<Mmap, CompilationE
|
|||||||
context.func = function;
|
context.func = function;
|
||||||
|
|
||||||
// Compile and encode the result to machine code.
|
// Compile and encode the result to machine code.
|
||||||
let code_info = context.compile(isa)?;
|
let compiled_code = context.compile(isa).map_err(|err| err.inner)?;
|
||||||
let mut code_page = MmapMut::map_anon(code_info.total_size as usize)?;
|
let mut code_page = MmapMut::map_anon(compiled_code.code_info().total_size as usize)?;
|
||||||
|
|
||||||
unsafe {
|
code_page.copy_from_slice(compiled_code.code_buffer());
|
||||||
context.emit_to_memory(code_page.as_mut_ptr());
|
|
||||||
};
|
|
||||||
|
|
||||||
let code_page = code_page.make_exec()?;
|
let code_page = code_page.make_exec()?;
|
||||||
trace!(
|
trace!(
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
use crate::subtest::{run_filecheck, Context, SubTest};
|
use crate::subtest::{run_filecheck, Context, SubTest};
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use cranelift_codegen::binemit::CodeInfo;
|
|
||||||
use cranelift_codegen::ir;
|
use cranelift_codegen::ir;
|
||||||
use cranelift_reader::{TestCommand, TestOption};
|
use cranelift_reader::{TestCommand, TestOption};
|
||||||
use log::info;
|
use log::info;
|
||||||
@@ -54,17 +53,12 @@ impl SubTest for TestCompile {
|
|||||||
// With `MachBackend`s, we need to explicitly request dissassembly results.
|
// With `MachBackend`s, we need to explicitly request dissassembly results.
|
||||||
comp_ctx.set_disasm(true);
|
comp_ctx.set_disasm(true);
|
||||||
|
|
||||||
let CodeInfo { total_size, .. } = comp_ctx
|
let compiled_code = comp_ctx
|
||||||
.compile(isa)
|
.compile(isa)
|
||||||
.map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, e))?;
|
.map_err(|e| crate::pretty_anyhow_error(&e.func, e.inner))?;
|
||||||
|
let total_size = compiled_code.code_info().total_size;
|
||||||
|
|
||||||
let disasm = comp_ctx
|
let disasm = compiled_code.disasm.as_ref().unwrap();
|
||||||
.mach_compile_result
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.disasm
|
|
||||||
.as_ref()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
info!("Generated {} bytes of code:\n{}", total_size, disasm);
|
info!("Generated {} bytes of code:\n{}", total_size, disasm);
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,7 @@ use crate::{compiled_blob::CompiledBlob, memory::Memory};
|
|||||||
use cranelift_codegen::isa::TargetIsa;
|
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::Reloc, CodegenError};
|
||||||
binemit::{CodeInfo, Reloc},
|
|
||||||
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,
|
||||||
@@ -684,10 +681,8 @@ impl Module for JITModule {
|
|||||||
return Err(ModuleError::DuplicateDefinition(decl.name.to_owned()));
|
return Err(ModuleError::DuplicateDefinition(decl.name.to_owned()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let CodeInfo {
|
let compiled_code = ctx.compile(self.isa())?;
|
||||||
total_size: code_size,
|
let code_size = compiled_code.code_info().total_size;
|
||||||
..
|
|
||||||
} = ctx.compile(self.isa())?;
|
|
||||||
|
|
||||||
let size = code_size as usize;
|
let size = code_size as usize;
|
||||||
let ptr = self
|
let ptr = self
|
||||||
@@ -696,14 +691,12 @@ impl Module for JITModule {
|
|||||||
.allocate(size, EXECUTABLE_DATA_ALIGNMENT)
|
.allocate(size, EXECUTABLE_DATA_ALIGNMENT)
|
||||||
.expect("TODO: handle OOM etc.");
|
.expect("TODO: handle OOM etc.");
|
||||||
|
|
||||||
unsafe { ctx.emit_to_memory(ptr) };
|
{
|
||||||
let relocs = ctx
|
let mem = unsafe { std::slice::from_raw_parts_mut(ptr, size) };
|
||||||
.mach_compile_result
|
mem.copy_from_slice(compiled_code.code_buffer());
|
||||||
.as_ref()
|
}
|
||||||
.unwrap()
|
|
||||||
.buffer
|
let relocs = compiled_code.buffer.relocs().to_vec();
|
||||||
.relocs()
|
|
||||||
.to_vec();
|
|
||||||
|
|
||||||
self.record_function_for_perf(ptr, size, &decl.name);
|
self.record_function_for_perf(ptr, size, &decl.name);
|
||||||
self.compiled_functions[id] = Some(CompiledBlob { ptr, size, relocs });
|
self.compiled_functions[id] = Some(CompiledBlob { ptr, size, relocs });
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use super::HashMap;
|
|||||||
use crate::data_context::DataContext;
|
use crate::data_context::DataContext;
|
||||||
use cranelift_codegen::entity::{entity_impl, PrimaryMap};
|
use cranelift_codegen::entity::{entity_impl, PrimaryMap};
|
||||||
use cranelift_codegen::{binemit, MachReloc};
|
use cranelift_codegen::{binemit, MachReloc};
|
||||||
use cranelift_codegen::{ir, isa, CodegenError, Context};
|
use cranelift_codegen::{ir, isa, CodegenError, CompileError, Context};
|
||||||
use std::borrow::ToOwned;
|
use std::borrow::ToOwned;
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
|
|
||||||
@@ -192,6 +192,12 @@ pub enum ModuleError {
|
|||||||
Backend(anyhow::Error),
|
Backend(anyhow::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> From<CompileError<'a>> for ModuleError {
|
||||||
|
fn from(err: CompileError<'a>) -> Self {
|
||||||
|
Self::Compilation(err.inner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This is manually implementing Error and Display instead of using thiserror to reduce the amount
|
// This is manually implementing Error and Display instead of using thiserror to reduce the amount
|
||||||
// of dependencies used by Cranelift.
|
// of dependencies used by Cranelift.
|
||||||
impl std::error::Error for ModuleError {
|
impl std::error::Error for ModuleError {
|
||||||
|
|||||||
@@ -313,11 +313,7 @@ impl Module for ObjectModule {
|
|||||||
|
|
||||||
ctx.compile_and_emit(self.isa(), &mut code)?;
|
ctx.compile_and_emit(self.isa(), &mut code)?;
|
||||||
|
|
||||||
self.define_function_bytes(
|
self.define_function_bytes(func_id, &code, ctx.compiled_code().unwrap().buffer.relocs())
|
||||||
func_id,
|
|
||||||
&code,
|
|
||||||
ctx.mach_compile_result.as_ref().unwrap().buffer.relocs(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn define_function_bytes(
|
fn define_function_bytes(
|
||||||
|
|||||||
@@ -68,17 +68,17 @@ fn handle_module(options: &Options, path: &Path, name: &str, fisa: FlagsOrIsa) -
|
|||||||
let mut mem = vec![];
|
let mut mem = vec![];
|
||||||
|
|
||||||
// Compile and encode the result to machine code.
|
// Compile and encode the result to machine code.
|
||||||
context
|
let compiled_code = 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(&err.func, err.inner)))?;
|
||||||
let result = context.mach_compile_result.as_ref().unwrap();
|
let code_info = compiled_code.code_info();
|
||||||
let code_info = result.code_info();
|
|
||||||
|
|
||||||
if options.print {
|
if options.print {
|
||||||
println!("{}", context.func.display());
|
println!("{}", context.func.display());
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.disasm {
|
if options.disasm {
|
||||||
|
let result = context.compiled_code().unwrap();
|
||||||
print_all(
|
print_all(
|
||||||
isa,
|
isa,
|
||||||
&mem,
|
&mem,
|
||||||
|
|||||||
@@ -257,11 +257,10 @@ fn handle_module(options: &Options, path: &Path, name: &str, fisa: FlagsOrIsa) -
|
|||||||
}
|
}
|
||||||
(vec![], vec![], vec![])
|
(vec![], vec![], vec![])
|
||||||
} else {
|
} else {
|
||||||
context
|
let compiled_code = 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(&err.func, err.inner)))?;
|
||||||
let result = context.mach_compile_result.as_ref().unwrap();
|
let code_info = compiled_code.code_info();
|
||||||
let code_info = result.code_info();
|
|
||||||
|
|
||||||
if options.print_size {
|
if options.print_size {
|
||||||
println!(
|
println!(
|
||||||
@@ -280,9 +279,9 @@ fn handle_module(options: &Options, path: &Path, name: &str, fisa: FlagsOrIsa) -
|
|||||||
saved_size = Some(code_info.total_size);
|
saved_size = Some(code_info.total_size);
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
result.buffer.relocs().to_vec(),
|
compiled_code.buffer.relocs().to_vec(),
|
||||||
result.buffer.traps().to_vec(),
|
compiled_code.buffer.traps().to_vec(),
|
||||||
result.buffer.stack_maps().to_vec(),
|
compiled_code.buffer.stack_maps().to_vec(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -299,14 +298,7 @@ fn handle_module(options: &Options, path: &Path, name: &str, fisa: FlagsOrIsa) -
|
|||||||
println!("; Exported as \"{}\"", export_name);
|
println!("; Exported as \"{}\"", export_name);
|
||||||
}
|
}
|
||||||
let value_ranges = if options.value_ranges {
|
let value_ranges = if options.value_ranges {
|
||||||
Some(
|
Some(context.compiled_code().unwrap().value_labels_ranges.clone())
|
||||||
context
|
|
||||||
.mach_compile_result
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.value_labels_ranges
|
|
||||||
.clone(),
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -98,14 +98,13 @@ impl Compiler {
|
|||||||
let start_srcloc = FilePos::new(offset as u32);
|
let start_srcloc = FilePos::new(offset as u32);
|
||||||
let end_srcloc = FilePos::new((offset + len) as u32);
|
let end_srcloc = FilePos::new((offset + len) as u32);
|
||||||
|
|
||||||
// New-style backend: we have a `MachCompileResult` that will give us `MachSrcLoc` mapping
|
// New-style backend: we have a `CompiledCode` that will give us `MachSrcLoc` mapping
|
||||||
// tuples.
|
// tuples.
|
||||||
let instructions = if tunables.generate_address_map {
|
let instructions = if tunables.generate_address_map {
|
||||||
collect_address_maps(
|
collect_address_maps(
|
||||||
body_len,
|
body_len,
|
||||||
context
|
context
|
||||||
.mach_compile_result
|
.compiled_code()
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.buffer
|
.buffer
|
||||||
.get_srclocs_sorted()
|
.get_srclocs_sorted()
|
||||||
@@ -212,27 +211,25 @@ impl wasmtime_environ::Compiler for Compiler {
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut code_buf: Vec<u8> = Vec::new();
|
let mut code_buf: Vec<u8> = Vec::new();
|
||||||
context
|
let compiled_code = 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(&error.func, error.inner)))?;
|
||||||
|
|
||||||
let result = context.mach_compile_result.as_ref().unwrap();
|
let func_relocs = compiled_code
|
||||||
|
|
||||||
let func_relocs = result
|
|
||||||
.buffer
|
.buffer
|
||||||
.relocs()
|
.relocs()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(mach_reloc_to_reloc)
|
.map(mach_reloc_to_reloc)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let traps = result
|
let traps = compiled_code
|
||||||
.buffer
|
.buffer
|
||||||
.traps()
|
.traps()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(mach_trap_to_trap)
|
.map(mach_trap_to_trap)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let stack_maps = mach_stack_maps_to_stack_maps(result.buffer.stack_maps());
|
let stack_maps = mach_stack_maps_to_stack_maps(compiled_code.buffer.stack_maps());
|
||||||
|
|
||||||
let unwind_info = if isa.flags().unwind_info() {
|
let unwind_info = if isa.flags().unwind_info() {
|
||||||
context
|
context
|
||||||
@@ -246,14 +243,7 @@ impl wasmtime_environ::Compiler for Compiler {
|
|||||||
self.get_function_address_map(&context, &input, code_buf.len() as u32, tunables);
|
self.get_function_address_map(&context, &input, code_buf.len() as u32, tunables);
|
||||||
|
|
||||||
let ranges = if tunables.generate_native_debuginfo {
|
let ranges = if tunables.generate_native_debuginfo {
|
||||||
Some(
|
Some(context.compiled_code().unwrap().value_labels_ranges.clone())
|
||||||
context
|
|
||||||
.mach_compile_result
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.value_labels_ranges
|
|
||||||
.clone(),
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
@@ -681,19 +671,18 @@ 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();
|
||||||
context
|
let compiled_code = 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(&error.func, error.inner)))?;
|
||||||
let result = context.mach_compile_result.as_ref().unwrap();
|
|
||||||
|
|
||||||
// Processing relocations isn't the hardest thing in the world here but
|
// Processing relocations isn't the hardest thing in the world here but
|
||||||
// no trampoline should currently generate a relocation, so assert that
|
// no trampoline should currently generate a relocation, so assert that
|
||||||
// they're all empty and if this ever trips in the future then handling
|
// they're all empty and if this ever trips in the future then handling
|
||||||
// will need to be added here to ensure they make their way into the
|
// will need to be added here to ensure they make their way into the
|
||||||
// `CompiledFunction` below.
|
// `CompiledFunction` below.
|
||||||
assert!(result.buffer.relocs().is_empty());
|
assert!(compiled_code.buffer.relocs().is_empty());
|
||||||
|
|
||||||
let traps = result
|
let traps = compiled_code
|
||||||
.buffer
|
.buffer
|
||||||
.traps()
|
.traps()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|||||||
Reference in New Issue
Block a user