Skip to content

Commit

Permalink
Add intrinsics for operating on raw_ptrs (FuelLabs#3157)
Browse files Browse the repository at this point in the history
This PR:
- Adds `__ptr_add<T>(ptr: raw_ptr, count: u64)` (which basically does
`ptr + __size_of::<T>() * count`) and its sub counterpart to be used in
pointer offset calculations.
- Makes `std::alloc::*` and `raw_ptr::*` have type arguments and work in
increments of `size_of::<T>()` instead of bytes.
- Removes test `fixing_generic_type` which was broken instead of
maintaining it. This test was created to reproduce an issue I was having
a few months ago. Fixing this allowed other things to be built so there
are other tests that touch this case.
  • Loading branch information
AlicanC authored Nov 2, 2022
1 parent 37a3687 commit 81f7f57
Show file tree
Hide file tree
Showing 19 changed files with 223 additions and 385 deletions.
6 changes: 6 additions & 0 deletions sway-ast/src/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ pub enum Intrinsic {
Mul,
Div,
Revert,
PtrAdd,
PtrSub,
}

impl fmt::Display for Intrinsic {
Expand All @@ -41,6 +43,8 @@ impl fmt::Display for Intrinsic {
Intrinsic::Mul => "mul",
Intrinsic::Div => "div",
Intrinsic::Revert => "revert",
Intrinsic::PtrAdd => "ptr_add",
Intrinsic::PtrSub => "ptr_sub",
};
write!(f, "{}", s)
}
Expand All @@ -67,6 +71,8 @@ impl Intrinsic {
"__mul" => Mul,
"__div" => Div,
"__revert" => Revert,
"__ptr_add" => PtrAdd,
"__ptr_sub" => PtrSub,
_ => return None,
})
}
Expand Down
26 changes: 26 additions & 0 deletions sway-core/src/ir_generation/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,32 @@ impl FnCompiler {
.revert(revert_code_val)
.add_metadatum(context, span_md_idx))
}
Intrinsic::PtrAdd | Intrinsic::PtrSub => {
let op = match kind {
Intrinsic::PtrAdd => BinaryOpKind::Add,
Intrinsic::PtrSub => BinaryOpKind::Sub,
_ => unreachable!(),
};

let len = type_arguments[0].clone();
let ir_type = convert_resolved_typeid(context, &len.type_id, &len.span)?;
let len_value =
Constant::get_uint(context, 64, ir_type_size_in_bytes(context, &ir_type));

let lhs = arguments[0].clone();
let count = arguments[1].clone();
let lhs_value = self.compile_expression(context, md_mgr, lhs)?;
let count_value = self.compile_expression(context, md_mgr, count)?;
let rhs_value = self.current_block.ins(context).binary_op(
BinaryOpKind::Mul,
len_value,
count_value,
);
Ok(self
.current_block
.ins(context)
.binary_op(op, lhs_value, rhs_value))
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,103 @@ impl ty::TyIntrinsicFunctionKind {
// available
)
}
Intrinsic::PtrAdd | Intrinsic::PtrSub => {
if arguments.len() != 2 {
errors.push(CompileError::IntrinsicIncorrectNumArgs {
name: kind.to_string(),
expected: 2,
span,
});
return err(warnings, errors);
}
if type_arguments.len() != 1 {
errors.push(CompileError::IntrinsicIncorrectNumTArgs {
name: kind.to_string(),
expected: 1,
span,
});
return err(warnings, errors);
}
let targ = type_arguments[0].clone();
let initial_type_info = check!(
CompileResult::from(
to_typeinfo(targ.type_id, &targ.span).map_err(CompileError::from)
),
TypeInfo::ErrorRecovery,
warnings,
errors
);
let initial_type_id = insert_type(initial_type_info);
let type_id = check!(
ctx.resolve_type_with_self(
initial_type_id,
&targ.span,
EnforceTypeArguments::No,
None
),
insert_type(TypeInfo::ErrorRecovery),
warnings,
errors,
);

let mut ctx = ctx
.by_ref()
.with_type_annotation(insert_type(TypeInfo::Unknown));

let lhs = arguments[0].clone();
let lhs = check!(
ty::TyExpression::type_check(ctx.by_ref(), lhs),
return err(warnings, errors),
warnings,
errors
);

// Check for supported argument types
let lhs_ty = check!(
CompileResult::from(
to_typeinfo(lhs.return_type, &lhs.span).map_err(CompileError::from)
),
TypeInfo::ErrorRecovery,
warnings,
errors
);
if !matches!(lhs_ty, TypeInfo::RawUntypedPtr) {
errors.push(CompileError::IntrinsicUnsupportedArgType {
name: kind.to_string(),
span: lhs.span,
hint: Hint::empty(),
});
return err(warnings, errors);
}

let rhs = arguments[1].clone();
let ctx = ctx
.by_ref()
.with_help_text("Incorrect argument type")
.with_type_annotation(insert_type(TypeInfo::UnsignedInteger(
IntegerBits::SixtyFour,
)));
let rhs = check!(
ty::TyExpression::type_check(ctx, rhs),
return err(warnings, errors),
warnings,
errors
);

(
ty::TyIntrinsicFunctionKind {
kind,
arguments: vec![lhs, rhs],
type_arguments: vec![TypeArgument {
type_id,
initial_type_id,
span: targ.span,
}],
span,
},
insert_type(lhs_ty),
)
}
};
ok((intrinsic_function, return_type), warnings, errors)
}
Expand Down
27 changes: 12 additions & 15 deletions sway-lib-core/src/raw_ptr.sw
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,18 @@ library raw_ptr;
impl raw_ptr {
/// Returns `true` if the pointer is null.
pub fn is_null(self) -> bool {
let addr = asm(ptr: self) { ptr: u64 };
__eq(addr, 0)
let null_ptr = asm() { zero: raw_ptr };
__eq(self, null_ptr)
}

/// Calculates the offset from the pointer.
pub fn add(self, count: u64) -> raw_ptr {
let addr = asm(ptr: self) { ptr: u64 };
let addr = __add(addr, count);
asm(ptr: addr) { ptr: raw_ptr }
pub fn add<T>(self, count: u64) -> raw_ptr {
__ptr_add::<T>(self, count)
}

/// Calculates the offset from the pointer.
pub fn sub(self, count: u64) -> raw_ptr {
let addr = asm(ptr: self) { ptr: u64 };
let addr = __sub(addr, count);
asm(ptr: addr) { ptr: raw_ptr }
pub fn sub<T>(self, count: u64) -> raw_ptr {
__ptr_sub::<T>(self, count)
}

/// Reads the given type of value from the address.
Expand All @@ -32,11 +28,12 @@ impl raw_ptr {
}
}
}

/// Copies `size` bytes from `self` to `dst`.
pub fn copy_to(self, dst: raw_ptr, count: u64) {
asm(dst: dst, src: self, count: count) {
mcp dst src count;

/// Copies `count * size_of<T>` bytes from `self` to `dst`.
pub fn copy_to<T>(self, dst: raw_ptr, count: u64) {
let len = __mul(count, __size_of::<T>());
asm(dst: dst, src: self, len: len) {
mcp dst src len;
};
}

Expand Down
14 changes: 7 additions & 7 deletions sway-lib-std/src/alloc.sw
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ library alloc;
///
/// See: https://fuellabs.github.io/fuel-specs/master/vm#vm-initialization
/// See: https://fuellabs.github.io/fuel-specs/master/vm/instruction_set.html#aloc-allocate-memory
pub fn alloc(size: u64) -> raw_ptr {
asm(size: size, ptr) {
pub fn alloc<T>(count: u64) -> raw_ptr {
asm(size: __size_of::<T>() * count, ptr) {
aloc size;
// `$hp` points to unallocated space and heap grows downward so
// our newly allocated space will be right after it
Expand All @@ -32,11 +32,11 @@ pub fn alloc(size: u64) -> raw_ptr {
}

/// Reallocates the given area of memory
pub fn realloc(ptr: raw_ptr, size: u64, new_size: u64) -> raw_ptr {
if new_size > size {
let new_ptr = alloc(new_size);
if size > 0 {
ptr.copy_to(new_ptr, size);
pub fn realloc<T>(ptr: raw_ptr, count: u64, new_count: u64) -> raw_ptr {
if new_count > count {
let new_ptr = alloc::<T>(new_count);
if count > 0 {
ptr.copy_to::<T>(new_ptr, count);
}
new_ptr
} else {
Expand Down
24 changes: 12 additions & 12 deletions sway-lib-std/src/context/call_frames.sw
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ use ::intrinsics::is_reference_type;

// Note that everything when serialized is padded to word length.
//
// Call Frame : saved registers offset = 8*WORD_SIZE = 8*8 = 64
// Reserved Registers: previous frame pointer offset = 6*WORD_SIZE = 6*8 = 48
const SAVED_REGISTERS_OFFSET: u64 = 64;
const PREV_FRAME_POINTER_OFFSET: u64 = 48;
/// Where 584 is the current offset in bytes from the start of the call frame.
const FIRST_PARAMETER_OFFSET: u64 = 584;
/// Where 592 (584 + 8) is the current offset in bytes from the start of the call frame.
const SECOND_PARAMETER_OFFSET: u64 = 592;
// Call Frame : saved registers offset = 8
// Reserved Registers: previous frame pointer offset = 6
const SAVED_REGISTERS_OFFSET: u64 = 8;
const PREV_FRAME_POINTER_OFFSET: u64 = 6;
/// Where 73 is the current offset in words from the start of the call frame.
const FIRST_PARAMETER_OFFSET: u64 = 73;
/// Where 74 (73 + 1) is the current offset in words from the start of the call frame.
const SECOND_PARAMETER_OFFSET: u64 = 74;

///////////////////////////////////////////////////////////
// Accessing the current call frame
Expand Down Expand Up @@ -46,15 +46,15 @@ pub fn code_size() -> u64 {

/// Get the first parameter from the current call frame.
pub fn first_param() -> u64 {
frame_ptr().add(FIRST_PARAMETER_OFFSET).read()
frame_ptr().add::<u64>(FIRST_PARAMETER_OFFSET).read()
}

/// Get the second parameter from the current call frame.
pub fn second_param<T>() -> T {
if !is_reference_type::<T>() {
frame_ptr().add(SECOND_PARAMETER_OFFSET).read::<T>()
frame_ptr().add::<u64>(SECOND_PARAMETER_OFFSET).read::<T>()
} else {
frame_ptr().add(SECOND_PARAMETER_OFFSET).read::<raw_ptr>().read::<T>()
frame_ptr().add::<u64>(SECOND_PARAMETER_OFFSET).read::<raw_ptr>().read::<T>()
}
}

Expand All @@ -63,7 +63,7 @@ pub fn second_param<T>() -> T {
///////////////////////////////////////////////////////////
/// get a pointer to the previous (relative to the 'frame_pointer' param) call frame using offsets from a pointer.
pub fn get_previous_frame_pointer(frame_pointer: raw_ptr) -> raw_ptr {
let offset = frame_pointer.add(SAVED_REGISTERS_OFFSET + PREV_FRAME_POINTER_OFFSET);
let offset = frame_pointer.add::<u64>(SAVED_REGISTERS_OFFSET + PREV_FRAME_POINTER_OFFSET);
asm(res, ptr: offset) {
lw res ptr i0;
res: raw_ptr
Expand Down
10 changes: 5 additions & 5 deletions sway-lib-std/src/message.sw
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ pub fn send_message(recipient: b256, msg_data: Vec<u64>, coins: u64) {
// Otherwise, we allocate adjacent space on the heap for the data and the recipient and copy the
// data and recipient values there
if !msg_data.is_empty() {
size = msg_data.len() * 8;
recipient_heap_buffer = alloc(32 + size);
size = msg_data.len();
recipient_heap_buffer = alloc::<u64>(4 + size);
recipient_heap_buffer.write(recipient);
let data_heap_buffer = recipient_heap_buffer.add(32);
msg_data.buf.ptr.copy_to(data_heap_buffer, size);
let data_heap_buffer = recipient_heap_buffer.add::<b256>(1);
msg_data.buf.ptr.copy_to::<u64>(data_heap_buffer, size);
};

let mut index = 0;
Expand All @@ -34,7 +34,7 @@ pub fn send_message(recipient: b256, msg_data: Vec<u64>, coins: u64) {
while index < outputs {
let type_of_output = output_type(index);
if let Output::Message = type_of_output {
asm(r1: recipient_heap_buffer, r2: size, r3: index, r4: coins) {
asm(r1: recipient_heap_buffer, r2: size * 8, r3: index, r4: coins) {
smo r1 r2 r3 r4;
};
return;
Expand Down
6 changes: 3 additions & 3 deletions sway-lib-std/src/storage.sw
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pub fn store<T>(key: b256, value: T) {
__state_store_quad(local_key, ptr_to_value);

// Move by 32 bytes
ptr_to_value = ptr_to_value.add(32);
ptr_to_value = ptr_to_value.add::<b256>(1);
size_left -= 32;

// Generate a new key for each 32 byte chunk TODO Should eventually
Expand Down Expand Up @@ -59,7 +59,7 @@ pub fn get<T>(key: b256) -> T {

// Allocate a buffer for the result. It needs to be a multiple of 32 bytes so we can make
// 'quad' storage reads without overflowing.
let result_ptr = alloc((size_left + 31) & 0xffffffe0);
let result_ptr = alloc::<u64>(((size_left + 31) & 0xffffffe0) / 8);

let mut current_pointer = result_ptr;
while size_left > 32 {
Expand All @@ -68,7 +68,7 @@ pub fn get<T>(key: b256) -> T {

// Move by 32 bytes
size_left -= 32;
current_pointer += 32;
current_pointer = current_pointer.add::<b256>(1);

// Generate a new key for each 32 byte chunk TODO Should eventually
// replace this with `local_key = local_key + 1
Expand Down
Loading

0 comments on commit 81f7f57

Please sign in to comment.