Record information about sections of emitted code+data.
The result of the emitter is a vector of bytes holding machine code, jump tables, and (in the future) other read-only data. Some clients, notably Firefox's Wasm compiler, needs to separate the machine code from the data in order to insert more code directly after the code generated by Cranelift. To make such separation possible, we record more information about the emitted bytes: the sizes of each of the sections of code, jump tables, and read-only data, as well as the locations within the code that reference (PC-relatively) the jump tables and read-only data.
This commit is contained in:
committed by
Lars T Hansen
parent
70f79d23bf
commit
420850adf0
@@ -14,7 +14,7 @@
|
|||||||
//! relocations to a `RelocSink` trait object. Relocations are less frequent than the
|
//! 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.
|
//! `CodeSink::put*` methods, so the performance impact of the virtual callbacks is less severe.
|
||||||
|
|
||||||
use super::{Addend, CodeOffset, CodeSink, Reloc};
|
use super::{Addend, CodeInfo, CodeOffset, CodeSink, Reloc};
|
||||||
use crate::ir::{ExternalName, JumpTable, SourceLoc, TrapCode};
|
use crate::ir::{ExternalName, JumpTable, SourceLoc, TrapCode};
|
||||||
use core::ptr::write_unaligned;
|
use core::ptr::write_unaligned;
|
||||||
|
|
||||||
@@ -30,12 +30,14 @@ use core::ptr::write_unaligned;
|
|||||||
/// Note that `MemoryCodeSink` writes multi-byte values in the native byte order of the host. This
|
/// Note that `MemoryCodeSink` writes multi-byte values in the native byte order of the host. This
|
||||||
/// is not the right thing to do for cross compilation.
|
/// is not the right thing to do for cross compilation.
|
||||||
pub struct MemoryCodeSink<'a> {
|
pub struct MemoryCodeSink<'a> {
|
||||||
|
/// Pointer to start of sink's preallocated memory.
|
||||||
data: *mut u8,
|
data: *mut u8,
|
||||||
|
/// Offset is isize because its major consumer needs it in that form.
|
||||||
offset: isize,
|
offset: isize,
|
||||||
/// Size of the machine code portion of output
|
|
||||||
pub code_size: isize,
|
|
||||||
relocs: &'a mut RelocSink,
|
relocs: &'a mut RelocSink,
|
||||||
traps: &'a mut TrapSink,
|
traps: &'a mut TrapSink,
|
||||||
|
/// Information about the generated code and read-only data.
|
||||||
|
pub info: CodeInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> MemoryCodeSink<'a> {
|
impl<'a> MemoryCodeSink<'a> {
|
||||||
@@ -47,7 +49,12 @@ impl<'a> MemoryCodeSink<'a> {
|
|||||||
Self {
|
Self {
|
||||||
data,
|
data,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
code_size: 0,
|
info: CodeInfo {
|
||||||
|
code_size: 0,
|
||||||
|
jumptables_size: 0,
|
||||||
|
rodata_size: 0,
|
||||||
|
total_size: 0,
|
||||||
|
},
|
||||||
relocs,
|
relocs,
|
||||||
traps,
|
traps,
|
||||||
}
|
}
|
||||||
@@ -75,40 +82,35 @@ pub trait TrapSink {
|
|||||||
fn trap(&mut self, _: CodeOffset, _: SourceLoc, _: TrapCode);
|
fn trap(&mut self, _: CodeOffset, _: SourceLoc, _: TrapCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> MemoryCodeSink<'a> {
|
||||||
|
fn write<T>(&mut self, x: T) {
|
||||||
|
unsafe {
|
||||||
|
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
|
||||||
|
write_unaligned(self.data.offset(self.offset) as *mut T, x);
|
||||||
|
self.offset += std::mem::size_of::<T>() as isize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> CodeSink for MemoryCodeSink<'a> {
|
impl<'a> CodeSink for MemoryCodeSink<'a> {
|
||||||
fn offset(&self) -> CodeOffset {
|
fn offset(&self) -> CodeOffset {
|
||||||
self.offset as CodeOffset
|
self.offset as CodeOffset
|
||||||
}
|
}
|
||||||
|
|
||||||
fn put1(&mut self, x: u8) {
|
fn put1(&mut self, x: u8) {
|
||||||
unsafe {
|
self.write(x);
|
||||||
write_unaligned(self.data.offset(self.offset), x);
|
|
||||||
}
|
|
||||||
self.offset += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn put2(&mut self, x: u16) {
|
fn put2(&mut self, x: u16) {
|
||||||
unsafe {
|
self.write(x);
|
||||||
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
|
|
||||||
write_unaligned(self.data.offset(self.offset) as *mut u16, x);
|
|
||||||
}
|
|
||||||
self.offset += 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn put4(&mut self, x: u32) {
|
fn put4(&mut self, x: u32) {
|
||||||
unsafe {
|
self.write(x);
|
||||||
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
|
|
||||||
write_unaligned(self.data.offset(self.offset) as *mut u32, x);
|
|
||||||
}
|
|
||||||
self.offset += 4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn put8(&mut self, x: u64) {
|
fn put8(&mut self, x: u64) {
|
||||||
unsafe {
|
self.write(x);
|
||||||
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
|
|
||||||
write_unaligned(self.data.offset(self.offset) as *mut u64, x);
|
|
||||||
}
|
|
||||||
self.offset += 8;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reloc_ebb(&mut self, rel: Reloc, ebb_offset: CodeOffset) {
|
fn reloc_ebb(&mut self, rel: Reloc, ebb_offset: CodeOffset) {
|
||||||
@@ -131,8 +133,17 @@ impl<'a> CodeSink for MemoryCodeSink<'a> {
|
|||||||
self.traps.trap(ofs, srcloc, code);
|
self.traps.trap(ofs, srcloc, code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn begin_jumptables(&mut self) {
|
||||||
|
self.info.code_size = self.offset();
|
||||||
|
}
|
||||||
|
|
||||||
fn begin_rodata(&mut self) {
|
fn begin_rodata(&mut self) {
|
||||||
self.code_size = self.offset;
|
self.info.jumptables_size = self.offset() - self.info.code_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn end_codegen(&mut self) {
|
||||||
|
self.info.rodata_size = self.offset() - self.info.jumptables_size;
|
||||||
|
self.info.total_size = self.offset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ pub enum Reloc {
|
|||||||
Abs8,
|
Abs8,
|
||||||
/// x86 PC-relative 4-byte
|
/// x86 PC-relative 4-byte
|
||||||
X86PCRel4,
|
X86PCRel4,
|
||||||
|
/// x86 PC-relative 4-byte offset to trailing rodata
|
||||||
|
X86PCRelRodata4,
|
||||||
/// x86 call to PC-relative 4-byte
|
/// x86 call to PC-relative 4-byte
|
||||||
X86CallPCRel4,
|
X86CallPCRel4,
|
||||||
/// x86 call to PLT-relative 4-byte
|
/// x86 call to PLT-relative 4-byte
|
||||||
@@ -55,6 +57,7 @@ impl fmt::Display for Reloc {
|
|||||||
Reloc::Abs4 => write!(f, "Abs4"),
|
Reloc::Abs4 => write!(f, "Abs4"),
|
||||||
Reloc::Abs8 => write!(f, "Abs8"),
|
Reloc::Abs8 => write!(f, "Abs8"),
|
||||||
Reloc::X86PCRel4 => write!(f, "PCRel4"),
|
Reloc::X86PCRel4 => write!(f, "PCRel4"),
|
||||||
|
Reloc::X86PCRelRodata4 => write!(f, "PCRelRodata4"),
|
||||||
Reloc::X86CallPCRel4 => write!(f, "CallPCRel4"),
|
Reloc::X86CallPCRel4 => write!(f, "CallPCRel4"),
|
||||||
Reloc::X86CallPLTRel4 => write!(f, "CallPLTRel4"),
|
Reloc::X86CallPLTRel4 => write!(f, "CallPLTRel4"),
|
||||||
Reloc::X86GOTPCRel4 => write!(f, "GOTPCRel4"),
|
Reloc::X86GOTPCRel4 => write!(f, "GOTPCRel4"),
|
||||||
@@ -63,6 +66,38 @@ impl fmt::Display for Reloc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Container for information about a vector of compiled code and its supporting read-only data.
|
||||||
|
///
|
||||||
|
/// The code starts at offset 0 and is followed optionally by relocatable jump tables and copyable
|
||||||
|
/// (raw binary) read-only data. Any padding between sections is always part of the section that
|
||||||
|
/// precedes the boundary between the sections.
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub struct CodeInfo {
|
||||||
|
/// Number of bytes of machine code (the code starts at offset 0).
|
||||||
|
pub code_size: CodeOffset,
|
||||||
|
|
||||||
|
/// Number of bytes of jumptables.
|
||||||
|
pub jumptables_size: CodeOffset,
|
||||||
|
|
||||||
|
/// Number of bytes of rodata.
|
||||||
|
pub rodata_size: CodeOffset,
|
||||||
|
|
||||||
|
/// Number of bytes in total.
|
||||||
|
pub total_size: CodeOffset,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CodeInfo {
|
||||||
|
/// Offset of any relocatable jump tables, or equal to rodata if there are no jump tables.
|
||||||
|
pub fn jumptables(&self) -> CodeOffset {
|
||||||
|
self.code_size
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Offset of any copyable read-only data, or equal to total_size if there are no rodata.
|
||||||
|
pub fn rodata(&self) -> CodeOffset {
|
||||||
|
self.code_size + self.jumptables_size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Abstract interface for adding bytes to the code segment.
|
/// Abstract interface for adding bytes to the code segment.
|
||||||
///
|
///
|
||||||
/// A `CodeSink` will receive all of the machine code for a function. It also accepts relocations
|
/// A `CodeSink` will receive all of the machine code for a function. It also accepts relocations
|
||||||
@@ -95,8 +130,14 @@ pub trait CodeSink {
|
|||||||
/// Add trap information for the current offset.
|
/// Add trap information for the current offset.
|
||||||
fn trap(&mut self, _: TrapCode, _: SourceLoc);
|
fn trap(&mut self, _: TrapCode, _: SourceLoc);
|
||||||
|
|
||||||
/// Code output is complete, read-only data may follow.
|
/// Machine code output is complete, jump table data may follow.
|
||||||
|
fn begin_jumptables(&mut self);
|
||||||
|
|
||||||
|
/// Jump table output is complete, raw read-only data may follow.
|
||||||
fn begin_rodata(&mut self);
|
fn begin_rodata(&mut self);
|
||||||
|
|
||||||
|
/// Read-only data output is complete, we're done.
|
||||||
|
fn end_codegen(&mut self);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Report a bad encoding error.
|
/// Report a bad encoding error.
|
||||||
@@ -127,7 +168,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sink.begin_rodata();
|
sink.begin_jumptables();
|
||||||
|
|
||||||
// output jump tables
|
// output jump tables
|
||||||
for (jt, jt_data) in func.jump_tables.iter() {
|
for (jt, jt_data) in func.jump_tables.iter() {
|
||||||
@@ -137,4 +178,9 @@ where
|
|||||||
sink.put4(rel_offset as u32)
|
sink.put4(rel_offset as u32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sink.begin_rodata();
|
||||||
|
// TODO: No read-only data (constant pools) at this time.
|
||||||
|
|
||||||
|
sink.end_codegen();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
//! ebb23:
|
//! ebb23:
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use crate::binemit::CodeOffset;
|
use crate::binemit::{CodeInfo, CodeOffset};
|
||||||
use crate::cursor::{Cursor, FuncCursor};
|
use crate::cursor::{Cursor, FuncCursor};
|
||||||
use crate::ir::{Function, InstructionData, Opcode};
|
use crate::ir::{Function, InstructionData, Opcode};
|
||||||
use crate::isa::{EncInfo, TargetIsa};
|
use crate::isa::{EncInfo, TargetIsa};
|
||||||
@@ -40,7 +40,7 @@ use log::debug;
|
|||||||
/// Relax branches and compute the final layout of EBB headers in `func`.
|
/// Relax branches and compute the final layout of EBB headers in `func`.
|
||||||
///
|
///
|
||||||
/// Fill in the `func.offsets` table so the function is ready for binary emission.
|
/// Fill in the `func.offsets` table so the function is ready for binary emission.
|
||||||
pub fn relax_branches(func: &mut Function, isa: &TargetIsa) -> CodegenResult<CodeOffset> {
|
pub fn relax_branches(func: &mut Function, isa: &TargetIsa) -> CodegenResult<CodeInfo> {
|
||||||
let _tt = timing::relax_branches();
|
let _tt = timing::relax_branches();
|
||||||
|
|
||||||
let encinfo = isa.encoding_info();
|
let encinfo = isa.encoding_info();
|
||||||
@@ -109,6 +109,9 @@ pub fn relax_branches(func: &mut Function, isa: &TargetIsa) -> CodegenResult<Cod
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let code_size = offset;
|
||||||
|
let jumptables = offset;
|
||||||
|
|
||||||
for (jt, jt_data) in func.jump_tables.iter() {
|
for (jt, jt_data) in func.jump_tables.iter() {
|
||||||
func.jt_offsets[jt] = offset;
|
func.jt_offsets[jt] = offset;
|
||||||
// TODO: this should be computed based on the min size needed to hold
|
// TODO: this should be computed based on the min size needed to hold
|
||||||
@@ -116,7 +119,19 @@ pub fn relax_branches(func: &mut Function, isa: &TargetIsa) -> CodegenResult<Cod
|
|||||||
offset += jt_data.len() as u32 * 4;
|
offset += jt_data.len() as u32 * 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(offset)
|
let jumptables_size = offset - jumptables;
|
||||||
|
let rodata = offset;
|
||||||
|
|
||||||
|
// TODO: Once we have constant pools we'll do some processing here to update offset.
|
||||||
|
|
||||||
|
let rodata_size = offset - rodata;
|
||||||
|
|
||||||
|
Ok(CodeInfo {
|
||||||
|
code_size,
|
||||||
|
jumptables_size,
|
||||||
|
rodata_size,
|
||||||
|
total_size: offset,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert `jump` instructions to `fallthrough` instructions where possible and verify that any
|
/// Convert `jump` instructions to `fallthrough` instructions where possible and verify that any
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
//! single ISA instance.
|
//! single ISA instance.
|
||||||
|
|
||||||
use crate::binemit::{
|
use crate::binemit::{
|
||||||
relax_branches, shrink_instructions, CodeOffset, MemoryCodeSink, RelocSink, TrapSink,
|
relax_branches, shrink_instructions, CodeInfo, MemoryCodeSink, RelocSink, TrapSink,
|
||||||
};
|
};
|
||||||
use crate::dce::do_dce;
|
use crate::dce::do_dce;
|
||||||
use crate::dominator_tree::DominatorTree;
|
use crate::dominator_tree::DominatorTree;
|
||||||
@@ -93,20 +93,21 @@ impl Context {
|
|||||||
/// 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.
|
||||||
///
|
///
|
||||||
/// Returns the size of the function's code and the size of the read-only data.
|
/// Returns information about the function's code and read-only data.
|
||||||
pub fn compile_and_emit(
|
pub fn compile_and_emit(
|
||||||
&mut self,
|
&mut self,
|
||||||
isa: &TargetIsa,
|
isa: &TargetIsa,
|
||||||
mem: &mut Vec<u8>,
|
mem: &mut Vec<u8>,
|
||||||
relocs: &mut RelocSink,
|
relocs: &mut RelocSink,
|
||||||
traps: &mut TrapSink,
|
traps: &mut TrapSink,
|
||||||
) -> CodegenResult<(CodeOffset, CodeOffset)> {
|
) -> CodegenResult<CodeInfo> {
|
||||||
let total_size = self.compile(isa)?;
|
let info = self.compile(isa)?;
|
||||||
let old_len = mem.len();
|
let old_len = mem.len();
|
||||||
mem.resize(old_len + total_size as usize, 0);
|
mem.resize(old_len + info.total_size as usize, 0);
|
||||||
let code_size =
|
let new_info =
|
||||||
unsafe { self.emit_to_memory(isa, mem.as_mut_ptr().add(old_len), relocs, traps) };
|
unsafe { self.emit_to_memory(isa, mem.as_mut_ptr().add(old_len), relocs, traps) };
|
||||||
Ok((code_size, total_size - code_size))
|
debug_assert!(new_info == info);
|
||||||
|
Ok(info)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compile the function.
|
/// Compile the function.
|
||||||
@@ -115,8 +116,8 @@ impl Context {
|
|||||||
/// represented by `isa`. This does not include the final step of emitting machine code into a
|
/// represented by `isa`. This does not include the final step of emitting machine code into a
|
||||||
/// code sink.
|
/// code sink.
|
||||||
///
|
///
|
||||||
/// Returns the size of 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: &TargetIsa) -> CodegenResult<CodeOffset> {
|
pub fn compile(&mut self, isa: &TargetIsa) -> CodegenResult<CodeInfo> {
|
||||||
let _tt = timing::compile();
|
let _tt = timing::compile();
|
||||||
self.verify_if(isa)?;
|
self.verify_if(isa)?;
|
||||||
|
|
||||||
@@ -160,18 +161,18 @@ impl Context {
|
|||||||
/// This function is unsafe since it does not perform bounds checking on the memory buffer,
|
/// 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.
|
/// and it can't guarantee that the `mem` pointer is valid.
|
||||||
///
|
///
|
||||||
/// Returns the size of the function's code.
|
/// Returns information about the emitted code and data.
|
||||||
pub unsafe fn emit_to_memory(
|
pub unsafe fn emit_to_memory(
|
||||||
&self,
|
&self,
|
||||||
isa: &TargetIsa,
|
isa: &TargetIsa,
|
||||||
mem: *mut u8,
|
mem: *mut u8,
|
||||||
relocs: &mut RelocSink,
|
relocs: &mut RelocSink,
|
||||||
traps: &mut TrapSink,
|
traps: &mut TrapSink,
|
||||||
) -> CodeOffset {
|
) -> CodeInfo {
|
||||||
let _tt = timing::binemit();
|
let _tt = timing::binemit();
|
||||||
let mut sink = MemoryCodeSink::new(mem, relocs, traps);
|
let mut sink = MemoryCodeSink::new(mem, relocs, traps);
|
||||||
isa.emit_function_to_memory(&self.func, &mut sink);
|
isa.emit_function_to_memory(&self.func, &mut sink);
|
||||||
sink.code_size as CodeOffset
|
sink.info
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run the verifier on the function.
|
/// Run the verifier on the function.
|
||||||
@@ -325,12 +326,13 @@ impl Context {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run the branch relaxation pass and return the final code size.
|
/// Run the branch relaxation pass and return information about the function's code and
|
||||||
pub fn relax_branches(&mut self, isa: &TargetIsa) -> CodegenResult<CodeOffset> {
|
/// read-only data.
|
||||||
let code_size = relax_branches(&mut self.func, isa)?;
|
pub fn relax_branches(&mut self, isa: &TargetIsa) -> CodegenResult<CodeInfo> {
|
||||||
|
let info = relax_branches(&mut self.func, isa)?;
|
||||||
self.verify_if(isa)?;
|
self.verify_if(isa)?;
|
||||||
self.verify_locations_if(isa)?;
|
self.verify_locations_if(isa)?;
|
||||||
Ok(code_size)
|
Ok(info)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds ranges and location for specified value labels.
|
/// Builds ranges and location for specified value labels.
|
||||||
|
|||||||
@@ -339,4 +339,5 @@ fn disp4<CS: CodeSink + ?Sized>(destination: Ebb, func: &Function, sink: &mut CS
|
|||||||
fn jt_disp4<CS: CodeSink + ?Sized>(jt: JumpTable, func: &Function, sink: &mut CS) {
|
fn jt_disp4<CS: CodeSink + ?Sized>(jt: JumpTable, func: &Function, sink: &mut CS) {
|
||||||
let delta = func.jt_offsets[jt].wrapping_sub(sink.offset() + 4);
|
let delta = func.jt_offsets[jt].wrapping_sub(sink.offset() + 4);
|
||||||
sink.put4(delta);
|
sink.put4(delta);
|
||||||
|
sink.reloc_jt(Reloc::X86PCRelRodata4, jt);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -138,9 +138,9 @@ impl Backend for FaerieBackend {
|
|||||||
name: &str,
|
name: &str,
|
||||||
ctx: &cranelift_codegen::Context,
|
ctx: &cranelift_codegen::Context,
|
||||||
namespace: &ModuleNamespace<Self>,
|
namespace: &ModuleNamespace<Self>,
|
||||||
code_size: u32,
|
total_size: u32,
|
||||||
) -> ModuleResult<FaerieCompiledFunction> {
|
) -> ModuleResult<FaerieCompiledFunction> {
|
||||||
let mut code: Vec<u8> = vec![0; code_size as usize];
|
let mut code: Vec<u8> = vec![0; total_size as usize];
|
||||||
|
|
||||||
// Non-lexical lifetimes would obviate the braces here.
|
// Non-lexical lifetimes would obviate the braces here.
|
||||||
{
|
{
|
||||||
@@ -153,7 +153,7 @@ impl Backend for FaerieBackend {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(ref mut trap_manifest) = self.trap_manifest {
|
if let Some(ref mut trap_manifest) = self.trap_manifest {
|
||||||
let mut trap_sink = FaerieTrapSink::new(name, code_size);
|
let mut trap_sink = FaerieTrapSink::new(name, total_size);
|
||||||
unsafe {
|
unsafe {
|
||||||
ctx.emit_to_memory(
|
ctx.emit_to_memory(
|
||||||
&*self.isa,
|
&*self.isa,
|
||||||
|
|||||||
@@ -1424,8 +1424,8 @@ function %I64_JT(i64 [%rdi]) {
|
|||||||
ebb0(v0: i64 [%rdi]):
|
ebb0(v0: i64 [%rdi]):
|
||||||
; Note: The next two lines will need to change whenever instructions are
|
; Note: The next two lines will need to change whenever instructions are
|
||||||
; added or removed from this test.
|
; added or removed from this test.
|
||||||
[-, %rax] v1 = jump_table_base.i64 jt0 ; bin: 48 8d 05 00000039
|
[-, %rax] v1 = jump_table_base.i64 jt0 ; bin: 48 8d 05 00000039 PCRelRodata4(jt0)
|
||||||
[-, %r10] v2 = jump_table_base.i64 jt0 ; bin: 4c 8d 15 00000032
|
[-, %r10] v2 = jump_table_base.i64 jt0 ; bin: 4c 8d 15 00000032 PCRelRodata4(jt0)
|
||||||
|
|
||||||
[-, %rbx] v10 = iconst.i64 1
|
[-, %rbx] v10 = iconst.i64 1
|
||||||
[-, %r13] v11 = iconst.i64 2
|
[-, %r13] v11 = iconst.i64 2
|
||||||
|
|||||||
@@ -5,8 +5,7 @@
|
|||||||
|
|
||||||
use crate::match_directive::match_directive;
|
use crate::match_directive::match_directive;
|
||||||
use crate::subtest::{Context, SubTest, SubtestResult};
|
use crate::subtest::{Context, SubTest, SubtestResult};
|
||||||
use cranelift_codegen::binemit;
|
use cranelift_codegen::binemit::{self, CodeInfo, CodeSink, RegDiversions};
|
||||||
use cranelift_codegen::binemit::{CodeSink, RegDiversions};
|
|
||||||
use cranelift_codegen::dbg::DisplayList;
|
use cranelift_codegen::dbg::DisplayList;
|
||||||
use cranelift_codegen::ir;
|
use cranelift_codegen::ir;
|
||||||
use cranelift_codegen::ir::entities::AnyEntity;
|
use cranelift_codegen::ir::entities::AnyEntity;
|
||||||
@@ -96,9 +95,12 @@ impl binemit::CodeSink for TextSink {
|
|||||||
write!(self.text, "{} ", code).unwrap();
|
write!(self.text, "{} ", code).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn begin_rodata(&mut self) {
|
fn begin_jumptables(&mut self) {
|
||||||
self.code_size = self.offset
|
self.code_size = self.offset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn begin_rodata(&mut self) {}
|
||||||
|
fn end_codegen(&mut self) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SubTest for TestBinEmit {
|
impl SubTest for TestBinEmit {
|
||||||
@@ -164,7 +166,7 @@ impl SubTest for TestBinEmit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Relax branches and compute EBB offsets based on the encodings.
|
// Relax branches and compute EBB offsets based on the encodings.
|
||||||
let code_size = binemit::relax_branches(&mut func, isa)
|
let CodeInfo { total_size, .. } = binemit::relax_branches(&mut func, isa)
|
||||||
.map_err(|e| pretty_error(&func, context.isa, e))?;
|
.map_err(|e| pretty_error(&func, context.isa, e))?;
|
||||||
|
|
||||||
// Collect all of the 'bin:' directives on instructions.
|
// Collect all of the 'bin:' directives on instructions.
|
||||||
@@ -288,7 +290,7 @@ impl SubTest for TestBinEmit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sink.begin_rodata();
|
sink.begin_jumptables();
|
||||||
|
|
||||||
for (jt, jt_data) in func.jump_tables.iter() {
|
for (jt, jt_data) in func.jump_tables.iter() {
|
||||||
let jt_offset = func.jt_offsets[jt];
|
let jt_offset = func.jt_offsets[jt];
|
||||||
@@ -298,10 +300,15 @@ impl SubTest for TestBinEmit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if sink.offset != code_size {
|
sink.begin_rodata();
|
||||||
|
// TODO: Read-only (constant pool) data.
|
||||||
|
|
||||||
|
sink.end_codegen();
|
||||||
|
|
||||||
|
if sink.offset != total_size {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Expected code size {}, got {}",
|
"Expected code size {}, got {}",
|
||||||
code_size, sink.offset
|
total_size, sink.offset
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,9 @@
|
|||||||
|
|
||||||
use crate::subtest::{run_filecheck, Context, SubTest, SubtestResult};
|
use crate::subtest::{run_filecheck, Context, SubTest, SubtestResult};
|
||||||
use cranelift_codegen;
|
use cranelift_codegen;
|
||||||
|
use cranelift_codegen::binemit::{self, CodeInfo};
|
||||||
|
use cranelift_codegen::ir;
|
||||||
use cranelift_codegen::print_errors::pretty_error;
|
use cranelift_codegen::print_errors::pretty_error;
|
||||||
use cranelift_codegen::{binemit, ir};
|
|
||||||
use cranelift_reader::TestCommand;
|
use cranelift_reader::TestCommand;
|
||||||
use log::info;
|
use log::info;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
@@ -38,13 +39,13 @@ impl SubTest for TestCompile {
|
|||||||
let isa = context.isa.expect("compile needs an ISA");
|
let isa = context.isa.expect("compile needs an ISA");
|
||||||
let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned());
|
let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned());
|
||||||
|
|
||||||
let code_size = comp_ctx
|
let CodeInfo { total_size, .. } = comp_ctx
|
||||||
.compile(isa)
|
.compile(isa)
|
||||||
.map_err(|e| pretty_error(&comp_ctx.func, context.isa, e))?;
|
.map_err(|e| pretty_error(&comp_ctx.func, context.isa, e))?;
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"Generated {} bytes of code:\n{}",
|
"Generated {} bytes of code:\n{}",
|
||||||
code_size,
|
total_size,
|
||||||
comp_ctx.func.display(isa)
|
comp_ctx.func.display(isa)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -56,10 +57,10 @@ impl SubTest for TestCompile {
|
|||||||
&mut sink,
|
&mut sink,
|
||||||
);
|
);
|
||||||
|
|
||||||
if sink.offset != code_size {
|
if sink.offset != total_size {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Expected code size {}, got {}",
|
"Expected code size {}, got {}",
|
||||||
code_size, sink.offset
|
total_size, sink.offset
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,5 +106,7 @@ impl binemit::CodeSink for SizeSink {
|
|||||||
}
|
}
|
||||||
fn reloc_jt(&mut self, _reloc: binemit::Reloc, _jt: ir::JumpTable) {}
|
fn reloc_jt(&mut self, _reloc: binemit::Reloc, _jt: ir::JumpTable) {}
|
||||||
fn trap(&mut self, _code: ir::TrapCode, _srcloc: ir::SourceLoc) {}
|
fn trap(&mut self, _code: ir::TrapCode, _srcloc: ir::SourceLoc) {}
|
||||||
|
fn begin_jumptables(&mut self) {}
|
||||||
fn begin_rodata(&mut self) {}
|
fn begin_rodata(&mut self) {}
|
||||||
|
fn end_codegen(&mut self) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,9 @@
|
|||||||
use super::HashMap;
|
use super::HashMap;
|
||||||
use crate::data_context::DataContext;
|
use crate::data_context::DataContext;
|
||||||
use crate::Backend;
|
use crate::Backend;
|
||||||
|
use cranelift_codegen::binemit::{self, CodeInfo};
|
||||||
use cranelift_codegen::entity::{entity_impl, PrimaryMap};
|
use cranelift_codegen::entity::{entity_impl, PrimaryMap};
|
||||||
use cranelift_codegen::{binemit, ir, isa, CodegenError, Context};
|
use cranelift_codegen::{ir, isa, CodegenError, Context};
|
||||||
use failure::Fail;
|
use failure::Fail;
|
||||||
use log::info;
|
use log::info;
|
||||||
use std::borrow::ToOwned;
|
use std::borrow::ToOwned;
|
||||||
@@ -519,7 +520,7 @@ where
|
|||||||
|
|
||||||
/// Define a function, producing the function body from the given `Context`.
|
/// Define a function, producing the function body from the given `Context`.
|
||||||
///
|
///
|
||||||
/// Returns the size of the function's code.
|
/// Returns the size of the function's code and constant data.
|
||||||
///
|
///
|
||||||
/// Note: After calling this function the given `Context` will contain the compiled function.
|
/// Note: After calling this function the given `Context` will contain the compiled function.
|
||||||
pub fn define_function(
|
pub fn define_function(
|
||||||
@@ -527,7 +528,7 @@ where
|
|||||||
func: FuncId,
|
func: FuncId,
|
||||||
ctx: &mut Context,
|
ctx: &mut Context,
|
||||||
) -> ModuleResult<binemit::CodeOffset> {
|
) -> ModuleResult<binemit::CodeOffset> {
|
||||||
let code_size = ctx.compile(self.backend.isa()).map_err(|e| {
|
let CodeInfo { total_size, .. } = ctx.compile(self.backend.isa()).map_err(|e| {
|
||||||
info!(
|
info!(
|
||||||
"defining function {}: {}",
|
"defining function {}: {}",
|
||||||
func,
|
func,
|
||||||
@@ -550,12 +551,12 @@ where
|
|||||||
&ModuleNamespace::<B> {
|
&ModuleNamespace::<B> {
|
||||||
contents: &self.contents,
|
contents: &self.contents,
|
||||||
},
|
},
|
||||||
code_size,
|
total_size,
|
||||||
)?);
|
)?);
|
||||||
|
|
||||||
self.contents.functions[func].compiled = compiled;
|
self.contents.functions[func].compiled = compiled;
|
||||||
self.functions_to_finalize.push(func);
|
self.functions_to_finalize.push(func);
|
||||||
Ok(code_size)
|
Ok(total_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Define a function, producing the data contents from the given `DataContext`.
|
/// Define a function, producing the data contents from the given `DataContext`.
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ fn handle_module(
|
|||||||
let mut mem = vec![];
|
let mut mem = vec![];
|
||||||
|
|
||||||
// Compile and encode the result to machine code.
|
// Compile and encode the result to machine code.
|
||||||
let (code_size, rodata_size) = context
|
let code_info = context
|
||||||
.compile_and_emit(isa, &mut mem, &mut relocs, &mut traps)
|
.compile_and_emit(isa, &mut mem, &mut relocs, &mut traps)
|
||||||
.map_err(|err| pretty_error(&context.func, Some(isa), err))?;
|
.map_err(|err| pretty_error(&context.func, Some(isa), err))?;
|
||||||
|
|
||||||
@@ -74,7 +74,14 @@ fn handle_module(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if flag_disasm {
|
if flag_disasm {
|
||||||
print_all(isa, &mem, code_size, rodata_size, &relocs, &traps)?;
|
print_all(
|
||||||
|
isa,
|
||||||
|
&mem,
|
||||||
|
code_info.code_size,
|
||||||
|
code_info.jumptables_size + code_info.rodata_size,
|
||||||
|
&relocs,
|
||||||
|
&traps,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -176,16 +176,16 @@ fn handle_module(
|
|||||||
return Err(pretty_verifier_error(&context.func, fisa.isa, None, errors));
|
return Err(pretty_verifier_error(&context.func, fisa.isa, None, errors));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let (code_size, rodata_size) = context
|
let code_info = context
|
||||||
.compile_and_emit(isa, &mut mem, &mut relocs, &mut traps)
|
.compile_and_emit(isa, &mut mem, &mut relocs, &mut traps)
|
||||||
.map_err(|err| pretty_error(&context.func, fisa.isa, err))?;
|
.map_err(|err| pretty_error(&context.func, fisa.isa, err))?;
|
||||||
|
|
||||||
if flag_print_size {
|
if flag_print_size {
|
||||||
println!(
|
println!(
|
||||||
"Function #{} code size: {} bytes",
|
"Function #{} code size: {} bytes",
|
||||||
func_index, code_size + rodata_size
|
func_index, code_info.total_size,
|
||||||
);
|
);
|
||||||
total_module_code_size += code_size + rodata_size;
|
total_module_code_size += code_info.total_size;
|
||||||
println!(
|
println!(
|
||||||
"Function #{} bytecode size: {} bytes",
|
"Function #{} bytecode size: {} bytes",
|
||||||
func_index,
|
func_index,
|
||||||
@@ -194,7 +194,7 @@ fn handle_module(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if flag_print_disasm {
|
if flag_print_disasm {
|
||||||
saved_sizes = Some((code_size, rodata_size));
|
saved_sizes = Some((code_info.code_size, code_info.jumptables_size + code_info.rodata_size));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user