Skip to content

Commit ff6c728

Browse files
authored
ZJIT: Restore dropped_bytes after temporary OOM (ruby#15069)
1 parent 1cca3ef commit ff6c728

File tree

4 files changed

+29
-1
lines changed

4 files changed

+29
-1
lines changed

zjit/src/asm/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,14 @@ impl CodeBlock {
208208
self.dropped_bytes
209209
}
210210

211+
/// Set dropped_bytes to false if the current zjit_alloc_bytes() + code_region_size
212+
/// + page_size is below --zjit-mem-size.
213+
pub fn update_dropped_bytes(&mut self) {
214+
if self.mem_block.borrow().can_allocate() {
215+
self.dropped_bytes = false;
216+
}
217+
}
218+
211219
/// Allocate a new label with a given name
212220
pub fn new_label(&mut self, name: String) -> Label {
213221
assert!(!name.contains(' '), "use underscores in label names, not spaces");

zjit/src/backend/lir.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1750,7 +1750,15 @@ impl Assembler
17501750
#[cfg(feature = "disasm")]
17511751
let start_addr = cb.get_write_ptr();
17521752
let alloc_regs = Self::get_alloc_regs();
1753-
let ret = self.compile_with_regs(cb, alloc_regs);
1753+
let had_dropped_bytes = cb.has_dropped_bytes();
1754+
let ret = self.compile_with_regs(cb, alloc_regs).inspect_err(|err| {
1755+
// If we use too much memory to compile the Assembler, it would set cb.dropped_bytes = true.
1756+
// To avoid failing future compilation by cb.has_dropped_bytes(), attempt to reset dropped_bytes with
1757+
// the current zjit_alloc_bytes() which may be decreased after self is dropped in compile_with_regs().
1758+
if *err == CompileError::OutOfMemory && !had_dropped_bytes {
1759+
cb.update_dropped_bytes();
1760+
}
1761+
});
17541762

17551763
#[cfg(feature = "disasm")]
17561764
if get_option!(dump_disasm) {

zjit/src/codegen.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,11 @@ fn gen_iseq(cb: &mut CodeBlock, iseq: IseqPtr, function: Option<&Function>) -> R
220220

221221
/// Compile an ISEQ into machine code
222222
fn gen_iseq_body(cb: &mut CodeBlock, iseq: IseqPtr, function: Option<&Function>, payload: &mut IseqPayload) -> Result<IseqCodePtrs, CompileError> {
223+
// If we ran out of code region, we shouldn't attempt to generate new code.
224+
if cb.has_dropped_bytes() {
225+
return Err(CompileError::OutOfMemory);
226+
}
227+
223228
// Convert ISEQ into optimized High-level IR if not given
224229
let function = match function {
225230
Some(function) => function,

zjit/src/virtualmem.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,13 @@ impl<A: Allocator> VirtualMemory<A> {
258258
Ok(())
259259
}
260260

261+
/// Return true if write_byte() can allocate a new page
262+
pub fn can_allocate(&self) -> bool {
263+
let memory_usage_bytes = self.mapped_region_bytes + zjit_alloc_bytes();
264+
let memory_limit_bytes = self.memory_limit_bytes.unwrap_or(self.region_size_bytes);
265+
memory_usage_bytes + self.page_size_bytes < memory_limit_bytes
266+
}
267+
261268
/// Make all the code in the region executable. Call this at the end of a write session.
262269
/// See [Self] for usual usage flow.
263270
pub fn mark_all_executable(&mut self) {

0 commit comments

Comments
 (0)