Skip to content

Commit 56148ad

Browse files
authored
cranelift-jit: add bump memory allocator, make configurable (#10512)
* cranelift-jit: add bump memory allocator, make configurable This adds a new JIT memory allocator which operates on an address range that is reserved up-front. This is useful in order to side-step issues where allocation behavior affect relocations, i.e. on x86 where we run into issues when the system allocator returns memory that is more than 4 GiB apart. The main drawback of this allocator option is that the memory range has to be reserved up-front, so allocation will fail if this space is exhausted. This commit: - adds a 'JITMemoryProvider' trait - moves the previous on-demand allocator to this trait - adds a new 'Arena' allocator * ArenaMemoryProvider: add tests, simplify alignment logic * ArenaMemoryProvider: add asserts * Move pipeline flush back out of `set_readable_and_executable` * Fix aarch64 build * Document Windows overcommit limitation
1 parent 25a0a0b commit 56148ad

File tree

5 files changed

+473
-105
lines changed

5 files changed

+473
-105
lines changed

cranelift/jit/src/backend.rs

Lines changed: 48 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
//! Defines `JITModule`.
22
3-
use crate::{compiled_blob::CompiledBlob, memory::BranchProtection, memory::Memory};
3+
use crate::{
4+
compiled_blob::CompiledBlob,
5+
memory::{BranchProtection, JITMemoryProvider, SystemMemoryProvider},
6+
};
47
use cranelift_codegen::binemit::Reloc;
58
use cranelift_codegen::isa::{OwnedTargetIsa, TargetIsa};
69
use cranelift_codegen::settings::Configurable;
@@ -28,6 +31,7 @@ pub struct JITBuilder {
2831
symbols: HashMap<String, SendWrapper<*const u8>>,
2932
lookup_symbols: Vec<Box<dyn Fn(&str) -> Option<*const u8> + Send>>,
3033
libcall_names: Box<dyn Fn(ir::LibCall) -> String + Send + Sync>,
34+
memory: Option<Box<dyn JITMemoryProvider>>,
3135
}
3236

3337
impl JITBuilder {
@@ -91,6 +95,7 @@ impl JITBuilder {
9195
symbols,
9296
lookup_symbols,
9397
libcall_names,
98+
memory: None,
9499
}
95100
}
96101

@@ -141,6 +146,14 @@ impl JITBuilder {
141146
self.lookup_symbols.push(symbol_lookup_fn);
142147
self
143148
}
149+
150+
/// Set the memory provider for the module.
151+
///
152+
/// If unset, defaults to [`SystemMemoryProvider`].
153+
pub fn memory_provider(&mut self, provider: Box<dyn JITMemoryProvider>) -> &mut Self {
154+
self.memory = Some(provider);
155+
self
156+
}
144157
}
145158

146159
/// A wrapper that impls Send for the contents.
@@ -159,21 +172,14 @@ pub struct JITModule {
159172
symbols: RefCell<HashMap<String, SendWrapper<*const u8>>>,
160173
lookup_symbols: Vec<Box<dyn Fn(&str) -> Option<*const u8> + Send>>,
161174
libcall_names: Box<dyn Fn(ir::LibCall) -> String + Send + Sync>,
162-
memory: MemoryHandle,
175+
memory: Box<dyn JITMemoryProvider>,
163176
declarations: ModuleDeclarations,
164177
compiled_functions: SecondaryMap<FuncId, Option<CompiledBlob>>,
165178
compiled_data_objects: SecondaryMap<DataId, Option<CompiledBlob>>,
166179
functions_to_finalize: Vec<FuncId>,
167180
data_objects_to_finalize: Vec<DataId>,
168181
}
169182

170-
/// A handle to allow freeing memory allocated by the `Module`.
171-
struct MemoryHandle {
172-
code: Memory,
173-
readonly: Memory,
174-
writable: Memory,
175-
}
176-
177183
impl JITModule {
178184
/// Free memory allocated for code and data segments of compiled functions.
179185
///
@@ -184,9 +190,7 @@ impl JITModule {
184190
/// from that module are currently executing and none of the `fn` pointers
185191
/// are called afterwards.
186192
pub unsafe fn free_memory(mut self) {
187-
self.memory.code.free_memory();
188-
self.memory.readonly.free_memory();
189-
self.memory.writable.free_memory();
193+
self.memory.free_memory();
190194
}
191195

192196
fn lookup_symbol(&self, name: &str) -> Option<*const u8> {
@@ -325,8 +329,12 @@ impl JITModule {
325329
}
326330

327331
// Now that we're done patching, prepare the memory for execution!
328-
self.memory.readonly.set_readonly()?;
329-
self.memory.code.set_readable_and_executable()?;
332+
let branch_protection = if cfg!(target_arch = "aarch64") && use_bti(&self.isa.isa_flags()) {
333+
BranchProtection::BTI
334+
} else {
335+
BranchProtection::None
336+
};
337+
self.memory.finalize(branch_protection)?;
330338

331339
Ok(())
332340
}
@@ -338,23 +346,15 @@ impl JITModule {
338346
"cranelift-jit needs is_pic=false"
339347
);
340348

341-
let branch_protection =
342-
if cfg!(target_arch = "aarch64") && use_bti(&builder.isa.isa_flags()) {
343-
BranchProtection::BTI
344-
} else {
345-
BranchProtection::None
346-
};
349+
let memory = builder
350+
.memory
351+
.unwrap_or_else(|| Box::new(SystemMemoryProvider::new()));
347352
Self {
348353
isa: builder.isa,
349354
symbols: RefCell::new(builder.symbols),
350355
lookup_symbols: builder.lookup_symbols,
351356
libcall_names: builder.libcall_names,
352-
memory: MemoryHandle {
353-
code: Memory::new(branch_protection),
354-
// Branch protection is not applicable to non-executable memory.
355-
readonly: Memory::new(BranchProtection::None),
356-
writable: Memory::new(BranchProtection::None),
357-
},
357+
memory,
358358
declarations: ModuleDeclarations::default(),
359359
compiled_functions: SecondaryMap::new(),
360360
compiled_data_objects: SecondaryMap::new(),
@@ -436,15 +436,16 @@ impl Module for JITModule {
436436
let compiled_code = ctx.compiled_code().unwrap();
437437

438438
let size = compiled_code.code_info().total_size as usize;
439-
let align = alignment.max(self.isa.symbol_alignment());
440-
let ptr = self
441-
.memory
442-
.code
443-
.allocate(size, align)
444-
.map_err(|e| ModuleError::Allocation {
445-
message: "unable to alloc function",
446-
err: e,
447-
})?;
439+
let align = alignment
440+
.max(self.isa.function_alignment().minimum as u64)
441+
.max(self.isa.symbol_alignment());
442+
let ptr =
443+
self.memory
444+
.allocate_readexec(size, align)
445+
.map_err(|e| ModuleError::Allocation {
446+
message: "unable to alloc function",
447+
err: e,
448+
})?;
448449

449450
{
450451
let mem = unsafe { std::slice::from_raw_parts_mut(ptr, size) };
@@ -488,15 +489,16 @@ impl Module for JITModule {
488489
}
489490

490491
let size = bytes.len();
491-
let align = alignment.max(self.isa.symbol_alignment());
492-
let ptr = self
493-
.memory
494-
.code
495-
.allocate(size, align)
496-
.map_err(|e| ModuleError::Allocation {
497-
message: "unable to alloc function bytes",
498-
err: e,
499-
})?;
492+
let align = alignment
493+
.max(self.isa.function_alignment().minimum as u64)
494+
.max(self.isa.symbol_alignment());
495+
let ptr =
496+
self.memory
497+
.allocate_readexec(size, align)
498+
.map_err(|e| ModuleError::Allocation {
499+
message: "unable to alloc function bytes",
500+
err: e,
501+
})?;
500502

501503
unsafe {
502504
ptr::copy_nonoverlapping(bytes.as_ptr(), ptr, size);
@@ -548,16 +550,14 @@ impl Module for JITModule {
548550

549551
let ptr = if decl.writable {
550552
self.memory
551-
.writable
552-
.allocate(alloc_size, align.unwrap_or(WRITABLE_DATA_ALIGNMENT))
553+
.allocate_readwrite(alloc_size, align.unwrap_or(WRITABLE_DATA_ALIGNMENT))
553554
.map_err(|e| ModuleError::Allocation {
554555
message: "unable to alloc writable data",
555556
err: e,
556557
})?
557558
} else {
558559
self.memory
559-
.readonly
560-
.allocate(alloc_size, align.unwrap_or(READONLY_DATA_ALIGNMENT))
560+
.allocate_readonly(alloc_size, align.unwrap_or(READONLY_DATA_ALIGNMENT))
561561
.map_err(|e| ModuleError::Allocation {
562562
message: "unable to alloc readonly data",
563563
err: e,

cranelift/jit/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ mod compiled_blob;
1010
mod memory;
1111

1212
pub use crate::backend::{JITBuilder, JITModule};
13+
pub use crate::memory::{
14+
ArenaMemoryProvider, BranchProtection, JITMemoryProvider, SystemMemoryProvider,
15+
};
1316

1417
/// Version number of this crate.
1518
pub const VERSION: &str = env!("CARGO_PKG_VERSION");

0 commit comments

Comments
 (0)