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

Intrinsics for the VM's storage opcodes. #2508

Merged
merged 24 commits into from
Aug 16, 2022
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
ecee49b
Add storage load/store word intrinsics
vaivaswatha Aug 9, 2022
4ab8013
Storage load/store quad intrinsics
vaivaswatha Aug 10, 2022
9863a44
Merge branch 'master' into vaivaswatha/storage_intrinsics
vaivaswatha Aug 10, 2022
2c0a816
Update hash of a test
vaivaswatha Aug 10, 2022
3aee84a
fix clippy
vaivaswatha Aug 10, 2022
26807c7
Process key_reg after val_reg to avoid diffs in asm gen tests
vaivaswatha Aug 10, 2022
799b981
Merge branch 'master' into vaivaswatha/storage_intrinsics
vaivaswatha Aug 10, 2022
e7f8168
Fix a deployment based test with right hash
vaivaswatha Aug 10, 2022
75e7a9c
Add asm gen test
vaivaswatha Aug 10, 2022
ca164d3
Merge branch 'master' into vaivaswatha/storage_intrinsics
vaivaswatha Aug 10, 2022
82a2e6e
Check arguments after monomorphization, during IR gen
vaivaswatha Aug 11, 2022
88e954d
Replace asm with intrinsics in storage.sw
vaivaswatha Aug 11, 2022
2c2015f
Restore storage.sw
vaivaswatha Aug 11, 2022
d1f11e2
minor fixes
vaivaswatha Aug 11, 2022
ba5e609
Update storage.sw and add exhaustive tests
vaivaswatha Aug 12, 2022
4d433e3
Merge branch 'master' into vaivaswatha/storage_intrinsics
vaivaswatha Aug 12, 2022
66fe14c
Update JSON ABI
vaivaswatha Aug 12, 2022
cad780b
Move cfei into an alloca lib function
vaivaswatha Aug 12, 2022
6db548d
Update contract hash in calling script
vaivaswatha Aug 12, 2022
a6a59d1
Revert "Move cfei into an alloca lib function"
vaivaswatha Aug 12, 2022
594a5b9
Revert "Update contract hash in calling script"
vaivaswatha Aug 12, 2022
fa82947
Merge remote-tracking branch 'origin/master' into vaivaswatha/storage…
vaivaswatha Aug 16, 2022
a27f9b3
Merge branch 'master' into vaivaswatha/storage_intrinsics
vaivaswatha Aug 16, 2022
80858ab
Merge branch 'master' into vaivaswatha/storage_intrinsics
sezna Aug 16, 2022
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
12 changes: 12 additions & 0 deletions sway-ast/src/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ pub enum Intrinsic {
Eq,
Gtf,
AddrOf,
StateLoadWord,
StateStoreWord,
StateLoadQuad,
StateStoreQuad,
}

impl fmt::Display for Intrinsic {
Expand All @@ -21,6 +25,10 @@ impl fmt::Display for Intrinsic {
Intrinsic::Eq => "eq",
Intrinsic::Gtf => "gtf",
Intrinsic::AddrOf => "addr_of",
Intrinsic::StateLoadWord => "state_load_word",
Intrinsic::StateStoreWord => "state_store_word",
Intrinsic::StateLoadQuad => "state_load_quad",
Intrinsic::StateStoreQuad => "state_store_quad",
};
write!(f, "{}", s)
}
Expand All @@ -37,6 +45,10 @@ impl Intrinsic {
"__eq" => Eq,
"__gtf" => Gtf,
"__addr_of" => AddrOf,
"__state_load_word" => StateLoadWord,
"__state_store_word" => StateStoreWord,
"__state_load_quad" => StateLoadQuad,
"__state_store_quad" => StateStoreQuad,
_ => return None,
})
}
Expand Down
66 changes: 39 additions & 27 deletions sway-core/src/asm_generation/from_ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1591,6 +1591,7 @@ impl<'ir> AsmBuilder<'ir> {
// Make sure that both val and key are pointers to B256.
assert!(matches!(val.get_type(self.context), Some(Type::B256)));
assert!(matches!(key.get_type(self.context), Some(Type::B256)));
let owning_span = self.md_mgr.val_to_span(self.context, *instr_val);

let key_ptr = self.resolve_ptr(key);
if key_ptr.value.is_none() {
Expand All @@ -1602,39 +1603,50 @@ impl<'ir> AsmBuilder<'ir> {
assert!(offset == 0);
assert!(ptr_ty.eq(self.context, &Type::B256));

// Expect ptr_ty here to also be b256 and offset to be whatever...
let val_ptr = self.resolve_ptr(val);
if val_ptr.value.is_none() {
return val_ptr.map(|_| ());
}
let (val_ptr, ptr_ty, offset) = val_ptr.value.unwrap();

// Expect the ptr_ty for val to also be B256
assert!(ptr_ty.eq(self.context, &Type::B256));
let val_reg = if matches!(
&self.context.values[val.0].value,
ValueDatum::Instruction(Instruction::IntToPtr(..))
Comment on lines +1613 to +1615
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than special case IntToPtr here would it work instead to update resolve_ptr() to handle both GetPointer and IntToPtr?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than special case IntToPtr here would it work instead to update resolve_ptr() to handle both GetPointer and IntToPtr?

I considered that, but there's some computation that's done later which isn't needed for IntToPtr (but needed if resolved as a stack pointer in resolve_ptr. So it wouldn't serve any purpose since I'll need to handle them differently here anyway.

) {
match self.reg_map.get(val) {
Some(vreg) => vreg.clone(),
None => unreachable!("int_to_ptr instruction doesn't have vreg mapped"),
}
} else {
// Expect ptr_ty here to also be b256 and offset to be whatever...
let val_ptr = self.resolve_ptr(val);
if val_ptr.value.is_none() {
return val_ptr.map(|_| ());
}
let (val_ptr, ptr_ty, offset) = val_ptr.value.unwrap();
// Expect the ptr_ty for val to also be B256
assert!(ptr_ty.eq(self.context, &Type::B256));
match self.ptr_map.get(&val_ptr) {
Some(Storage::Stack(val_offset)) => {
let base_reg = self.stack_base_reg.as_ref().unwrap().clone();
let val_offset_in_bytes = val_offset * 8 + offset * 32;
self.offset_reg(&base_reg, val_offset_in_bytes, owning_span.clone())
}
_ => unreachable!("Unexpected storage locations for key and val"),
}
};

let owning_span = self.md_mgr.val_to_span(self.context, *instr_val);
match (self.ptr_map.get(&val_ptr), self.ptr_map.get(&key_ptr)) {
(Some(Storage::Stack(val_offset)), Some(Storage::Stack(key_offset))) => {
let key_reg = match self.ptr_map.get(&key_ptr) {
Some(Storage::Stack(key_offset)) => {
let base_reg = self.stack_base_reg.as_ref().unwrap().clone();
let val_offset_in_bytes = val_offset * 8 + offset * 32;
let key_offset_in_bytes = key_offset * 8;

let val_reg = self.offset_reg(&base_reg, val_offset_in_bytes, owning_span.clone());

let key_reg = self.offset_reg(&base_reg, key_offset_in_bytes, owning_span.clone());

self.bytecode.push(Op {
opcode: Either::Left(match access_type {
StateAccessType::Read => VirtualOp::SRWQ(val_reg, key_reg),
StateAccessType::Write => VirtualOp::SWWQ(key_reg, val_reg),
}),
comment: "quad word state access".into(),
owning_span,
});
self.offset_reg(&base_reg, key_offset_in_bytes, owning_span.clone())
}
_ => unreachable!("Unexpected storage locations for key and val"),
}
};

self.bytecode.push(Op {
opcode: Either::Left(match access_type {
StateAccessType::Read => VirtualOp::SRWQ(val_reg, key_reg),
StateAccessType::Write => VirtualOp::SWWQ(key_reg, val_reg),
}),
comment: "quad word state access".into(),
owning_span,
});
ok((), Vec::new(), Vec::new())
}

Expand Down
110 changes: 108 additions & 2 deletions sway-core/src/ir_generation/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ use super::{
use crate::{
asm_generation::from_ir::ir_type_size_in_bytes,
constants,
error::CompileError,
error::{CompileError, Hint},
ir_generation::const_eval::{
compile_constant_expression, compile_constant_expression_to_constant,
},
metadata::MetadataManager,
parse_tree::{AsmOp, AsmRegister, LazyOp, Literal},
semantic_analysis::*,
type_system::{resolve_type, TypeId, TypeInfo},
type_system::{resolve_type, IntegerBits, TypeId, TypeInfo},
};
use sway_ast::intrinsics::Intrinsic;
use sway_ir::{Context, *};
Expand Down Expand Up @@ -389,6 +389,41 @@ impl FnCompiler {
}: TypedIntrinsicFunctionKind,
span: Span,
) -> Result<Value, CompileError> {
fn store_key_in_local_mem(
compiler: &mut FnCompiler,
context: &mut Context,
value: Value,
span_md_idx: Option<MetadataIndex>,
) -> Result<Value, CompileError> {
// New name for the key
let key_name = "key_for_storage".to_string();
let alias_key_name = compiler.lexical_map.insert(key_name.as_str().to_owned());

// Local pointer for the key
let key_ptr = compiler
.function
.new_local_ptr(context, alias_key_name, Type::B256, true, None)
.map_err(|ir_error| {
CompileError::InternalOwned(ir_error.to_string(), Span::dummy())
})?;

// Convert the key pointer to a value using get_ptr
let key_ptr_ty = *key_ptr.get_type(context);
let key_ptr_val = compiler
.current_block
.ins(context)
.get_ptr(key_ptr, key_ptr_ty, 0)
.add_metadatum(context, span_md_idx);

// Store the value to the key pointer value
compiler
.current_block
.ins(context)
.store(key_ptr_val, value)
.add_metadatum(context, span_md_idx);
Ok(key_ptr_val)
}

// We safely index into arguments and type_arguments arrays below
// because the type-checker ensures that the arguments are all there.
match kind {
Expand Down Expand Up @@ -495,6 +530,77 @@ impl FnCompiler {
.addr_of(value)
.add_metadatum(context, span_md_idx))
}
Intrinsic::StateLoadWord => {
let exp = arguments[0].clone();
let value = self.compile_expression(context, md_mgr, exp)?;
let span_md_idx = md_mgr.span_to_md(context, &span);
let key_ptr_val = store_key_in_local_mem(self, context, value, span_md_idx)?;
Ok(self
.current_block
.ins(context)
.state_load_word(key_ptr_val)
.add_metadatum(context, span_md_idx))
}
Intrinsic::StateStoreWord => {
let key_exp = arguments[0].clone();
let val_exp = arguments[1].clone();
// Validate that the val_exp is of the right type. We couldn't do it
// earlier during type checking as the type arguments may not have been resolved.
let val_ty = resolve_type(val_exp.return_type, &span).unwrap();
if !val_ty.is_copy_type() {
return Err(CompileError::IntrinsicUnsupportedArgType {
name: kind.to_string(),
span,
hint: Hint::new("This argument must be a copy type".to_string()),
});
}
let key_value = self.compile_expression(context, md_mgr, key_exp)?;
let val_value = self.compile_expression(context, md_mgr, val_exp)?;
let span_md_idx = md_mgr.span_to_md(context, &span);
let key_ptr_val = store_key_in_local_mem(self, context, key_value, span_md_idx)?;
Ok(self
.current_block
.ins(context)
.state_store_word(val_value, key_ptr_val)
.add_metadatum(context, span_md_idx))
}
Intrinsic::StateLoadQuad | Intrinsic::StateStoreQuad => {
let key_exp = arguments[0].clone();
let val_exp = arguments[1].clone();
// Validate that the val_exp is of the right type. We couldn't do it
// earlier during type checking as the type arguments may not have been resolved.
let val_ty = resolve_type(val_exp.return_type, &span).unwrap();
if val_ty != TypeInfo::UnsignedInteger(IntegerBits::SixtyFour) {
return Err(CompileError::IntrinsicUnsupportedArgType {
name: kind.to_string(),
span,
hint: Hint::new("This argument must be u64".to_string()),
});
}
let key_value = self.compile_expression(context, md_mgr, key_exp)?;
let val_value = self.compile_expression(context, md_mgr, val_exp)?;
let span_md_idx = md_mgr.span_to_md(context, &span);
let key_ptr_val = store_key_in_local_mem(self, context, key_value, span_md_idx)?;
// For quad word, the IR instructions take in a pointer rather than a raw u64.
let val_ptr = self
.current_block
.ins(context)
.int_to_ptr(val_value, Type::B256)
.add_metadatum(context, span_md_idx);
match kind {
Intrinsic::StateLoadQuad => Ok(self
.current_block
.ins(context)
.state_load_quad_word(val_ptr, key_ptr_val)
.add_metadatum(context, span_md_idx)),
Intrinsic::StateStoreQuad => Ok(self
.current_block
.ins(context)
.state_store_quad_word(val_ptr, key_ptr_val)
.add_metadatum(context, span_md_idx)),
_ => unreachable!(),
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,119 @@ impl TypedIntrinsicFunctionKind {
let return_type = insert_type(TypeInfo::UnsignedInteger(IntegerBits::SixtyFour));
(intrinsic_function, return_type)
}
Intrinsic::StateLoadWord => {
if arguments.len() != 1 {
errors.push(CompileError::IntrinsicIncorrectNumArgs {
name: kind.to_string(),
expected: 1,
span,
});
return err(warnings, errors);
}
let ctx = ctx
.with_help_text("")
.with_type_annotation(insert_type(TypeInfo::Unknown));
let exp = check!(
TypedExpression::type_check(ctx, arguments[0].clone()),
return err(warnings, errors),
warnings,
errors
);
let key_ty = resolve_type(exp.return_type, &span).unwrap();
if key_ty != TypeInfo::B256 {
errors.push(CompileError::IntrinsicUnsupportedArgType {
name: kind.to_string(),
span,
hint: Hint::new(
"Argument type must be B256, a key into the state storage".to_string(),
),
});
return err(warnings, errors);
}
let intrinsic_function = TypedIntrinsicFunctionKind {
kind,
arguments: vec![exp],
type_arguments: vec![],
span,
};
let return_type = insert_type(TypeInfo::UnsignedInteger(IntegerBits::SixtyFour));
(intrinsic_function, return_type)
}
Intrinsic::StateStoreWord | Intrinsic::StateLoadQuad | Intrinsic::StateStoreQuad => {
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 mut ctx = ctx
.with_help_text("")
.with_type_annotation(insert_type(TypeInfo::Unknown));
let key_exp = check!(
TypedExpression::type_check(ctx.by_ref(), arguments[0].clone()),
return err(warnings, errors),
warnings,
errors
);
let key_ty = resolve_type(key_exp.return_type, &span).unwrap();
if key_ty != TypeInfo::B256 {
errors.push(CompileError::IntrinsicUnsupportedArgType {
name: kind.to_string(),
span,
hint: Hint::new(
"Argument type must be B256, a key into the state storage".to_string(),
),
});
return err(warnings, errors);
}
let mut ctx = ctx
.with_help_text("")
.with_type_annotation(insert_type(TypeInfo::Unknown));
let val_exp = check!(
TypedExpression::type_check(ctx.by_ref(), arguments[1].clone()),
return err(warnings, errors),
warnings,
errors
);
let type_argument = type_arguments.get(0).map(|targ| {
let mut ctx = ctx
.with_help_text("")
.with_type_annotation(insert_type(TypeInfo::Unknown));
let type_id = check!(
ctx.resolve_type_with_self(
insert_type(resolve_type(targ.type_id, &targ.span).unwrap()),
&targ.span,
EnforceTypeArguments::Yes,
None
),
insert_type(TypeInfo::ErrorRecovery),
warnings,
errors,
);
TypeArgument {
type_id,
span: span.clone(),
}
});
let intrinsic_function = TypedIntrinsicFunctionKind {
kind,
arguments: vec![key_exp, val_exp],
type_arguments: type_argument.map_or(vec![], |ta| vec![ta]),
span,
};
let return_type = insert_type(TypeInfo::Tuple(vec![]));
(intrinsic_function, return_type)
}
};
ok((intrinsic_function, return_type), warnings, errors)
}
Expand Down
Loading