use super::{
convert::*,
lexical_map::LexicalMap,
storage::{add_to_b256, get_storage_field_path_and_field_id, get_storage_key},
types::*,
CompiledFunctionCache,
};
use crate::{
decl_engine::DeclEngineGet as _,
engine_threading::*,
ir_generation::{
const_eval::{compile_constant_expression, compile_constant_expression_to_constant},
KeyedTyFunctionDecl, PanickingFunctionCache,
},
language::{
ty::{
self, ProjectionKind, TyConfigurableDecl, TyConstantDecl, TyExpression,
TyExpressionVariant, TyFunctionDisplay, TyStorageField,
},
*,
},
metadata::MetadataManager,
type_system::*,
types::*,
PanicOccurrence, PanicOccurrences, PanickingCallOccurrence, PanickingCallOccurrences,
};
use indexmap::IndexMap;
use itertools::Itertools;
use std::convert::TryFrom;
use std::{
collections::HashMap,
hash::{DefaultHasher, Hash as _},
};
use sway_ast::intrinsics::Intrinsic;
use sway_error::error::CompileError;
use sway_ir::{Context, *};
use sway_types::{
constants,
ident::Ident,
integer_bits::IntegerBits,
span::{Span, Spanned},
u256::U256,
Named,
};
#[derive(Debug, Clone, Copy)]
enum CompiledValue {
InMemory(Value),
InRegister(Value),
}
impl CompiledValue {
fn value(&self) -> Value {
match self {
CompiledValue::InMemory(value) | CompiledValue::InRegister(value) => *value,
}
}
fn is_terminator(&self, context: &Context) -> bool {
self.value().is_terminator(context)
}
fn get_type(&self, context: &Context) -> Option<Type> {
self.value().get_type(context)
}
fn expect_memory(self) -> Value {
match self {
CompiledValue::InMemory(value) => value,
CompiledValue::InRegister(_) => panic!("Expected InMemory, got InRegister"),
}
}
fn expect_register(self) -> Value {
match self {
CompiledValue::InMemory(_) => panic!("Expected InRegister, got InMemory"),
CompiledValue::InRegister(value) => value,
}
}
}
struct TerminatorValue {
value: CompiledValue,
is_terminator: bool,
}
impl TerminatorValue {
pub fn new(value: CompiledValue, context: &Context) -> Self {
Self {
value,
is_terminator: value.is_terminator(context),
}
}
}
macro_rules! return_on_termination_or_extract {
($value:expr) => {{
let val = $value;
if val.is_terminator {
return Ok(val);
};
val.value
}};
}
pub(crate) struct FnCompiler<'a> {
engines: &'a Engines,
module: Module,
pub(super) function: Function,
pub(super) current_block: Block,
block_to_break_to: Option<Block>,
block_to_continue_to: Option<Block>,
current_fn_param: Option<ty::TyFunctionParameter>,
lexical_map: LexicalMap,
pub ref_mut_args: rustc_hash::FxHashSet<String>,
compiled_fn_cache: &'a mut CompiledFunctionCache,
logged_types_map: &'a HashMap<TypeId, LogId>,
messages_types_map: &'a HashMap<TypeId, MessageId>,
panic_occurrences: &'a mut PanicOccurrences,
panicking_call_occurrences: &'a mut PanickingCallOccurrences,
panicking_fn_cache: &'a mut PanickingFunctionCache,
}
fn store_to_memory(
s: &mut FnCompiler<'_>,
context: &mut Context,
value: CompiledValue,
) -> Result<CompiledValue, CompileError> {
match value {
CompiledValue::InMemory(_) => Ok(value),
CompiledValue::InRegister(val) => {
let temp_arg_name = s.lexical_map.insert_anon();
let value_type = val.get_type(context).unwrap();
let local_var = s
.function
.new_local_var(context, temp_arg_name, value_type, None, false)
.map_err(|ir_error| {
CompileError::InternalOwned(ir_error.to_string(), Span::dummy())
})?;
let local_var_ptr = s.current_block.append(context).get_local(local_var);
let _ = s.current_block.append(context).store(local_var_ptr, val);
Ok(CompiledValue::InMemory(local_var_ptr))
}
}
}
fn load_to_register(
s: &mut FnCompiler<'_>,
context: &mut Context,
value: CompiledValue,
) -> CompiledValue {
match value {
CompiledValue::InMemory(ptr) => {
let val = s.current_block.append(context).load(ptr);
CompiledValue::InRegister(val)
}
CompiledValue::InRegister(_) => value,
}
}
fn calc_addr_as_ptr(
current_block: &mut Block,
context: &mut Context,
ptr: Value,
len: Value,
ptr_to: Type,
) -> Value {
assert!(ptr.get_type(context).unwrap().is_ptr(context));
assert!(len.get_type(context).unwrap().is_uint64(context));
let addr = current_block
.append(context)
.binary_op(BinaryOpKind::Add, ptr, len);
let ptr_to = Type::new_typed_pointer(context, ptr_to);
current_block
.append(context)
.cast_ptr(addr, ptr_to)
.add_metadatum(context, None)
}
impl<'a> FnCompiler<'a> {
pub(super) const BACKTRACE_FN_ARG_NAME: &'static str = "__backtrace";
const MAX_PANIC_ERROR_CODE: u64 = 255; const MAX_PANICKING_CALL_ID: u64 = 2047; const FN_DISPLAY_FOR_ABI_ERRORS: TyFunctionDisplay =
TyFunctionDisplay::full().without_signature();
#[allow(clippy::too_many_arguments)]
pub(super) fn new(
engines: &'a Engines,
context: &mut Context,
module: Module,
function: Function,
logged_types_map: &'a HashMap<TypeId, LogId>,
messages_types_map: &'a HashMap<TypeId, MessageId>,
panic_occurrences: &'a mut PanicOccurrences,
panicking_call_occurrences: &'a mut PanickingCallOccurrences,
panicking_fn_cache: &'a mut PanickingFunctionCache,
compiled_fn_cache: &'a mut CompiledFunctionCache,
) -> Self {
let lexical_map = LexicalMap::from_iter(
function
.args_iter(context)
.map(|(name, _value)| name.clone()),
);
FnCompiler {
engines,
module,
function,
current_block: function.get_entry_block(context),
block_to_break_to: None,
block_to_continue_to: None,
lexical_map,
ref_mut_args: rustc_hash::FxHashSet::default(),
compiled_fn_cache,
current_fn_param: None,
logged_types_map,
messages_types_map,
panic_occurrences,
panicking_call_occurrences,
panicking_fn_cache,
}
}
pub(super) fn fn_abi_errors_display(fn_decl: &ty::TyFunctionDecl, engines: &Engines) -> String {
Self::FN_DISPLAY_FOR_ABI_ERRORS.display(fn_decl, engines)
}
fn compile_with_new_scope<F, T, R>(&mut self, inner: F) -> Result<T, R>
where
F: FnOnce(&mut FnCompiler) -> Result<T, R>,
{
self.lexical_map.enter_scope();
let result = inner(self);
self.lexical_map.leave_scope();
result
}
pub(super) fn compile_fn_to_value(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
ast_block: &ty::TyCodeBlock,
) -> Result<Value, Vec<CompileError>> {
let entry = self.function.get_entry_block(context);
for (arg_name, arg_value) in self
.function
.args_iter(context)
.cloned()
.collect::<Vec<_>>()
{
let local_name = self.lexical_map.insert(arg_name.as_str().to_owned());
let local_var = self.function.new_unique_local_var(
context,
local_name.clone(),
arg_value.get_type(context).unwrap(),
None,
false,
);
if self.ref_mut_args.contains(&arg_name) {
self.ref_mut_args.insert(local_name);
}
let local_val = entry.append(context).get_local(local_var);
entry.append(context).store(local_val, arg_value);
}
match self.compile_code_block(context, md_mgr, ast_block)?.value {
CompiledValue::InRegister(val) => Ok(val),
CompiledValue::InMemory(_val) => {
Err(vec![CompileError::Internal(
"Final value is in memory",
ast_block.whole_block_span.clone(),
)])
}
}
}
fn compile_code_block(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
ast_block: &ty::TyCodeBlock,
) -> Result<TerminatorValue, Vec<CompileError>> {
self.compile_with_new_scope(|fn_compiler| {
let mut errors = vec![];
let mut ast_nodes = ast_block.contents.iter();
let v = loop {
let ast_node = match ast_nodes.next() {
Some(ast_node) => ast_node,
None => {
break TerminatorValue::new(
CompiledValue::InRegister(ConstantContent::get_unit(context)),
context,
)
}
};
match fn_compiler.compile_ast_node(context, md_mgr, ast_node) {
Ok(Some(val)) => break val,
Ok(None) => (),
Err(e) => {
errors.push(e);
}
}
};
if !errors.is_empty() {
Err(errors)
} else {
Ok(v)
}
})
}
fn compile_ast_node(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
ast_node: &ty::TyAstNode,
) -> Result<Option<TerminatorValue>, CompileError> {
let unexpected_decl = |decl_type: &'static str| {
Err(CompileError::UnexpectedDeclaration {
decl_type,
span: ast_node.span.clone(),
})
};
let span_md_idx = md_mgr.span_to_md(context, &ast_node.span);
match &ast_node.content {
ty::TyAstNodeContent::Declaration(td) => match td {
ty::TyDecl::VariableDecl(tvd) => {
self.compile_var_decl(context, md_mgr, tvd, span_md_idx)
}
ty::TyDecl::ConstantDecl(ty::ConstantDecl { decl_id, .. }) => {
let tcd = self.engines.de().get_constant(decl_id);
self.compile_const_decl(context, md_mgr, &tcd, span_md_idx, false)?;
Ok(None)
}
ty::TyDecl::ConfigurableDecl(ty::ConfigurableDecl { .. }) => {
unreachable!()
}
ty::TyDecl::ConstGenericDecl(_) => {
unreachable!("ConstGenericDecl is not reachable from AstNode")
}
ty::TyDecl::EnumDecl(ty::EnumDecl { decl_id, .. }) => {
let ted = self.engines.de().get_enum(decl_id);
create_tagged_union_type(
self.engines,
context,
md_mgr,
self.module,
&ted.variants,
)
.map(|_| ())?;
Ok(None)
}
ty::TyDecl::TypeAliasDecl { .. } => unexpected_decl("type alias"),
ty::TyDecl::ImplSelfOrTrait { .. } => {
Ok(None)
}
ty::TyDecl::FunctionDecl { .. } => unexpected_decl("function"),
ty::TyDecl::TraitDecl { .. } => unexpected_decl("trait"),
ty::TyDecl::StructDecl { .. } => unexpected_decl("struct"),
ty::TyDecl::AbiDecl { .. } => unexpected_decl("abi"),
ty::TyDecl::GenericTypeForFunctionScope { .. } => unexpected_decl("generic type"),
ty::TyDecl::ErrorRecovery { .. } => unexpected_decl("error recovery"),
ty::TyDecl::StorageDecl { .. } => unexpected_decl("storage"),
ty::TyDecl::EnumVariantDecl { .. } => unexpected_decl("enum variant"),
ty::TyDecl::TraitTypeDecl { .. } => unexpected_decl("trait type"),
},
ty::TyAstNodeContent::Expression(te) => {
match &te.expression {
TyExpressionVariant::ImplicitReturn(exp) => self
.compile_expression_to_register(context, md_mgr, exp)
.map(Some),
_ => {
let value = self.compile_expression_to_register(context, md_mgr, te)?;
if value.is_terminator {
Ok(Some(value))
} else {
Ok(None)
}
}
}
}
ty::TyAstNodeContent::SideEffect(_) => Ok(None),
ty::TyAstNodeContent::Error(_, _) => {
unreachable!("error node found when generating IR");
}
}
}
fn compile_expression_to_register(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
ast_expr: &ty::TyExpression,
) -> Result<TerminatorValue, CompileError> {
let compiled =
return_on_termination_or_extract!(self.compile_expression(context, md_mgr, ast_expr)?);
Ok(TerminatorValue::new(
load_to_register(self, context, compiled),
context,
))
}
fn compile_expression_to_memory(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
ast_expr: &ty::TyExpression,
) -> Result<TerminatorValue, CompileError> {
let val =
return_on_termination_or_extract!(self.compile_expression(context, md_mgr, ast_expr)?);
Ok(TerminatorValue::new(
store_to_memory(self, context, val)?,
context,
))
}
fn compile_slice(
&mut self,
context: &mut Context,
span_md_idx: Option<MetadataIndex>,
slice_ptr: Value,
slice_len: u64,
) -> Result<TerminatorValue, CompileError> {
let int_ty = Type::get_uint64(context);
let ptr_ty = Type::get_ptr(context);
let ptr_val = self
.current_block
.append(context)
.cast_ptr(slice_ptr, ptr_ty)
.add_metadatum(context, span_md_idx);
let len_val = ConstantContent::get_uint(context, 64, slice_len);
let field_types = vec![ptr_ty, int_ty];
let struct_type = Type::new_struct(context, field_types.clone());
let struct_var = self
.function
.new_local_var(
context,
self.lexical_map.insert_anon(),
struct_type,
None,
false,
)
.map_err(|ir_error| CompileError::InternalOwned(ir_error.to_string(), Span::dummy()))?;
let struct_val = self
.current_block
.append(context)
.get_local(struct_var)
.add_metadatum(context, span_md_idx);
[ptr_val, len_val]
.into_iter()
.zip(field_types)
.enumerate()
.for_each(|(insert_idx, (insert_val, field_type))| {
let gep_val = self.current_block.append(context).get_elem_ptr_with_idx(
struct_val,
field_type,
insert_idx as u64,
);
self.current_block
.append(context)
.store(gep_val, insert_val)
.add_metadatum(context, span_md_idx);
});
let slice_type = Type::get_slice(context);
let slice_var = self
.function
.new_local_var(
context,
self.lexical_map.insert_anon(),
slice_type,
None,
false,
)
.map_err(|ir_error| CompileError::InternalOwned(ir_error.to_string(), Span::dummy()))?;
let slice_val = self
.current_block
.append(context)
.get_local(slice_var)
.add_metadatum(context, span_md_idx);
self.current_block
.append(context)
.mem_copy_bytes(slice_val, struct_val, 16);
Ok(TerminatorValue::new(
CompiledValue::InMemory(slice_val),
context,
))
}
fn compile_expression(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
ast_expr: &ty::TyExpression,
) -> Result<TerminatorValue, CompileError> {
let span_md_idx = md_mgr.span_to_md(context, &ast_expr.span);
match &ast_expr.expression {
ty::TyExpressionVariant::Literal(Literal::String(s)) => {
let string_data =
ConstantContent::get_string(context, s.as_str().as_bytes().to_vec())
.get_constant(context)
.unwrap();
let string_data_ptr = self.module.new_unique_global_var(
context,
"__const_global".into(),
string_data.get_content(context).ty,
Some(*string_data),
false,
);
let string_ptr = self
.current_block
.append(context)
.get_global(string_data_ptr);
let string_len = s.as_str().len() as u64;
self.compile_slice(context, span_md_idx, string_ptr, string_len)
}
ty::TyExpressionVariant::Literal(Literal::Binary(bytes)) => {
let data = ConstantContent::get_untyped_slice(context, bytes.clone())
.get_constant(context)
.unwrap();
let data_ptr = self.module.new_unique_global_var(
context,
"__const_global".into(),
data.get_content(context).ty,
Some(*data),
false,
);
let slice_ptr = self.current_block.append(context).get_global(data_ptr);
let slice_len = bytes.len() as u64;
self.compile_slice(context, span_md_idx, slice_ptr, slice_len)
}
ty::TyExpressionVariant::Literal(Literal::Numeric(n)) => {
let implied_lit = match &*self.engines.te().get(ast_expr.return_type) {
TypeInfo::UnsignedInteger(IntegerBits::Eight) => Literal::U8(*n as u8),
TypeInfo::UnsignedInteger(IntegerBits::V256) => Literal::U256(U256::from(*n)),
_ =>
{
Literal::U64(*n)
}
};
let val = convert_literal_to_value(context, &implied_lit)
.add_metadatum(context, span_md_idx);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
ty::TyExpressionVariant::Literal(l) => {
let val = convert_literal_to_value(context, l).add_metadatum(context, span_md_idx);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
ty::TyExpressionVariant::FunctionApplication {
call_path: name,
contract_call_params,
arguments,
fn_ref,
selector,
..
} => {
if let Some(metadata) = selector {
self.compile_contract_call_encoding_v0(
context,
md_mgr,
metadata,
contract_call_params,
name.suffix.as_str(),
arguments,
ast_expr.return_type,
span_md_idx,
)
} else {
let function_decl = self.engines.de().get_function(fn_ref);
self.compile_fn_call(
context,
md_mgr,
arguments,
&function_decl,
span_md_idx,
name,
)
}
}
ty::TyExpressionVariant::LazyOperator { op, lhs, rhs } => {
self.compile_lazy_op(context, md_mgr, op, lhs, rhs, span_md_idx)
}
ty::TyExpressionVariant::ConstantExpression {
decl: const_decl, ..
} => self.compile_const_expr(context, md_mgr, const_decl, span_md_idx),
ty::TyExpressionVariant::ConfigurableExpression {
decl: const_decl, ..
} => self.compile_config_expr(context, const_decl, span_md_idx),
ty::TyExpressionVariant::ConstGenericExpression { decl, span, .. } => {
if let Some(value) = decl.value.as_ref() {
self.compile_expression(context, md_mgr, value)
} else {
Err(CompileError::Internal(
"Const generic not materialized",
span.clone(),
))
}
}
ty::TyExpressionVariant::VariableExpression {
name, call_path, ..
} => self.compile_var_expr(context, call_path, name, span_md_idx),
ty::TyExpressionVariant::ArrayExplicit {
elem_type,
contents,
} => {
self.compile_array_explicit_expr(context, md_mgr, *elem_type, contents, span_md_idx)
}
ty::TyExpressionVariant::ArrayRepeat {
elem_type,
value,
length,
} => self.compile_array_repeat_expr(
context,
md_mgr,
*elem_type,
value,
length,
span_md_idx,
),
ty::TyExpressionVariant::ArrayIndex { prefix, index } => {
self.compile_indexing(context, md_mgr, prefix, index, span_md_idx)
}
ty::TyExpressionVariant::StructExpression { fields, .. } => {
self.compile_struct_expr(context, md_mgr, ast_expr, fields, span_md_idx)
}
ty::TyExpressionVariant::CodeBlock(cb) => {
self.compile_code_block(context, md_mgr, cb)
.map_err(|mut x| x.pop().unwrap())
}
ty::TyExpressionVariant::FunctionParameter => Err(CompileError::Internal(
"Unexpected function parameter declaration.",
ast_expr.span.clone(),
)),
ty::TyExpressionVariant::MatchExp { desugared, .. } => {
self.compile_expression_to_register(context, md_mgr, desugared)
}
ty::TyExpressionVariant::IfExp {
condition,
then,
r#else,
} => self.compile_if(
context,
md_mgr,
condition,
then,
r#else.as_deref(),
ast_expr.return_type,
),
ty::TyExpressionVariant::AsmExpression {
registers,
body,
returns,
whole_block_span,
} => {
let span_md_idx = md_mgr.span_to_md(context, whole_block_span);
self.compile_asm_expr(
context,
md_mgr,
registers,
body,
ast_expr.return_type,
returns.as_ref(),
span_md_idx,
)
}
ty::TyExpressionVariant::StructFieldAccess {
prefix,
field_to_access,
resolved_type_of_parent,
..
} => {
let span_md_idx = md_mgr.span_to_md(context, &field_to_access.span);
self.compile_struct_field_expr(
context,
md_mgr,
prefix,
*resolved_type_of_parent,
field_to_access,
span_md_idx,
)
}
ty::TyExpressionVariant::EnumInstantiation {
enum_ref,
tag,
contents,
..
} => {
let enum_decl = self.engines.de().get_enum(enum_ref);
self.compile_enum_expr(context, md_mgr, &enum_decl, *tag, contents.as_deref())
}
ty::TyExpressionVariant::Tuple { fields } => {
self.compile_tuple_expr(context, md_mgr, ast_expr, fields, span_md_idx)
}
ty::TyExpressionVariant::TupleElemAccess {
prefix,
elem_to_access_num: idx,
elem_to_access_span: span,
resolved_type_of_parent: tuple_type,
} => self.compile_tuple_elem_expr(
context,
md_mgr,
prefix,
*tuple_type,
*idx,
span.clone(),
),
ty::TyExpressionVariant::AbiCast { span, .. } => {
let span_md_idx = md_mgr.span_to_md(context, span);
let val = ConstantContent::get_unit(context).add_metadatum(context, span_md_idx);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
ty::TyExpressionVariant::StorageAccess(access) => {
let span_md_idx: Option<MetadataIndex> = md_mgr.span_to_md(context, &access.span());
let key = TyStorageField::get_key_expression_const(
&access.key_expression.clone().map(|v| *v),
self.engines,
context,
md_mgr,
self.module,
)?;
self.compile_storage_access(
context,
md_mgr,
access.storage_field_path.clone(),
access.struct_field_names.clone(),
key,
&access.fields,
span_md_idx,
)
}
ty::TyExpressionVariant::IntrinsicFunction(kind) => self.compile_intrinsic_function(
context,
md_mgr,
kind,
ast_expr.span.clone(),
ast_expr.return_type,
),
ty::TyExpressionVariant::AbiName(_) => {
let val = ConstantContent::get_unit(context);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
ty::TyExpressionVariant::UnsafeDowncast {
exp,
variant,
call_path_decl: _,
} => self.compile_unsafe_downcast(context, md_mgr, exp, variant),
ty::TyExpressionVariant::EnumTag { exp } => {
self.compile_enum_tag(context, md_mgr, exp.to_owned())
}
ty::TyExpressionVariant::WhileLoop { body, condition } => {
self.compile_while_loop(context, md_mgr, body, condition, span_md_idx)
}
ty::TyExpressionVariant::ForLoop { desugared } => {
self.compile_expression(context, md_mgr, desugared)
}
ty::TyExpressionVariant::Break => {
match self.block_to_break_to {
Some(block_to_break_to) => {
let val = self
.current_block
.append(context)
.branch(block_to_break_to, vec![]);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
None => Err(CompileError::BreakOutsideLoop {
span: ast_expr.span.clone(),
}),
}
}
ty::TyExpressionVariant::Continue => match self.block_to_continue_to {
Some(block_to_continue_to) => {
let val = self
.current_block
.append(context)
.branch(block_to_continue_to, vec![]);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
None => Err(CompileError::ContinueOutsideLoop {
span: ast_expr.span.clone(),
}),
},
ty::TyExpressionVariant::Reassignment(reassignment) => {
self.compile_reassignment(context, md_mgr, reassignment, span_md_idx)
}
ty::TyExpressionVariant::ImplicitReturn(_exp) => {
unreachable!();
}
ty::TyExpressionVariant::Return(exp) => {
self.compile_return(context, md_mgr, exp, span_md_idx)
}
ty::TyExpressionVariant::Panic(exp) => {
self.compile_panic(context, md_mgr, exp, span_md_idx)
}
ty::TyExpressionVariant::Ref(exp) => {
self.compile_ref(context, md_mgr, exp, span_md_idx)
}
ty::TyExpressionVariant::Deref(exp) => {
self.compile_deref(context, md_mgr, exp, span_md_idx)
}
}
}
fn compile_to_encode_buffer(
&mut self,
context: &mut Context,
ptr: Value,
cap: Value,
len: Value,
) -> Result<CompiledValue, CompileError> {
let uint64 = Type::get_uint64(context);
let ptr_ty = Type::get_ptr(context);
assert!(ptr.get_type(context).unwrap().is_ptr(context));
assert!(cap.get_type(context).unwrap().is_uint64(context));
assert!(len.get_type(context).unwrap().is_uint64(context));
let init = self.compile_tuple_from_values(
context,
vec![ptr, cap, len],
vec![ptr_ty, uint64, uint64],
None,
)?;
let return_type = Type::new_struct(context, vec![ptr_ty, uint64, uint64]);
let buffer = self.current_block.append(context).asm_block(
vec![AsmArg {
name: Ident::new_no_span("buffer".into()),
initializer: Some(init),
}],
vec![],
return_type,
Some(Ident::new_no_span("buffer".into())),
);
let buffer_type = buffer.get_type(context).unwrap();
assert!(buffer_type
.get_field_type(context, 0)
.unwrap()
.is_ptr(context));
assert!(buffer_type
.get_field_type(context, 1)
.unwrap()
.is_uint64(context));
assert!(buffer_type
.get_field_type(context, 2)
.unwrap()
.is_uint64(context));
assert!(buffer_type.get_field_type(context, 3).is_none());
Ok(CompiledValue::InRegister(buffer))
}
fn compile_buffer_into_parts(
&mut self,
context: &mut Context,
buffer: Value,
) -> Result<(Value, Value, Value), CompileError> {
let uint64 = Type::get_uint64(context);
let ptr_ty = Type::get_ptr(context);
let buffer_type = buffer.get_type(context).unwrap();
assert!(buffer_type
.get_field_type(context, 0)
.unwrap()
.is_ptr(context));
assert!(buffer_type
.get_field_type(context, 1)
.unwrap()
.is_uint64(context));
assert!(buffer_type
.get_field_type(context, 2)
.unwrap()
.is_uint64(context));
assert!(buffer_type.get_field_type(context, 3).is_none());
let return_type = Type::new_struct(context, vec![ptr_ty, uint64, uint64]);
let buffer = self.current_block.append(context).asm_block(
vec![AsmArg {
name: Ident::new_no_span("buffer".into()),
initializer: Some(buffer),
}],
vec![],
return_type,
Some(Ident::new_no_span("buffer".into())),
);
let name = self.lexical_map.insert_anon();
let buffer_local = self
.function
.new_local_var(context, name, return_type, None, false)
.map_err(|ir_error| CompileError::InternalOwned(ir_error.to_string(), Span::dummy()))?;
let buffer_local_value = self.current_block.append(context).get_local(buffer_local);
self.current_block
.append(context)
.store(buffer_local_value, buffer);
let ptr =
self.current_block
.append(context)
.get_elem_ptr_with_idx(buffer_local_value, ptr_ty, 0);
let ptr = self.current_block.append(context).load(ptr);
let cap =
self.current_block
.append(context)
.get_elem_ptr_with_idx(buffer_local_value, uint64, 1);
let cap = self.current_block.append(context).load(cap);
let len =
self.current_block
.append(context)
.get_elem_ptr_with_idx(buffer_local_value, uint64, 2);
let len = self.current_block.append(context).load(len);
assert!(ptr.get_type(context).unwrap().is_ptr(context));
assert!(cap.get_type(context).unwrap().is_uint64(context));
assert!(len.get_type(context).unwrap().is_uint64(context));
Ok((ptr, cap, len))
}
fn compile_intrinsic_function(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
ty::TyIntrinsicFunctionKind {
kind,
arguments,
type_arguments,
span: _,
}: &ty::TyIntrinsicFunctionKind,
span: Span,
return_type: TypeId,
) -> Result<TerminatorValue, CompileError> {
let engines = self.engines;
match kind {
Intrinsic::SizeOfVal => {
let exp = &arguments[0];
let ir_type = convert_resolved_type_id(
self.engines,
context,
md_mgr,
self.module,
Some(self),
exp.return_type,
&exp.span,
)?;
self.compile_expression_to_register(context, md_mgr, exp)?;
let val = ConstantContent::get_uint(context, 64, ir_type.size(context).in_bytes());
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
Intrinsic::SizeOfType => {
let targ = type_arguments[0].clone();
let ir_type = convert_resolved_type_id(
self.engines,
context,
md_mgr,
self.module,
Some(self),
targ.type_id(),
&targ.span(),
)?;
let val = ConstantContent::get_uint(context, 64, ir_type.size(context).in_bytes());
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
Intrinsic::SizeOfStr => {
let targ = type_arguments[0].clone();
let ir_type = convert_resolved_type_id(
self.engines,
context,
md_mgr,
self.module,
Some(self),
targ.type_id(),
&targ.span(),
)?;
let val = ConstantContent::get_uint(
context,
64,
ir_type.get_string_len(context).unwrap_or_default(),
);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
Intrinsic::IsReferenceType => {
let targ = type_arguments[0].clone();
let is_val = !engines.te().get_unaliased(targ.type_id()).is_copy_type();
let val = ConstantContent::get_bool(context, is_val);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
Intrinsic::IsStrArray => {
let targ = type_arguments[0].clone();
let is_val = matches!(
&*engines.te().get_unaliased(targ.type_id()),
TypeInfo::StringArray(_) | TypeInfo::StringSlice
);
let val = ConstantContent::get_bool(context, is_val);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
Intrinsic::AssertIsStrArray => {
let targ = type_arguments[0].clone();
let ir_type = convert_resolved_type_id(
self.engines,
context,
md_mgr,
self.module,
Some(self),
targ.type_id(),
&targ.span(),
)?;
match ir_type.get_content(context) {
TypeContent::StringSlice | TypeContent::StringArray(_) => {
let val = ConstantContent::get_unit(context);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
_ => Err(CompileError::NonStrGenericType { span: targ.span() }),
}
}
Intrinsic::ToStrArray => match arguments[0].expression.extract_literal_value() {
Some(Literal::String(span)) => {
let val =
ConstantContent::get_string(context, span.as_str().as_bytes().to_vec());
Ok(TerminatorValue::new(
store_to_memory(self, context, CompiledValue::InRegister(val))?,
context,
))
}
_ => unreachable!(),
},
Intrinsic::Eq | Intrinsic::Gt | Intrinsic::Lt => {
let lhs = &arguments[0];
let rhs = &arguments[1];
let lhs_value = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, lhs)?
)
.expect_register();
let rhs_value = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, rhs)?
)
.expect_register();
let pred = match kind {
Intrinsic::Eq => Predicate::Equal,
Intrinsic::Gt => Predicate::GreaterThan,
Intrinsic::Lt => Predicate::LessThan,
_ => unreachable!(),
};
let val = self
.current_block
.append(context)
.cmp(pred, lhs_value, rhs_value);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
Intrinsic::Gtf => {
let index = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, &arguments[0])?
)
.expect_register();
let Ok(tx_field_id_const) = compile_constant_expression_to_constant(
engines,
context,
md_mgr,
self.module,
None,
None,
&arguments[1],
) else {
return Err(CompileError::IntrinsicArgNotConstant {
intrinsic: kind.to_string(),
arg: "tx_field_id".to_string(),
expected_type: "u64".to_string(),
span: arguments[1].span.clone(),
});
};
let tx_field_id = match tx_field_id_const.get_content(context).value {
ConstantValue::Uint(n) => n,
_ => {
return Err(CompileError::Internal(
"Transaction field ID for \"__gtf\" intrinsic is not an integer. \
This should have been caught in type checking.",
span,
))
}
};
let target_type = &type_arguments[0];
let target_ir_type = convert_resolved_type_id(
self.engines,
context,
md_mgr,
self.module,
Some(self),
target_type.type_id(),
&target_type.span(),
)?;
let span_md_idx = md_mgr.span_to_md(context, &span);
let gtf_reg = self
.current_block
.append(context)
.gtf(index, tx_field_id)
.add_metadatum(context, span_md_idx);
if engines
.te()
.get_unaliased(target_type.type_id())
.is_copy_type()
{
let val = self
.current_block
.append(context)
.bitcast(gtf_reg, target_ir_type)
.add_metadatum(context, span_md_idx);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
} else {
let ptr_ty = Type::new_typed_pointer(context, target_ir_type);
let val = self
.current_block
.append(context)
.int_to_ptr(gtf_reg, ptr_ty)
.add_metadatum(context, span_md_idx);
Ok(TerminatorValue::new(CompiledValue::InMemory(val), context))
}
}
Intrinsic::AddrOf => {
let exp = &arguments[0];
let value = return_on_termination_or_extract!(
self.compile_expression_to_memory(context, md_mgr, exp)?
)
.expect_memory();
let ptr_ty = Type::get_ptr(context);
let span_md_idx = md_mgr.span_to_md(context, &span);
let val = self
.current_block
.append(context)
.cast_ptr(value, ptr_ty)
.add_metadatum(context, span_md_idx);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
Intrinsic::StateClear | Intrinsic::StateClearSlots => {
let key_exp = arguments[0].clone();
let number_of_slots_exp = arguments[1].clone();
let span_md_idx = md_mgr.span_to_md(context, &span);
let key_value = return_on_termination_or_extract!(
self.compile_expression_to_memory(context, md_mgr, &key_exp)?
)
.expect_memory()
.add_metadatum(context, span_md_idx);
let number_of_slots_value = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, &number_of_slots_exp)?
)
.expect_register()
.add_metadatum(context, span_md_idx);
let val = match kind {
Intrinsic::StateClear => self
.current_block
.append(context)
.state_clear(key_value, number_of_slots_value)
.add_metadatum(context, span_md_idx),
Intrinsic::StateClearSlots => {
self
.current_block
.append(context)
.state_clear_slots(key_value, number_of_slots_value)
.add_metadatum(context, span_md_idx);
ConstantContent::get_unit(context).add_metadatum(context, span_md_idx)
},
_ => unreachable!("`kind` is either `StateClear` or `StateClearSlots`, which is handled in the match arm above"),
};
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
Intrinsic::StateLoadWord => {
let key_exp = &arguments[0];
let span_md_idx = md_mgr.span_to_md(context, &span);
let key_value = return_on_termination_or_extract!(
self.compile_expression_to_memory(context, md_mgr, key_exp)?
)
.expect_memory()
.add_metadatum(context, span_md_idx);
let Ok(offset_const) = compile_constant_expression_to_constant(
engines,
context,
md_mgr,
self.module,
None,
None,
&arguments[1],
) else {
return Err(CompileError::IntrinsicArgNotConstant {
intrinsic: kind.to_string(),
arg: "offset".to_string(),
expected_type: "u64".to_string(),
span: arguments[1].span.clone(),
});
};
let offset = match offset_const.get_content(context).value {
ConstantValue::Uint(n) => n,
_ => {
return Err(CompileError::Internal(
"Offset for \"__state_load_word\" intrinsic is not an integer. \
This should have been caught in type checking.",
span,
))
}
};
let val = self
.current_block
.append(context)
.state_load_word(key_value, offset)
.add_metadatum(context, span_md_idx);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
Intrinsic::StateStoreWord => {
let key_exp = &arguments[0];
let val_exp = &arguments[1];
let span_md_idx = md_mgr.span_to_md(context, &span);
let key_value = return_on_termination_or_extract!(
self.compile_expression_to_memory(context, md_mgr, key_exp)?
)
.expect_memory()
.add_metadatum(context, span_md_idx);
let val_value = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, val_exp)?
)
.expect_register()
.add_metadatum(context, span_md_idx);
let val = self
.current_block
.append(context)
.state_store_word(val_value, key_value)
.add_metadatum(context, span_md_idx);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
Intrinsic::StatePreload => {
let key_exp = &arguments[0];
let span_md_idx = md_mgr.span_to_md(context, &span);
let key_value = return_on_termination_or_extract!(
self.compile_expression_to_memory(context, md_mgr, key_exp)?
)
.expect_memory()
.add_metadatum(context, span_md_idx);
let val = self
.current_block
.append(context)
.state_preload(key_value)
.add_metadatum(context, span_md_idx);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
Intrinsic::StateLoadQuad | Intrinsic::StateStoreQuad => {
let key_exp = arguments[0].clone();
let ptr_exp = arguments[1].clone();
let slots_exp = arguments[2].clone();
let span_md_idx = md_mgr.span_to_md(context, &span);
let key_value = return_on_termination_or_extract!(
self.compile_expression_to_memory(context, md_mgr, &key_exp)?
)
.expect_memory()
.add_metadatum(context, span_md_idx);
let ptr_value = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, &ptr_exp)?
)
.expect_register()
.add_metadatum(context, span_md_idx);
let slots_value = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, &slots_exp)?
)
.expect_register()
.add_metadatum(context, span_md_idx);
let b256_ty = Type::get_b256(context);
let b256_ptr_ty = Type::new_typed_pointer(context, b256_ty);
let val_ptr = self
.current_block
.append(context)
.cast_ptr(ptr_value, b256_ptr_ty)
.add_metadatum(context, span_md_idx);
let val = match kind {
Intrinsic::StateLoadQuad => self
.current_block
.append(context)
.state_load_quad_word(val_ptr, key_value, slots_value)
.add_metadatum(context, span_md_idx),
Intrinsic::StateStoreQuad => self
.current_block
.append(context)
.state_store_quad_word(val_ptr, key_value, slots_value)
.add_metadatum(context, span_md_idx),
_ => unreachable!(),
};
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
Intrinsic::StateLoadSlot => {
let key_exp = arguments[0].clone();
let ptr_exp = arguments[1].clone();
let offset_exp = arguments[2].clone();
let len_exp = arguments[3].clone();
let span_md_idx = md_mgr.span_to_md(context, &span);
let key_value = return_on_termination_or_extract!(
self.compile_expression_to_memory(context, md_mgr, &key_exp)?
)
.expect_memory()
.add_metadatum(context, span_md_idx);
let ptr_value = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, &ptr_exp)?
)
.expect_register()
.add_metadatum(context, span_md_idx);
let offset_value = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, &offset_exp)?
)
.expect_register()
.add_metadatum(context, span_md_idx);
let len_value = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, &len_exp)?
)
.expect_register()
.add_metadatum(context, span_md_idx);
self.current_block
.append(context)
.state_read_slot(ptr_value, key_value, offset_value, len_value)
.add_metadatum(context, span_md_idx);
let uint64 = Type::get_uint64(context);
let err_val = self
.current_block
.append(context)
.asm_block(
vec![],
vec![],
uint64,
Some(Ident::new_no_span("err".into())),
)
.add_metadatum(context, span_md_idx);
let zero = ConstantContent::get_uint(context, 64, 0);
let val = self
.current_block
.append(context)
.cmp(Predicate::Equal, err_val, zero)
.add_metadatum(context, span_md_idx);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
Intrinsic::StateStoreSlot => {
let key_exp = arguments[0].clone();
let ptr_exp = arguments[1].clone();
let len_exp = arguments[2].clone();
let span_md_idx = md_mgr.span_to_md(context, &span);
let key_value = return_on_termination_or_extract!(
self.compile_expression_to_memory(context, md_mgr, &key_exp)?
)
.expect_memory()
.add_metadatum(context, span_md_idx);
let ptr_value = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, &ptr_exp)?
)
.expect_register()
.add_metadatum(context, span_md_idx);
let len_value = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, &len_exp)?
)
.expect_register()
.add_metadatum(context, span_md_idx);
self.current_block
.append(context)
.state_write_slot(ptr_value, key_value, len_value)
.add_metadatum(context, span_md_idx);
let val = ConstantContent::get_unit(context).add_metadatum(context, span_md_idx);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
Intrinsic::StateUpdateSlot => {
let key_exp = arguments[0].clone();
let ptr_exp = arguments[1].clone();
let offset_exp = arguments[2].clone();
let len_exp = arguments[3].clone();
let span_md_idx = md_mgr.span_to_md(context, &span);
let key_value = return_on_termination_or_extract!(
self.compile_expression_to_memory(context, md_mgr, &key_exp)?
)
.expect_memory()
.add_metadatum(context, span_md_idx);
let ptr_value = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, &ptr_exp)?
)
.expect_register()
.add_metadatum(context, span_md_idx);
let offset_value = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, &offset_exp)?
)
.expect_register()
.add_metadatum(context, span_md_idx);
let len_value = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, &len_exp)?
)
.expect_register()
.add_metadatum(context, span_md_idx);
self.current_block
.append(context)
.state_update_slot(ptr_value, key_value, offset_value, len_value)
.add_metadatum(context, span_md_idx);
let val = ConstantContent::get_unit(context).add_metadatum(context, span_md_idx);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
Intrinsic::Log => {
if context.program_kind == Kind::Predicate {
return Err(CompileError::DisallowedIntrinsicInPredicate {
intrinsic: kind.to_string(),
span: span.clone(),
});
}
let logged_expression = TypeMetadata::get_logged_expression(
&arguments[0],
context.experimental.new_encoding,
)?;
let log_event_data =
self.build_log_event_metadata(context, md_mgr, logged_expression)?;
let log_val = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, &arguments[0])?
)
.expect_register();
let logged_type_id = logged_expression.return_type;
let log_id = match self.logged_types_map.get(&logged_type_id) {
None => {
return Err(CompileError::Internal(
"Unable to determine log instance ID for `__log` intrinsic.",
span,
));
}
Some(log_id) => {
convert_literal_to_value(context, &Literal::U64(log_id.hash_id))
}
};
match log_val.get_type(context) {
None => Err(CompileError::Internal(
"Unable to determine logged value type in the `__log` intrinsic.",
span,
)),
Some(log_ty) => {
let span_md_idx = md_mgr.span_to_md(context, &span);
self.current_block
.append(context)
.log(log_val, log_ty, log_id, log_event_data)
.add_metadatum(context, span_md_idx);
let val =
ConstantContent::get_unit(context).add_metadatum(context, span_md_idx);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
}
}
Intrinsic::Alloc => {
let targ = type_arguments[0].clone();
let ir_type = convert_resolved_type_id(
self.engines,
context,
md_mgr,
self.module,
Some(self),
targ.type_id(),
&targ.span(),
)?;
let count_exp = &arguments[0];
let count_value = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, count_exp)?
)
.expect_register();
let span_md_idx = md_mgr.span_to_md(context, &span);
let val = self
.current_block
.append(context)
.alloc(ir_type, count_value)
.add_metadatum(context, span_md_idx);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
Intrinsic::Add
| Intrinsic::Sub
| Intrinsic::Mul
| Intrinsic::Div
| Intrinsic::And
| Intrinsic::Or
| Intrinsic::Xor
| Intrinsic::Mod
| Intrinsic::Rsh
| Intrinsic::Lsh => {
let op = match kind {
Intrinsic::Add => BinaryOpKind::Add,
Intrinsic::Sub => BinaryOpKind::Sub,
Intrinsic::Mul => BinaryOpKind::Mul,
Intrinsic::Div => BinaryOpKind::Div,
Intrinsic::And => BinaryOpKind::And,
Intrinsic::Or => BinaryOpKind::Or,
Intrinsic::Xor => BinaryOpKind::Xor,
Intrinsic::Mod => BinaryOpKind::Mod,
Intrinsic::Rsh => BinaryOpKind::Rsh,
Intrinsic::Lsh => BinaryOpKind::Lsh,
_ => unreachable!(),
};
let lhs = &arguments[0];
let rhs = &arguments[1];
let lhs_value = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, lhs)?
)
.expect_register();
let rhs_value = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, rhs)?
)
.expect_register();
let val = self
.current_block
.append(context)
.binary_op(op, lhs_value, rhs_value);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
Intrinsic::Revert => {
let revert_code_val = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, &arguments[0])?
)
.expect_register();
let span_md_idx = md_mgr.span_to_md(context, &span);
let val = self
.current_block
.append(context)
.revert(revert_code_val)
.add_metadatum(context, span_md_idx);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
Intrinsic::JmpMem => {
let span_md_idx = md_mgr.span_to_md(context, &span);
let val = self
.current_block
.append(context)
.jmp_mem()
.add_metadatum(context, span_md_idx);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
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_type_id(
self.engines,
context,
md_mgr,
self.module,
Some(self),
len.type_id(),
&len.span(),
)?;
let len_value =
ConstantContent::get_uint(context, 64, ir_type.size(context).in_bytes());
let lhs = &arguments[0];
let count = &arguments[1];
let lhs_value = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, lhs)?
)
.expect_register();
let count_value = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, count)?
)
.expect_register();
let rhs_value = self.current_block.append(context).binary_op(
BinaryOpKind::Mul,
len_value,
count_value,
);
let val = self
.current_block
.append(context)
.binary_op(op, lhs_value, rhs_value);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
Intrinsic::Smo => {
let span_md_idx = md_mgr.span_to_md(context, &span);
let recipient_value = return_on_termination_or_extract!(
self.compile_expression_to_memory(context, md_mgr, &arguments[0])?
)
.expect_memory()
.add_metadatum(context, span_md_idx);
let user_message = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, &arguments[1])?
)
.expect_register()
.add_metadatum(context, span_md_idx);
let user_message_type = user_message.get_type(context).ok_or_else(|| {
CompileError::Internal(
"Unable to determine type for message data.",
span.clone(),
)
})?;
let u64_ty = Type::get_uint64(context);
let field_types = [u64_ty, user_message_type];
let message_aggregate = Type::new_struct(context, field_types.to_vec());
let message_aggregate_local_name = self.lexical_map.insert_anon();
let message_ptr = self
.function
.new_local_var(
context,
message_aggregate_local_name,
message_aggregate,
None,
false,
)
.map_err(|ir_error| {
CompileError::InternalOwned(ir_error.to_string(), Span::dummy())
})?;
let message = self
.current_block
.append(context)
.get_local(message_ptr)
.add_metadatum(context, span_md_idx);
let message_id_val = self
.messages_types_map
.get(&arguments[1].return_type)
.map(|&msg_id| ConstantContent::get_uint(context, 64, *msg_id as u64))
.ok_or_else(|| {
CompileError::Internal(
"Unable to determine ID for smo instance.",
span.clone(),
)
})?;
let gep_val = self
.current_block
.append(context)
.get_elem_ptr_with_idx(message, u64_ty, 0)
.add_metadatum(context, span_md_idx);
self.current_block
.append(context)
.store(gep_val, message_id_val)
.add_metadatum(context, span_md_idx);
let gep_val = self
.current_block
.append(context)
.get_elem_ptr_with_idx(message, user_message_type, 1)
.add_metadatum(context, span_md_idx);
let user_message_size = 8 + user_message_type.size(context).in_bytes();
self.current_block
.append(context)
.store(gep_val, user_message)
.add_metadatum(context, span_md_idx);
let user_message_size_val =
ConstantContent::get_uint(context, 64, user_message_size);
let coins = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, &arguments[2])?
)
.expect_register()
.add_metadatum(context, span_md_idx);
self.current_block
.append(context)
.smo(recipient_value, message, user_message_size_val, coins)
.add_metadatum(context, span_md_idx);
let val = ConstantContent::get_unit(context).add_metadatum(context, span_md_idx);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
Intrinsic::Not => {
assert!(arguments.len() == 1);
let op = &arguments[0];
let value = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, op)?
)
.expect_register();
let val = self
.current_block
.append(context)
.unary_op(UnaryOpKind::Not, value);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
Intrinsic::ContractCall => {
assert!(type_arguments.is_empty());
let params = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, &arguments[0])?
)
.expect_register();
let coins = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, &arguments[1])?
)
.expect_register();
let b256_ty = Type::get_b256(context);
let asset_id = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, &arguments[2])?
)
.expect_register();
let tmp_asset_id_name = self.lexical_map.insert_anon();
let tmp_var = self
.function
.new_local_var(context, tmp_asset_id_name, b256_ty, None, false)
.map_err(|ir_error| {
CompileError::InternalOwned(ir_error.to_string(), Span::dummy())
})?;
let tmp_val = self.current_block.append(context).get_local(tmp_var);
self.current_block.append(context).store(tmp_val, asset_id);
let asset_id = self.current_block.append(context).get_local(tmp_var);
let gas = return_on_termination_or_extract!(self.compile_expression_to_register(
context,
md_mgr,
&arguments[3],
)?)
.expect_register();
let span_md_idx = md_mgr.span_to_md(context, &span);
let return_type = Type::get_unit(context);
let return_type = Type::new_typed_pointer(context, return_type);
let returned_value = self
.current_block
.append(context)
.contract_call(return_type, None, params, coins, asset_id, gas)
.add_metadatum(context, span_md_idx);
Ok(TerminatorValue::new(
CompiledValue::InRegister(returned_value),
context,
))
}
Intrinsic::ContractRet => {
let span_md_idx = md_mgr.span_to_md(context, &span);
let ptr = return_on_termination_or_extract!(self.compile_expression_to_register(
context,
md_mgr,
&arguments[0],
)?)
.expect_register();
let len = return_on_termination_or_extract!(self.compile_expression_to_register(
context,
md_mgr,
&arguments[1],
)?)
.expect_register();
let r = self
.current_block
.append(context)
.retd(ptr, len)
.add_metadatum(context, span_md_idx);
Ok(TerminatorValue::new(CompiledValue::InRegister(r), context))
}
Intrinsic::EncodeBufferEmpty => {
assert!(arguments.is_empty());
let cap = Value::new_u64_constant(context, 1024);
let args = vec![AsmArg {
name: Ident::new_no_span("cap".into()),
initializer: Some(cap),
}];
let body = vec![AsmInstruction {
op_name: Ident::new_no_span("aloc".into()),
args: vec![Ident::new_no_span("cap".into())],
immediate: None,
metadata: None,
}];
let ptr_ty = Type::get_ptr(context);
let ptr = self.current_block.append(context).asm_block(
args,
body,
ptr_ty,
Some(Ident::new_no_span("hp".into())),
);
let len = Value::new_u64_constant(context, 0);
let buffer = self.compile_to_encode_buffer(context, ptr, cap, len)?;
Ok(TerminatorValue::new(buffer, context))
}
Intrinsic::EncodeBufferAppend => {
self.compile_encode_buffer_append(context, md_mgr, arguments, engines)
}
Intrinsic::EncodeBufferAsRawSlice => {
assert!(arguments.len() == 1);
let buffer = &arguments[0];
let buffer = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, buffer)?
)
.expect_register();
let uint64_ty = Type::get_uint64(context);
let ptr_ty = Type::get_ptr(context);
let (ptr, _, len) = self.compile_buffer_into_parts(context, buffer)?;
let slice_as_tuple = self.compile_tuple_from_values(
context,
vec![ptr, len],
vec![ptr_ty, uint64_ty],
None,
)?;
let return_type = Type::get_slice(context);
let buffer = self.current_block.append(context).asm_block(
vec![AsmArg {
name: Ident::new_no_span("s".into()),
initializer: Some(slice_as_tuple),
}],
vec![],
return_type,
Some(Ident::new_no_span("s".into())),
);
Ok(TerminatorValue::new(
CompiledValue::InRegister(buffer),
context,
))
}
Intrinsic::Slice => self.compile_intrinsic_slice(arguments, context, md_mgr),
Intrinsic::ElemAt => self.compile_intrinsic_elem_at(arguments, context, md_mgr),
Intrinsic::Transmute => {
self.compile_intrinsic_transmute(arguments, return_type, context, md_mgr, &span)
}
Intrinsic::Dbg => {
unreachable!("__dbg should not exist in the typed tree")
}
Intrinsic::RuntimeMemoryId => {
assert!(type_arguments.len() == 1);
assert!(arguments.is_empty());
let arg = type_arguments[0].as_type_argument().unwrap();
let t = convert_resolved_type_id(
self.engines,
context,
md_mgr,
self.module,
Some(self),
arg.type_id,
&arg.span,
)?;
let id = get_memory_id(context, t);
let val = ConstantContent::get_uint(context, 64, id);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
Intrinsic::EncodingMemoryId => {
assert!(type_arguments.len() == 1);
assert!(arguments.is_empty());
let arg = type_arguments[0].as_type_argument().unwrap();
let id = get_encoding_id(self.engines, arg.type_id);
let val = ConstantContent::get_uint(context, 64, id);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
}
}
fn compile_encode_buffer_append(
&mut self,
context: &mut Context<'_>,
md_mgr: &mut MetadataManager,
arguments: &[TyExpression],
engines: &Engines,
) -> Result<TerminatorValue, CompileError> {
assert!(arguments.len() == 2);
let buffer = &arguments[0];
let buffer = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, buffer)?
)
.expect_register();
let (ptr, cap, len) = self.compile_buffer_into_parts(context, buffer)?;
let item = &arguments[1];
let item_span = item.span.clone();
let item_type = engines.te().get(item.return_type);
let item = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, item)?
)
.expect_register();
fn increase_len(
current_block: &mut Block,
context: &mut Context,
len: Value,
step: u64,
) -> Value {
assert!(len.get_type(context).unwrap().is_uint64(context));
let step = Value::new_u64_constant(context, step);
current_block
.append(context)
.binary_op(BinaryOpKind::Add, len, step)
}
fn append_with_store(
current_block: &mut Block,
context: &mut Context,
addr: Value,
len: Value,
item: Value,
) -> Value {
assert!(addr.get_type(context).unwrap().is_ptr(context));
assert!(addr
.get_type(context)
.unwrap()
.get_pointee_type(context)
.unwrap()
.eq(context, &item.get_type(context).unwrap()));
let _ = current_block.append(context).store(addr, item);
let step = Value::new_u64_constant(context, 1);
current_block
.append(context)
.binary_op(BinaryOpKind::Add, len, step)
}
fn append_u64(
current_block: &mut Block,
context: &mut Context,
addr: Value,
len: Value,
item: Value,
) -> Value {
assert!(addr.get_type(context).unwrap().is_ptr(context));
assert!(addr
.get_type(context)
.unwrap()
.get_pointee_type(context)
.unwrap()
.is_uint64(context));
assert!(item.get_type(context).unwrap().is_uint64(context));
let _ = current_block.append(context).store(addr, item);
let step = Value::new_u64_constant(context, 8);
current_block
.append(context)
.binary_op(BinaryOpKind::Add, len, step)
}
fn append_with_memcpy(
s: &mut FnCompiler<'_>,
context: &mut Context,
item: Value,
ptr: Value,
len: Value,
offset: u64,
) -> Result<Value, CompileError> {
let item_ptr =
store_to_memory(s, context, CompiledValue::InRegister(item))?.expect_memory();
let offset_value = Value::new_u64_constant(context, offset);
let item_ptr = calc_addr_as_ptr(
&mut s.current_block,
context,
item_ptr,
offset_value,
Type::get_uint8(context),
);
let addr = calc_addr_as_ptr(
&mut s.current_block,
context,
ptr,
len,
Type::get_uint8(context),
);
s.current_block
.append(context)
.mem_copy_bytes(addr, item_ptr, 8 - offset);
Ok(increase_len(&mut s.current_block, context, len, 8 - offset))
}
fn grow_if_needed(
s: &mut FnCompiler<'_>,
context: &mut Context,
ptr: Value,
cap: Value,
len: Value,
needed_size: Value,
) -> (Value, Value) {
assert!(ptr.get_type(context).unwrap().is_ptr(context));
assert!(cap.get_type(context).unwrap().is_uint64(context));
let ptr_ty = Type::get_ptr(context);
let merge_block = s.function.create_block(context, None);
let merge_block_ptr = Value::new_argument(
context,
BlockArgument {
block: merge_block,
idx: 0,
ty: ptr_ty,
is_immutable: false,
},
);
merge_block.add_arg(context, merge_block_ptr);
let merge_block_cap = Value::new_argument(
context,
BlockArgument {
block: merge_block,
idx: 1,
ty: Type::get_uint64(context),
is_immutable: false,
},
);
merge_block.add_arg(context, merge_block_cap);
let true_block_begin = s.function.create_block(context, None);
let false_block_begin = s.function.create_block(context, None);
let needed_cap =
s.current_block
.append(context)
.binary_op(BinaryOpKind::Add, len, needed_size);
let needs_realloc =
s.current_block
.append(context)
.cmp(Predicate::GreaterThan, needed_cap, cap);
s.current_block.append(context).conditional_branch(
needs_realloc,
true_block_begin,
false_block_begin,
vec![],
vec![],
);
s.current_block = true_block_begin;
let u8 = Type::get_uint8(context);
let ptr_u8 = Type::new_typed_pointer(context, u8);
let two = Value::new_u64_constant(context, 2);
let new_cap_part =
s.current_block
.append(context)
.binary_op(BinaryOpKind::Mul, cap, two);
let new_cap = s.current_block.append(context).binary_op(
BinaryOpKind::Add,
new_cap_part,
needed_size,
);
let new_ptr = s.current_block.append(context).asm_block(
vec![
AsmArg {
name: Ident::new_no_span("new_cap".into()),
initializer: Some(new_cap),
},
AsmArg {
name: Ident::new_no_span("old_ptr".into()),
initializer: Some(ptr),
},
AsmArg {
name: Ident::new_no_span("len".into()),
initializer: Some(len),
},
],
vec![
AsmInstruction {
op_name: Ident::new_no_span("aloc".into()),
args: vec![Ident::new_no_span("new_cap".into())],
immediate: None,
metadata: None,
},
AsmInstruction {
op_name: Ident::new_no_span("mcp".into()),
args: vec![
Ident::new_no_span("hp".into()),
Ident::new_no_span("old_ptr".into()),
Ident::new_no_span("len".into()),
],
immediate: None,
metadata: None,
},
],
ptr_u8,
Some(Ident::new_no_span("hp".into())),
);
s.current_block
.append(context)
.branch(merge_block, vec![new_ptr, new_cap]);
s.current_block = false_block_begin;
s.current_block
.append(context)
.branch(merge_block, vec![ptr, cap]);
s.current_block = merge_block;
assert!(merge_block_ptr.get_type(context).unwrap().is_ptr(context));
assert!(merge_block_cap
.get_type(context)
.unwrap()
.is_uint64(context));
(merge_block_ptr, merge_block_cap)
}
let (ptr, cap) = match &*item_type {
TypeInfo::Boolean => {
let needed_size = Value::new_u64_constant(context, 1);
grow_if_needed(self, context, ptr, cap, len, needed_size)
}
TypeInfo::UnsignedInteger(IntegerBits::Eight) => {
let needed_size = Value::new_u64_constant(context, 1);
grow_if_needed(self, context, ptr, cap, len, needed_size)
}
TypeInfo::UnsignedInteger(IntegerBits::Sixteen) => {
let needed_size = Value::new_u64_constant(context, 2);
grow_if_needed(self, context, ptr, cap, len, needed_size)
}
TypeInfo::UnsignedInteger(IntegerBits::ThirtyTwo) => {
let needed_size = Value::new_u64_constant(context, 4);
grow_if_needed(self, context, ptr, cap, len, needed_size)
}
TypeInfo::UnsignedInteger(IntegerBits::SixtyFour) => {
let needed_size = Value::new_u64_constant(context, 8);
grow_if_needed(self, context, ptr, cap, len, needed_size)
}
TypeInfo::UnsignedInteger(IntegerBits::V256) | TypeInfo::B256 => {
let needed_size = Value::new_u64_constant(context, 32);
grow_if_needed(self, context, ptr, cap, len, needed_size)
}
TypeInfo::StringArray(length) => {
let value = length.expr().as_literal_val().unwrap() as u64;
let needed_size = Value::new_u64_constant(context, value);
grow_if_needed(self, context, ptr, cap, len, needed_size)
}
TypeInfo::StringSlice | TypeInfo::RawUntypedSlice => {
let uint64 = Type::get_uint64(context);
let u64_u64_type = Type::new_struct(context, vec![uint64, uint64]);
let item = self.current_block.append(context).asm_block(
vec![AsmArg {
name: Ident::new_no_span("item".into()),
initializer: Some(item),
}],
vec![],
u64_u64_type,
Some(Ident::new_no_span("item".into())),
);
let name = self.lexical_map.insert_anon();
let item_local = self
.function
.new_local_var(context, name, u64_u64_type, None, false)
.map_err(|ir_error| {
CompileError::InternalOwned(ir_error.to_string(), Span::dummy())
})?;
let ptr_to_local_item = self.current_block.append(context).get_local(item_local);
self.current_block
.append(context)
.store(ptr_to_local_item, item);
let needed_size = self.current_block.append(context).get_elem_ptr_with_idx(
ptr_to_local_item,
uint64,
1,
);
let needed_size = self.current_block.append(context).load(needed_size);
let eight = Value::new_u64_constant(context, 8);
let needed_size = self.current_block.append(context).binary_op(
BinaryOpKind::Add,
needed_size,
eight,
);
grow_if_needed(self, context, ptr, cap, len, needed_size)
}
TypeInfo::Tuple(items) => {
if items.len() != 2 {
return Err(CompileError::EncodingUnsupportedType { span: item_span });
}
let is_ptr = matches!(
engines.te().get(items[0].type_id).as_ref(),
TypeInfo::RawUntypedPtr
);
let is_u64 = matches!(
engines.te().get(items[1].type_id).as_ref(),
TypeInfo::UnsignedInteger(IntegerBits::SixtyFour)
);
if !is_ptr || !is_u64 {
return Err(CompileError::EncodingUnsupportedType { span: item_span });
}
let raw_ptr = Type::get_ptr(context);
let uint64 = Type::get_uint64(context);
let raw_ptr_u64_type = Type::new_struct(context, vec![raw_ptr, uint64]);
let name = self.lexical_map.insert_anon();
let item_local = self
.function
.new_local_var(context, name, raw_ptr_u64_type, None, false)
.map_err(|ir_error| {
CompileError::InternalOwned(ir_error.to_string(), Span::dummy())
})?;
let ptr_to_local_item = self.current_block.append(context).get_local(item_local);
self.current_block
.append(context)
.store(ptr_to_local_item, item);
let needed_size = self.current_block.append(context).get_elem_ptr_with_idx(
ptr_to_local_item,
uint64,
1,
);
let needed_size = self.current_block.append(context).load(needed_size);
grow_if_needed(self, context, ptr, cap, len, needed_size)
}
_ => return Err(CompileError::EncodingUnsupportedType { span: item_span }),
};
let new_len = match &*item_type {
TypeInfo::Boolean => {
assert!(item.get_type(context).unwrap().is_bool(context));
let addr = calc_addr_as_ptr(
&mut self.current_block,
context,
ptr,
len,
Type::get_bool(context),
);
append_with_store(&mut self.current_block, context, addr, len, item)
}
TypeInfo::UnsignedInteger(IntegerBits::Eight) => {
assert!(item.get_type(context).unwrap().is_uint8(context),);
let addr = calc_addr_as_ptr(
&mut self.current_block,
context,
ptr,
len,
Type::get_uint8(context),
);
append_with_store(&mut self.current_block, context, addr, len, item)
}
TypeInfo::UnsignedInteger(IntegerBits::Sixteen) => {
assert!(item.get_type(context).unwrap().is_uint64(context));
append_with_memcpy(self, context, item, ptr, len, 6)?
}
TypeInfo::UnsignedInteger(IntegerBits::ThirtyTwo) => {
assert!(item.get_type(context).unwrap().is_uint64(context));
append_with_memcpy(self, context, item, ptr, len, 4)?
}
TypeInfo::UnsignedInteger(IntegerBits::SixtyFour) => {
assert!(item.get_type(context).unwrap().is_uint64(context));
let addr = calc_addr_as_ptr(
&mut self.current_block,
context,
ptr,
len,
Type::get_uint64(context),
);
append_u64(&mut self.current_block, context, addr, len, item)
}
TypeInfo::UnsignedInteger(IntegerBits::V256) | TypeInfo::B256 => {
let item_ptr = store_to_memory(self, context, CompiledValue::InRegister(item))?
.expect_memory();
let addr = calc_addr_as_ptr(
&mut self.current_block,
context,
ptr,
len,
Type::get_uint8(context),
);
self.current_block
.append(context)
.mem_copy_bytes(addr, item_ptr, 32);
increase_len(&mut self.current_block, context, len, 32)
}
TypeInfo::StringArray(length) => {
let string_len = length.expr().as_literal_val().unwrap() as u64;
let item_ptr = store_to_memory(self, context, CompiledValue::InRegister(item))?
.expect_memory();
let addr = calc_addr_as_ptr(
&mut self.current_block,
context,
ptr,
len,
Type::get_uint8(context),
);
self.current_block
.append(context)
.mem_copy_bytes(addr, item_ptr, string_len);
increase_len(&mut self.current_block, context, len, string_len)
}
TypeInfo::StringSlice | TypeInfo::RawUntypedSlice => {
let uint64 = Type::get_uint64(context);
let item_ptr = store_to_memory(self, context, CompiledValue::InRegister(item))?
.expect_memory();
let addr = calc_addr_as_ptr(
&mut self.current_block,
context,
ptr,
len,
Type::get_uint8(context),
);
let addr_ident = Ident::new_no_span("addr".into());
let len_ident = Ident::new_no_span("len".into());
let item_ptr_ident = Ident::new_no_span("item_ptr".into());
let data_ptr_ident = Ident::new_no_span("data_ptr".into());
let item_len_ident = Ident::new_no_span("item_len".into());
let new_len_ident = Ident::new_no_span("new_len".into());
self.current_block.append(context).asm_block(
vec![
AsmArg {
name: item_ptr_ident.clone(),
initializer: Some(item_ptr),
},
AsmArg {
name: len_ident.clone(),
initializer: Some(len),
},
AsmArg {
name: addr_ident.clone(),
initializer: Some(addr),
},
AsmArg {
name: data_ptr_ident.clone(),
initializer: None,
},
AsmArg {
name: item_len_ident.clone(),
initializer: None,
},
AsmArg {
name: new_len_ident.clone(),
initializer: None,
},
],
vec![
AsmInstruction {
op_name: Ident::new_no_span("lw".into()),
args: vec![item_len_ident.clone(), item_ptr_ident.clone()],
immediate: Some(Ident::new_no_span("i1".into())),
metadata: None,
},
AsmInstruction {
op_name: Ident::new_no_span("sw".into()),
args: vec![addr_ident.clone(), item_len_ident.clone()],
immediate: Some(Ident::new_no_span("i0".into())),
metadata: None,
},
AsmInstruction {
op_name: Ident::new_no_span("addi".into()),
args: vec![addr_ident.clone(), addr_ident.clone()],
immediate: Some(Ident::new_no_span("i8".into())),
metadata: None,
},
AsmInstruction {
op_name: Ident::new_no_span("lw".into()),
args: vec![data_ptr_ident.clone(), item_ptr_ident.clone()],
immediate: Some(Ident::new_no_span("i0".into())),
metadata: None,
},
AsmInstruction {
op_name: Ident::new_no_span("mcp".into()),
args: vec![addr_ident, data_ptr_ident, item_len_ident.clone()],
immediate: None,
metadata: None,
},
AsmInstruction {
op_name: Ident::new_no_span("addi".into()),
args: vec![new_len_ident.clone(), len_ident],
immediate: Some(Ident::new_no_span("i8".into())),
metadata: None,
},
AsmInstruction {
op_name: Ident::new_no_span("add".into()),
args: vec![
new_len_ident.clone(),
new_len_ident.clone(),
item_len_ident,
],
immediate: None,
metadata: None,
},
],
uint64,
Some(new_len_ident),
)
}
TypeInfo::Tuple(items) => {
if items.len() != 2 {
return Err(CompileError::EncodingUnsupportedType { span: item_span });
}
let is_ptr = matches!(
engines.te().get(items[0].type_id).as_ref(),
TypeInfo::RawUntypedPtr
);
let is_u64 = matches!(
engines.te().get(items[1].type_id).as_ref(),
TypeInfo::UnsignedInteger(IntegerBits::SixtyFour)
);
if !is_ptr || !is_u64 {
return Err(CompileError::EncodingUnsupportedType { span: item_span });
}
let uint64 = Type::get_uint64(context);
let item_ptr = store_to_memory(self, context, CompiledValue::InRegister(item))?
.expect_memory();
let addr = calc_addr_as_ptr(
&mut self.current_block,
context,
ptr,
len,
Type::get_uint8(context),
);
let addr_ident = Ident::new_no_span("addr".into());
let len_ident = Ident::new_no_span("len".into());
let item_ptr_ident = Ident::new_no_span("item_ptr".into());
let data_ptr_ident = Ident::new_no_span("data_ptr".into());
let item_len_ident = Ident::new_no_span("item_len".into());
let new_len_ident = Ident::new_no_span("new_len".into());
self.current_block.append(context).asm_block(
vec![
AsmArg {
name: item_ptr_ident.clone(),
initializer: Some(item_ptr),
},
AsmArg {
name: len_ident.clone(),
initializer: Some(len),
},
AsmArg {
name: addr_ident.clone(),
initializer: Some(addr),
},
AsmArg {
name: data_ptr_ident.clone(),
initializer: None,
},
AsmArg {
name: item_len_ident.clone(),
initializer: None,
},
AsmArg {
name: new_len_ident.clone(),
initializer: None,
},
],
vec![
AsmInstruction {
op_name: Ident::new_no_span("lw".into()),
args: vec![item_len_ident.clone(), item_ptr_ident.clone()],
immediate: Some(Ident::new_no_span("i1".into())),
metadata: None,
},
AsmInstruction {
op_name: Ident::new_no_span("lw".into()),
args: vec![data_ptr_ident.clone(), item_ptr_ident.clone()],
immediate: Some(Ident::new_no_span("i0".into())),
metadata: None,
},
AsmInstruction {
op_name: Ident::new_no_span("mcp".into()),
args: vec![addr_ident, data_ptr_ident, item_len_ident.clone()],
immediate: None,
metadata: None,
},
AsmInstruction {
op_name: Ident::new_no_span("add".into()),
args: vec![new_len_ident.clone(), len_ident.clone(), item_len_ident],
immediate: None,
metadata: None,
},
],
uint64,
Some(new_len_ident),
)
}
_ => return Err(CompileError::EncodingUnsupportedType { span: item_span }),
};
let buffer = self.compile_to_encode_buffer(context, ptr, cap, new_len)?;
Ok(TerminatorValue::new(buffer, context))
}
fn compile_intrinsic_transmute(
&mut self,
arguments: &[ty::TyExpression],
return_type: TypeId,
context: &mut Context,
md_mgr: &mut MetadataManager,
span: &Span,
) -> Result<TerminatorValue, CompileError> {
assert!(arguments.len() == 1);
let return_type_ir_type = convert_resolved_type_id(
self.engines,
context,
md_mgr,
self.module,
Some(self),
return_type,
span,
)?;
let first_argument_expr = &arguments[0];
let first_argument_value = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, first_argument_expr)?
);
let first_argument_type = first_argument_value
.get_type(context)
.expect("transmute first argument type not found");
let is_first_argument_ptr = first_argument_type.is_ptr(context);
let is_return_type_ptr = return_type_ir_type.is_ptr(context);
let final_value = match (is_first_argument_ptr, is_return_type_ptr) {
(true, false) | (false, true) => {
return Err(CompileError::Internal(
"__transmute both types need to be references, or both need to be not references",
span.clone(),
));
}
(true, true) => {
let first_argument_value = first_argument_value.value();
self.current_block
.append(context)
.cast_ptr(first_argument_value, return_type_ir_type)
}
(false, false) => {
let first_arg_size = first_argument_type.size(context).in_bytes();
let return_type_size = return_type_ir_type.size(context).in_bytes();
if first_arg_size != return_type_size {
return Err(CompileError::Internal(
"Types size do not match",
span.clone(),
));
}
let return_type_ir_type_ptr = Type::new_typed_pointer(context, return_type_ir_type);
let first_argument_ptr =
store_to_memory(self, context, first_argument_value)?.expect_memory();
let casted_ptr = self
.current_block
.append(context)
.cast_ptr(first_argument_ptr, return_type_ir_type_ptr);
self.current_block.append(context).load(casted_ptr)
}
};
Ok(TerminatorValue::new(
CompiledValue::InRegister(final_value),
context,
))
}
fn ptr_to_first_element(
&mut self,
context: &mut Context,
expr: &TyExpression,
value: Value,
md_mgr: &mut MetadataManager,
) -> Result<(Value, TypeId), CompileError> {
let te = self.engines.te();
let err = CompileError::TypeArgumentsNotAllowed {
span: expr.span.clone(),
};
let (is_slice, elem_ty) = match te.get(expr.return_type).as_ref() {
TypeInfo::Ref {
referenced_type, ..
} => match &*te.get(referenced_type.type_id) {
TypeInfo::Array(elem_ty, _) => Ok((false, elem_ty.type_id)),
TypeInfo::Slice(elem_ty) => Ok((true, elem_ty.type_id)),
_ => Err(err),
},
TypeInfo::Slice(elem_ty) => Ok((true, elem_ty.type_id)),
_ => Err(err),
}?;
if is_slice {
let ptr_arg = AsmArg {
name: Ident::new_no_span("ptr".into()),
initializer: Some(value),
};
let ptr_out_arg = AsmArg {
name: Ident::new_no_span("ptr_out".into()),
initializer: None,
};
let elem_ir_ty = convert_resolved_type_id(
self.engines,
context,
md_mgr,
self.module,
Some(self),
elem_ty,
&expr.span.clone(),
)?;
let return_type = Type::new_typed_pointer(context, elem_ir_ty);
let ptr_to_first_element = self.current_block.append(context).asm_block(
vec![ptr_arg, ptr_out_arg],
vec![AsmInstruction::lw_no_span("ptr_out", "ptr", "i0")],
return_type,
Some(Ident::new_no_span("ptr_out".into())),
);
Ok((ptr_to_first_element, elem_ty))
} else {
Ok((value, elem_ty))
}
}
fn advance_ptr_n_elements(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
first_argument_expr: &TyExpression,
ptr: Value,
elem_type_id: TypeId,
idx: Value,
) -> Result<(Value, Type), CompileError> {
let elem_ir_type = convert_resolved_type_id(
self.engines,
context,
md_mgr,
self.module,
Some(self),
elem_type_id,
&first_argument_expr.span.clone(),
)?;
let elem_ir_type_size = elem_ir_type.size(context);
let elem_ir_type_size = Value::new_u64_constant(context, elem_ir_type_size.in_bytes());
let elem_ir_type_size_arg = AsmArg {
name: Ident::new_no_span("elem_ir_type_size".into()),
initializer: Some(elem_ir_type_size),
};
let offset_temp_arg = AsmArg {
name: Ident::new_no_span("offset_temp".into()),
initializer: None,
};
let idx_arg = AsmArg {
name: Ident::new_no_span("idx".into()),
initializer: Some(idx),
};
let ptr_arg = AsmArg {
name: Ident::new_no_span("ptr".into()),
initializer: Some(ptr),
};
let ptr_out_arg = AsmArg {
name: Ident::new_no_span("ptr_out".into()),
initializer: None,
};
let return_type = Type::new_typed_pointer(context, elem_ir_type);
let ptr_out = self.current_block.append(context).asm_block(
vec![
idx_arg,
elem_ir_type_size_arg,
ptr_arg,
offset_temp_arg,
ptr_out_arg,
],
vec![
AsmInstruction::mul_no_span("offset_temp", "idx", "elem_ir_type_size"),
AsmInstruction::add_no_span("ptr_out", "ptr", "offset_temp"),
],
return_type,
Some(Ident::new_no_span("ptr_out".into())),
);
Ok((ptr_out, elem_ir_type))
}
fn compile_intrinsic_elem_at(
&mut self,
arguments: &[ty::TyExpression],
context: &mut Context,
md_mgr: &mut MetadataManager,
) -> Result<TerminatorValue, CompileError> {
assert!(arguments.len() == 2);
let first_argument_expr = &arguments[0];
let first_argument_value = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, first_argument_expr)?
)
.expect_register();
let (ptr_to_first_elem, elem_type_id) =
self.ptr_to_first_element(context, first_argument_expr, first_argument_value, md_mgr)?;
let idx = &arguments[1];
let idx = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, idx)?
)
.expect_register();
let (ptr_to_elem, _) = self.advance_ptr_n_elements(
context,
md_mgr,
first_argument_expr,
ptr_to_first_elem,
elem_type_id,
idx,
)?;
Ok(TerminatorValue::new(
CompiledValue::InRegister(ptr_to_elem),
context,
))
}
fn compile_intrinsic_slice(
&mut self,
arguments: &[ty::TyExpression],
context: &mut Context,
md_mgr: &mut MetadataManager,
) -> Result<TerminatorValue, CompileError> {
assert!(arguments.len() == 3);
let first_argument_expr = &arguments[0];
let first_argument_value = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, first_argument_expr)?
)
.expect_register();
let (ptr_to_first_elem, elem_type_id) =
self.ptr_to_first_element(context, first_argument_expr, first_argument_value, md_mgr)?;
let start = &arguments[1];
let start = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, start)?
)
.expect_register();
let (ptr_to_elem, elem_ir_type) = self.advance_ptr_n_elements(
context,
md_mgr,
first_argument_expr,
ptr_to_first_elem,
elem_type_id,
start,
)?;
let end = &arguments[2];
let end = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, end)?
)
.expect_register();
let slice_len = self
.current_block
.append(context)
.binary_op(BinaryOpKind::Sub, end, start);
let slice = self.slices_from_ptr_and_len(context, elem_ir_type, ptr_to_elem, slice_len)?;
Ok(TerminatorValue::new(
CompiledValue::InRegister(slice),
context,
))
}
fn slices_from_ptr_and_len(
&mut self,
context: &mut Context<'_>,
elem_ir_type: Type,
ptr_to_elem: Value,
slice_len: Value,
) -> Result<Value, CompileError> {
let ptr_to_elem_ty = Type::new_typed_pointer(context, elem_ir_type);
let return_type = Type::get_typed_slice(context, elem_ir_type);
let slice_as_tuple = self.compile_tuple_from_values(
context,
vec![ptr_to_elem, slice_len],
vec![ptr_to_elem_ty, Type::get_uint64(context)],
None,
)?;
let slice = self.current_block.append(context).asm_block(
vec![AsmArg {
name: Ident::new_no_span("s".into()),
initializer: Some(slice_as_tuple),
}],
vec![],
return_type,
Some(Ident::new_no_span("s".into())),
);
Ok(slice)
}
fn compile_return(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
ast_expr: &ty::TyExpression,
span_md_idx: Option<MetadataIndex>,
) -> Result<TerminatorValue, CompileError> {
let ret_value = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, ast_expr)?
)
.expect_register();
ret_value
.get_type(context)
.map(|ret_ty| {
let val = self
.current_block
.append(context)
.ret(ret_value, ret_ty)
.add_metadatum(context, span_md_idx);
TerminatorValue::new(CompiledValue::InRegister(val), context)
})
.ok_or_else(|| {
CompileError::Internal(
"Unable to determine type for return expression.",
ast_expr.span.clone(),
)
})
}
fn build_log_event_metadata(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
logged_expression: &ty::TyExpression,
) -> Result<Option<sway_ir::LogEventData>, CompileError> {
self.collect_event_metadata_for_type(
context,
md_mgr,
logged_expression.return_type,
&logged_expression.span,
)
}
fn collect_event_metadata_for_type(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
type_id: TypeId,
span: &Span,
) -> Result<Option<sway_ir::LogEventData>, CompileError> {
let type_engine = self.engines.te();
let decl_engine = self.engines.de();
let type_info = type_engine.get(type_id);
Ok(match type_info.as_ref() {
TypeInfo::Alias {
ty: GenericTypeArgument { type_id, .. },
..
} => self.collect_event_metadata_for_type(context, md_mgr, *type_id, span)?,
TypeInfo::Struct(decl_ref) => {
let decl = decl_engine.get_struct(decl_ref);
if decl.attributes.event().is_none() {
None
} else {
let indexed_fields: Vec<_> = decl
.fields
.iter()
.take_while(|field| field.attributes.indexed().is_some())
.collect();
let indexed_count = indexed_fields.len();
let field_size = if let Some(first_field) = indexed_fields.first() {
let size =
self.indexed_field_size_in_bytes(context, md_mgr, first_field)?;
for field in indexed_fields.iter().skip(1) {
let current_size =
self.indexed_field_size_in_bytes(context, md_mgr, field)?;
if current_size != size {
return Err(CompileError::Internal(
"Indexed event fields must have matching sizes.",
field.span.clone(),
));
}
}
Some(size)
} else {
None
};
let count = u16::try_from(indexed_count).map_err(|_| {
CompileError::Internal(
"Too many indexed fields on event for current metadata format.",
span.clone(),
)
})?;
Some(sway_ir::LogEventData::for_event(field_size, count))
}
}
TypeInfo::Enum(decl_ref) => {
if decl_engine.get_enum(decl_ref).attributes.event().is_some() {
Some(sway_ir::LogEventData::for_event(None, 0))
} else {
None
}
}
_ => None,
})
}
fn indexed_field_size_in_bytes(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
field: &ty::TyStructField,
) -> Result<u8, CompileError> {
let ir_ty = convert_resolved_type_id(
self.engines,
context,
md_mgr,
self.module,
Some(self),
field.type_argument.type_id,
&field.span,
)?;
let size = ir_ty.size(context).in_bytes();
u8::try_from(size).map_err(|_| {
CompileError::InternalOwned(
format!(
"Indexed event field size ({} bytes) exceeds maximum supported size ({} bytes).",
size,
u8::MAX
),
field.span.clone(),
)
})
}
fn compile_panic(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
ast_expr: &ty::TyExpression,
span_md_idx: Option<MetadataIndex>,
) -> Result<TerminatorValue, CompileError> {
let mut panic_occurrence = PanicOccurrence::default();
let logged_expression =
TypeMetadata::get_logged_expression(ast_expr, context.experimental.new_encoding)?;
let const_eval_string =
if logged_expression.return_type == self.engines.te().id_of_string_slice() {
let const_expr_val = compile_constant_expression_to_constant(
self.engines,
context,
md_mgr,
self.module,
None,
Some(self),
logged_expression,
);
match const_expr_val {
Ok(constant) => constant.get_content(context).as_string(),
Err(_) => None,
}
} else {
None
};
if let Some(const_eval_string) = const_eval_string {
panic_occurrence.msg = Some(const_eval_string);
} else {
if context.program_kind != Kind::Predicate {
let panic_val = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, ast_expr)?
)
.expect_register();
let logged_type_id = logged_expression.return_type;
let log_event_data =
self.build_log_event_metadata(context, md_mgr, logged_expression)?;
let log_id = match self.logged_types_map.get(&logged_type_id) {
None => {
return Err(CompileError::Internal(
"Unable to determine log instance ID for `panic` expression.",
ast_expr.span.clone(),
));
}
Some(log_id) => {
panic_occurrence.log_id = Some(*log_id);
convert_literal_to_value(context, &Literal::U64(log_id.hash_id))
}
};
match panic_val.get_type(context) {
None => {
return Err(CompileError::Internal(
"Unable to determine logged value type in the `panic` expression.",
ast_expr.span.clone(),
))
}
Some(log_ty) => {
let span_md_idx = md_mgr.span_to_md(context, &ast_expr.span);
self.current_block
.append(context)
.log(panic_val, log_ty, log_id, log_event_data)
.add_metadatum(context, span_md_idx);
}
};
}
}
let panic_span = md_mgr
.md_to_span(context, span_md_idx)
.unwrap_or(ast_expr.span.clone());
panic_occurrence.loc = self.engines.se().get_source_location(&panic_span);
panic_occurrence.function = self.function.get_abi_errors_display(context);
let panic_error_code = match self.panic_occurrences.get(&panic_occurrence) {
Some(panic_error_code) => *panic_error_code,
None => {
let panic_error_code = context.get_unique_panic_error_code();
if panic_error_code > Self::MAX_PANIC_ERROR_CODE {
return Err(CompileError::MaxNumOfPanicExpressionsReached {
current: panic_error_code + 1,
max_num: Self::MAX_PANIC_ERROR_CODE + 1,
span: panic_span,
});
}
self.panic_occurrences
.insert(panic_occurrence, panic_error_code);
panic_error_code
}
};
#[allow(clippy::unusual_byte_groupings)]
let encoded_error_code = (panic_error_code << 55)
| 0b1_00000000_00000000000_00000000000_00000000000_00000000000_00000000000;
let encoded_error_code = Value::new_u64_constant(context, encoded_error_code);
let revert_code =
if let Some(backtrace) = self.function.get_arg(context, Self::BACKTRACE_FN_ARG_NAME) {
#[allow(clippy::unusual_byte_groupings)]
let backtrace_mask = Value::new_u64_constant(
context,
0b0_00000000_11111111111_11111111111_11111111111_11111111111_11111111111,
);
let backtrace = self
.current_block
.append(context)
.binary_op(BinaryOpKind::And, backtrace, backtrace_mask)
.add_metadatum(context, span_md_idx);
self.current_block
.append(context)
.binary_op(BinaryOpKind::Or, encoded_error_code, backtrace)
.add_metadatum(context, span_md_idx)
} else {
encoded_error_code
};
let val = self
.current_block
.append(context)
.revert(revert_code)
.add_metadatum(context, span_md_idx);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
fn compile_ref(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
ast_expr: &ty::TyExpression,
_span_md_idx: Option<MetadataIndex>,
) -> Result<TerminatorValue, CompileError> {
let value = return_on_termination_or_extract!(
self.compile_expression_to_memory(context, md_mgr, ast_expr)?
)
.expect_memory();
Ok(TerminatorValue::new(
CompiledValue::InRegister(value),
context,
))
}
fn compile_deref(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
ast_expr: &ty::TyExpression,
_span_md_idx: Option<MetadataIndex>,
) -> Result<TerminatorValue, CompileError> {
let (ptr, referenced_ast_type) = self.compile_deref_up_to_ptr(context, md_mgr, ast_expr)?;
let ptr = return_on_termination_or_extract!(ptr).expect_memory();
let referenced_type = self.engines.te().get_unaliased(referenced_ast_type);
if referenced_type.is_copy_type() || referenced_type.is_reference() {
let result = self.current_block.append(context).load(ptr);
Ok(TerminatorValue::new(
CompiledValue::InRegister(result),
context,
))
} else {
Ok(TerminatorValue::new(CompiledValue::InMemory(ptr), context))
}
}
fn compile_deref_up_to_ptr(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
ast_expr: &ty::TyExpression,
) -> Result<(TerminatorValue, TypeId), CompileError> {
let ref_value = self.compile_expression_to_memory(context, md_mgr, ast_expr)?;
let ref_value = if ref_value.is_terminator {
return Ok((ref_value, 0usize.into()));
} else {
ref_value.value.expect_memory()
};
let ptr = if ref_value
.get_type(context)
.is_some_and(|ref_value_type| ref_value_type.is_ptr(context))
{
self.current_block.append(context).load(ref_value)
} else {
ref_value
};
let reference_type = self.engines.te().get_unaliased(ast_expr.return_type);
let referenced_ast_type = match *reference_type {
TypeInfo::Ref {
ref referenced_type,
..
} => Ok(referenced_type.type_id),
_ => Err(CompileError::Internal(
"Cannot dereference a non-reference expression.",
ast_expr.span.clone(),
)),
}?;
Ok((
TerminatorValue::new(CompiledValue::InMemory(ptr), context),
referenced_ast_type,
))
}
fn compile_lazy_op(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
ast_op: &LazyOp,
ast_lhs: &ty::TyExpression,
ast_rhs: &ty::TyExpression,
span_md_idx: Option<MetadataIndex>,
) -> Result<TerminatorValue, CompileError> {
let lhs_val = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, ast_lhs)?
)
.expect_register();
let cond_block_end = self.current_block;
let rhs_block = self.function.create_block(context, None);
let final_block = self.function.create_block(context, None);
let merge_val_arg_idx = final_block.new_arg(context, lhs_val.get_type(context).unwrap());
let cond_builder = cond_block_end.append(context);
match ast_op {
LazyOp::And => cond_builder.conditional_branch(
lhs_val,
rhs_block,
final_block,
vec![],
vec![lhs_val],
),
LazyOp::Or => cond_builder.conditional_branch(
lhs_val,
final_block,
rhs_block,
vec![lhs_val],
vec![],
),
}
.add_metadatum(context, span_md_idx);
self.current_block = rhs_block;
let rhs_val = self.compile_expression_to_register(context, md_mgr, ast_rhs)?;
if !rhs_val.is_terminator {
self.current_block
.append(context)
.branch(final_block, vec![rhs_val.value.expect_register()])
.add_metadatum(context, span_md_idx);
}
self.current_block = final_block;
let val = final_block.get_arg(context, merge_val_arg_idx).unwrap();
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
#[allow(clippy::too_many_arguments)]
fn compile_contract_call_encoding_v0(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
call_params: &ty::ContractCallParams,
contract_call_parameters: &IndexMap<String, ty::TyExpression>,
ast_name: &str,
ast_args: &[(Ident, ty::TyExpression)],
ast_return_type: TypeId,
span_md_idx: Option<MetadataIndex>,
) -> Result<TerminatorValue, CompileError> {
let mut compiled_args = Vec::<Value>::new();
for (_, arg) in ast_args.iter() {
let val = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, arg)?
)
.expect_register();
compiled_args.push(val)
}
let u64_ty = Type::get_uint64(context);
let user_args_val = match compiled_args.len() {
0 => ConstantContent::get_uint(context, 64, 0),
1 => {
let arg0 = compiled_args[0];
let arg0_type = self.engines.te().get_unaliased(ast_args[0].1.return_type);
match arg0_type {
_ if arg0_type.is_copy_type() => self
.current_block
.append(context)
.bitcast(arg0, u64_ty)
.add_metadatum(context, span_md_idx),
_ => {
let arg0_type = arg0.get_type(context).unwrap();
let temp_arg_name = self
.lexical_map
.insert(format!("{}{}", "arg_for_", ast_name));
let temp_var = self
.function
.new_local_var(context, temp_arg_name, arg0_type, None, false)
.map_err(|ir_error| {
CompileError::InternalOwned(ir_error.to_string(), Span::dummy())
})?;
let temp_val = self.current_block.append(context).get_local(temp_var);
self.current_block.append(context).store(temp_val, arg0);
self.current_block
.append(context)
.ptr_to_int(temp_val, u64_ty)
}
}
}
_ => {
let field_types = compiled_args
.iter()
.filter_map(|val| val.get_type(context))
.collect::<Vec<_>>();
let user_args_struct_type = Type::new_struct(context, field_types.clone());
let user_args_struct_local_name = self
.lexical_map
.insert(format!("{}{}", "args_struct_for_", ast_name));
let user_args_struct_var = self
.function
.new_local_var(
context,
user_args_struct_local_name,
user_args_struct_type,
None,
false,
)
.map_err(|ir_error| {
CompileError::InternalOwned(ir_error.to_string(), Span::dummy())
})?;
let user_args_struct_val = self
.current_block
.append(context)
.get_local(user_args_struct_var)
.add_metadatum(context, span_md_idx);
compiled_args
.into_iter()
.zip(field_types)
.enumerate()
.for_each(|(insert_idx, (field_val, field_type))| {
let gep_val = self
.current_block
.append(context)
.get_elem_ptr_with_idx(
user_args_struct_val,
field_type,
insert_idx as u64,
)
.add_metadatum(context, span_md_idx);
self.current_block
.append(context)
.store(gep_val, field_val)
.add_metadatum(context, span_md_idx);
});
self.current_block
.append(context)
.ptr_to_int(user_args_struct_val, u64_ty)
.add_metadatum(context, span_md_idx)
}
};
let b256_ty = Type::get_b256(context);
let ra_struct_type = Type::new_struct(context, [b256_ty, u64_ty, u64_ty].to_vec());
let ra_struct_var = self
.function
.new_local_var(
context,
self.lexical_map.insert_anon(),
ra_struct_type,
None,
false,
)
.map_err(|ir_error| CompileError::InternalOwned(ir_error.to_string(), Span::dummy()))?;
let ra_struct_ptr_val = self
.current_block
.append(context)
.get_local(ra_struct_var)
.add_metadatum(context, span_md_idx);
let addr = return_on_termination_or_extract!(self.compile_expression_to_register(
context,
md_mgr,
&call_params.contract_address,
)?)
.expect_register();
let gep_val =
self.current_block
.append(context)
.get_elem_ptr_with_idx(ra_struct_ptr_val, b256_ty, 0);
self.current_block
.append(context)
.store(gep_val, addr)
.add_metadatum(context, span_md_idx);
assert!(!context.experimental.new_encoding);
let sel = call_params.func_selector.as_ref().unwrap();
let sel_val = convert_literal_to_value(
context,
&Literal::U64(
sel[3] as u64 + 256 * (sel[2] as u64 + 256 * (sel[1] as u64 + 256 * sel[0] as u64)),
),
)
.add_metadatum(context, span_md_idx);
let gep_val =
self.current_block
.append(context)
.get_elem_ptr_with_idx(ra_struct_ptr_val, u64_ty, 1);
self.current_block
.append(context)
.store(gep_val, sel_val)
.add_metadatum(context, span_md_idx);
let gep_val =
self.current_block
.append(context)
.get_elem_ptr_with_idx(ra_struct_ptr_val, u64_ty, 2);
self.current_block
.append(context)
.store(gep_val, user_args_val)
.add_metadatum(context, span_md_idx);
let coins = match contract_call_parameters
.get(&constants::CONTRACT_CALL_COINS_PARAMETER_NAME.to_string())
{
Some(coins_expr) => {
return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, coins_expr)?
)
}
None => CompiledValue::InRegister(
convert_literal_to_value(
context,
&Literal::U64(constants::CONTRACT_CALL_COINS_PARAMETER_DEFAULT_VALUE),
)
.add_metadatum(context, span_md_idx),
),
}
.expect_register();
let asset_id = match contract_call_parameters
.get(&constants::CONTRACT_CALL_ASSET_ID_PARAMETER_NAME.to_string())
{
Some(asset_id_expr) => {
return_on_termination_or_extract!(self.compile_expression_to_memory(
context,
md_mgr,
asset_id_expr,
)?)
}
None => {
let asset_id_val = convert_literal_to_value(
context,
&Literal::B256(constants::CONTRACT_CALL_ASSET_ID_PARAMETER_DEFAULT_VALUE),
)
.add_metadatum(context, span_md_idx);
let tmp_asset_id_name = self.lexical_map.insert_anon();
let tmp_var = self
.function
.new_local_var(context, tmp_asset_id_name, b256_ty, None, false)
.map_err(|ir_error| {
CompileError::InternalOwned(ir_error.to_string(), Span::dummy())
})?;
let tmp_val = self.current_block.append(context).get_local(tmp_var);
self.current_block
.append(context)
.store(tmp_val, asset_id_val);
CompiledValue::InMemory(tmp_val)
}
}
.expect_memory();
let gas = match contract_call_parameters
.get(&constants::CONTRACT_CALL_GAS_PARAMETER_NAME.to_string())
{
Some(gas_expr) => return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, gas_expr)?
)
.expect_register(),
None => self
.current_block
.append(context)
.read_register(sway_ir::Register::Cgas)
.add_metadatum(context, span_md_idx),
};
let return_type = convert_resolved_typeid_no_span(
self.engines,
context,
md_mgr,
self.module,
Some(self),
ast_return_type,
)?;
let ret_is_copy_type = self
.engines
.te()
.get_unaliased(ast_return_type)
.is_copy_type();
let return_type = if ret_is_copy_type {
return_type
} else {
Type::new_typed_pointer(context, return_type)
};
let call_val = self
.current_block
.append(context)
.contract_call(
return_type,
Some(ast_name.to_string()),
ra_struct_ptr_val,
coins,
asset_id,
gas,
)
.add_metadatum(context, span_md_idx);
let res = if ret_is_copy_type {
call_val
} else {
self.current_block.append(context).load(call_val)
};
Ok(TerminatorValue::new(
CompiledValue::InRegister(res),
context,
))
}
#[allow(clippy::too_many_arguments)]
fn compile_fn_call(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
ast_args: &[(Ident, ty::TyExpression)],
callee: &ty::TyFunctionDecl,
span_md_idx: Option<MetadataIndex>,
call_path: &CallPath,
) -> Result<TerminatorValue, CompileError> {
let keyed_callee = KeyedTyFunctionDecl::new(callee, self.engines);
let new_callee = self.compiled_fn_cache.get_compiled_function(
self.engines,
context,
self.module,
md_mgr,
&keyed_callee,
self.logged_types_map,
self.messages_types_map,
self.panic_occurrences,
self.panicking_call_occurrences,
self.panicking_fn_cache,
)?;
let mut args = Vec::with_capacity(ast_args.len());
for ((_, expr), param) in ast_args.iter().zip(callee.parameters.iter()) {
self.current_fn_param = Some(param.clone());
let arg = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, expr)?
)
.expect_register();
self.current_fn_param = None;
args.push(arg);
}
if context.backtrace != Backtrace::None
&& self
.panicking_fn_cache
.can_panic(&keyed_callee, self.engines)
{
let trace = callee.trace();
let current_backtrace = self.function.get_arg(context, Self::BACKTRACE_FN_ARG_NAME);
let panicking_call_id = if context.backtrace == Backtrace::All
|| context.backtrace == Backtrace::AllExceptNever
&& !matches!(trace, Some(Trace::Never))
|| context.backtrace == Backtrace::OnlyAlways
&& matches!(trace, Some(Trace::Always))
{
let call_span = md_mgr
.md_to_span(context, span_md_idx)
.unwrap_or(Span::dummy());
let loc = self.engines.se().get_source_location(&call_span);
let panicking_call = PanickingCallOccurrence {
loc,
caller_function: self.function.get_abi_errors_display(context),
function: Self::FN_DISPLAY_FOR_ABI_ERRORS.display(callee, self.engines),
};
let panicking_call_id = match self.panicking_call_occurrences.get(&panicking_call) {
Some(panicking_call_id) => *panicking_call_id,
None => {
let panicking_call_id = context.get_unique_panicking_call_id();
if panicking_call_id > Self::MAX_PANICKING_CALL_ID {
return Err(CompileError::MaxNumOfPanickingCallsReached {
current: panicking_call_id,
max_num: Self::MAX_PANICKING_CALL_ID,
span: call_span,
});
}
self.panicking_call_occurrences
.insert(panicking_call, panicking_call_id);
panicking_call_id
}
};
Some(panicking_call_id)
} else {
None
};
let new_backtrace = match (current_backtrace, panicking_call_id) {
(None, None) | (None, Some(_)) => {
Value::new_u64_constant(context, panicking_call_id.unwrap_or_default())
}
(Some(current_backtrace), None) => {
current_backtrace
}
(Some(current_backtrace), Some(panicking_call_id)) => {
let eleven_const = Value::new_u64_constant(context, 11);
let shifted_current_backtrace = self
.current_block
.append(context)
.binary_op(BinaryOpKind::Lsh, current_backtrace, eleven_const)
.add_metadatum(context, span_md_idx);
let panicking_call_id = Value::new_u64_constant(context, panicking_call_id);
self.current_block
.append(context)
.binary_op(
BinaryOpKind::Or,
shifted_current_backtrace,
panicking_call_id,
)
.add_metadatum(context, span_md_idx)
}
};
args.push(new_backtrace);
}
let call_path_span_md_idx = md_mgr.fn_call_path_span_to_md(context, call_path);
let md_idx = combine(context, &span_md_idx, &call_path_span_md_idx);
let val = self
.current_block
.append(context)
.call(new_callee, &args)
.add_metadatum(context, md_idx);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
fn compile_if(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
ast_condition: &ty::TyExpression,
ast_then: &ty::TyExpression,
ast_else: Option<&ty::TyExpression>,
return_type: TypeId,
) -> Result<TerminatorValue, CompileError> {
let cond_span_md_idx = md_mgr.span_to_md(context, &ast_condition.span);
let try_const_eval_condition = matches!(
ast_condition.expression,
TyExpressionVariant::ConstantExpression { .. }
| TyExpressionVariant::ConstGenericExpression { .. }
);
if try_const_eval_condition {
let condition_const_value = compile_constant_expression(
self.engines,
context,
md_mgr,
self.module,
None,
Some(self),
ast_condition,
);
if let Ok(condition_const_value) = condition_const_value {
let condition_bool = condition_const_value
.get_constant(context)
.ok_or_else(|| {
CompileError::Internal(
"compile_constant_expression did not return a constant.",
ast_condition.span.clone(),
)
})?
.get_content(context)
.as_bool()
.ok_or_else(|| {
CompileError::Internal(
"if condition returned non-bool",
ast_condition.span.clone(),
)
})?;
let branch_value = if condition_bool {
self.compile_expression_to_register(context, md_mgr, ast_then)?
} else {
match ast_else {
Some(ast_else) => {
self.compile_expression_to_register(context, md_mgr, ast_else)?
}
None => {
let v = Constant::unique(context, ConstantContent::new_unit(context));
let v = Value::new_constant(context, v);
TerminatorValue {
value: CompiledValue::InRegister(v),
is_terminator: false,
}
}
}
};
return Ok(branch_value);
}
}
let cond_value = return_on_termination_or_extract!(self.compile_expression_to_register(
context,
md_mgr,
ast_condition,
)?)
.expect_register();
let cond_block = self.current_block;
let true_block_begin = self.function.create_block(context, None);
self.current_block = true_block_begin;
let true_value = self.compile_expression_to_register(context, md_mgr, ast_then)?;
let true_block_end = self.current_block;
let false_block_begin = self.function.create_block(context, None);
self.current_block = false_block_begin;
let false_value = match ast_else {
None => TerminatorValue::new(
CompiledValue::InRegister(ConstantContent::get_unit(context)),
context,
),
Some(expr) => self.compile_expression_to_register(context, md_mgr, expr)?,
};
let false_block_end = self.current_block;
cond_block
.append(context)
.conditional_branch(
cond_value,
true_block_begin,
false_block_begin,
vec![],
vec![],
)
.add_metadatum(context, cond_span_md_idx);
let merge_block = self.function.create_block(context, None);
let val = if true_value.is_terminator && false_value.is_terminator {
merge_block.append(context).branch(true_block_begin, vec![])
} else {
let return_type = convert_resolved_typeid_no_span(
self.engines,
context,
md_mgr,
self.module,
Some(self),
return_type,
)
.unwrap_or_else(|_| Type::get_unit(context));
let merge_val_arg_idx = merge_block.new_arg(context, return_type);
if !true_value.is_terminator {
true_block_end
.append(context)
.branch(merge_block, vec![true_value.value.expect_register()]);
}
if !false_value.is_terminator {
false_block_end
.append(context)
.branch(merge_block, vec![false_value.value.expect_register()]);
}
self.current_block = merge_block;
merge_block.get_arg(context, merge_val_arg_idx).unwrap()
};
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
fn compile_unsafe_downcast(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
exp: &ty::TyExpression,
variant: &ty::TyEnumVariant,
) -> Result<TerminatorValue, CompileError> {
let enum_type = match convert_resolved_type_id(
self.engines,
context,
md_mgr,
self.module,
Some(self),
exp.return_type,
&exp.span,
)? {
ty if ty.is_struct(context) => ty,
_ => {
return Err(CompileError::Internal(
"Enum type for `unsafe downcast` is not an enum.",
exp.span.clone(),
));
}
};
let compiled_value = return_on_termination_or_extract!(
self.compile_expression_to_memory(context, md_mgr, exp)?
);
match enum_type.get_indexed_type(context, &[1, variant.tag as u64]) {
Some(variant_type) => {
let val = self
.current_block
.append(context)
.get_elem_ptr_with_indices(
compiled_value.expect_memory(),
variant_type,
&[1, variant.tag as u64],
);
Ok(TerminatorValue::new(CompiledValue::InMemory(val), context))
}
None => {
let enum_type_info = self.engines.te().get(exp.return_type);
match enum_type_info.as_ref() {
TypeInfo::Enum(decl_id) => {
let decl = self.engines.de().get(decl_id);
let all_unit = decl
.variants
.iter()
.all(|x| self.engines.te().get(x.type_argument.type_id).is_unit());
if !all_unit {
return Err(CompileError::InternalOwned(
format!(
"unsafe downcast cannot find variant data for this case `{}`",
self.engines.help_out(exp.return_type)
),
exp.span.clone(),
));
}
}
_ => {
return Err(CompileError::InternalOwned(
format!(
"unsafe downcast used with `{}`, only enums are supported",
self.engines.help_out(exp.return_type)
),
exp.span.clone(),
))
}
}
let addressable_zero_u64_ptr = match self
.module
.get_global_variable(context, &vec!["__ADDRESSABLE_ZERO_U64".to_string()])
{
Some(var) => var,
None => {
let init = ConstantContent::new_uint(context, 64, 0);
let init = Constant::unique(context, init);
self.module.new_unique_global_var(
context,
"__ADDRESSABLE_ZERO_U64".into(),
Type::get_uint64(context),
Some(init),
false,
)
}
};
let ptr = self
.current_block
.append(context)
.get_global(addressable_zero_u64_ptr);
let ptr_to_unit_type = Type::new_typed_pointer(context, Type::get_unit(context));
let ptr = self
.current_block
.append(context)
.cast_ptr(ptr, ptr_to_unit_type);
Ok(TerminatorValue::new(CompiledValue::InMemory(ptr), context))
}
}
}
fn compile_enum_tag(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
exp: Box<ty::TyExpression>,
) -> Result<TerminatorValue, CompileError> {
let tag_span_md_idx = md_mgr.span_to_md(context, &exp.span);
let struct_val = return_on_termination_or_extract!(
self.compile_expression_to_memory(context, md_mgr, &exp)?
)
.expect_memory();
let u64_ty = Type::get_uint64(context);
let val = self
.current_block
.append(context)
.get_elem_ptr_with_idx(struct_val, u64_ty, 0)
.add_metadatum(context, tag_span_md_idx);
Ok(TerminatorValue::new(CompiledValue::InMemory(val), context))
}
fn compile_while_loop(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
body: &ty::TyCodeBlock,
condition: &ty::TyExpression,
span_md_idx: Option<MetadataIndex>,
) -> Result<TerminatorValue, CompileError> {
let cond_block = self.function.create_block(context, Some("while".into()));
self.current_block
.append(context)
.branch(cond_block, vec![]);
self.current_block = cond_block;
let cond_value = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, condition)?
)
.expect_register();
let cond_end_block = self.current_block;
let break_block = self
.function
.create_block(context, Some("while_break".into()));
let prev_block_to_break_to = self.block_to_break_to;
let prev_block_to_continue_to = self.block_to_continue_to;
self.block_to_break_to = Some(break_block);
self.block_to_continue_to = Some(cond_block);
let body_block = self
.function
.create_block(context, Some("while_body".into()));
self.current_block = body_block;
let body_block_val = self
.compile_code_block(context, md_mgr, body)
.map_err(|mut x| x.pop().unwrap())?;
if !body_block_val.is_terminator {
self.current_block
.append(context)
.branch(cond_block, vec![]);
}
self.block_to_break_to = prev_block_to_break_to;
self.block_to_continue_to = prev_block_to_continue_to;
let final_block = self
.function
.create_block(context, Some("end_while".into()));
break_block.append(context).branch(final_block, vec![]);
cond_end_block.append(context).conditional_branch(
cond_value,
body_block,
final_block,
vec![],
vec![],
);
self.current_block = final_block;
let val = ConstantContent::get_unit(context).add_metadatum(context, span_md_idx);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
pub(crate) fn get_function_var(&self, context: &mut Context, name: &str) -> Option<LocalVar> {
self.lexical_map
.get(name)
.and_then(|local_name| self.function.get_local_var(context, local_name))
}
pub(crate) fn get_function_arg(&self, context: &mut Context, name: &str) -> Option<Value> {
self.function.get_arg(context, name)
}
fn compile_const_expr(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
const_decl: &TyConstantDecl,
span_md_idx: Option<MetadataIndex>,
) -> Result<TerminatorValue, CompileError> {
let result = self
.compile_var_expr(
context,
&Some(const_decl.call_path.clone()),
const_decl.name(),
span_md_idx,
)
.or(self.compile_const_decl(context, md_mgr, const_decl, span_md_idx, true))?;
if let Some(TypeContent::StringSlice) = result
.value
.get_type(context)
.map(|t| t.get_content(context))
{
return Err(CompileError::TypeNotAllowed {
reason: sway_error::error::TypeNotAllowedReason::StringSliceInConst,
span: const_decl.span.clone(),
});
}
Ok(result)
}
fn compile_config_expr(
&mut self,
context: &mut Context,
decl: &TyConfigurableDecl,
span_md_idx: Option<MetadataIndex>,
) -> Result<TerminatorValue, CompileError> {
let name = decl.call_path.suffix.as_str();
let val = self
.current_block
.append(context)
.get_config(self.module, name.to_string())
.add_metadatum(context, span_md_idx);
Ok(TerminatorValue::new(CompiledValue::InMemory(val), context))
}
fn compile_var_expr(
&mut self,
context: &mut Context,
call_path: &Option<CallPath>,
name: &Ident,
span_md_idx: Option<MetadataIndex>,
) -> Result<TerminatorValue, CompileError> {
let call_path = call_path
.clone()
.unwrap_or_else(|| CallPath::from(name.clone()));
if let Some(var) = self.get_function_var(context, name.as_str()) {
let val = self
.current_block
.append(context)
.get_local(var)
.add_metadatum(context, span_md_idx);
Ok(TerminatorValue::new(CompiledValue::InMemory(val), context))
} else if let Some(val) = self.function.get_arg(context, name.as_str()) {
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
} else if let Some(global_val) = self
.module
.get_global_variable(context, &call_path.as_vec_string())
{
let val = self
.current_block
.append(context)
.get_global(global_val)
.add_metadatum(context, span_md_idx);
Ok(TerminatorValue::new(CompiledValue::InMemory(val), context))
} else if self
.module
.get_config(context, &call_path.suffix.to_string())
.is_some()
{
let name = call_path.suffix.to_string();
let config_val = Value::new_instruction(
context,
self.current_block,
InstOp::GetConfig(self.module, name),
);
Ok(TerminatorValue::new(
CompiledValue::InMemory(config_val),
context,
))
} else {
Err(CompileError::InternalOwned(
format!("Unable to resolve variable '{}'.", name.as_str()),
Span::dummy(),
))
}
}
fn compile_var_decl(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
ast_var_decl: &ty::TyVariableDecl,
span_md_idx: Option<MetadataIndex>,
) -> Result<Option<TerminatorValue>, CompileError> {
let ty::TyVariableDecl {
name,
body,
mutability,
..
} = ast_var_decl;
if matches!(
&&*self.engines.te().get_unaliased(body.return_type),
TypeInfo::ContractCaller { .. }
) {
return Ok(None);
}
let init_val = self.compile_expression_to_register(context, md_mgr, body);
let return_type = convert_resolved_type_id(
self.engines,
context,
md_mgr,
self.module,
Some(self),
body.return_type,
&body.span,
)?;
let mutable = matches!(mutability, ty::VariableMutability::Mutable);
let local_name = self.lexical_map.insert(name.as_str().to_owned());
let local_var = self
.function
.new_local_var(context, local_name.clone(), return_type, None, mutable)
.map_err(|ir_error| CompileError::InternalOwned(ir_error.to_string(), Span::dummy()))?;
let val = init_val?;
if val.is_terminator {
return Ok(Some(val));
};
let var_ty = local_var.get_type(context);
if var_ty.size(context).in_bytes() > 0 {
let local_ptr = self
.current_block
.append(context)
.get_local(local_var)
.add_metadatum(context, span_md_idx);
self.current_block
.append(context)
.store(local_ptr, val.value.expect_register())
.add_metadatum(context, span_md_idx);
}
Ok(None)
}
fn compile_const_decl(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
ast_const_decl: &ty::TyConstantDecl,
span_md_idx: Option<MetadataIndex>,
is_expression: bool,
) -> Result<TerminatorValue, CompileError> {
let ty::TyConstantDecl {
call_path, value, ..
} = ast_const_decl;
if let Some(value) = value {
let const_expr_val = compile_constant_expression(
self.engines,
context,
md_mgr,
self.module,
None,
Some(self),
value,
)?;
if is_expression {
Ok(TerminatorValue::new(
CompiledValue::InRegister(const_expr_val),
context,
))
} else {
let local_name = self
.lexical_map
.insert(call_path.suffix.as_str().to_owned());
let return_type = convert_resolved_type_id(
self.engines,
context,
md_mgr,
self.module,
Some(self),
value.return_type,
&value.span,
)?;
let local_var = self
.function
.new_local_var(context, local_name, return_type, None, false)
.map_err(|ir_error| {
CompileError::InternalOwned(ir_error.to_string(), Span::dummy())
})?;
let val = const_expr_val;
if val.is_terminator(context) {
return Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
));
};
let var_ty = local_var.get_type(context);
Ok(if var_ty.size(context).in_bytes() > 0 {
let local_val = self
.current_block
.append(context)
.get_local(local_var)
.add_metadatum(context, span_md_idx);
let val = self
.current_block
.append(context)
.store(local_val, val)
.add_metadatum(context, span_md_idx);
TerminatorValue::new(CompiledValue::InRegister(val), context)
} else {
TerminatorValue::new(CompiledValue::InRegister(val), context)
})
}
} else {
unreachable!("cannot compile const declaration without an expression")
}
}
fn compile_reassignment(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
ast_reassignment: &ty::TyReassignment,
span_md_idx: Option<MetadataIndex>,
) -> Result<TerminatorValue, CompileError> {
let rhs = return_on_termination_or_extract!(self.compile_expression_to_register(
context,
md_mgr,
&ast_reassignment.rhs,
)?)
.expect_register();
let lhs_ptr = match &ast_reassignment.lhs {
ty::TyReassignmentTarget::ElementAccess {
base_name,
base_type,
indices,
} => {
let name = self
.lexical_map
.get(base_name.as_str())
.expect("All local symbols must be in the lexical symbol map.");
let lhs_val = self
.function
.get_local_var(context, name)
.map(|var| {
self.current_block
.append(context)
.get_local(var)
.add_metadatum(context, span_md_idx)
})
.or_else(||
self.function
.args_iter(context)
.find_map(|(arg_name, arg_val)| (arg_name == name).then_some(*arg_val)))
.ok_or_else(|| {
CompileError::InternalOwned(
format!("Variable not found: {name}."),
base_name.span(),
)
})?;
if indices.is_empty() {
if self.ref_mut_args.contains(name) {
self.current_block
.append(context)
.load(lhs_val)
.add_metadatum(context, span_md_idx)
} else {
lhs_val
}
} else {
let (terminator, gep_indices) =
self.compile_indices(context, md_mgr, *base_type, indices)?;
if let Some(terminator) = terminator {
return Ok(terminator);
}
let field_type = rhs.get_type(context).ok_or_else(|| {
CompileError::Internal(
"Failed to determine type of reassignment.",
base_name.span(),
)
})?;
self.current_block
.append(context)
.get_elem_ptr(lhs_val, field_type, gep_indices)
.add_metadatum(context, span_md_idx)
}
}
ty::TyReassignmentTarget::DerefAccess {
exp: dereference_exp,
indices,
} => {
let TyExpressionVariant::Deref(reference_exp) = &dereference_exp.expression else {
return Err(CompileError::Internal(
"Left-hand side of the reassignment must be dereferencing.",
dereference_exp.span.clone(),
));
};
let (ptr, _) = self.compile_deref_up_to_ptr(context, md_mgr, reference_exp)?;
if indices.is_empty() {
return_on_termination_or_extract!(ptr).expect_memory()
} else {
let (terminator, gep_indices) = self.compile_indices(
context,
md_mgr,
dereference_exp.return_type,
indices,
)?;
if let Some(terminator) = terminator {
return Ok(terminator);
}
let field_type = rhs.get_type(context).ok_or_else(|| {
CompileError::Internal(
"Failed to determine type of reassignment.",
dereference_exp.span.clone(),
)
})?;
self.current_block
.append(context)
.get_elem_ptr(ptr.value.expect_memory(), field_type, gep_indices)
.add_metadatum(context, span_md_idx)
}
}
};
self.current_block
.append(context)
.store(lhs_ptr, rhs)
.add_metadatum(context, span_md_idx);
let val = ConstantContent::get_unit(context).add_metadatum(context, span_md_idx);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
fn compile_indices(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
base_type: TypeId,
indices: &[ProjectionKind],
) -> Result<(Option<TerminatorValue>, Vec<Value>), CompileError> {
let mut gep_indices = Vec::<Value>::new();
let mut cur_type_id = base_type;
for idx_kind in indices.iter() {
while let TypeInfo::Ref {
referenced_type, ..
} = &*self.engines.te().get_unaliased(cur_type_id)
{
cur_type_id = referenced_type.type_id;
}
let cur_type_info_arc = self.engines.te().get_unaliased(cur_type_id);
let cur_type_info = &*cur_type_info_arc;
match (idx_kind, cur_type_info) {
(
ProjectionKind::StructField {
name: idx_name,
field_to_access: _,
},
TypeInfo::Struct(decl_ref),
) => {
let struct_decl = self.engines.de().get_struct(decl_ref);
match struct_decl.get_field_index_and_type(idx_name) {
None => {
return Err(CompileError::InternalOwned(
format!(
"Unknown field name \"{idx_name}\" for struct \"{}\" \
in reassignment.",
struct_decl.call_path.suffix.as_str(),
),
idx_name.span(),
))
}
Some((field_idx, field_type_id)) => {
cur_type_id = field_type_id;
gep_indices.push(ConstantContent::get_uint(context, 64, field_idx));
}
}
}
(ProjectionKind::TupleField { index, .. }, TypeInfo::Tuple(field_tys)) => {
cur_type_id = field_tys[*index].type_id;
gep_indices.push(ConstantContent::get_uint(context, 64, *index as u64));
}
(ProjectionKind::ArrayIndex { index, .. }, TypeInfo::Array(elem_ty, _)) => {
cur_type_id = elem_ty.type_id;
let val = self.compile_expression_to_register(context, md_mgr, index)?;
if val.is_terminator {
return Ok((Some(val), vec![]));
}
gep_indices.push(val.value.expect_register());
}
_ => {
return Err(CompileError::Internal(
"Unknown field in reassignment.",
idx_kind.span(),
))
}
}
}
Ok((None, gep_indices))
}
fn compile_array_repeat_expr(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
elem_type: TypeId,
value_expr: &ty::TyExpression,
length_expr: &ty::TyExpression,
span_md_idx: Option<MetadataIndex>,
) -> Result<TerminatorValue, CompileError> {
let elem_type = convert_resolved_typeid_no_span(
self.engines,
context,
md_mgr,
self.module,
Some(self),
elem_type,
)?;
let length_as_u64 = compile_constant_expression_to_constant(
self.engines,
context,
md_mgr,
self.module,
None,
Some(self),
length_expr,
)?;
let length_as_u64 = length_as_u64
.get_content(context)
.as_uint()
.expect("safe by type-checking, that only allows `u64` as an array length");
let array_type = Type::new_array(context, elem_type, length_as_u64);
let temp_name = self.lexical_map.insert_unique_named("array_init");
let array_local_var = self
.function
.new_local_var(context, temp_name, array_type, None, false)
.map_err(|ir_error| CompileError::InternalOwned(ir_error.to_string(), Span::dummy()))?;
let array_val = self
.current_block
.append(context)
.get_local(array_local_var)
.add_metadatum(context, span_md_idx);
enum InitializationKind {
ZeroedConst,
InRegisterValue(Value),
}
let init_kind = if Self::is_single_store_initializeable(context, array_type) {
InitializationKind::InRegisterValue(
return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, value_expr)?
)
.expect_register(),
)
} else {
match compile_constant_expression_to_constant(
self.engines,
context,
md_mgr,
self.module,
None,
Some(self),
value_expr,
) {
Ok(constant) if constant.is_runtime_zeroed(context) => {
InitializationKind::ZeroedConst
}
Ok(constant) if constant.is_copy_type(context) => {
InitializationKind::InRegisterValue(Value::new_constant(context, constant))
}
_ => InitializationKind::InRegisterValue(
return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, value_expr)?
)
.expect_register(),
),
}
};
let array_val = match init_kind {
InitializationKind::ZeroedConst => {
self.current_block
.append(context)
.mem_clear_val(array_val)
.add_metadatum(context, span_md_idx);
array_val
}
InitializationKind::InRegisterValue(repeated_item_value) => {
let init_aggr = self
.current_block
.append(context)
.init_aggr(
array_val,
std::iter::repeat_n(repeated_item_value, length_as_u64 as usize).collect(),
)
.add_metadatum(context, span_md_idx);
init_aggr
}
};
Ok(TerminatorValue::new(
CompiledValue::InMemory(array_val),
context,
))
}
fn compile_array_explicit_expr(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
elem_type: TypeId,
contents: &[ty::TyExpression],
span_md_idx: Option<MetadataIndex>,
) -> Result<TerminatorValue, CompileError> {
let elem_type = convert_resolved_typeid_no_span(
self.engines,
context,
md_mgr,
self.module,
Some(self),
elem_type,
)?;
let array_type = Type::new_array(context, elem_type, contents.len() as u64);
let temp_name = self.lexical_map.insert_unique_named("array_init");
let array_var = self
.function
.new_local_var(context, temp_name, array_type, None, false)
.map_err(|ir_error| CompileError::InternalOwned(ir_error.to_string(), Span::dummy()))?;
let array_val = self
.current_block
.append(context)
.get_local(array_var)
.add_metadatum(context, span_md_idx);
if contents.is_empty() {
return Ok(TerminatorValue::new(
CompiledValue::InMemory(array_val),
context,
));
}
fn all_elements_are_same_literal(contents: &[ty::TyExpression]) -> Option<&Literal> {
let first_literal_opt = match &contents.first()?.expression {
TyExpressionVariant::Literal(lit) => Some(lit),
_ => None,
};
if let Some(first_literal) = first_literal_opt {
if contents.iter().skip(1).all(|elm| {
matches!(
&elm.expression,
TyExpressionVariant::Literal(lit) if lit == first_literal
)
}) {
return Some(first_literal);
}
}
None
}
enum InitializationKind {
ZeroedConst,
InRegisterValue(Value),
ElemByElem,
}
let init_kind = if Self::is_single_store_initializeable(context, array_type) {
InitializationKind::ElemByElem
} else {
match all_elements_are_same_literal(contents) {
Some(literal) if literal.is_runtime_zeroed() => InitializationKind::ZeroedConst,
Some(_) => InitializationKind::InRegisterValue(
return_on_termination_or_extract!(self.compile_expression_to_register(
context,
md_mgr,
&contents[0],
)?)
.expect_register(),
),
_ => InitializationKind::ElemByElem,
}
};
let array_val = match init_kind {
InitializationKind::ZeroedConst => {
self.current_block
.append(context)
.mem_clear_val(array_val)
.add_metadatum(context, span_md_idx);
array_val
}
InitializationKind::InRegisterValue(repeated_item_value) => {
let init_aggr = self
.current_block
.append(context)
.init_aggr(
array_val,
std::iter::repeat_n(repeated_item_value, contents.len()).collect(),
)
.add_metadatum(context, span_md_idx);
init_aggr
}
InitializationKind::ElemByElem => {
let mut elem_values = Vec::with_capacity(contents.len());
for elem in contents.iter() {
let elem_val = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, elem)?
);
elem_values.push(elem_val);
}
let init_aggr = self
.current_block
.append(context)
.init_aggr(
array_val,
elem_values
.into_iter()
.map(|val| val.expect_register())
.collect(),
)
.add_metadatum(context, span_md_idx);
init_aggr
}
};
Ok(TerminatorValue::new(
CompiledValue::InMemory(array_val),
context,
))
}
#[allow(clippy::too_many_arguments)]
fn compile_indexing_arrays(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
prefix_expr: &ty::TyExpression,
index_expr: &ty::TyExpression,
span_md_idx: Option<MetadataIndex>,
prefix_val: Value,
index_val: Value,
) -> Result<TerminatorValue, CompileError> {
let index_expr_span = index_expr.span.clone();
let array_type = prefix_val
.get_type(context)
.and_then(|ty| ty.get_pointee_type(context))
.and_then(|ty| ty.is_array(context).then_some(ty))
.ok_or_else(|| {
CompileError::Internal(
"Unsupported array value for index expression.",
prefix_expr.span.clone(),
)
})?;
let elem_type = array_type.get_array_elem_type(context).ok_or_else(|| {
CompileError::Internal(
"Array type is already confirmed as an array. Getting the element type can't fail.",
prefix_expr.span.clone(),
)
})?;
if let Ok(ConstantContent {
value: ConstantValue::Uint(constant_value),
..
}) = compile_constant_expression_to_constant(
self.engines,
context,
md_mgr,
self.module,
None,
Some(self),
index_expr,
)
.map(|c| c.get_content(context))
{
let count = array_type.get_array_len(context).unwrap();
if *constant_value >= count {
return Err(CompileError::ArrayOutOfBounds {
index: *constant_value,
count,
span: index_expr_span,
});
}
}
let val = self
.current_block
.append(context)
.get_elem_ptr(prefix_val, elem_type, vec![index_val])
.add_metadatum(context, span_md_idx);
Ok(TerminatorValue::new(CompiledValue::InMemory(val), context))
}
#[allow(clippy::too_many_arguments)]
fn compile_indexing_slices(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
prefix_expr: &ty::TyExpression,
_index_expr: &ty::TyExpression,
_span_md_idx: Option<MetadataIndex>,
prefix_value: Value,
index_value: Value,
) -> Result<TerminatorValue, CompileError> {
let (ptr, elem_type_id) =
self.ptr_to_first_element(context, prefix_expr, prefix_value, md_mgr)?;
let (ptr_to_elem, _) = self.advance_ptr_n_elements(
context,
md_mgr,
prefix_expr,
ptr,
elem_type_id,
index_value,
)?;
Ok(TerminatorValue::new(
CompiledValue::InMemory(ptr_to_elem),
context,
))
}
fn compile_indexing(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
prefix_expr: &ty::TyExpression,
index_expr: &ty::TyExpression,
span_md_idx: Option<MetadataIndex>,
) -> Result<TerminatorValue, CompileError> {
let prefix_value = return_on_termination_or_extract!(self.compile_expression_to_memory(
context,
md_mgr,
prefix_expr
)?)
.expect_memory();
let prefix_type = prefix_value.get_type(context).unwrap();
let index_value = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, index_expr)?
)
.expect_register();
match prefix_type.get_content(context) {
TypeContent::TypedPointer(p) => match p.get_content(context) {
TypeContent::Array(..) => self.compile_indexing_arrays(
context,
md_mgr,
prefix_expr,
index_expr,
span_md_idx,
prefix_value,
index_value,
),
_ => todo!(),
},
TypeContent::TypedSlice(..) => self.compile_indexing_slices(
context,
md_mgr,
prefix_expr,
index_expr,
span_md_idx,
prefix_value,
index_value,
),
_ => Err(CompileError::Internal(
"Expression is not indexeable",
prefix_expr.span.clone(),
)),
}
}
fn compile_struct_expr(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
struct_init_exp: &TyExpression,
fields: &[ty::TyStructExpressionField],
span_md_idx: Option<MetadataIndex>,
) -> Result<TerminatorValue, CompileError> {
self.compile_product_type_initialization(
"struct_init",
context,
md_mgr,
struct_init_exp,
fields.iter().map(|f| &f.value).collect_vec().as_slice(),
span_md_idx,
)
}
fn compile_struct_field_expr(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
ast_struct_expr: &ty::TyExpression,
struct_type_id: TypeId,
ast_field: &ty::TyStructField,
span_md_idx: Option<MetadataIndex>,
) -> Result<TerminatorValue, CompileError> {
let struct_val = return_on_termination_or_extract!(self.compile_expression_to_memory(
context,
md_mgr,
ast_struct_expr,
)?)
.expect_memory();
let decl = self.engines.te().get_unaliased(struct_type_id);
let TypeInfo::Struct(decl_ref) = &*decl else {
return Err(CompileError::Internal(
"Unknown struct in field expression.",
ast_field.span.clone(),
));
};
let struct_decl = self.engines.de().get_struct(decl_ref);
let (field_idx, field_type_id) = struct_decl
.get_field_index_and_type(&ast_field.name)
.ok_or_else(|| {
CompileError::InternalOwned(
format!(
"Unknown field name '{}' for struct '{}' in field expression.",
struct_decl.call_path.suffix.as_str(),
ast_field.name
),
ast_field.span.clone(),
)
})?;
let field_type = convert_resolved_type_id(
self.engines,
context,
md_mgr,
self.module,
Some(self),
field_type_id,
&ast_field.span,
)?;
let val = self
.current_block
.append(context)
.get_elem_ptr_with_idx(struct_val, field_type, field_idx)
.add_metadatum(context, span_md_idx);
Ok(TerminatorValue::new(CompiledValue::InMemory(val), context))
}
fn compile_enum_expr(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
enum_decl: &ty::TyEnumDecl,
tag: usize,
contents: Option<&ty::TyExpression>,
) -> Result<TerminatorValue, CompileError> {
let span_md_idx = md_mgr.span_to_md(context, &enum_decl.span);
let enum_type = create_tagged_union_type(
self.engines,
context,
md_mgr,
self.module,
&enum_decl.variants,
)?;
let tag_value =
ConstantContent::get_uint(context, 64, tag as u64).add_metadatum(context, span_md_idx);
let temp_name = self.lexical_map.insert_anon();
let enum_var = self
.function
.new_local_var(context, temp_name, enum_type, None, false)
.map_err(|ir_error| CompileError::InternalOwned(ir_error.to_string(), Span::dummy()))?;
let enum_ptr = self
.current_block
.append(context)
.get_local(enum_var)
.add_metadatum(context, span_md_idx);
let u64_ty = Type::get_uint64(context);
let tag_gep_val = self
.current_block
.append(context)
.get_elem_ptr_with_idx(enum_ptr, u64_ty, 0)
.add_metadatum(context, span_md_idx);
self.current_block
.append(context)
.store(tag_gep_val, tag_value)
.add_metadatum(context, span_md_idx);
let field_tys = enum_type.get_field_types(context);
if field_tys.len() != 1 {
if let Some(contents_expr) = contents {
let contents_value = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, contents_expr)?
)
.expect_register();
let contents_type = contents_value.get_type(context).ok_or_else(|| {
CompileError::Internal(
"Unable to get type for enum contents.",
enum_decl.span.clone(),
)
})?;
let variant_type = field_tys[1].get_field_type(context, tag as u64).unwrap();
if contents_type != variant_type {
return Err(CompileError::Internal(
format!(
"Expression type \"{}\" and Variant type \"{}\" do not match",
contents_type.as_string(context),
variant_type.as_string(context)
)
.leak(),
contents_expr.span.clone(),
));
}
let gep_val = self
.current_block
.append(context)
.get_elem_ptr_with_indices(enum_ptr, contents_type, &[1, tag as u64])
.add_metadatum(context, span_md_idx);
self.current_block
.append(context)
.store(gep_val, contents_value)
.add_metadatum(context, span_md_idx);
}
}
Ok(TerminatorValue::new(
CompiledValue::InMemory(enum_ptr),
context,
))
}
fn compile_tuple_from_values(
&mut self,
context: &mut Context,
init_values: Vec<Value>,
init_types: Vec<Type>,
span_md_idx: Option<MetadataIndex>,
) -> Result<Value, CompileError> {
assert!(init_values.len() == init_types.len());
assert!(!init_values.is_empty());
let tuple_type = Type::new_struct(context, init_types.clone());
let temp_name = self.lexical_map.insert_anon();
let tuple_var = self
.function
.new_local_var(context, temp_name, tuple_type, None, false)
.map_err(|ir_error| CompileError::InternalOwned(ir_error.to_string(), Span::dummy()))?;
let tuple_val = self
.current_block
.append(context)
.get_local(tuple_var)
.add_metadatum(context, span_md_idx);
init_values
.into_iter()
.zip(init_types)
.enumerate()
.for_each(|(insert_idx, (field_val, field_type))| {
let gep_val = self
.current_block
.append(context)
.get_elem_ptr_with_idx(tuple_val, field_type, insert_idx as u64)
.add_metadatum(context, span_md_idx);
self.current_block
.append(context)
.store(gep_val, field_val)
.add_metadatum(context, span_md_idx);
});
Ok(tuple_val)
}
fn compile_tuple_expr(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
tuple_init_exp: &TyExpression,
fields: &[ty::TyExpression],
span_md_idx: Option<MetadataIndex>,
) -> Result<TerminatorValue, CompileError> {
if fields.is_empty() {
let val = ConstantContent::get_unit(context).add_metadatum(context, span_md_idx);
return Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
));
}
self.compile_product_type_initialization(
"tuple_init",
context,
md_mgr,
tuple_init_exp,
fields.iter().collect_vec().as_slice(),
span_md_idx,
)
}
fn compile_tuple_elem_expr(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
tuple: &ty::TyExpression,
tuple_type: TypeId,
idx: usize,
span: Span,
) -> Result<TerminatorValue, CompileError> {
let tuple_value = return_on_termination_or_extract!(
self.compile_expression_to_memory(context, md_mgr, tuple)?
)
.expect_memory();
let tuple_type = convert_resolved_type_id(
self.engines,
context,
md_mgr,
self.module,
Some(self),
tuple_type,
&span,
)?;
let val = tuple_type
.get_field_type(context, idx as u64)
.map(|field_type| {
let span_md_idx = md_mgr.span_to_md(context, &span);
self.current_block
.append(context)
.get_elem_ptr_with_idx(tuple_value, field_type, idx as u64)
.add_metadatum(context, span_md_idx)
})
.ok_or(CompileError::Internal(
"Invalid (non-aggregate?) tuple type for TupleElemAccess.",
span,
))?;
Ok(TerminatorValue::new(CompiledValue::InMemory(val), context))
}
fn compile_product_type_initialization(
&mut self,
anon_name: &str,
context: &mut Context,
md_mgr: &mut MetadataManager,
init_exp: &TyExpression,
fields: &[&ty::TyExpression],
span_md_idx: Option<MetadataIndex>,
) -> Result<TerminatorValue, CompileError> {
let mut field_types = Vec::with_capacity(fields.len());
for field in fields.iter() {
let field_type = convert_resolved_typeid_no_span(
self.engines,
context,
md_mgr,
self.module,
Some(self),
field.return_type,
)?;
field_types.push(field_type);
}
let struct_type = Type::new_struct(context, field_types.clone());
let temp_name = self.lexical_map.insert_unique_named(anon_name);
let struct_var = self
.function
.new_local_var(context, temp_name, struct_type, None, false)
.map_err(|ir_error| CompileError::InternalOwned(ir_error.to_string(), Span::dummy()))?;
let struct_val = self
.current_block
.append(context)
.get_local(struct_var)
.add_metadatum(context, span_md_idx);
if field_types.is_empty() {
return Ok(TerminatorValue::new(
CompiledValue::InMemory(struct_val),
context,
));
}
enum InitializationKind {
ZeroedConst,
Const(Constant),
FieldByField,
}
let init_kind = if Self::is_single_store_initializeable(context, struct_type) {
InitializationKind::FieldByField
} else {
match compile_constant_expression_to_constant(
self.engines,
context,
md_mgr,
self.module,
None,
Some(self),
init_exp,
) {
Ok(constant) if constant.is_runtime_zeroed(context) => {
InitializationKind::ZeroedConst
}
Ok(constant)
if Self::is_suitable_const_for_memcpy_aggregate_init(context, constant) =>
{
InitializationKind::Const(constant)
}
_ => InitializationKind::FieldByField,
}
};
let struct_val = match init_kind {
InitializationKind::ZeroedConst => {
self.current_block
.append(context)
.mem_clear_val(struct_val)
.add_metadatum(context, span_md_idx);
struct_val
}
InitializationKind::Const(_constant) => {
todo!("Implement constant product type aggregate initialization via `memcpy`.");
}
InitializationKind::FieldByField => {
let mut field_values = Vec::with_capacity(fields.len());
for field in fields.iter() {
let field_val = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, field)?
);
field_values.push(field_val);
}
let init_aggr = self
.current_block
.append(context)
.init_aggr(
struct_val,
field_values
.into_iter()
.map(|val| val.expect_register())
.collect(),
)
.add_metadatum(context, span_md_idx);
init_aggr
}
};
Ok(TerminatorValue::new(
CompiledValue::InMemory(struct_val),
context,
))
}
fn is_suitable_const_for_memcpy_aggregate_init(
_context: &Context,
_constant: Constant,
) -> bool {
false
}
fn is_single_store_initializeable(context: &Context, r#type: Type) -> bool {
match r#type.get_content(context) {
TypeContent::Never => false,
TypeContent::Unit => true,
TypeContent::Bool => true,
TypeContent::Uint(size) => *size <= 64,
TypeContent::B256 => false,
TypeContent::StringSlice => false,
TypeContent::StringArray(length) => *length == 1,
TypeContent::Array(elem_ty, length) => {
*length == 1 && Self::is_single_store_initializeable(context, *elem_ty)
}
TypeContent::Union(variants) => {
variants.len() == 1 && Self::is_single_store_initializeable(context, variants[0])
}
TypeContent::Struct(fields) => {
fields.len() == 1 && Self::is_single_store_initializeable(context, fields[0])
}
TypeContent::Slice => false,
TypeContent::Pointer => true,
TypeContent::TypedPointer(_) => true,
TypeContent::TypedSlice(_) => false,
}
}
#[allow(clippy::too_many_arguments)]
fn compile_storage_access(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
storage_field_path: Vec<String>,
struct_field_names: Vec<String>,
key: Option<U256>,
fields: &[ty::TyStorageAccessDescriptor],
span_md_idx: Option<MetadataIndex>,
) -> Result<TerminatorValue, CompileError> {
let base_type = fields[0].type_id;
let field_indices = get_indices_for_struct_access(
self.engines.te(),
self.engines.de(),
base_type,
&fields[1..],
)?;
let base_type = convert_resolved_typeid_no_span(
self.engines,
context,
md_mgr,
self.module,
Some(self),
base_type,
)?;
self.compile_get_storage_key(
context,
md_mgr,
storage_field_path,
struct_field_names,
key,
&field_indices,
&base_type,
span_md_idx,
)
}
#[allow(clippy::too_many_arguments)]
fn compile_asm_expr(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
registers: &[ty::TyAsmRegisterDeclaration],
body: &[AsmOp],
return_type: TypeId,
returns: Option<&(AsmRegister, Span)>,
whole_block_span_md_idx: Option<MetadataIndex>,
) -> Result<TerminatorValue, CompileError> {
let mut compiled_registers = Vec::<AsmArg>::new();
for reg in registers.iter() {
let (init, name) = match reg {
ty::TyAsmRegisterDeclaration {
initializer, name, ..
} if initializer.is_none() => (None, name),
ty::TyAsmRegisterDeclaration {
initializer, name, ..
} => {
let init_expr = initializer.as_ref().unwrap();
let initializer_val = return_on_termination_or_extract!(
self.compile_expression_to_register(context, md_mgr, init_expr)?
)
.expect_register();
(Some(initializer_val), name)
}
};
compiled_registers.push(AsmArg {
name: name.clone(),
initializer: init,
});
}
let body = body
.iter()
.map(
|AsmOp {
op_name,
op_args,
immediate,
span,
}| AsmInstruction {
op_name: op_name.clone(),
args: op_args.clone(),
immediate: immediate.clone(),
metadata: md_mgr.span_to_md(context, span),
},
)
.collect();
let returns = returns.as_ref().map(|(reg, asm_reg_span)| {
if asm_reg_span == &Span::dummy() {
Ident::new_no_span(reg.name.clone())
} else {
Ident::new(asm_reg_span.clone())
}
});
let return_type = convert_resolved_typeid_no_span(
self.engines,
context,
md_mgr,
self.module,
Some(self),
return_type,
)?;
let val = self
.current_block
.append(context)
.asm_block(compiled_registers, body, return_type, returns)
.add_metadatum(context, whole_block_span_md_idx);
Ok(TerminatorValue::new(
CompiledValue::InRegister(val),
context,
))
}
#[allow(clippy::too_many_arguments)]
fn compile_get_storage_key(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
storage_field_path: Vec<String>,
struct_field_names: Vec<String>,
key: Option<U256>,
indices: &[u64],
base_type: &Type,
span_md_idx: Option<MetadataIndex>,
) -> Result<TerminatorValue, CompileError> {
let storage_field_key = get_storage_key(&storage_field_path, key);
let (path, field_id) =
get_storage_field_path_and_field_id(&storage_field_path, &struct_field_names);
let storage_key = match self.module.get_storage_key(context, &path) {
Some(storage_key) => *storage_key,
None => {
let offset_in_bytes = match base_type.get_indexed_offset(context, indices) {
Some(offset_in_bytes) => {
assert!(
offset_in_bytes % 8 == 0,
"Expected struct fields to be aligned to word boundary. The field offset in bytes was {offset_in_bytes}.",
);
offset_in_bytes
}
None => {
return Err(CompileError::Internal(
"Cannot get the offset within the slot while compiling getting of storage key.",
md_mgr
.md_to_span(context, span_md_idx)
.unwrap_or_else(Span::dummy),
))
}
};
let storage_key = if context.experimental.dynamic_storage {
StorageKey::new(
context,
storage_field_key.into(),
offset_in_bytes,
storage_field_key.into(),
)
} else {
let offset_in_words = offset_in_bytes / 8;
let (slot, offset_within_slot) = {
let offset_in_slots = offset_in_words / 4;
let offset_remaining = offset_in_words % 4;
(
add_to_b256(storage_field_key, offset_in_slots),
offset_remaining,
)
};
StorageKey::new(context, slot.into(), offset_within_slot, field_id.into())
};
self.module.add_storage_key(context, path, storage_key);
storage_key
}
};
let get_storage_key_inst = self
.current_block
.append(context)
.get_storage_key(storage_key)
.add_metadatum(context, span_md_idx);
Ok(TerminatorValue::new(
CompiledValue::InMemory(get_storage_key_inst),
context,
))
}
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum MemoryRepresentation {
Padding { len_in_bytes: u64 },
Blob { len_in_bytes: u64 },
And(Vec<MemoryRepresentation>),
Or(Vec<MemoryRepresentation>),
Array(Box<MemoryRepresentation>, u64),
}
impl std::fmt::Debug for MemoryRepresentation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Padding { len_in_bytes } => f.write_fmt(format_args!("p{}", len_in_bytes)),
Self::Blob { len_in_bytes } => f.write_fmt(format_args!("b{}", len_in_bytes)),
Self::And(items) => {
f.write_str("{").unwrap();
let mut first = true;
for item in items {
if !first {
f.write_str(",").unwrap();
}
first = false;
item.fmt(f).unwrap();
}
f.write_str("}").unwrap();
Ok(())
}
Self::Or(items) => {
f.write_str("(").unwrap();
let mut first = true;
for item in items {
if !first {
f.write_str("|").unwrap();
}
first = false;
item.fmt(f).unwrap();
}
f.write_str(")").unwrap();
Ok(())
}
Self::Array(item, len) => {
f.write_str("[").unwrap();
item.fmt(f).unwrap();
f.write_fmt(format_args!(";{}]", len))
}
}
}
}
impl MemoryRepresentation {
pub fn len_in_bytes(&self) -> u64 {
match self {
MemoryRepresentation::Padding { len_in_bytes } => *len_in_bytes,
MemoryRepresentation::Blob { len_in_bytes, .. } => *len_in_bytes,
MemoryRepresentation::And(items) => items.iter().map(|x| x.len_in_bytes()).sum(),
MemoryRepresentation::Or(items) => items
.iter()
.map(|x| x.len_in_bytes())
.max()
.unwrap_or_default(),
MemoryRepresentation::Array(item, len) => item.len_in_bytes() * *len,
}
}
}
pub fn get_runtime_representation(ctx: &Context, t: Type) -> MemoryRepresentation {
match t.get_content(ctx) {
TypeContent::Unit | TypeContent::Never => MemoryRepresentation::And(vec![]),
TypeContent::Bool => MemoryRepresentation::Blob { len_in_bytes: 1 },
TypeContent::Uint(8) => MemoryRepresentation::Blob { len_in_bytes: 1 },
TypeContent::Uint(64) => MemoryRepresentation::Blob { len_in_bytes: 8 },
TypeContent::Uint(256) => MemoryRepresentation::Blob { len_in_bytes: 32 },
TypeContent::B256 => MemoryRepresentation::Blob { len_in_bytes: 32 },
TypeContent::Struct(fields) => {
let mut items = vec![];
let mut offset_in_bytes = 0;
for idx in 0..fields.len() {
let (position_in_bytes, t) =
t.get_struct_field_offset_and_type(ctx, idx as u64).unwrap();
assert!(offset_in_bytes == position_in_bytes);
let field_mem_rep = get_runtime_representation(ctx, t);
let field_len_in_bytes = field_mem_rep.len_in_bytes();
items.push(field_mem_rep);
offset_in_bytes += field_len_in_bytes;
if !offset_in_bytes.is_multiple_of(8) {
let next = offset_in_bytes.next_multiple_of(8);
items.push(MemoryRepresentation::Padding {
len_in_bytes: next.checked_sub(offset_in_bytes).unwrap(),
});
offset_in_bytes = next;
}
}
MemoryRepresentation::And(items)
}
TypeContent::Union(variants) => {
let mut items = variants
.iter()
.map(|variant| get_runtime_representation(ctx, *variant))
.collect::<Vec<_>>();
let biggest_len_in_bytes = items
.iter()
.map(|x| x.len_in_bytes())
.max()
.unwrap_or_default()
.max(8);
for item in items.iter_mut() {
let item_len_in_bytes = item.len_in_bytes();
if item_len_in_bytes == biggest_len_in_bytes {
continue;
}
let padding = MemoryRepresentation::Padding {
len_in_bytes: biggest_len_in_bytes - item_len_in_bytes,
};
if let MemoryRepresentation::And(old_items) = item {
old_items.push(padding);
} else {
*item = MemoryRepresentation::And(vec![item.clone(), padding])
}
}
MemoryRepresentation::Or(items)
}
TypeContent::StringArray(len) => {
if ctx.experimental.str_array_no_padding {
MemoryRepresentation::Blob { len_in_bytes: *len }
} else {
let item = MemoryRepresentation::Blob { len_in_bytes: *len };
let item_len_as_bytes = item.len_in_bytes();
if !item_len_as_bytes.is_multiple_of(8) {
MemoryRepresentation::And(vec![
item,
MemoryRepresentation::Padding {
len_in_bytes: item_len_as_bytes.next_multiple_of(8) - item_len_as_bytes,
},
])
} else {
item
}
}
}
TypeContent::Array(t, len) => {
let item = get_runtime_representation(ctx, *t);
let total_len_in_bytes = item.len_in_bytes() * len;
if !total_len_in_bytes.is_multiple_of(8) {
MemoryRepresentation::And(vec![
MemoryRepresentation::Array(Box::new(item), *len),
MemoryRepresentation::Padding {
len_in_bytes: total_len_in_bytes.next_multiple_of(8) - total_len_in_bytes,
},
])
} else {
MemoryRepresentation::Array(Box::new(item), *len)
}
}
TypeContent::Pointer | TypeContent::TypedPointer(_) => {
MemoryRepresentation::Blob { len_in_bytes: 8 }
}
TypeContent::Slice => MemoryRepresentation::Blob { len_in_bytes: 16 },
TypeContent::TypedSlice(_) => MemoryRepresentation::Blob { len_in_bytes: 16 },
x => todo!("{x:#?}"),
}
}
pub fn get_memory_id(ctx: &Context, t: Type) -> u64 {
let r = get_runtime_representation(ctx, t);
use std::hash::Hasher;
let mut state = DefaultHasher::default();
r.hash(&mut state);
state.finish()
}
pub fn get_encoding_representation_by_id(
engines: &Engines,
type_id: TypeId,
) -> Option<MemoryRepresentation> {
get_encoding_representation(engines, &engines.te().get(type_id))
}
pub fn get_encoding_representation(
engines: &Engines,
type_info: &TypeInfo,
) -> Option<MemoryRepresentation> {
match type_info {
TypeInfo::Never => None,
TypeInfo::Boolean => Some(MemoryRepresentation::Blob { len_in_bytes: 1 }),
TypeInfo::UnsignedInteger(IntegerBits::Eight) => {
Some(MemoryRepresentation::Blob { len_in_bytes: 1 })
}
TypeInfo::UnsignedInteger(IntegerBits::Sixteen) => {
Some(MemoryRepresentation::Blob { len_in_bytes: 2 })
}
TypeInfo::UnsignedInteger(IntegerBits::ThirtyTwo) => {
Some(MemoryRepresentation::Blob { len_in_bytes: 4 })
}
TypeInfo::UnsignedInteger(IntegerBits::SixtyFour) => {
Some(MemoryRepresentation::Blob { len_in_bytes: 8 })
}
TypeInfo::UnsignedInteger(IntegerBits::V256) => {
Some(MemoryRepresentation::Blob { len_in_bytes: 32 })
}
TypeInfo::B256 => Some(MemoryRepresentation::Blob { len_in_bytes: 32 }),
TypeInfo::Tuple(fields) => {
let items = fields
.iter()
.map(|field| get_encoding_representation_by_id(engines, field.type_id))
.collect::<Option<Vec<_>>>()?;
Some(MemoryRepresentation::And(items))
}
TypeInfo::Struct(id) => {
let decl = engines.de().get(id);
let items = decl
.fields
.iter()
.map(|field| {
get_encoding_representation_by_id(engines, field.type_argument.type_id)
})
.collect::<Option<Vec<_>>>()?;
Some(MemoryRepresentation::And(items))
}
TypeInfo::Enum(id) => {
let decl = engines.de().get(id);
if decl.variants.is_empty() {
Some(MemoryRepresentation::Blob { len_in_bytes: 8 })
} else {
let variants = decl
.variants
.iter()
.map(|variant| {
get_encoding_representation_by_id(engines, variant.type_argument.type_id)
})
.collect::<Option<Vec<_>>>()?;
if variants.iter().all(|x| x.len_in_bytes() == 0) {
Some(MemoryRepresentation::And(vec![
MemoryRepresentation::Blob { len_in_bytes: 8 },
]))
} else {
Some(MemoryRepresentation::And(vec![
MemoryRepresentation::Blob { len_in_bytes: 8 },
MemoryRepresentation::Or(variants),
]))
}
}
}
TypeInfo::StringArray(len) => Some(MemoryRepresentation::Blob {
len_in_bytes: len.extract_literal(engines).unwrap(),
}),
TypeInfo::StringSlice => None,
TypeInfo::Array(item, len) => Some(MemoryRepresentation::Array(
Box::new(get_encoding_representation_by_id(engines, item.type_id)?),
len.extract_literal(engines)?,
)),
TypeInfo::RawUntypedPtr => None,
TypeInfo::RawUntypedSlice => None,
TypeInfo::Slice(_) => None,
TypeInfo::Ref { .. } => None,
TypeInfo::Alias { ty, .. } => get_encoding_representation_by_id(engines, ty.type_id),
x => todo!("{x:#?}"),
}
}
pub fn get_encoding_id(engines: &Engines, type_id: TypeId) -> u64 {
use std::hash::Hasher;
if let Some(r) = get_encoding_representation_by_id(engines, type_id) {
let mut state = DefaultHasher::default();
r.hash(&mut state);
state.finish()
} else {
0
}
}