-
Notifications
You must be signed in to change notification settings - Fork 5.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add std::alloc
and std::ptr
#1627
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks really good, will defer to the others though 😄
boo: bool,
uwu: u64,
kek: bool,
bur: u64
lol
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this looks good. I'd like to do some refactoring of other stuff to use this code!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great though I have a few thoughts:
- Eventually I'd like a lot of this to be replaced with intrinsics, though that's true for most of the ASM blocks in the stdlib.
- I'm a bit concerned about converting any old reference type to a pointer, and then assuming you can do what you like with it. Especially if you take the address of a literal which is still a reference type, but resides in the data section, it is essentially read only memory, and so if you try to
copy()
to it, the VM will panic. Maybe this isn't possible though, not sure. - If we were to get a little more serious we could store the size of allocations in the heap. This is what 'real' allocators do. It allows
realloc()
to work without needing pass the old size in. It also gives usfree()
although then we'd need to keep track of allocated and unallocated blocks for it to be useful. But it also would blur the distinction between pointer and buffer in this code -- buffer wouldn't necessarily be needed, and we could useVec
when it arrives for collections of values.
sway-lib-std/src/mem.sw
Outdated
aloc size; | ||
addi ptr hp i1; | ||
ptr: u64 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why add 1 to $hp
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$hp
points to the free space on the heap and not to the allocated space. Heap grows from end to start so you need hp + 1
(and not hp - size
) to get to the alloc'd space.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will add a comment that explains this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure? My impression from the spec is that the stack grows up, and so sp
points to free space before you allocate. Therefore you copy sp
before CFEI
. Conversely, the heap grows down, so hp
points to previously allocated memory before you allocate. You copy hp
after you ALOC
and it'll just point to your new buffer.
So I think the above should just be move ptr hp;
. Adding a 1
really doesn't make sense either way. If hp
was 0x1000 and you allocate 256 bytes with move r0 256; aloc r0
then hp
will be 0xf00. Adding 1 to it makes it 0xf01, which is just discards one of the bytes you allocated and actually makes the buffer 1 byte smaller.
Maybe @adlerjohn can confirm or elaborate?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wrote how I understood it in a comment:
/// Allocates zeroed memory on the heap
///
/// In FuelVM, the heap begins at `VM_MAX_RAM - 1` and grows downward.
/// Heap pointer `$hp` will always point to unallocated space.
///
/// Initially the heap will look like this:
/// ... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
/// $hp^ ^VM_MAX_RAM
///
/// After allocating with `let ptr = alloc(8)`:
/// ... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
/// $hp^ ^ptr ^VM_MAX_RAM
///
/// After writing with `sw(ptr, u64::max())`:
/// ... 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF |
/// $hp^ ^ptr ^VM_MAX_RAM
///
/// See: https://github.com/FuelLabs/fuel-specs/blob/master/specs/vm/main.md#vm-initialization
/// See: https://github.com/FuelLabs/fuel-specs/blob/master/specs/vm/opcodes.md#aloc-allocate-memory
pub fn alloc(size: u64) -> u64 {
asm(size: size, ptr) {
aloc size;
// `$hp` points to unallocated space and heap grows downward so
// our newly allocated space will be right after it
addi ptr hp i1;
ptr: u64
}
}
@otrho thanks for the review! My intention is to create an unsafe low level library to help w/ debugging and for building safe variants on later. I've read the comments and made some research on what Rust has available and now I'm thinking:
Comments appreciated! |
Yep, cool, looks like a good plan! I think we'll be trying to remove the |
/// Allocates zeroed memory on the heap | ||
/// | ||
/// In FuelVM, the heap begins at `VM_MAX_RAM - 1` and grows downward. | ||
/// Heap pointer `$hp` will always point to unallocated space. | ||
/// | ||
/// Initially the heap will look like this: | ||
/// ... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | | ||
/// $hp^ ^VM_MAX_RAM | ||
/// | ||
/// After allocating with `let ptr = alloc(8)`: | ||
/// ... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | | ||
/// $hp^ ^ptr ^VM_MAX_RAM | ||
/// | ||
/// After writing with `sw(ptr, u64::max())`: | ||
/// ... 00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF | | ||
/// $hp^ ^ptr ^VM_MAX_RAM | ||
/// | ||
/// See: https://github.com/FuelLabs/fuel-specs/blob/master/specs/vm/main.md#vm-initialization | ||
/// See: https://github.com/FuelLabs/fuel-specs/blob/master/specs/vm/opcodes.md#aloc-allocate-memory |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I believe the initialisation of the VM is broken, and $hp
should be initialised to VM_MAX_RAM
. Putting in hacks to account for the neglected last byte isn't the role of the stdlib.
Especially since if another part of the library used ALOC
and didn't do the little $hp + 1
trick then there'd be conflict and potential corruption.
Update:
|
To elaborate on the Fully implementing Allocator would look like this: trait Allocator {
fn allocate(self, size: u64) -> u64;
fn deallocate(self, ptr: u64, size: u64) -> u64;
fn allocate_zeroed(self, size: u64) -> u64;
fn grow(self, ptr: u64, old_size: u64, new_size: u64) -> u64;
fn grow_zeroed(self, ptr: u64, old_size: u64, new_size: u64) -> u64;
fn shrink(self, ptr: u64, old_size: u64, new_size: u64) -> u64;
} But in reality we only have these: trait Allocator {
fn allocate_zeroed(self, size: u64) -> u64;
fn grow_zeroed(self, ptr: u64, old_size: u64, new_size: u64) -> u64;
} And in my latest commit we call them I see three options:
|
At this stage we really don't need to go apeshit with the |
Converting to draft until FuelLabs/fuel-specs#322 is implemented and pushed through to fuel-core. Alternatively, merge as-is with the off-by-one, and fix later. |
There's no reason not to make the + 1 fix now and merge. We don't have to wait for the VM fix. It'd just mean that until then the last byte in the heap will be ignored/unused. |
Hmmm. But the VM's memory is |
After the VM fix |
Ah I got it, I was getting the direction mixed up. I though it was that the top byte of allocated mem would be outside the VM's memory, but it's instead that the top byte of the VM's memory wouldn't be allocatable. Right? In that case, that's fine. |
I need to return |
If you commit my suggestion above for |
I commited the suggestion, removed the comment and also removed
This CI run should fail: https://github.com/FuelLabs/sway/runs/6605214966 |
Alright, FYI, @AlicanC and I hopped in a huddle and we're seeing weirdness regarding writing to the allocated buffer. I'm going to take a closer look at the VM implementation and see what's going on. |
An update: I've made FuelLabs/fuel-specs#322 and FuelLabs/fuel-vm#137 because I believe the way we're using But at the moment if we want to go ahead with this PR, we can use the original behaviour of expecting |
It seems like some change broke the way I use generics. Will try to isolate and open an issue. |
Resolved in #1979 |
This adds two new libraries to
sway-lib-std
:alloc
andptr
.I needed these for my work on the Multicall script and just wanted to send them upstream. (Though it's not very usable ATM for me because of an issue.)
alloc::alloc
,alloc::realloc
intrinsics::size_of_val
,intrinsics::addr_of
,intrinsics::copy
,intrinsics::raw_eq
struct RawPointer
for unsafely working with memory