From a500c9dbb1766a6bde312ad34929dad66f81cfb7 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 16 Aug 2021 21:02:03 +0100 Subject: [PATCH 01/22] Make mutex new constant and reduce unsafe block size --- agb/src/interrupt.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/agb/src/interrupt.rs b/agb/src/interrupt.rs index 21e65016..05f58752 100644 --- a/agb/src/interrupt.rs +++ b/agb/src/interrupt.rs @@ -347,7 +347,7 @@ impl Mutex { } } - pub fn new(val: T) -> Self { + pub const fn new(val: T) -> Self { Mutex { internal: UnsafeCell::new(val), state: UnsafeCell::new(MutexState::Unlocked), @@ -362,14 +362,14 @@ pub struct MutexRef<'a, T> { impl<'a, T> Drop for MutexRef<'a, T> { fn drop(&mut self) { - unsafe { - let state = &mut *self.state.get(); - let prev_state = *state; - *state = MutexState::Unlocked; - match prev_state { - MutexState::Locked(b) => INTERRUPTS_ENABLED.set(b as u16), - MutexState::Unlocked => {} - } + let state = unsafe { &mut *self.state.get() }; + + let prev_state = *state; + *state = MutexState::Unlocked; + + match prev_state { + MutexState::Locked(b) => INTERRUPTS_ENABLED.set(b as u16), + MutexState::Unlocked => {} } } } From 1c07268f117f8ee9d745becbfeb96b93c49cef52 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 16 Aug 2021 21:02:36 +0100 Subject: [PATCH 02/22] Include alloc --- agb/.cargo/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agb/.cargo/config.toml b/agb/.cargo/config.toml index f56bbdda..54493f4b 100644 --- a/agb/.cargo/config.toml +++ b/agb/.cargo/config.toml @@ -1,5 +1,5 @@ [unstable] -build-std = ["core"] +build-std = ["core", "alloc"] build-std-features = ["compiler-builtins-mem"] [build] From 7062610abade962824019b902532bc389e30fe35 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 16 Aug 2021 21:02:55 +0100 Subject: [PATCH 03/22] Start a basic allocator with a super simple test --- agb/src/agb_alloc/mod.rs | 76 ++++++++++++++++++++++++++++++++++++++++ agb/src/lib.rs | 6 +++- 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 agb/src/agb_alloc/mod.rs diff --git a/agb/src/agb_alloc/mod.rs b/agb/src/agb_alloc/mod.rs new file mode 100644 index 00000000..6908892e --- /dev/null +++ b/agb/src/agb_alloc/mod.rs @@ -0,0 +1,76 @@ +use core::alloc::{GlobalAlloc, Layout}; + +use super::interrupt::Mutex; + +extern "C" { + static __ewram_data_end: usize; +} + +fn get_data_end() -> usize { + // TODO: This seems completely wrong, but without the &, rust generates + // a double dereference :/. Maybe a bug in nightly? + (unsafe { &__ewram_data_end }) as *const _ as usize +} + +struct BumpAllocator { + current_ptr: Mutex<*mut u8>, +} + +impl BumpAllocator { + fn alloc_safe(&self, layout: Layout) -> *mut u8 { + let mut current_ptr = self.current_ptr.lock(); + + let mut ptr = *current_ptr as usize; + + if ptr == 0 { + ptr = get_data_end(); + } + + let alignment_bitmask = layout.align() - 1; + let fixup = ptr & alignment_bitmask; + + let amount_to_add = layout.align() - fixup; + + let resulting_ptr = ptr + amount_to_add; + *current_ptr = (resulting_ptr + layout.size()) as *mut _; + + resulting_ptr as *mut _ + } +} + +unsafe impl GlobalAlloc for BumpAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + self.alloc_safe(layout) + } + + unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} +} + +#[alloc_error_handler] +fn alloc_error(layout: Layout) -> ! { + panic!( + "Failed to allocate size {} with alignment {}", + layout.size(), + layout.align() + ); +} + +#[global_allocator] +static GLOBAL_ALLOC: BumpAllocator = BumpAllocator { + current_ptr: Mutex::new(core::ptr::null_mut()), +}; + +#[cfg(test)] +mod test { + use alloc::boxed::Box; + + #[test_case] + fn test_box(_gba: &mut crate::Gba) { + let first_box = Box::new(1); + let second_box = Box::new(2); + + assert!(&*first_box as *const _ < &*second_box as *const _); + assert_eq!(*first_box, 1); + assert_eq!(*second_box, 2); + } +} diff --git a/agb/src/lib.rs b/agb/src/lib.rs index cb2a98fd..38a8f443 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -4,15 +4,17 @@ #![feature(asm)] #![deny(clippy::all)] #![feature(custom_test_frameworks)] +#![feature(alloc_error_handler)] #![test_runner(crate::test_runner)] #![reexport_test_harness_main = "test_main"] - //! # agb //! `agb` is a library for making games on the Game Boy Advance using the Rust //! programming language. It attempts to be a high level abstraction over the //! internal workings of the Game Boy Advance whilst still being high //! performance and memory efficient. +extern crate alloc; + /// Implements everything relating to things that are displayed on screen. pub mod display; /// Button inputs to the system. @@ -32,6 +34,8 @@ pub mod mgba; pub mod number; mod single; +mod agb_alloc; + /// System BIOS calls / syscalls. pub mod syscall; From 2bacbf35dc36406c2c357a3693792479ed34447e Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 16 Aug 2021 21:04:37 +0100 Subject: [PATCH 04/22] Extern definition can be made smaller --- agb/src/agb_alloc/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/agb/src/agb_alloc/mod.rs b/agb/src/agb_alloc/mod.rs index 6908892e..4cd317f2 100644 --- a/agb/src/agb_alloc/mod.rs +++ b/agb/src/agb_alloc/mod.rs @@ -2,11 +2,11 @@ use core::alloc::{GlobalAlloc, Layout}; use super::interrupt::Mutex; -extern "C" { - static __ewram_data_end: usize; -} - fn get_data_end() -> usize { + extern "C" { + static __ewram_data_end: usize; + } + // TODO: This seems completely wrong, but without the &, rust generates // a double dereference :/. Maybe a bug in nightly? (unsafe { &__ewram_data_end }) as *const _ as usize From 22c6efa5faf233e8e12e834b6e870e6b9580b6cb Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 16 Aug 2021 21:07:25 +0100 Subject: [PATCH 05/22] Have a new() method --- agb/src/agb_alloc/mod.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/agb/src/agb_alloc/mod.rs b/agb/src/agb_alloc/mod.rs index 4cd317f2..35e3f496 100644 --- a/agb/src/agb_alloc/mod.rs +++ b/agb/src/agb_alloc/mod.rs @@ -16,6 +16,14 @@ struct BumpAllocator { current_ptr: Mutex<*mut u8>, } +impl BumpAllocator { + pub const fn new() -> Self { + Self { + current_ptr: Mutex::new(core::ptr::null_mut()) + } + } +} + impl BumpAllocator { fn alloc_safe(&self, layout: Layout) -> *mut u8 { let mut current_ptr = self.current_ptr.lock(); @@ -56,9 +64,7 @@ fn alloc_error(layout: Layout) -> ! { } #[global_allocator] -static GLOBAL_ALLOC: BumpAllocator = BumpAllocator { - current_ptr: Mutex::new(core::ptr::null_mut()), -}; +static GLOBAL_ALLOC: BumpAllocator = BumpAllocator::new(); #[cfg(test)] mod test { From cd4718f8c50ae80ca0e87f89ebe7b96494b347ef Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 16 Aug 2021 21:08:39 +0100 Subject: [PATCH 06/22] Remove the need for this to be mut and write it in a more 'rusty' way --- agb/src/agb_alloc/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/agb/src/agb_alloc/mod.rs b/agb/src/agb_alloc/mod.rs index 35e3f496..3833b27b 100644 --- a/agb/src/agb_alloc/mod.rs +++ b/agb/src/agb_alloc/mod.rs @@ -28,11 +28,11 @@ impl BumpAllocator { fn alloc_safe(&self, layout: Layout) -> *mut u8 { let mut current_ptr = self.current_ptr.lock(); - let mut ptr = *current_ptr as usize; - - if ptr == 0 { - ptr = get_data_end(); - } + let ptr = if current_ptr.is_null() { + get_data_end() + } else { + *current_ptr as usize + }; let alignment_bitmask = layout.align() - 1; let fixup = ptr & alignment_bitmask; From 8a0cb86eda197c97e26862b7047bb25fc13e3584 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 16 Aug 2021 21:11:40 +0100 Subject: [PATCH 07/22] Assert that it is actually in ewram --- agb/src/agb_alloc/mod.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/agb/src/agb_alloc/mod.rs b/agb/src/agb_alloc/mod.rs index 3833b27b..88d75c2d 100644 --- a/agb/src/agb_alloc/mod.rs +++ b/agb/src/agb_alloc/mod.rs @@ -19,7 +19,7 @@ struct BumpAllocator { impl BumpAllocator { pub const fn new() -> Self { Self { - current_ptr: Mutex::new(core::ptr::null_mut()) + current_ptr: Mutex::new(core::ptr::null_mut()), } } } @@ -78,5 +78,12 @@ mod test { assert!(&*first_box as *const _ < &*second_box as *const _); assert_eq!(*first_box, 1); assert_eq!(*second_box, 2); + + let address = &*first_box as *const _ as usize; + assert!( + address >= 0x0200_0000 && address < 0x0204_0000, + "ewram is located between 0x0200_0000 and 0x0204_0000, address was actually found to be {:#010X}", + address + ); } } From 94d5a710074bc11367e3307da30f94cc9a58b167 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 16 Aug 2021 21:14:28 +0100 Subject: [PATCH 08/22] Split out bump_allocator --- agb/src/agb_alloc/bump_allocator.rs | 86 ++++++++++++++++++++++++++++ agb/src/agb_alloc/mod.rs | 87 +---------------------------- 2 files changed, 88 insertions(+), 85 deletions(-) create mode 100644 agb/src/agb_alloc/bump_allocator.rs diff --git a/agb/src/agb_alloc/bump_allocator.rs b/agb/src/agb_alloc/bump_allocator.rs new file mode 100644 index 00000000..631fa217 --- /dev/null +++ b/agb/src/agb_alloc/bump_allocator.rs @@ -0,0 +1,86 @@ +use core::alloc::{GlobalAlloc, Layout}; + +use crate::interrupt::Mutex; + +fn get_data_end() -> usize { + extern "C" { + static __ewram_data_end: usize; + } + + // TODO: This seems completely wrong, but without the &, rust generates + // a double dereference :/. Maybe a bug in nightly? + (unsafe { &__ewram_data_end }) as *const _ as usize +} + +pub(super) struct BumpAllocator { + current_ptr: Mutex<*mut u8>, +} + +impl BumpAllocator { + pub const fn new() -> Self { + Self { + current_ptr: Mutex::new(core::ptr::null_mut()), + } + } +} + +impl BumpAllocator { + fn alloc_safe(&self, layout: Layout) -> *mut u8 { + let mut current_ptr = self.current_ptr.lock(); + + let ptr = if current_ptr.is_null() { + get_data_end() + } else { + *current_ptr as usize + }; + + let alignment_bitmask = layout.align() - 1; + let fixup = ptr & alignment_bitmask; + + let amount_to_add = layout.align() - fixup; + + let resulting_ptr = ptr + amount_to_add; + *current_ptr = (resulting_ptr + layout.size()) as *mut _; + + resulting_ptr as *mut _ + } +} + +unsafe impl GlobalAlloc for BumpAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + self.alloc_safe(layout) + } + + unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} +} + +#[alloc_error_handler] +fn alloc_error(layout: Layout) -> ! { + panic!( + "Failed to allocate size {} with alignment {}", + layout.size(), + layout.align() + ); +} + +#[cfg(test)] +mod test { + use alloc::boxed::Box; + + #[test_case] + fn test_box(_gba: &mut crate::Gba) { + let first_box = Box::new(1); + let second_box = Box::new(2); + + assert!(&*first_box as *const _ < &*second_box as *const _); + assert_eq!(*first_box, 1); + assert_eq!(*second_box, 2); + + let address = &*first_box as *const _ as usize; + assert!( + address >= 0x0200_0000 && address < 0x0204_0000, + "ewram is located between 0x0200_0000 and 0x0204_0000, address was actually found to be {:#010X}", + address + ); + } +} diff --git a/agb/src/agb_alloc/mod.rs b/agb/src/agb_alloc/mod.rs index 88d75c2d..6d26da3f 100644 --- a/agb/src/agb_alloc/mod.rs +++ b/agb/src/agb_alloc/mod.rs @@ -1,89 +1,6 @@ -use core::alloc::{GlobalAlloc, Layout}; +mod bump_allocator; -use super::interrupt::Mutex; - -fn get_data_end() -> usize { - extern "C" { - static __ewram_data_end: usize; - } - - // TODO: This seems completely wrong, but without the &, rust generates - // a double dereference :/. Maybe a bug in nightly? - (unsafe { &__ewram_data_end }) as *const _ as usize -} - -struct BumpAllocator { - current_ptr: Mutex<*mut u8>, -} - -impl BumpAllocator { - pub const fn new() -> Self { - Self { - current_ptr: Mutex::new(core::ptr::null_mut()), - } - } -} - -impl BumpAllocator { - fn alloc_safe(&self, layout: Layout) -> *mut u8 { - let mut current_ptr = self.current_ptr.lock(); - - let ptr = if current_ptr.is_null() { - get_data_end() - } else { - *current_ptr as usize - }; - - let alignment_bitmask = layout.align() - 1; - let fixup = ptr & alignment_bitmask; - - let amount_to_add = layout.align() - fixup; - - let resulting_ptr = ptr + amount_to_add; - *current_ptr = (resulting_ptr + layout.size()) as *mut _; - - resulting_ptr as *mut _ - } -} - -unsafe impl GlobalAlloc for BumpAllocator { - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - self.alloc_safe(layout) - } - - unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} -} - -#[alloc_error_handler] -fn alloc_error(layout: Layout) -> ! { - panic!( - "Failed to allocate size {} with alignment {}", - layout.size(), - layout.align() - ); -} +use bump_allocator::BumpAllocator; #[global_allocator] static GLOBAL_ALLOC: BumpAllocator = BumpAllocator::new(); - -#[cfg(test)] -mod test { - use alloc::boxed::Box; - - #[test_case] - fn test_box(_gba: &mut crate::Gba) { - let first_box = Box::new(1); - let second_box = Box::new(2); - - assert!(&*first_box as *const _ < &*second_box as *const _); - assert_eq!(*first_box, 1); - assert_eq!(*second_box, 2); - - let address = &*first_box as *const _ as usize; - assert!( - address >= 0x0200_0000 && address < 0x0204_0000, - "ewram is located between 0x0200_0000 and 0x0204_0000, address was actually found to be {:#010X}", - address - ); - } -} From 81e97892723de5ff8a04b6dc50e936281cb033ef Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 16 Aug 2021 21:19:51 +0100 Subject: [PATCH 09/22] Correctly handle OOM --- agb/src/agb_alloc/bump_allocator.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/agb/src/agb_alloc/bump_allocator.rs b/agb/src/agb_alloc/bump_allocator.rs index 631fa217..4c26d44a 100644 --- a/agb/src/agb_alloc/bump_allocator.rs +++ b/agb/src/agb_alloc/bump_allocator.rs @@ -2,6 +2,9 @@ use core::alloc::{GlobalAlloc, Layout}; use crate::interrupt::Mutex; +const EWRAM_START: usize = 0x0200_0000; +const EWRAM_END: usize = 0x0204_0000; + fn get_data_end() -> usize { extern "C" { static __ewram_data_end: usize; @@ -12,7 +15,7 @@ fn get_data_end() -> usize { (unsafe { &__ewram_data_end }) as *const _ as usize } -pub(super) struct BumpAllocator { +pub(crate) struct BumpAllocator { current_ptr: Mutex<*mut u8>, } @@ -42,6 +45,10 @@ impl BumpAllocator { let resulting_ptr = ptr + amount_to_add; *current_ptr = (resulting_ptr + layout.size()) as *mut _; + if *current_ptr as usize >= EWRAM_END { + return core::ptr::null_mut(); + } + resulting_ptr as *mut _ } } From c8ac436b84db7ed7fe6b9c024376f21c732cdf78 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 16 Aug 2021 21:20:29 +0100 Subject: [PATCH 10/22] Use constants rather than hard coded stuff --- agb/src/agb_alloc/bump_allocator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agb/src/agb_alloc/bump_allocator.rs b/agb/src/agb_alloc/bump_allocator.rs index 4c26d44a..ab68757a 100644 --- a/agb/src/agb_alloc/bump_allocator.rs +++ b/agb/src/agb_alloc/bump_allocator.rs @@ -85,7 +85,7 @@ mod test { let address = &*first_box as *const _ as usize; assert!( - address >= 0x0200_0000 && address < 0x0204_0000, + address >= super::EWRAM_START && address < super::EWRAM_END, "ewram is located between 0x0200_0000 and 0x0204_0000, address was actually found to be {:#010X}", address ); From a7a1cb3ecd7a6c22fd7df8143af4ddca8d6fefa0 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 16 Aug 2021 21:20:52 +0100 Subject: [PATCH 11/22] use super::*; --- agb/src/agb_alloc/bump_allocator.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/agb/src/agb_alloc/bump_allocator.rs b/agb/src/agb_alloc/bump_allocator.rs index ab68757a..51526dbd 100644 --- a/agb/src/agb_alloc/bump_allocator.rs +++ b/agb/src/agb_alloc/bump_allocator.rs @@ -72,6 +72,7 @@ fn alloc_error(layout: Layout) -> ! { #[cfg(test)] mod test { + use super::*; use alloc::boxed::Box; #[test_case] @@ -85,7 +86,7 @@ mod test { let address = &*first_box as *const _ as usize; assert!( - address >= super::EWRAM_START && address < super::EWRAM_END, + address >= EWRAM_START && address < EWRAM_END, "ewram is located between 0x0200_0000 and 0x0204_0000, address was actually found to be {:#010X}", address ); From f2ce19c35619f4abcec81d1ccb8689835e80df25 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 16 Aug 2021 22:24:32 +0100 Subject: [PATCH 12/22] Switch to non-null --- agb/src/agb_alloc/bump_allocator.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/agb/src/agb_alloc/bump_allocator.rs b/agb/src/agb_alloc/bump_allocator.rs index 51526dbd..65335f10 100644 --- a/agb/src/agb_alloc/bump_allocator.rs +++ b/agb/src/agb_alloc/bump_allocator.rs @@ -1,4 +1,5 @@ use core::alloc::{GlobalAlloc, Layout}; +use core::ptr::NonNull; use crate::interrupt::Mutex; @@ -16,13 +17,13 @@ fn get_data_end() -> usize { } pub(crate) struct BumpAllocator { - current_ptr: Mutex<*mut u8>, + current_ptr: Mutex>>, } impl BumpAllocator { pub const fn new() -> Self { Self { - current_ptr: Mutex::new(core::ptr::null_mut()), + current_ptr: Mutex::new(None), } } } @@ -31,10 +32,10 @@ impl BumpAllocator { fn alloc_safe(&self, layout: Layout) -> *mut u8 { let mut current_ptr = self.current_ptr.lock(); - let ptr = if current_ptr.is_null() { - get_data_end() + let ptr = if let Some(c) = *current_ptr { + c.as_ptr() as usize } else { - *current_ptr as usize + get_data_end() }; let alignment_bitmask = layout.align() - 1; @@ -43,12 +44,14 @@ impl BumpAllocator { let amount_to_add = layout.align() - fixup; let resulting_ptr = ptr + amount_to_add; - *current_ptr = (resulting_ptr + layout.size()) as *mut _; + let new_current_ptr = resulting_ptr + layout.size(); - if *current_ptr as usize >= EWRAM_END { + if new_current_ptr as usize >= EWRAM_END { return core::ptr::null_mut(); } + *current_ptr = NonNull::new(new_current_ptr as *mut _); + resulting_ptr as *mut _ } } From 7425e9973d5f497224758bf5de38f5785fd85a91 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 16 Aug 2021 22:25:08 +0100 Subject: [PATCH 13/22] Add a really basic block allocator --- agb/src/agb_alloc/block_allocator.rs | 66 ++++++++++++++++++++++++++++ agb/src/agb_alloc/mod.rs | 1 + 2 files changed, 67 insertions(+) create mode 100644 agb/src/agb_alloc/block_allocator.rs diff --git a/agb/src/agb_alloc/block_allocator.rs b/agb/src/agb_alloc/block_allocator.rs new file mode 100644 index 00000000..9ca55fa4 --- /dev/null +++ b/agb/src/agb_alloc/block_allocator.rs @@ -0,0 +1,66 @@ +use core::alloc::{GlobalAlloc, Layout}; +use core::ptr::NonNull; + +use crate::interrupt::Mutex; + +use super::bump_allocator::BumpAllocator; + +struct Block { + used: bool, + next: Option>, +} + +impl Block { + pub unsafe fn from_data_ptr(data_ptr: *mut u8, layout: Layout) -> *mut Block { + let block_layout = Layout::new::(); + let (_, offset) = block_layout.extend(layout).expect("Overflow on allocation"); + + data_ptr.sub(offset).cast() + } +} + +struct BlockAllocatorState { + first_block: Option>, + last_block: Option>, +} + +pub(crate) struct BlockAllocator { + inner_allocator: BumpAllocator, + state: Mutex, +} + +unsafe impl GlobalAlloc for BlockAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let block_layout = Layout::new::(); + let (overall_layout, offset) = block_layout.extend(layout).expect("Overflow on allocation"); + + let block_ptr = self.inner_allocator.alloc(overall_layout); + + if block_ptr.is_null() { + return core::ptr::null_mut(); + } + + let block_ptr = NonNull::new_unchecked(block_ptr).cast(); + + let mut state = self.state.lock(); + + *block_ptr.cast::().as_mut() = Block { + used: true, + next: None, + }; + + state.first_block.get_or_insert(block_ptr); + + if let Some(last_block) = state.last_block { + last_block.cast::().as_mut().next = Some(block_ptr); + } + state.last_block = Some(block_ptr); + + block_ptr.as_ptr().cast::().add(offset) + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + let block = Block::from_data_ptr(ptr, layout); + (&mut *block).used = false; + } +} diff --git a/agb/src/agb_alloc/mod.rs b/agb/src/agb_alloc/mod.rs index 6d26da3f..45220b1e 100644 --- a/agb/src/agb_alloc/mod.rs +++ b/agb/src/agb_alloc/mod.rs @@ -1,3 +1,4 @@ +mod block_allocator; mod bump_allocator; use bump_allocator::BumpAllocator; From 4b9e4ce42a369ae040e6ff0abd3a95a18ef99acc Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 16 Aug 2021 22:28:41 +0100 Subject: [PATCH 14/22] Move tests to agb_alloc mod.rs --- agb/src/agb_alloc/bump_allocator.rs | 37 +--------------------------- agb/src/agb_alloc/mod.rs | 38 +++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 36 deletions(-) diff --git a/agb/src/agb_alloc/bump_allocator.rs b/agb/src/agb_alloc/bump_allocator.rs index 65335f10..733e6667 100644 --- a/agb/src/agb_alloc/bump_allocator.rs +++ b/agb/src/agb_alloc/bump_allocator.rs @@ -3,9 +3,6 @@ use core::ptr::NonNull; use crate::interrupt::Mutex; -const EWRAM_START: usize = 0x0200_0000; -const EWRAM_END: usize = 0x0204_0000; - fn get_data_end() -> usize { extern "C" { static __ewram_data_end: usize; @@ -46,7 +43,7 @@ impl BumpAllocator { let resulting_ptr = ptr + amount_to_add; let new_current_ptr = resulting_ptr + layout.size(); - if new_current_ptr as usize >= EWRAM_END { + if new_current_ptr as usize >= super::EWRAM_END { return core::ptr::null_mut(); } @@ -63,35 +60,3 @@ unsafe impl GlobalAlloc for BumpAllocator { unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} } - -#[alloc_error_handler] -fn alloc_error(layout: Layout) -> ! { - panic!( - "Failed to allocate size {} with alignment {}", - layout.size(), - layout.align() - ); -} - -#[cfg(test)] -mod test { - use super::*; - use alloc::boxed::Box; - - #[test_case] - fn test_box(_gba: &mut crate::Gba) { - let first_box = Box::new(1); - let second_box = Box::new(2); - - assert!(&*first_box as *const _ < &*second_box as *const _); - assert_eq!(*first_box, 1); - assert_eq!(*second_box, 2); - - let address = &*first_box as *const _ as usize; - assert!( - address >= EWRAM_START && address < EWRAM_END, - "ewram is located between 0x0200_0000 and 0x0204_0000, address was actually found to be {:#010X}", - address - ); - } -} diff --git a/agb/src/agb_alloc/mod.rs b/agb/src/agb_alloc/mod.rs index 45220b1e..ec01fb04 100644 --- a/agb/src/agb_alloc/mod.rs +++ b/agb/src/agb_alloc/mod.rs @@ -1,7 +1,45 @@ +use core::alloc::Layout; + mod block_allocator; mod bump_allocator; use bump_allocator::BumpAllocator; +const EWRAM_END: usize = 0x0204_0000; + #[global_allocator] static GLOBAL_ALLOC: BumpAllocator = BumpAllocator::new(); + +#[alloc_error_handler] +fn alloc_error(layout: Layout) -> ! { + panic!( + "Failed to allocate size {} with alignment {}", + layout.size(), + layout.align() + ); +} + +#[cfg(test)] +mod test { + const EWRAM_START: usize = 0x0200_0000; + + use super::*; + use alloc::boxed::Box; + + #[test_case] + fn test_box(_gba: &mut crate::Gba) { + let first_box = Box::new(1); + let second_box = Box::new(2); + + assert!(&*first_box as *const _ < &*second_box as *const _); + assert_eq!(*first_box, 1); + assert_eq!(*second_box, 2); + + let address = &*first_box as *const _ as usize; + assert!( + address >= EWRAM_START && address < EWRAM_END, + "ewram is located between 0x0200_0000 and 0x0204_0000, address was actually found to be {:#010X}", + address + ); + } +} From d372ca74bf8b4cbc74a6f8121d30fa5c4c235d23 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 16 Aug 2021 22:31:10 +0100 Subject: [PATCH 15/22] Use the block allocator by default now --- agb/src/agb_alloc/block_allocator.rs | 12 ++++++++++++ agb/src/agb_alloc/mod.rs | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/agb/src/agb_alloc/block_allocator.rs b/agb/src/agb_alloc/block_allocator.rs index 9ca55fa4..2e903381 100644 --- a/agb/src/agb_alloc/block_allocator.rs +++ b/agb/src/agb_alloc/block_allocator.rs @@ -29,6 +29,18 @@ pub(crate) struct BlockAllocator { state: Mutex, } +impl BlockAllocator { + pub(super) const unsafe fn new() -> Self { + Self { + inner_allocator: BumpAllocator::new(), + state: Mutex::new(BlockAllocatorState { + first_block: None, + last_block: None, + }), + } + } +} + unsafe impl GlobalAlloc for BlockAllocator { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { let block_layout = Layout::new::(); diff --git a/agb/src/agb_alloc/mod.rs b/agb/src/agb_alloc/mod.rs index ec01fb04..b67ff9ff 100644 --- a/agb/src/agb_alloc/mod.rs +++ b/agb/src/agb_alloc/mod.rs @@ -3,12 +3,12 @@ use core::alloc::Layout; mod block_allocator; mod bump_allocator; -use bump_allocator::BumpAllocator; +use block_allocator::BlockAllocator; const EWRAM_END: usize = 0x0204_0000; #[global_allocator] -static GLOBAL_ALLOC: BumpAllocator = BumpAllocator::new(); +static GLOBAL_ALLOC: BlockAllocator = unsafe { BlockAllocator::new() }; #[alloc_error_handler] fn alloc_error(layout: Layout) -> ! { From e80ad601a37c410a701359f02def9df9642dc189 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 16 Aug 2021 22:33:56 +0100 Subject: [PATCH 16/22] Extract current alloc to a new_block function --- agb/src/agb_alloc/block_allocator.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/agb/src/agb_alloc/block_allocator.rs b/agb/src/agb_alloc/block_allocator.rs index 2e903381..4be4143c 100644 --- a/agb/src/agb_alloc/block_allocator.rs +++ b/agb/src/agb_alloc/block_allocator.rs @@ -39,10 +39,8 @@ impl BlockAllocator { }), } } -} -unsafe impl GlobalAlloc for BlockAllocator { - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + unsafe fn new_block(&self, layout: Layout) -> *mut u8 { let block_layout = Layout::new::(); let (overall_layout, offset) = block_layout.extend(layout).expect("Overflow on allocation"); @@ -70,6 +68,12 @@ unsafe impl GlobalAlloc for BlockAllocator { block_ptr.as_ptr().cast::().add(offset) } +} + +unsafe impl GlobalAlloc for BlockAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + self.new_block(layout) + } unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { let block = Block::from_data_ptr(ptr, layout); From b30cc7715c8296e35c402c65ff0a1cdf1bec9b62 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 16 Aug 2021 22:55:02 +0100 Subject: [PATCH 17/22] Add really simple block reuse --- agb/src/agb_alloc/block_allocator.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/agb/src/agb_alloc/block_allocator.rs b/agb/src/agb_alloc/block_allocator.rs index 4be4143c..03bd4359 100644 --- a/agb/src/agb_alloc/block_allocator.rs +++ b/agb/src/agb_alloc/block_allocator.rs @@ -72,6 +72,30 @@ impl BlockAllocator { unsafe impl GlobalAlloc for BlockAllocator { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + // find a block that this current request fits in + let block_layout = Layout::new::(); + let (full_layout, offset) = block_layout.extend(layout).unwrap(); + + { + let state = self.state.lock(); + let mut current_block = state.first_block; + while let Some(mut curr) = current_block { + let mut curr_block = curr.as_mut(); + + if !curr_block.used { + if let Some(next) = curr_block.next { + let size = next.cast::().as_ptr().offset_from(curr.as_ptr().cast()); + if size >= full_layout.size() as isize { + curr_block.used = true; + return curr.as_ptr().cast::().add(offset); + } + } + } + + current_block = curr_block.next; + } + } + self.new_block(layout) } From 22189eb809f89a456846b18003ddfda8f5704ca4 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 16 Aug 2021 23:20:51 +0100 Subject: [PATCH 18/22] Add some more allocation tests --- agb/src/agb_alloc/block_allocator.rs | 2 +- agb/src/agb_alloc/mod.rs | 46 ++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/agb/src/agb_alloc/block_allocator.rs b/agb/src/agb_alloc/block_allocator.rs index 03bd4359..f876e0a5 100644 --- a/agb/src/agb_alloc/block_allocator.rs +++ b/agb/src/agb_alloc/block_allocator.rs @@ -75,7 +75,7 @@ unsafe impl GlobalAlloc for BlockAllocator { // find a block that this current request fits in let block_layout = Layout::new::(); let (full_layout, offset) = block_layout.extend(layout).unwrap(); - + { let state = self.state.lock(); let mut current_block = state.first_block; diff --git a/agb/src/agb_alloc/mod.rs b/agb/src/agb_alloc/mod.rs index b67ff9ff..edc8efc1 100644 --- a/agb/src/agb_alloc/mod.rs +++ b/agb/src/agb_alloc/mod.rs @@ -25,6 +25,8 @@ mod test { use super::*; use alloc::boxed::Box; + use alloc::vec; + use alloc::vec::Vec; #[test_case] fn test_box(_gba: &mut crate::Gba) { @@ -42,4 +44,48 @@ mod test { address ); } + + #[test_case] + fn test_vec(_gba: &mut crate::Gba) { + let mut v = Vec::with_capacity(5); + + for i in 0..100 { + v.push(i); + } + + for i in 0..100 { + assert_eq!(v[i], i); + } + } + + #[test_case] + fn test_creating_and_removing_things(_gba: &mut crate::Gba) { + let item = Box::new(1); + for i in 0..1_000 { + let x = Box::new(i); + assert_eq!(*x, i); + let address = &*x as *const _ as usize; + assert!( + address >= EWRAM_START && address < EWRAM_END, + "ewram is located between 0x0200_0000 and 0x0204_0000, address was actually found to be {:#010X}", + address + ); + } + + assert_eq!(*item, 1); + } + + #[test_case] + fn test_adding_to_2_different_vectors(_gba: &mut crate::Gba) { + let mut v1 = vec![1, 2, 3]; + let mut v2 = vec![4, 5, 6]; + + for i in 0..100 { + v1.push(i + 100); + v2.push(i + 1000); + } + + assert_eq!(v1[40], 137); + assert_eq!(v2[78], 1075); + } } From 469db774371d75cdb031e7d9c892cef0c4a3a068 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 16 Aug 2021 23:28:33 +0100 Subject: [PATCH 19/22] Add allocation example --- agb/examples/allocation.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 agb/examples/allocation.rs diff --git a/agb/examples/allocation.rs b/agb/examples/allocation.rs new file mode 100644 index 00000000..327ed07e --- /dev/null +++ b/agb/examples/allocation.rs @@ -0,0 +1,15 @@ +#![no_std] +#![no_main] + +extern crate agb; +extern crate alloc; + +use alloc::boxed::Box; + +#[agb::entry] +fn main() -> ! { + loop { + let b = Box::new(1); + agb::println!("dynamic allocation made to {:?}", &*b as *const _); + } +} From bec0cc6055a16dcacd368d6937df3acd19da35dd Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 16 Aug 2021 23:32:31 +0100 Subject: [PATCH 20/22] Make alloc an optional default feature --- agb/Cargo.toml | 5 +++-- agb/src/lib.rs | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/agb/Cargo.toml b/agb/Cargo.toml index 8b651160..12b7bf1f 100644 --- a/agb/Cargo.toml +++ b/agb/Cargo.toml @@ -14,8 +14,9 @@ debug = true lto = true debug = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +default = ["alloc"] +alloc = [] [dependencies] bitflags = "1.2" diff --git a/agb/src/lib.rs b/agb/src/lib.rs index 38a8f443..8864b605 100644 --- a/agb/src/lib.rs +++ b/agb/src/lib.rs @@ -13,7 +13,10 @@ //! internal workings of the Game Boy Advance whilst still being high //! performance and memory efficient. +#[cfg(feature = "alloc")] extern crate alloc; +#[cfg(feature = "alloc")] +mod agb_alloc; /// Implements everything relating to things that are displayed on screen. pub mod display; @@ -34,8 +37,6 @@ pub mod mgba; pub mod number; mod single; -mod agb_alloc; - /// System BIOS calls / syscalls. pub mod syscall; From 6d11302725536cad866856174b23c37c1ad11456 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 16 Aug 2021 23:40:16 +0100 Subject: [PATCH 21/22] Fix clippy lint in the allocator --- agb/src/agb_alloc/block_allocator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agb/src/agb_alloc/block_allocator.rs b/agb/src/agb_alloc/block_allocator.rs index f876e0a5..8347b007 100644 --- a/agb/src/agb_alloc/block_allocator.rs +++ b/agb/src/agb_alloc/block_allocator.rs @@ -101,6 +101,6 @@ unsafe impl GlobalAlloc for BlockAllocator { unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { let block = Block::from_data_ptr(ptr, layout); - (&mut *block).used = false; + (*block).used = false; } } From 9350e837b7f5631928bb9503331ec8fdb4661fc2 Mon Sep 17 00:00:00 2001 From: Gwilym Kuiper Date: Mon, 16 Aug 2021 23:41:44 +0100 Subject: [PATCH 22/22] Add alloc to the template's config.toml --- template/.cargo/config.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template/.cargo/config.toml b/template/.cargo/config.toml index d352818b..76de9890 100644 --- a/template/.cargo/config.toml +++ b/template/.cargo/config.toml @@ -1,5 +1,5 @@ [unstable] -build-std = ["core"] +build-std = ["core", "alloc"] build-std-features = ["compiler-builtins-mem"] [build]