Skip to content

Commit

Permalink
feat: add std::alloc and std::ptr
Browse files Browse the repository at this point in the history
  • Loading branch information
AlicanC committed May 31, 2022
1 parent 59a4d05 commit ac264ab
Show file tree
Hide file tree
Showing 12 changed files with 406 additions and 10 deletions.
45 changes: 45 additions & 0 deletions sway-lib-std/src/alloc.sw
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//! Library for allocating memory
//! Inspired from: https://doc.rust-lang.org/std/alloc/index.html
library alloc;

use ::intrinsics::*;

/// 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
}
}

/// Reallocates the given area of memory
pub fn realloc(ptr: u64, size: u64, new_size: u64) -> u64 {
if new_size > size {
let new_ptr = alloc(new_size);
copy(new_ptr, ptr, size);
new_ptr
} else {
ptr
}
}
36 changes: 36 additions & 0 deletions sway-lib-std/src/intrinsics.sw
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,39 @@ pub fn is_reference_type<T>() -> bool {
pub fn size_of<T>() -> u64 {
__size_of::<T>()
}

/// Returns the size of a value in bytes.
pub fn size_of_val<T>(val: T) -> u64 {
__size_of_val::<T>(val)
}

/// Returns the address of the given value.
pub fn addr_of<T>(val: T) -> u64 {
// TODO: Replace with intrinsic: https://github.com/FuelLabs/sway/issues/855
if !__is_reference_type::<T>() {
// std::revert not available here
asm() {
rvrt zero;
}
}
asm(ptr: val) {
ptr: u64
}
}

/// Copies data from source to destination.
pub fn copy(dst: u64, src: u64, size: u64) {
// TODO: Replace with intrinsic: https://github.com/FuelLabs/sway/issues/855
asm(dst: dst, src: src, size: size) {
mcp dst src size;
};
}

/// Compares data at two points of memory.
pub fn raw_eq(first: u64, second: u64, len: u64) -> bool {
// TODO: Replace with intrinsic: https://github.com/FuelLabs/sway/issues/855
asm(first: first, second: second, len: len, result) {
meq result first second len;
result: bool
}
}
2 changes: 2 additions & 0 deletions sway-lib-std/src/lib.sw
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ dep logging;
dep assert;
dep option;
dep result;
dep alloc;
dep ptr;
dep constants;
dep contract_id;
dep context;
Expand Down
112 changes: 112 additions & 0 deletions sway-lib-std/src/ptr.sw
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
//! Library for working with addresses in memory
//! Inspired from: https://doc.rust-lang.org/std/primitive.pointer.html
//! Inspired from: https://doc.rust-lang.org/std/ptr/index.html
library ptr;

use ::assert::*;
use ::intrinsics::*;

/// A point in memory with unknown type
pub struct RawPointer {
addr: u64,
}

impl RawPointer {
pub fn new(addr: u64) -> Self {
RawPointer {
addr: addr,
}
}

/// Creates a new pointer to the given reference-type value
pub fn from<T>(val: T) -> Self {
assert(is_reference_type::<T>());
RawPointer {
addr: addr_of(val),
}
}

pub fn addr(self) -> u64 {
self.addr
}

/// Creates a new pointer with the given offset added to the current address
pub fn add(self, offset: u64) -> Self {
RawPointer {
addr: self.addr + offset,
}
}

/// Creates a new pointer with the given offset subtracted from the current address
pub fn sub(self, offset: u64) -> Self {
RawPointer {
addr: self.addr - offset,
}
}

/// Reads the given type of value from the pointer's address
pub fn read<T>(self) -> T {
if is_reference_type::<T>() {
asm(r1: self.addr) {
r1: T
}
} else {
asm(r1: self.addr) {
lw r1 r1 i0;
r1: T
}
}
}

/// Writes the given value to the pointer's address
pub fn write<T>(self, val: T) {
if is_reference_type::<T>() {
copy(self.addr, addr_of(val), size_of::<T>());
} else {
asm(ptr: self.addr, val: val) {
sw ptr val i0;
};
}
}

/// Copies the data at the given pointer to the pointer's address
pub fn copy_from(self, src: Self, len: u64) {
copy(self.addr, src.addr, len);
}

/// Copies the data at the pointer's address to the given pointer
pub fn copy_to(self, dst: Self, len: u64) {
copy(dst.addr, self.addr, len);
}

// Non-generic aliases to workaround generics bugs
// See: https://github.com/FuelLabs/sway/issues/1628
pub fn read_bool(self) -> bool {
asm(r1: self.addr) {
lw r1 r1 i0;
r1: bool
}
}
pub fn read_u64(self) -> u64 {
asm(r1: self.addr) {
lw r1 r1 i0;
r1: u64
}
}
pub fn write_bool(self, val: bool) {
asm(ptr: self.addr, val: val) {
sw ptr val i0;
};
}
pub fn write_u64(self, val: u64) {
asm(ptr: self.addr, val: val) {
sw ptr val i0;
};
}
}

impl core::ops::Eq for RawPointer {
fn eq(self, other: Self) -> bool {
self.addr == other.addr
}
}
2 changes: 2 additions & 0 deletions test/src/e2e_vm_tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ pub fn run(filter_regex: Option<regex::Regex>) {
("should_pass/stdlib/u128_test", ProgramState::Return(1)), // true
("should_pass/stdlib/u128_div_test", ProgramState::Return(1)), // true
("should_pass/stdlib/u128_mul_test", ProgramState::Return(1)), // true
("should_pass/stdlib/alloc", ProgramState::Return(1)), // true
("should_pass/stdlib/ptr", ProgramState::Return(1)), // true
(
"should_pass/language/generic_structs",
ProgramState::Return(1), // true
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[[package]]
name = 'core'
dependencies = []

[[package]]
name = 'std'
dependencies = ['core']

[[package]]
name = 'std_alloc_test'
dependencies = ['std']
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[project]
authors = ["Fuel Labs <[email protected]>"]
entry = "main.sw"
license = "Apache-2.0"
name = "std_alloc_test"

[dependencies]
std = { path = "../../../../../../../sway-lib-std" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
script;

use core::num::*;
use std::alloc::*;
use std::intrinsics::*;
use std::context::registers::*;
use std::assert::assert;

fn lw(ptr: u64) -> u64 {
asm(r1: ptr) {
lw r1 r1 i0;
r1: u64
}
}

fn sw(ptr: u64, val: u64) {
asm(r1: ptr, val: val) {
sw r1 val i0;
};
}

fn main() -> bool {
let hp_start = heap_ptr();

// Allocate some memory
let hp = heap_ptr();
let ptr = alloc(8);
assert(ptr == hp - 8 + 1);
assert(heap_ptr() == hp - 8);

// Read from it
let val = lw(ptr);
assert(val == 0);

// Write to it
let val = ~u64::max();
sw(ptr, val);
assert(lw(ptr) == val);

// Grow it
let hp = heap_ptr();
let ptr = realloc(ptr, 8, 16);
assert(ptr == hp - 16 + 1);
assert(heap_ptr() == hp - 16);

true
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ fn is_ref_type<T>(param: T) -> bool {
is_reference_type::<T>()
}

fn get_size_of<T>(param: T) -> u64 {
size_of::<T>()
}

fn main() -> bool {
let zero = ~b256::min();
let a: u64 = 1;
Expand Down Expand Up @@ -43,12 +39,35 @@ fn main() -> bool {
assert(is_ref_type(e));
assert(is_ref_type(f));

assert(get_size_of(a) == 8);
assert(get_size_of(b) == 8);
assert(get_size_of(c) == 8);
assert(get_size_of(d) == 8);
assert(get_size_of(e) == 32);
assert(get_size_of(f) == 16);
assert(size_of::<u64>() == 8);
assert(size_of::<u32>() == 8);
assert(size_of::<u16>() == 8);
assert(size_of::<u8>() == 8);
assert(size_of::<b256>() == 32);
assert(size_of::<str[11]>() == 16);
assert(size_of::<[u16;
3]>() == 24);
assert(size_of::<TestStruct>() == 16);

assert(size_of_val(a) == 8);
assert(size_of_val(b) == 8);
assert(size_of_val(c) == 8);
assert(size_of_val(d) == 8);
assert(size_of_val(e) == 32);
assert(size_of_val(f) == 16);

assert(addr_of(test_struct) == asm(r1: test_struct) {
r1: u64
});

let test_struct_2 = TestStruct {
field_1: true,
field_2: 12,
};
assert(!raw_eq(addr_of(test_struct), addr_of(test_struct_2), size_of_val(test_struct)));

copy(addr_of(test_struct), addr_of(test_struct_2), size_of_val(test_struct));
assert(raw_eq(addr_of(test_struct), addr_of(test_struct_2), size_of_val(test_struct)));

true
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[[package]]
name = 'core'
dependencies = []

[[package]]
name = 'std'
dependencies = ['core']

[[package]]
name = 'std_ptr_test'
dependencies = ['std']
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[project]
authors = ["Fuel Labs <[email protected]>"]
entry = "main.sw"
license = "Apache-2.0"
name = "std_ptr_test"

[dependencies]
std = { path = "../../../../../../../sway-lib-std" }
Loading

0 comments on commit ac264ab

Please sign in to comment.