Skip to content
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 intrinsics for operating on raw_ptrs #3157

Merged
merged 6 commits into from
Nov 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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