Skip to content

Commit

Permalink
WIP: use a bump allocator for small objects
Browse files Browse the repository at this point in the history
  • Loading branch information
yorickpeterse committed Nov 5, 2024
1 parent 3b20a16 commit c0087a6
Show file tree
Hide file tree
Showing 8 changed files with 548 additions and 9 deletions.
22 changes: 17 additions & 5 deletions compiler/src/llvm/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -755,22 +755,34 @@ impl<'ctx> Builder<'ctx> {
names: &crate::symbol_names::SymbolNames,
class: ClassId,
) -> PointerValue<'ctx> {
let typ = module.layouts.instances[class.0 as usize];
let ptr = self.malloc(module, typ);

self.init_instance(module, db, names, class, ptr);
ptr
}

pub(crate) fn init_instance<'a, 'b>(
&self,
module: &'a mut Module<'b, 'ctx>,
db: &Database,
names: &crate::symbol_names::SymbolNames,
class: ClassId,
pointer: PointerValue<'ctx>,
) {
let atomic = class.is_atomic(db);
let name = &names.classes[&class];
let global = module.add_class(name).as_pointer_value();
let class_ptr = self.load_pointer(global);
let typ = module.layouts.instances[class.0 as usize];
let res = self.malloc(module, typ);
let header = module.layouts.header;

// Atomic values start with a reference count of 1, so atomic decrements
// returns the correct result for a value for which no extra references
// have been created (instead of underflowing).
let refs = self.u32_literal(if atomic { 1 } else { 0 });

self.store_field(header, res, HEADER_CLASS_INDEX, class_ptr);
self.store_field(header, res, HEADER_REFS_INDEX, refs);
res
self.store_field(header, pointer, HEADER_CLASS_INDEX, class_ptr);
self.store_field(header, pointer, HEADER_REFS_INDEX, refs);
}

pub(crate) fn malloc<'a, 'b, T: BasicType<'ctx>>(
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/llvm/layouts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use types::{
/// The size of an object header.
const HEADER_SIZE: u32 = 16;

fn size_of_type(target_data: &TargetData, typ: &dyn AnyType) -> u64 {
pub(crate) fn size_of_type(target_data: &TargetData, typ: &dyn AnyType) -> u64 {
target_data.get_bit_size(typ)
/ (target_data.get_pointer_byte_size(None) as u64)
}
Expand Down
54 changes: 51 additions & 3 deletions compiler/src/llvm/passes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::llvm::constants::{
STACK_DATA_EPOCH_INDEX, STACK_DATA_PROCESS_INDEX, STATE_EPOCH_INDEX,
};
use crate::llvm::context::Context;
use crate::llvm::layouts::Layouts;
use crate::llvm::layouts::{size_of_type, Layouts};
use crate::llvm::methods::Methods;
use crate::llvm::module::Module;
use crate::llvm::runtime_function::RuntimeFunction;
Expand Down Expand Up @@ -2299,9 +2299,8 @@ impl<'shared, 'module, 'ctx> LowerMethod<'shared, 'module, 'ctx> {
Instruction::Free(ins) => {
let var = self.variables[&ins.register];
let ptr = self.builder.load_pointer(var);
let func = self.module.runtime_function(RuntimeFunction::Free);

self.builder.call_void(func, &[ptr.into()]);
self.free(ins.class, ptr);
}
Instruction::Increment(ins) => {
let reg_var = self.variables[&ins.register];
Expand Down Expand Up @@ -2729,14 +2728,63 @@ impl<'shared, 'module, 'ctx> LowerMethod<'shared, 'module, 'ctx> {
.into_int_value()
}

fn free(&mut self, class: ClassId, pointer: PointerValue<'ctx>) {
if self.bump_allocation_size(class).is_some() {
let func = self.module.runtime_function(RuntimeFunction::BumpFree);

self.builder.call_void(func, &[pointer.into()]);
return;
}

let func = self.module.runtime_function(RuntimeFunction::Free);

self.builder.call_void(func, &[pointer.into()]);
}

fn allocate(&mut self, class: ClassId) -> PointerValue<'ctx> {
if let Some(size) = self.bump_allocation_size(class) {
let func =
self.module.runtime_function(RuntimeFunction::BumpAllocate);
let proc = self.load_process();
let size = self.builder.u64_literal(size);
let ptr = self
.builder
.call(func, &[proc.into(), size.into()])
.into_pointer_value();

self.builder.init_instance(
self.module,
&self.shared.state.db,
self.shared.names,
class,
ptr,
);
return ptr;
}

self.builder.allocate_instance(
self.module,
&self.shared.state.db,
self.shared.names,
class,
)
}

fn bump_allocation_size(&self, class: ClassId) -> Option<u64> {
// Builtin types are either unboxed (Int, Float, etc) or use a special
// allocation strategy (String, ByteArray).
if class.is_builtin() {
return None;
}

let layout = self.layouts.instances[class.0 as usize];
let size = size_of_type(&self.layouts.target_data, &layout);

match size {
16 | 24 | 32 => Some(size),
_ => None,
}
}
}

/// A pass for generating the entry module and method (i.e. `main()`).
Expand Down
17 changes: 17 additions & 0 deletions compiler/src/llvm/runtime_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ pub(crate) enum RuntimeFunction {
RuntimeStackMask,
Free,
AllocationError,
BumpAllocate,
BumpFree,
}

impl RuntimeFunction {
Expand All @@ -46,6 +48,8 @@ impl RuntimeFunction {
RuntimeFunction::RuntimeStackMask => "inko_runtime_stack_mask",
RuntimeFunction::Free => "free",
RuntimeFunction::AllocationError => "inko_alloc_error",
RuntimeFunction::BumpAllocate => "inko_bump_allocate",
RuntimeFunction::BumpFree => "inko_bump_free",
}
}

Expand Down Expand Up @@ -169,6 +173,19 @@ impl RuntimeFunction {

ret.fn_type(&[size], false)
}
RuntimeFunction::BumpAllocate => {
let proc = context.pointer_type().into();
let size = context.i64_type().into();
let ret = context.pointer_type();

ret.fn_type(&[proc, size], false)
}
RuntimeFunction::BumpFree => {
let ptr = context.pointer_type().into();
let ret = context.void_type();

ret.fn_type(&[ptr], false)
}
};

module.add_function(self.name(), fn_type, None)
Expand Down
Loading

0 comments on commit c0087a6

Please sign in to comment.