summaryrefslogtreecommitdiff
path: root/yjit/src/asm/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'yjit/src/asm/mod.rs')
-rw-r--r--yjit/src/asm/mod.rs144
1 files changed, 32 insertions, 112 deletions
diff --git a/yjit/src/asm/mod.rs b/yjit/src/asm/mod.rs
index 1d31facb78..e16e856925 100644
--- a/yjit/src/asm/mod.rs
+++ b/yjit/src/asm/mod.rs
@@ -3,49 +3,16 @@ use std::mem;
#[cfg(feature = "asm_comments")]
use std::collections::BTreeMap;
+use crate::virtualmem::{VirtualMem, CodePtr};
+
// Lots of manual vertical alignment in there that rustfmt doesn't handle well.
#[rustfmt::skip]
pub mod x86_64;
-/// Pointer to a piece of machine code
-/// We may later change this to wrap an u32
-/// Note: there is no NULL constant for CodePtr. You should use Option<CodePtr> instead.
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Debug)]
-#[repr(C)]
-pub struct CodePtr(*const u8);
-
-impl CodePtr {
- pub fn raw_ptr(&self) -> *const u8 {
- let CodePtr(ptr) = *self;
- return ptr;
- }
-
- fn into_i64(&self) -> i64 {
- let CodePtr(ptr) = self;
- *ptr as i64
- }
-
- #[allow(unused)]
- fn into_usize(&self) -> usize {
- let CodePtr(ptr) = self;
- *ptr as usize
- }
-}
-
-impl From<*mut u8> for CodePtr {
- fn from(value: *mut u8) -> Self {
- assert!(value as usize != 0);
- return CodePtr(value);
- }
-}
-
//
// TODO: need a field_size_of macro, to compute the size of a struct field in bytes
//
-// 1 is not aligned so this won't match any pages
-const ALIGNED_WRITE_POSITION_NONE: usize = 1;
-
/// Reference to an ASM label
struct LabelRef {
// Position in the code block where the label reference exists
@@ -57,13 +24,8 @@ struct LabelRef {
/// Block of memory into which instructions can be assembled
pub struct CodeBlock {
- // Block of non-executable memory used for dummy code blocks
- // This memory is owned by this block and lives as long as the block
- #[allow(unused)]
- dummy_block: Vec<u8>,
-
- // Pointer to memory we are writing into
- mem_block: *mut u8,
+ // Memory for storing the encoded instructions
+ mem_block: VirtualMem,
// Memory block size
mem_size: usize,
@@ -84,14 +46,6 @@ pub struct CodeBlock {
#[cfg(feature = "asm_comments")]
asm_comments: BTreeMap<usize, Vec<String>>,
- // Keep track of the current aligned write position.
- // Used for changing protection when writing to the JIT buffer
- current_aligned_write_pos: usize,
-
- // Memory protection works at page granularity and this is the
- // the size of each page. Used to implement W^X.
- page_size: usize,
-
// Set if the CodeBlock is unable to output some instructions,
// for example, when there is not enough space or when a jump
// target is too far away.
@@ -99,47 +53,22 @@ pub struct CodeBlock {
}
impl CodeBlock {
- #[cfg(test)]
- pub fn new_dummy(mem_size: usize) -> Self {
- // Allocate some non-executable memory
- let mut dummy_block = vec![0; mem_size];
- let mem_ptr = dummy_block.as_mut_ptr();
-
- Self {
- dummy_block: dummy_block,
- mem_block: mem_ptr,
- mem_size: mem_size,
- write_pos: 0,
- label_addrs: Vec::new(),
- label_names: Vec::new(),
- label_refs: Vec::new(),
- #[cfg(feature = "asm_comments")]
- asm_comments: BTreeMap::new(),
- current_aligned_write_pos: ALIGNED_WRITE_POSITION_NONE,
- page_size: 4096,
- dropped_bytes: false,
- }
- }
-
- #[cfg(not(test))]
- pub fn new(mem_block: *mut u8, mem_size: usize, page_size: usize) -> Self {
+ /// Make a new CodeBlock
+ pub fn new(mem_block: VirtualMem) -> Self {
Self {
- dummy_block: vec![0; 0],
- mem_block: mem_block,
- mem_size: mem_size,
+ mem_size: mem_block.virtual_region_size(),
+ mem_block,
write_pos: 0,
label_addrs: Vec::new(),
label_names: Vec::new(),
label_refs: Vec::new(),
#[cfg(feature = "asm_comments")]
asm_comments: BTreeMap::new(),
- current_aligned_write_pos: ALIGNED_WRITE_POSITION_NONE,
- page_size,
dropped_bytes: false,
}
}
- // Check if this code block has sufficient remaining capacity
+ /// Check if this code block has sufficient remaining capacity
pub fn has_capacity(&self, num_bytes: usize) -> bool {
self.write_pos + num_bytes < self.mem_size
}
@@ -175,6 +104,10 @@ impl CodeBlock {
self.write_pos
}
+ pub fn get_mem(&mut self) -> &mut VirtualMem {
+ &mut self.mem_block
+ }
+
// Set the current write position
pub fn set_pos(&mut self, pos: usize) {
// Assert here since while CodeBlock functions do bounds checking, there is
@@ -204,16 +137,13 @@ impl CodeBlock {
// Set the current write position from a pointer
pub fn set_write_ptr(&mut self, code_ptr: CodePtr) {
- let pos = (code_ptr.raw_ptr() as usize) - (self.mem_block as usize);
+ let pos = code_ptr.into_usize() - self.mem_block.start_ptr().into_usize();
self.set_pos(pos);
}
// Get a direct pointer into the executable memory block
pub fn get_ptr(&self, offset: usize) -> CodePtr {
- unsafe {
- let ptr = self.mem_block.add(offset);
- CodePtr(ptr)
- }
+ self.mem_block.start_ptr().add_bytes(offset)
}
// Get a direct pointer to the current write position
@@ -223,9 +153,9 @@ impl CodeBlock {
// Write a single byte at the current position
pub fn write_byte(&mut self, byte: u8) {
- if self.write_pos < self.mem_size {
- self.mark_position_writable(self.write_pos);
- unsafe { self.mem_block.add(self.write_pos).write(byte) };
+ let write_ptr = self.get_write_ptr();
+
+ if self.mem_block.write_byte(write_ptr, byte).is_ok() {
self.write_pos += 1;
} else {
self.dropped_bytes = true;
@@ -328,33 +258,23 @@ impl CodeBlock {
assert!(self.label_refs.is_empty());
}
- pub fn mark_position_writable(&mut self, write_pos: usize) {
- let page_size = self.page_size;
- let aligned_position = (write_pos / page_size) * page_size;
+ pub fn mark_all_executable(&mut self) {
+ self.mem_block.mark_all_executable();
+ }
+}
- if self.current_aligned_write_pos != aligned_position {
- self.current_aligned_write_pos = aligned_position;
+#[cfg(test)]
+impl CodeBlock {
+ /// Stubbed CodeBlock for testing. Can't execute generated code.
+ pub fn new_dummy(mem_size: usize) -> Self {
+ use crate::virtualmem::*;
+ use crate::virtualmem::tests::TestingAllocator;
- #[cfg(not(test))]
- unsafe {
- use core::ffi::c_void;
- let page_ptr = self.get_ptr(aligned_position).raw_ptr() as *mut c_void;
- crate::cruby::rb_yjit_mark_writable(page_ptr, page_size.try_into().unwrap());
- }
- }
- }
+ let alloc = TestingAllocator::new(mem_size);
+ let mem_start: *const u8 = alloc.mem_start();
+ let virt_mem = VirtualMem::new(alloc, 1, mem_start as *mut u8, mem_size);
- pub fn mark_all_executable(&mut self) {
- self.current_aligned_write_pos = ALIGNED_WRITE_POSITION_NONE;
-
- #[cfg(not(test))]
- unsafe {
- use core::ffi::c_void;
- // NOTE(alan): Right now we do allocate one big chunck and give the top half to the outlined codeblock
- // The start of the top half of the region isn't necessarily a page boundary...
- let cb_start = self.get_ptr(0).raw_ptr() as *mut c_void;
- crate::cruby::rb_yjit_mark_executable(cb_start, self.mem_size.try_into().unwrap());
- }
+ Self::new(virt_mem)
}
}