Files
wasmtime/lib/cretonne/src/binemit/mod.rs
Dan Gohman 30f8daa9d6 Replace assert! with debug_assert! in production code paths.
This allows the assertions to be disabled in release builds, so that
the code is faster and smaller, at the expense of not performing the
checks. Assertions can be re-enabled in release builds with the
debug-assertions flag in Cargo.toml, as the top-level Cargo.toml
file does.
2018-03-12 12:38:30 -07:00

119 lines
3.7 KiB
Rust

//! Binary machine code emission.
//!
//! The `binemit` module contains code for translating Cretonne's intermediate representation into
//! binary machine code.
mod relaxation;
mod memorysink;
pub use regalloc::RegDiversions;
pub use self::relaxation::relax_branches;
pub use self::memorysink::{MemoryCodeSink, RelocSink};
use ir::{ExternalName, JumpTable, Function, Inst};
use std::fmt;
/// Offset in bytes from the beginning of the function.
///
/// Cretonne can be used as a cross compiler, so we don't want to use a type like `usize` which
/// depends on the *host* platform, not the *target* platform.
pub type CodeOffset = u32;
/// Addend to add to the symbol value.
pub type Addend = i64;
/// Relocation kinds for every ISA
#[derive(Debug)]
pub enum Reloc {
/// Intel PC-relative 4-byte
IntelPCRel4,
/// Intel absolute 4-byte
IntelAbs4,
/// Intel absolute 8-byte
IntelAbs8,
/// Intel GOT PC-relative 4-byte
IntelGOTPCRel4,
/// Intel PLT-relative 4-byte
IntelPLTRel4,
/// Arm32 call target
Arm32Call,
/// Arm64 call target
Arm64Call,
/// RISC-V call target
RiscvCall,
}
impl fmt::Display for Reloc {
/// Display trait implementation drops the arch, since its used in contexts where the arch is
/// already unambigious, e.g. cton syntax with isa specified. In other contexts, use Debug.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Reloc::IntelPCRel4 => write!(f, "{}", "PCRel4"),
Reloc::IntelAbs4 => write!(f, "{}", "Abs4"),
Reloc::IntelAbs8 => write!(f, "{}", "Abs8"),
Reloc::IntelGOTPCRel4 => write!(f, "{}", "GOTPCRel4"),
Reloc::IntelPLTRel4 => write!(f, "{}", "PLTRel4"),
Reloc::Arm32Call | Reloc::Arm64Call | Reloc::RiscvCall => write!(f, "{}", "Call"),
}
}
}
/// 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
/// which are locations in the code section that need to be fixed up when linking.
pub trait CodeSink {
/// Get the current position.
fn offset(&self) -> CodeOffset;
/// Add 1 byte to the code section.
fn put1(&mut self, u8);
/// Add 2 bytes to the code section.
fn put2(&mut self, u16);
/// Add 4 bytes to the code section.
fn put4(&mut self, u32);
/// Add 8 bytes to the code section.
fn put8(&mut self, u64);
/// Add a relocation referencing an EBB at the current offset.
fn reloc_ebb(&mut self, Reloc, CodeOffset);
/// Add a relocation referencing an external symbol plus the addend at the current offset.
fn reloc_external(&mut self, Reloc, &ExternalName, Addend);
/// Add a relocation referencing a jump table.
fn reloc_jt(&mut self, Reloc, JumpTable);
}
/// Report a bad encoding error.
#[inline(never)]
pub fn bad_encoding(func: &Function, inst: Inst) -> ! {
panic!(
"Bad encoding {} for {}",
func.encodings[inst],
func.dfg.display_inst(inst, None)
);
}
/// Emit a function to `sink`, given an instruction emitter function.
///
/// This function is called from the `TargetIsa::emit_function()` implementations with the
/// appropriate instruction emitter.
pub fn emit_function<CS, EI>(func: &Function, emit_inst: EI, sink: &mut CS)
where
CS: CodeSink,
EI: Fn(&Function, Inst, &mut RegDiversions, &mut CS),
{
let mut divert = RegDiversions::new();
for ebb in func.layout.ebbs() {
divert.clear();
debug_assert_eq!(func.offsets[ebb], sink.offset());
for inst in func.layout.ebb_insts(ebb) {
emit_inst(func, inst, &mut divert, sink);
}
}
}