Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement array repeat without repeating item at the AST/CST trees #6901

Merged
merged 11 commits into from
Feb 11, 2025
31 changes: 30 additions & 1 deletion sway-core/src/control_flow_analysis/dead_code_analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1791,7 +1791,7 @@ fn connect_expression<'eng: 'cfg, 'cfg>(
address.span.clone(),
options,
),
Array {
ArrayExplicit {
elem_type: _,
contents,
} => {
Expand All @@ -1818,6 +1818,35 @@ fn connect_expression<'eng: 'cfg, 'cfg>(

Ok(last)
}
ArrayRepeat {
elem_type: _,
value,
length,
} => {
let value_idx = connect_expression(
engines,
&value.expression,
graph,
leaves,
exit_node,
"",
tree_type,
value.span.clone(),
options,
)?;
let length_idx = connect_expression(
engines,
&length.expression,
graph,
leaves,
exit_node,
"",
tree_type,
length.span.clone(),
options,
)?;
Ok([value_idx, length_idx].concat())
}
ArrayIndex { prefix, index } => {
let prefix_idx = connect_expression(
engines,
Expand Down
77 changes: 52 additions & 25 deletions sway-core/src/ir_generation/const_eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,38 @@ pub(crate) fn compile_constant_expression_to_constant(
}
}

fn create_array_from_vec(
lookup: &mut LookupEnv,
elem_type: crate::TypeId,
element_typs: Vec<crate::TypeId>,
element_vals: Vec<Constant>,
) -> Option<Constant> {
let te = lookup.engines.te();
assert!({
let unify_check = UnifyCheck::coercion(lookup.engines);
element_typs
.iter()
.all(|tid| unify_check.check(*tid, elem_type))
});

let arr = create_array_aggregate(
te,
lookup.engines.de(),
lookup.context,
elem_type,
element_typs.len().try_into().unwrap(),
)
.map_or(None, |array_ty| {
Some(Constant::new_array(
lookup.context,
array_ty.get_array_elem_type(lookup.context).unwrap(),
element_vals,
))
});

arr
}

/// Given an environment mapping names to constants,
/// attempt to evaluate a typed expression to a constant.
fn const_eval_typed_expr(
Expand Down Expand Up @@ -413,7 +445,7 @@ fn const_eval_typed_expr(
))
})
}
ty::TyExpressionVariant::Array {
ty::TyExpressionVariant::ArrayExplicit {
elem_type,
contents,
} => {
Expand All @@ -434,30 +466,25 @@ fn const_eval_typed_expr(
assert!(element_typs.len() == contents.len());
assert!(element_vals.len() == contents.len());

let te = lookup.engines.te();
assert!({
let unify_check = UnifyCheck::coercion(lookup.engines);
element_typs
.iter()
.all(|tid| unify_check.check(*tid, *elem_type))
});

let arr = create_array_aggregate(
te,
lookup.engines.de(),
lookup.context,
*elem_type,
element_typs.len().try_into().unwrap(),
)
.map_or(None, |array_ty| {
Some(Constant::new_array(
lookup.context,
array_ty.get_array_elem_type(lookup.context).unwrap(),
element_vals,
))
});

arr
create_array_from_vec(lookup, *elem_type, element_typs, element_vals)
}
ty::TyExpressionVariant::ArrayRepeat {
elem_type,
value,
length,
} => {
let constant = const_eval_typed_expr(lookup, known_consts, value)?.unwrap();
let length = const_eval_typed_expr(lookup, known_consts, length)?
.unwrap()
.as_uint()
.unwrap() as usize;
let element_vals = (0..length).map(|_| constant.clone()).collect::<Vec<_>>();
let element_typs = (0..length).map(|_| value.return_type).collect::<Vec<_>>();

assert!(element_typs.len() == length);
assert!(element_vals.len() == length);

create_array_from_vec(lookup, *elem_type, element_typs, element_vals)
}
ty::TyExpressionVariant::EnumInstantiation {
enum_ref,
Expand Down
200 changes: 147 additions & 53 deletions sway-core/src/ir_generation/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -545,10 +545,24 @@ impl<'eng> FnCompiler<'eng> {
ty::TyExpressionVariant::VariableExpression {
name, call_path, ..
} => self.compile_var_expr(context, call_path, name, span_md_idx),
ty::TyExpressionVariant::Array {
ty::TyExpressionVariant::ArrayExplicit {
elem_type,
contents,
} => self.compile_array_expr(context, md_mgr, *elem_type, contents, span_md_idx),
} => {
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_array_index(context, md_mgr, prefix, index, span_md_idx)
}
Expand Down Expand Up @@ -3635,7 +3649,67 @@ impl<'eng> FnCompiler<'eng> {
Ok(TerminatorValue::new(val, context))
}

fn compile_array_expr(
fn compile_array_repeat_expr(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
elem_type: TypeId,
value: &ty::TyExpression,
length: &ty::TyExpression,
span_md_idx: Option<MetadataIndex>,
) -> Result<TerminatorValue, CompileError> {
let elem_type = convert_resolved_typeid_no_span(
self.engines.te(),
self.engines.de(),
context,
elem_type,
)?;

let length_as_u64 = length.as_literal_u64().unwrap();
let array_type = Type::new_array(context, elem_type, length_as_u64);

let temp_name = self.lexical_map.insert_anon();
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_value = self
.current_block
.append(context)
.get_local(array_local_var)
.add_metadatum(context, span_md_idx);

let value_value = return_on_termination_or_extract!(
self.compile_expression_to_value(context, md_mgr, value)?
);

if length_as_u64 > 5 {
self.compile_array_init_loop(
context,
array_value,
elem_type,
value_value,
length_as_u64,
span_md_idx,
);
} else {
for i in 0..length_as_u64 {
let gep_val = self.current_block.append(context).get_elem_ptr_with_idx(
array_value,
elem_type,
i,
);
self.current_block
.append(context)
.store(gep_val, value_value)
.add_metadatum(context, span_md_idx);
}
}

Ok(TerminatorValue::new(array_value, context))
}

fn compile_array_explicit_expr(
&mut self,
context: &mut Context,
md_mgr: &mut MetadataManager,
Expand Down Expand Up @@ -3700,59 +3774,14 @@ impl<'eng> FnCompiler<'eng> {
})
});
if let Some(const_initializer) = const_initialiser_opt {
// Create a loop to insert const_initializer to all array elements.
let loop_block = self
.function
.create_block(context, Some("array_init_loop".into()));
// The loop begins with 0.
let zero = Constant::new_uint(context, 64, 0);
let zero = Value::new_constant(context, zero);
// Branch to the loop block, passing the initial iteration value.
self.current_block
.append(context)
.branch(loop_block, vec![zero]);
// Add a block argument (for the IV) to the loop block.
let index_var_index = loop_block.new_arg(context, Type::get_uint64(context));
let index = loop_block.get_arg(context, index_var_index).unwrap();
// Create an exit block.
let exit_block = self
.function
.create_block(context, Some("array_init_exit".into()));
// Start building the loop block.
self.current_block = loop_block;
let gep_val = self.current_block.append(context).get_elem_ptr(
self.compile_array_init_loop(
context,
array_value,
elem_type,
vec![index],
);
self.current_block
.append(context)
.store(gep_val, *const_initializer)
.add_metadatum(context, span_md_idx);
// Increment index by one.
let one = Constant::new_uint(context, 64, 1);
let one = Value::new_constant(context, one);
let index_inc =
self.current_block
.append(context)
.binary_op(BinaryOpKind::Add, index, one);
// continue = index_inc < contents.len()
let len = Constant::new_uint(context, 64, contents.len() as u64);
let len = Value::new_constant(context, len);
let r#continue =
self.current_block
.append(context)
.cmp(Predicate::LessThan, index_inc, len);
// if continue then loop_block else exit_block.
self.current_block.append(context).conditional_branch(
r#continue,
loop_block,
exit_block,
vec![index_inc],
vec![],
*const_initializer,
contents.len() as u64,
span_md_idx,
);
// Continue compilation in the exit block.
self.current_block = exit_block;
} else {
// Insert each element separately.
for (idx, elem_value) in compiled_elems.iter().enumerate() {
Expand Down Expand Up @@ -3788,6 +3817,71 @@ impl<'eng> FnCompiler<'eng> {
Ok(TerminatorValue::new(array_value, context))
}

// initialize an array with all elements equals to "init_value",
// which should be "Copy", concept that sway still don´t have.
fn compile_array_init_loop(
&mut self,
context: &mut Context,
array_value: Value,
elem_type: Type,
init_value: Value,
length: u64,
span_md_idx: Option<MetadataIndex>,
) {
// Create a loop to insert const_initializer to all array elements.
let loop_block = self
.function
.create_block(context, Some("array_init_loop".into()));
// The loop begins with 0.
let zero = Constant::new_uint(context, 64, 0);
let zero = Value::new_constant(context, zero);
// Branch to the loop block, passing the initial iteration value.
self.current_block
.append(context)
.branch(loop_block, vec![zero]);
// Add a block argument (for the IV) to the loop block.
let index_var_index = loop_block.new_arg(context, Type::get_uint64(context));
let index = loop_block.get_arg(context, index_var_index).unwrap();
// Create an exit block.
let exit_block = self
.function
.create_block(context, Some("array_init_exit".into()));
// Start building the loop block.
self.current_block = loop_block;
let gep_val =
self.current_block
.append(context)
.get_elem_ptr(array_value, elem_type, vec![index]);
self.current_block
.append(context)
.store(gep_val, init_value)
.add_metadatum(context, span_md_idx);
// Increment index by one.
let one = Constant::new_uint(context, 64, 1);
let one = Value::new_constant(context, one);
let index_inc = self
.current_block
.append(context)
.binary_op(BinaryOpKind::Add, index, one);
// continue = index_inc < contents.len()
let len = Constant::new_uint(context, 64, length);
let len = Value::new_constant(context, len);
let r#continue =
self.current_block
.append(context)
.cmp(Predicate::LessThan, index_inc, len);
// if continue then loop_block else exit_block.
self.current_block.append(context).conditional_branch(
r#continue,
loop_block,
exit_block,
vec![index_inc],
vec![],
);
// Continue compilation in the exit block.
self.current_block = exit_block;
}

fn compile_array_index(
&mut self,
context: &mut Context,
Expand Down
Loading
Loading