From c318607380b33a312b2e9842fe58300e418eeaaa Mon Sep 17 00:00:00 2001 From: Vaivaswatha Nagaraj Date: Thu, 6 Feb 2025 17:01:36 +0530 Subject: [PATCH 01/21] unique constants in the IR --- .../src/asm_generation/fuel/data_section.rs | 6 +- .../asm_generation/fuel/fuel_asm_builder.rs | 15 +- .../src/asm_generation/fuel/functions.rs | 8 +- sway-core/src/ir_generation/compile.rs | 7 +- sway-core/src/ir_generation/const_eval.rs | 403 ++++++++++++------ sway-core/src/ir_generation/convert.rs | 39 +- sway-core/src/ir_generation/function.rs | 159 +++---- sway-core/src/ir_generation/storage.rs | 14 +- .../ast_node/declaration/storage.rs | 4 +- sway-ir/src/constant.rs | 114 +++-- sway-ir/src/context.rs | 8 +- sway-ir/src/function.rs | 3 +- sway-ir/src/instruction.rs | 10 +- sway-ir/src/irtype.rs | 6 +- sway-ir/src/local_var.rs | 2 +- sway-ir/src/optimize/const_demotion.rs | 4 +- sway-ir/src/optimize/constants.rs | 84 ++-- sway-ir/src/optimize/cse.rs | 2 +- sway-ir/src/optimize/mem2reg.rs | 22 +- sway-ir/src/optimize/misc_demotion.rs | 9 +- sway-ir/src/optimize/sroa.rs | 31 +- sway-ir/src/parser.rs | 44 +- sway-ir/src/printer.rs | 10 +- sway-ir/src/value.rs | 5 +- sway-ir/src/verify.rs | 2 +- 25 files changed, 638 insertions(+), 373 deletions(-) diff --git a/sway-core/src/asm_generation/fuel/data_section.rs b/sway-core/src/asm_generation/fuel/data_section.rs index 86cb5a1133a..9e9ba081ed5 100644 --- a/sway-core/src/asm_generation/fuel/data_section.rs +++ b/sway-core/src/asm_generation/fuel/data_section.rs @@ -1,5 +1,7 @@ use rustc_hash::FxHashMap; -use sway_ir::{size_bytes_round_up_to_word_alignment, Constant, ConstantValue, Context, Padding}; +use sway_ir::{ + size_bytes_round_up_to_word_alignment, ConstantContent, ConstantValue, Context, Padding, +}; use std::{fmt, iter::repeat}; @@ -89,7 +91,7 @@ impl Entry { pub(crate) fn from_constant( context: &Context, - constant: &Constant, + constant: &ConstantContent, name: EntryName, padding: Option, ) -> Entry { diff --git a/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs b/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs index 1ef655c0885..a053f7b520c 100644 --- a/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs +++ b/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs @@ -102,7 +102,7 @@ impl AsmBuilder for FuelAsmBuilder<'_, '_> { ConfigContent::V0 { name, constant, .. } => { let entry = Entry::from_constant( self.context, - constant, + constant.get_content(self.context), EntryName::Configurable(name.clone()), None, ); @@ -1130,7 +1130,7 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> { idx_val .get_constant(self.context) .and_then(|idx_const| { - if let ConstantValue::Uint(idx) = idx_const.value { + if let ConstantValue::Uint(idx) = idx_const.get_content(self.context).value { Some(idx as usize) } else { None @@ -2072,7 +2072,7 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> { config_name: Option, span: Option, ) -> (VirtualRegister, Option) { - match &constant.value { + match &constant.get_content(self.context).value { // Use cheaper $zero or $one registers if possible. ConstantValue::Unit | ConstantValue::Bool(false) | ConstantValue::Uint(0) if config_name.is_none() => @@ -2091,7 +2091,12 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> { } else { EntryName::NonConfigurable }; - let entry = Entry::from_constant(self.context, constant, config_name, None); + let entry = Entry::from_constant( + self.context, + constant.get_content(self.context), + config_name, + None, + ); let data_id = self.data_section.insert_data_value(entry); // Allocate a register for it, and a load instruction. @@ -2137,7 +2142,7 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> { .or_else(|| { value.get_constant(self.context).map(|constant| { let span = self.md_mgr.val_to_span(self.context, *value); - match constant.value { + match constant.get_content(self.context).value { // If it's a small enough constant, just initialize using an IMM value. // (exceptions for zero and one as they have special registers). ConstantValue::Uint(c) diff --git a/sway-core/src/asm_generation/fuel/functions.rs b/sway-core/src/asm_generation/fuel/functions.rs index a37109a3a7d..0805ce2f11f 100644 --- a/sway-core/src/asm_generation/fuel/functions.rs +++ b/sway-core/src/asm_generation/fuel/functions.rs @@ -834,7 +834,7 @@ impl FuelAsmBuilder<'_, '_> { ptr.is_mutable(self.context), ptr.get_initializer(self.context), ) { - match constant.value { + match constant.get_content(self.context).value { ConstantValue::Uint(c) if c <= compiler_constants::EIGHTEEN_BITS => { self.ptr_map.insert( *ptr, @@ -848,7 +848,7 @@ impl FuelAsmBuilder<'_, '_> { let data_id = self.data_section.insert_data_value(Entry::from_constant( self.context, - constant, + constant.get_content(self.context), EntryName::NonConfigurable, None, )); @@ -863,7 +863,7 @@ impl FuelAsmBuilder<'_, '_> { let var_size = ptr_ty.size(self.context); if let Some(constant) = ptr.get_initializer(self.context) { - match constant.value { + match constant.get_content(self.context).value { ConstantValue::Uint(c) if c <= compiler_constants::EIGHTEEN_BITS => { let imm = VirtualImmediate18::new_unchecked( c, @@ -879,7 +879,7 @@ impl FuelAsmBuilder<'_, '_> { let data_id = self.data_section.insert_data_value(Entry::from_constant( self.context, - constant, + constant.get_content(self.context), EntryName::NonConfigurable, None, )); diff --git a/sway-core/src/ir_generation/compile.rs b/sway-core/src/ir_generation/compile.rs index 1bb39ea5dc3..f94f2405106 100644 --- a/sway-core/src/ir_generation/compile.rs +++ b/sway-core/src/ir_generation/compile.rs @@ -356,7 +356,7 @@ pub(crate) fn compile_configurables( let opt_metadata = md_mgr.span_to_md(context, &decl.span); if context.experimental.new_encoding { - let mut encoded_bytes = match constant.value { + let mut encoded_bytes = match constant.get_content(context).value.clone() { ConstantValue::RawUntypedSlice(bytes) => bytes, _ => unreachable!(), }; @@ -651,11 +651,12 @@ fn compile_fn( // Special case: sometimes the returned value at the end of the function block is hacked // together and is invalid. This can happen with diverging control flow or with implicit // returns. We can double check here and make sure the return value type is correct. + let undef = Constant::unique(context, ConstantContent::get_undef(ret_type)); ret_val = match ret_val.get_type(context) { Some(ret_val_type) if ret_type.eq(context, &ret_val_type) => ret_val, // Mismatched or unavailable type. Set ret_val to a correctly typed Undef. - _otherwise => Value::new_constant(context, Constant::get_undef(ret_type)), + _otherwise => Value::new_constant(context, undef), }; // Another special case: if the last expression in a function is a return then we don't want to @@ -676,7 +677,7 @@ fn compile_fn( || compiler.current_block.num_predecessors(context) > 0) { if ret_type.is_unit(context) { - ret_val = Constant::get_unit(context); + ret_val = ConstantContent::get_unit(context); } compiler .current_block diff --git a/sway-core/src/ir_generation/const_eval.rs b/sway-core/src/ir_generation/const_eval.rs index da9e840c7d4..4545d58f833 100644 --- a/sway-core/src/ir_generation/const_eval.rs +++ b/sway-core/src/ir_generation/const_eval.rs @@ -23,11 +23,11 @@ use super::{ use sway_ast::Intrinsic; use sway_error::error::CompileError; use sway_ir::{ - constant::{Constant, ConstantValue}, + constant::{ConstantContent, ConstantValue}, context::Context, module::Module, value::Value, - InstOp, Instruction, Type, TypeContent, + Constant, InstOp, Instruction, Type, TypeContent, }; use sway_types::{ident::Ident, integer_bits::IntegerBits, span::Spanned, Named, Span}; use sway_utils::mapped_stack::MappedStack; @@ -374,11 +374,16 @@ fn const_eval_typed_expr( &field_typs, ) .map_or(None, |struct_ty| { - Some(Constant::new_struct( + let c = ConstantContent::new_struct( lookup.context, struct_ty.get_field_types(lookup.context), - field_vals, - )) + field_vals + .iter() + .map(|fv| fv.get_content(lookup.context).clone()) + .collect(), + ); + let c = Constant::unique(lookup.context, c); + Some(c) }) } ty::TyExpressionVariant::Tuple { fields } => { @@ -406,11 +411,16 @@ fn const_eval_typed_expr( &field_typs, ) .map_or(None, |tuple_ty| { - Some(Constant::new_struct( + let c = ConstantContent::new_struct( lookup.context, tuple_ty.get_field_types(lookup.context), - field_vals, - )) + field_vals + .iter() + .map(|fv| fv.get_content(lookup.context).clone()) + .collect(), + ); + let c = Constant::unique(lookup.context, c); + Some(c) }) } ty::TyExpressionVariant::Array { @@ -450,11 +460,16 @@ fn const_eval_typed_expr( element_typs.len().try_into().unwrap(), ) .map_or(None, |array_ty| { - Some(Constant::new_array( + let arr = ConstantContent::new_array( lookup.context, array_ty.get_array_elem_type(lookup.context).unwrap(), - element_vals, - )) + element_vals + .iter() + .map(|cv| cv.get_content(lookup.context).clone()) + .collect(), + ); + let arr = Constant::unique(lookup.context, arr); + Some(arr) }); arr @@ -475,13 +490,13 @@ fn const_eval_typed_expr( ); if let Ok(enum_ty) = aggregate { - let tag_value = Constant::new_uint(lookup.context, 64, *tag as u64); - let mut fields: Vec = vec![tag_value]; + let tag_value = ConstantContent::new_uint(lookup.context, 64, *tag as u64); + let mut fields: Vec = vec![tag_value]; match contents { - None => fields.push(Constant::new_unit(lookup.context)), + None => fields.push(ConstantContent::new_unit(lookup.context)), Some(subexpr) => match const_eval_typed_expr(lookup, known_consts, subexpr)? { - Some(constant) => fields.push(constant), + Some(constant) => fields.push(constant.get_content(lookup.context).clone()), None => { return Err(ConstEvalError::CannotBeEvaluatedToConst { span: variant_instantiation_span.clone(), @@ -491,7 +506,9 @@ fn const_eval_typed_expr( } let fields_tys = enum_ty.get_field_types(lookup.context); - Some(Constant::new_struct(lookup.context, fields_tys, fields)) + let c = ConstantContent::new_struct(lookup.context, fields_tys, fields); + let c = Constant::unique(lookup.context, c); + Some(c) } else { return Err(ConstEvalError::CannotBeEvaluatedToConst { span: expr.span.clone(), @@ -503,8 +520,10 @@ fn const_eval_typed_expr( field_to_access, resolved_type_of_parent, .. - } => match const_eval_typed_expr(lookup, known_consts, prefix)? { - Some(Constant { + } => match const_eval_typed_expr(lookup, known_consts, prefix)? + .map(|c| c.get_content(lookup.context).clone()) + { + Some(ConstantContent { value: ConstantValue::Struct(fields), .. }) => { @@ -520,7 +539,12 @@ fn const_eval_typed_expr( .and_then(|(_struct_name, field_idx_and_type_opt)| { field_idx_and_type_opt.map(|(field_idx, _field_type)| field_idx) }) - .and_then(|field_idx| fields.get(field_idx as usize).cloned()) + .and_then(|field_idx| { + fields + .get(field_idx as usize) + .cloned() + .map(|c| Constant::unique(lookup.context, c)) + }) } _ => { return Err(ConstEvalError::CannotBeEvaluatedToConst { @@ -532,11 +556,16 @@ fn const_eval_typed_expr( prefix, elem_to_access_num, .. - } => match const_eval_typed_expr(lookup, known_consts, prefix)? { - Some(Constant { + } => match const_eval_typed_expr(lookup, known_consts, prefix)? + .map(|c| c.get_content(lookup.context)) + { + Some(ConstantContent { value: ConstantValue::Struct(fields), .. - }) => fields.get(*elem_to_access_num).cloned(), + }) => fields + .get(*elem_to_access_num) + .cloned() + .map(|c| Constant::unique(lookup.context, c)), _ => { return Err(ConstEvalError::CannotBeEvaluatedToConst { span: expr.span.clone(), @@ -572,12 +601,14 @@ fn const_eval_typed_expr( then, r#else, } => { - match const_eval_typed_expr(lookup, known_consts, condition)? { - Some(Constant { + match const_eval_typed_expr(lookup, known_consts, condition)? + .map(|c| c.get_content(lookup.context)) + { + Some(ConstantContent { value: ConstantValue::Bool(cond), .. }) => { - if cond { + if *cond { const_eval_typed_expr(lookup, known_consts, then)? } else if let Some(r#else) = r#else { const_eval_typed_expr(lookup, known_consts, r#else)? @@ -599,22 +630,25 @@ fn const_eval_typed_expr( const_eval_codeblock(lookup, known_consts, codeblock)? } ty::TyExpressionVariant::ArrayIndex { prefix, index } => { - let prefix = const_eval_typed_expr(lookup, known_consts, prefix)?; - let index = const_eval_typed_expr(lookup, known_consts, index)?; + let prefix = const_eval_typed_expr(lookup, known_consts, prefix)? + .map(|c| c.get_content(lookup.context).clone()); + let index = const_eval_typed_expr(lookup, known_consts, index)? + .map(|c| c.get_content(lookup.context)); match (prefix, index) { ( - Some(Constant { + Some(ConstantContent { value: ConstantValue::Array(items), .. }), - Some(Constant { + Some(ConstantContent { value: ConstantValue::Uint(index), .. }), ) => { let count = items.len() as u64; - if index < count { - Some(items[index as usize].clone()) + if *index < count { + let c = Constant::unique(lookup.context, items[*index as usize].clone()); + Some(c) } else { return Err(ConstEvalError::CompileError); } @@ -635,29 +669,35 @@ fn const_eval_typed_expr( .as_intrinsic() .filter(|x| matches!(x.kind, Intrinsic::ElemAt)) .ok_or(ConstEvalError::CompileError) - .and_then(|kind| const_eval_intrinsic(lookup, known_consts, kind)); - if let Ok(Some(Constant { + .and_then(|kind| { + const_eval_intrinsic(lookup, known_consts, kind) + .map(|c| c.map(|c| c.get_content(lookup.context).clone())) + }); + if let Ok(Some(ConstantContent { value: ConstantValue::Reference(value), .. })) = value { - Some(*value.clone()) + let c = Constant::unique(lookup.context, *value.clone()); + Some(c) } else { return Err(ConstEvalError::CompileError); } } ty::TyExpressionVariant::EnumTag { exp } => { - let value = const_eval_typed_expr(lookup, known_consts, exp)?.map(|x| x.value); + let value = const_eval_typed_expr(lookup, known_consts, exp)? + .map(|x| x.get_content(lookup.context).value.clone()); if let Some(ConstantValue::Struct(fields)) = value { - Some(fields[0].clone()) + Some(Constant::unique(lookup.context, fields[0].clone())) } else { return Err(ConstEvalError::CompileError); } } ty::TyExpressionVariant::UnsafeDowncast { exp, .. } => { - let value = const_eval_typed_expr(lookup, known_consts, exp)?.map(|x| x.value); + let value = const_eval_typed_expr(lookup, known_consts, exp)? + .map(|x| x.get_content(lookup.context).value.clone()); if let Some(ConstantValue::Struct(fields)) = value { - Some(fields[1].clone()) + Some(Constant::unique(lookup.context, fields[1].clone())) } else { return Err(ConstEvalError::CompileError); } @@ -673,7 +713,7 @@ fn const_eval_typed_expr( limit -= 1; let condition = const_eval_typed_expr(lookup, known_consts, condition)?; - match condition.map(|x| x.value) { + match condition.map(|x| x.get_content(&lookup.context).value.clone()) { Some(ConstantValue::Bool(true)) => { // Break and continue are not implemented, so there is need for flow control here let _ = const_eval_codeblock(lookup, known_consts, body)?; @@ -812,8 +852,8 @@ fn const_eval_codeblock( result } -fn as_encode_buffer(buffer: &Constant) -> Option<(&Vec, u64)> { - match &buffer.value { +fn as_encode_buffer<'a>(context: &'a Context, buffer: &Constant) -> Option<(&'a Vec, u64)> { + match &buffer.get_content(context).value { ConstantValue::Struct(fields) => { let slice = match &fields[0].value { ConstantValue::RawUntypedSlice(bytes) => bytes, @@ -830,7 +870,7 @@ fn as_encode_buffer(buffer: &Constant) -> Option<(&Vec, u64)> { } fn to_encode_buffer(lookup: &mut LookupEnv, bytes: Vec, len: u64) -> Constant { - Constant { + let c = ConstantContent { ty: Type::new_struct( lookup.context, vec![ @@ -839,16 +879,17 @@ fn to_encode_buffer(lookup: &mut LookupEnv, bytes: Vec, len: u64) -> Constan ], ), value: ConstantValue::Struct(vec![ - Constant { + ConstantContent { ty: Type::get_slice(lookup.context), value: ConstantValue::RawUntypedSlice(bytes), }, - Constant { + ConstantContent { ty: Type::get_uint64(lookup.context), value: ConstantValue::Uint(len), }, ]), - } + }; + Constant::unique(lookup.context, c) } fn const_eval_intrinsic( @@ -871,11 +912,16 @@ fn const_eval_intrinsic( match intrinsic.kind { Intrinsic::Add | Intrinsic::Sub | Intrinsic::Mul | Intrinsic::Div | Intrinsic::Mod => { - let ty = args[0].ty; - assert!(args.len() == 2 && ty.eq(lookup.context, &args[1].ty)); + let ty = args[0].get_content(lookup.context).ty; + assert!( + args.len() == 2 && ty.eq(lookup.context, &args[1].get_content(lookup.context).ty) + ); use ConstantValue::*; - match (&args[0].value, &args[1].value) { + let c = match ( + &args[0].get_content(&lookup.context).value, + &args[1].get_content(lookup.context).value, + ) { (Uint(arg1), Uint(ref arg2)) => { // All arithmetic is done as if it were u64 let result = match intrinsic.kind { @@ -888,7 +934,7 @@ fn const_eval_intrinsic( }; match result { - Some(result) => Ok(Some(Constant { + Some(result) => Ok(Some(ConstantContent { ty, value: ConstantValue::Uint(result), })), @@ -908,7 +954,7 @@ fn const_eval_intrinsic( }; match result { - Some(result) => Ok(Some(Constant { + Some(result) => Ok(Some(ConstantContent { ty, value: ConstantValue::U256(result), })), @@ -920,14 +966,20 @@ fn const_eval_intrinsic( _ => { panic!("Type checker allowed incorrect args to binary op"); } - } + }; + c.map(|c| c.map(|c| Constant::unique(lookup.context, c))) } Intrinsic::And | Intrinsic::Or | Intrinsic::Xor => { - let ty = args[0].ty; - assert!(args.len() == 2 && ty.eq(lookup.context, &args[1].ty)); + let ty = args[0].get_content(lookup.context).ty; + assert!( + args.len() == 2 && ty.eq(lookup.context, &args[1].get_content(lookup.context).ty) + ); use ConstantValue::*; - match (&args[0].value, &args[1].value) { + let c = match ( + &args[0].get_content(lookup.context).value, + &args[1].get_content(&lookup.context).value, + ) { (Uint(arg1), Uint(ref arg2)) => { // All arithmetic is done as if it were u64 let result = match intrinsic.kind { @@ -938,7 +990,7 @@ fn const_eval_intrinsic( }; match result { - Some(sum) => Ok(Some(Constant { + Some(sum) => Ok(Some(ConstantContent { ty, value: ConstantValue::Uint(sum), })), @@ -956,7 +1008,7 @@ fn const_eval_intrinsic( }; match result { - Some(sum) => Ok(Some(Constant { + Some(sum) => Ok(Some(ConstantContent { ty, value: ConstantValue::U256(sum), })), @@ -974,7 +1026,7 @@ fn const_eval_intrinsic( }; match result { - Some(result) => Ok(Some(Constant { + Some(result) => Ok(Some(ConstantContent { ty, value: ConstantValue::B256(result), })), @@ -986,17 +1038,33 @@ fn const_eval_intrinsic( _ => { panic!("Type checker allowed incorrect args to binary op"); } - } + }; + c.map(|c| c.map(|c| Constant::unique(lookup.context, c))) } Intrinsic::Lsh | Intrinsic::Rsh => { assert!(args.len() == 2); - assert!(args[0].ty.is_uint(lookup.context) || args[0].ty.is_b256(lookup.context)); - assert!(args[1].ty.is_uint64(lookup.context)); + assert!( + args[0] + .get_content(lookup.context) + .ty + .is_uint(lookup.context) + || args[0] + .get_content(lookup.context) + .ty + .is_b256(lookup.context) + ); + assert!(args[1] + .get_content(lookup.context) + .ty + .is_uint64(lookup.context)); - let ty = args[0].ty; + let ty = args[0].get_content(lookup.context).ty; use ConstantValue::*; - match (&args[0].value, &args[1].value) { + let c = match ( + &args[0].get_content(lookup.context).value, + &args[1].get_content(lookup.context).value, + ) { (Uint(arg1), Uint(ref arg2)) => { let result = match intrinsic.kind { Intrinsic::Lsh => u32::try_from(*arg2) @@ -1009,7 +1077,7 @@ fn const_eval_intrinsic( }; match result { - Some(sum) => Ok(Some(Constant { + Some(sum) => Ok(Some(ConstantContent { ty, value: ConstantValue::Uint(sum), })), @@ -1026,7 +1094,7 @@ fn const_eval_intrinsic( }; match result { - Some(value) => Ok(Some(Constant { + Some(value) => Ok(Some(ConstantContent { ty, value: ConstantValue::U256(value), })), @@ -1043,7 +1111,7 @@ fn const_eval_intrinsic( }; match result { - Some(result) => Ok(Some(Constant { + Some(result) => Ok(Some(ConstantContent { ty, value: ConstantValue::B256(result), })), @@ -1055,7 +1123,8 @@ fn const_eval_intrinsic( _ => { panic!("Type checker allowed incorrect args to binary op"); } - } + }; + c.map(|c| c.map(|c| Constant::unique(lookup.context, c))) } Intrinsic::SizeOfType => { let targ = &intrinsic.type_arguments[0]; @@ -1067,10 +1136,12 @@ fn const_eval_intrinsic( &targ.span, ) .map_err(|_| ConstEvalError::CompileError)?; - Ok(Some(Constant { + let c = ConstantContent { ty: Type::get_uint64(lookup.context), value: ConstantValue::Uint(ir_type.size(lookup.context).in_bytes()), - })) + }; + + Ok(Some(Constant::unique(lookup.context, c))) } Intrinsic::SizeOfVal => { let val = &intrinsic.arguments[0]; @@ -1083,10 +1154,11 @@ fn const_eval_intrinsic( &val.span, ) .map_err(|_| ConstEvalError::CompileError)?; - Ok(Some(Constant { + let c = ConstantContent { ty: Type::get_uint64(lookup.context), value: ConstantValue::Uint(ir_type.size(lookup.context).in_bytes()), - })) + }; + Ok(Some(Constant::unique(lookup.context, c))) } Intrinsic::SizeOfStr => { let targ = &intrinsic.type_arguments[0]; @@ -1098,12 +1170,13 @@ fn const_eval_intrinsic( &targ.span, ) .map_err(|_| ConstEvalError::CompileError)?; - Ok(Some(Constant { + let c = ConstantContent { ty: Type::get_uint64(lookup.context), value: ConstantValue::Uint( ir_type.get_string_len(lookup.context).unwrap_or_default(), ), - })) + }; + Ok(Some(Constant::unique(lookup.context, c))) } Intrinsic::AssertIsStrArray => { let targ = &intrinsic.type_arguments[0]; @@ -1116,18 +1189,22 @@ fn const_eval_intrinsic( ) .map_err(|_| ConstEvalError::CompileError)?; match ir_type.get_content(lookup.context) { - TypeContent::StringSlice | TypeContent::StringArray(_) => Ok(Some(Constant { - ty: Type::get_unit(lookup.context), - value: ConstantValue::Unit, - })), + TypeContent::StringSlice | TypeContent::StringArray(_) => { + let c = ConstantContent { + ty: Type::get_unit(lookup.context), + value: ConstantValue::Unit, + }; + Ok(Some(Constant::unique(lookup.context, c))) + } _ => Err(ConstEvalError::CompileError), } } Intrinsic::ToStrArray => { assert!(args.len() == 1); - match &args[0].value { + match &args[0].get_content(lookup.context).value { ConstantValue::String(s) => { - Ok(Some(Constant::new_string(lookup.context, s.to_vec()))) + let c = ConstantContent::new_string(lookup.context, s.to_vec()); + Ok(Some(Constant::unique(lookup.context, c))) } _ => { unreachable!("Type checker allowed non string value for ToStrArray") @@ -1136,33 +1213,52 @@ fn const_eval_intrinsic( } Intrinsic::Eq => { assert!(args.len() == 2); - Ok(Some(Constant { + let c = ConstantContent { ty: Type::get_bool(lookup.context), - value: ConstantValue::Bool(args[0].eq(lookup.context, &args[1])), - })) + value: ConstantValue::Bool(args[0] == args[1]), + }; + Ok(Some(Constant::unique(lookup.context, c))) } - Intrinsic::Gt => match (&args[0].value, &args[1].value) { - (ConstantValue::Uint(val1), ConstantValue::Uint(val2)) => Ok(Some(Constant { - ty: Type::get_bool(lookup.context), - value: ConstantValue::Bool(val1 > val2), - })), - (ConstantValue::U256(val1), ConstantValue::U256(val2)) => Ok(Some(Constant { - ty: Type::get_bool(lookup.context), - value: ConstantValue::Bool(val1 > val2), - })), + Intrinsic::Gt => match ( + &args[0].get_content(lookup.context).value, + &args[1].get_content(lookup.context).value, + ) { + (ConstantValue::Uint(val1), ConstantValue::Uint(val2)) => { + let c = ConstantContent { + ty: Type::get_bool(lookup.context), + value: ConstantValue::Bool(val1 > val2), + }; + Ok(Some(Constant::unique(lookup.context, c))) + } + (ConstantValue::U256(val1), ConstantValue::U256(val2)) => { + let c = ConstantContent { + ty: Type::get_bool(lookup.context), + value: ConstantValue::Bool(val1 > val2), + }; + Ok(Some(Constant::unique(lookup.context, c))) + } _ => { unreachable!("Type checker allowed non integer value for GreaterThan") } }, - Intrinsic::Lt => match (&args[0].value, &args[1].value) { - (ConstantValue::Uint(val1), ConstantValue::Uint(val2)) => Ok(Some(Constant { - ty: Type::get_bool(lookup.context), - value: ConstantValue::Bool(val1 < val2), - })), - (ConstantValue::U256(val1), ConstantValue::U256(val2)) => Ok(Some(Constant { - ty: Type::get_bool(lookup.context), - value: ConstantValue::Bool(val1 < val2), - })), + Intrinsic::Lt => match ( + &args[0].get_content(lookup.context).value, + &args[1].get_content(lookup.context).value, + ) { + (ConstantValue::Uint(val1), ConstantValue::Uint(val2)) => { + let c = ConstantContent { + ty: Type::get_bool(lookup.context), + value: ConstantValue::Bool(val1 < val2), + }; + Ok(Some(Constant::unique(lookup.context, c))) + } + (ConstantValue::U256(val1), ConstantValue::U256(val2)) => { + let c = ConstantContent { + ty: Type::get_bool(lookup.context), + value: ConstantValue::Bool(val1 < val2), + }; + Ok(Some(Constant::unique(lookup.context, c))) + } _ => { unreachable!("Type checker allowed non integer value for LessThan") } @@ -1189,38 +1285,52 @@ fn const_eval_intrinsic( // `bool` ops::Not implementation uses `__eq`. assert!(args.len() == 1); - assert!(args[0].ty.is_uint(lookup.context) || args[0].ty.is_b256(lookup.context)); + assert!( + args[0] + .get_content(lookup.context) + .ty + .is_uint(lookup.context) + || args[0] + .get_content(lookup.context) + .ty + .is_b256(lookup.context) + ); let Some(arg) = args.into_iter().next() else { unreachable!("Unexpected 'not' without any arguments"); }; - match arg.value { + let c = match &arg.get_content(lookup.context).value { ConstantValue::Uint(n) => { - let n = match arg.ty.get_uint_width(lookup.context) { - Some(8) => !(n as u8) as u64, - Some(16) => !(n as u16) as u64, - Some(32) => !(n as u32) as u64, + let n = match arg + .get_content(lookup.context) + .ty + .get_uint_width(lookup.context) + { + Some(8) => !(*n as u8) as u64, + Some(16) => !(*n as u16) as u64, + Some(32) => !(*n as u32) as u64, Some(64) => !n, _ => unreachable!("Invalid unsigned integer width"), }; - Ok(Some(Constant { - ty: arg.ty, + Ok(Some(ConstantContent { + ty: arg.get_content(lookup.context).ty, value: ConstantValue::Uint(n), })) } - ConstantValue::U256(n) => Ok(Some(Constant { - ty: arg.ty, + ConstantValue::U256(n) => Ok(Some(ConstantContent { + ty: arg.get_content(lookup.context).ty, value: ConstantValue::U256(n.not()), })), - ConstantValue::B256(v) => Ok(Some(Constant { - ty: arg.ty, + ConstantValue::B256(v) => Ok(Some(ConstantContent { + ty: arg.get_content(lookup.context).ty, value: ConstantValue::B256(v.not()), })), _ => { unreachable!("Type checker allowed non integer value for Not"); } - } + }; + c.map(|c| c.map(|c| Constant::unique(lookup.context, c))) } Intrinsic::ContractCall | Intrinsic::ContractRet => { Err(ConstEvalError::CannotBeEvaluatedToConst { @@ -1231,11 +1341,11 @@ fn const_eval_intrinsic( Intrinsic::EncodeBufferAppend => { assert!(args.len() == 2); - let (slice, mut len) = as_encode_buffer(&args[0]).unwrap(); + let (slice, mut len) = as_encode_buffer(lookup.context, &args[0]).unwrap(); let mut bytes = slice.clone(); use ConstantValue::*; - match &args[1].value { + match &args[1].get_content(lookup.context).value { Bool(v) => { bytes.extend(if *v { [1] } else { [0] }); len += 1; @@ -1299,31 +1409,41 @@ fn const_eval_intrinsic( Intrinsic::EncodeBufferAsRawSlice => { assert!(args.len() == 1); - let (slice, len) = as_encode_buffer(&args[0]).unwrap(); + let (slice, len) = as_encode_buffer(lookup.context, &args[0]).unwrap(); let bytes = slice.clone(); - Ok(Some(Constant { + let c = ConstantContent { ty: Type::get_slice(lookup.context), value: ConstantValue::RawUntypedSlice(bytes[0..(len as usize)].to_vec()), - })) + }; + Ok(Some(Constant::unique(lookup.context, c))) } Intrinsic::Slice => { - let start = args[1].as_uint().expect("Type check allowed non u64") as usize; - let end = args[2].as_uint().expect("Type check allowed non u64") as usize; - - match &args[0].value { + let start = args[1] + .get_content(lookup.context) + .as_uint() + .expect("Type check allowed non u64") as usize; + let end = args[2] + .get_content(lookup.context) + .as_uint() + .expect("Type check allowed non u64") as usize; + + match &args[0].get_content(lookup.context).value { ConstantValue::Array(elements) => { let slice = elements .get(start..end) .ok_or(ConstEvalError::CompileError)?; let elem_type = args[0] + .get_content(lookup.context) .ty .get_array_elem_type(lookup.context) .expect("unexpected non array"); - Ok(Some(Constant { + let s = slice.to_vec(); + let c = ConstantContent { ty: Type::get_typed_slice(lookup.context, elem_type), - value: ConstantValue::Slice(slice.to_vec()), - })) + value: ConstantValue::Slice(s), + }; + Ok(Some(Constant::unique(lookup.context, c))) } ConstantValue::Reference(r) => match &r.value { ConstantValue::Slice(elements) => { @@ -1331,13 +1451,16 @@ fn const_eval_intrinsic( .get(start..end) .ok_or(ConstEvalError::CompileError)?; let elem_type = args[0] + .get_content(lookup.context) .ty .get_typed_slice_elem_type(lookup.context) .expect("unexpected non slice"); - Ok(Some(Constant { + let s = slice.to_vec(); + let c = ConstantContent { ty: Type::get_typed_slice(lookup.context, elem_type), - value: ConstantValue::Slice(slice.to_vec()), - })) + value: ConstantValue::Slice(s), + }; + Ok(Some(Constant::unique(lookup.context, c))) } _ => Err(ConstEvalError::CannotBeEvaluatedToConst { span: intrinsic.span.clone(), @@ -1349,16 +1472,20 @@ fn const_eval_intrinsic( } } Intrinsic::ElemAt => { - let idx = args[1].as_uint().expect("Type check allowed non u64") as usize; + let idx = args[1] + .get_content(lookup.context) + .as_uint() + .expect("Type check allowed non u64") as usize; - match &args[0].value { + match &args[0].get_content(lookup.context).value { ConstantValue::Reference(r) => match &r.value { ConstantValue::Slice(elements) => { let v = elements[idx].clone(); - Ok(Some(Constant { + let c = ConstantContent { ty: Type::new_ptr(lookup.context, v.ty), value: ConstantValue::Reference(Box::new(v)), - })) + }; + Ok(Some(Constant::unique(lookup.context, c))) } _ => Err(ConstEvalError::CannotBeEvaluatedToConst { span: intrinsic.span.clone(), @@ -1448,12 +1575,12 @@ fn const_eval_intrinsic( ctx: &Context<'_>, bytes: &mut std::io::Cursor>, t: &Type, - ) -> Result { + ) -> Result { Ok(match t.get_content(ctx) { TypeContent::Uint(8) => { let mut buffer = [0u8]; let _ = bytes.read_exact(&mut buffer); - Constant { + ConstantContent { ty: Type::get_uint8(ctx), value: ConstantValue::Uint(buffer[0] as u64), } @@ -1462,7 +1589,7 @@ fn const_eval_intrinsic( let mut buffer = [0u8; 8]; // u16 = u64 at runtime let _ = bytes.read_exact(&mut buffer); let buffer = [buffer[6], buffer[7]]; - Constant { + ConstantContent { ty: Type::get_uint16(ctx), value: ConstantValue::Uint(u16::from_be_bytes(buffer) as u64), } @@ -1471,7 +1598,7 @@ fn const_eval_intrinsic( let mut buffer = [0u8; 8]; // u32 = u64 at runtime let _ = bytes.read_exact(&mut buffer); let buffer = [buffer[4], buffer[5], buffer[6], buffer[7]]; - Constant { + ConstantContent { ty: Type::get_uint32(ctx), value: ConstantValue::Uint(u32::from_be_bytes(buffer) as u64), } @@ -1479,7 +1606,7 @@ fn const_eval_intrinsic( TypeContent::Uint(64) => { let mut buffer = [0u8; 8]; let _ = bytes.read_exact(&mut buffer); - Constant { + ConstantContent { ty: Type::get_uint64(ctx), value: ConstantValue::Uint(u64::from_be_bytes(buffer)), } @@ -1493,11 +1620,11 @@ fn const_eval_intrinsic( lookup.context, &mut runtime_bytes, &src_ir_type, - &args[0].value, + &args[0].get_content(lookup.context).value, )?; let mut cursor = std::io::Cursor::new(runtime_bytes); let c = transmute_bytes(lookup.context, &mut cursor, &dst_ir_type)?; - Ok(Some(c)) + Ok(Some(Constant::unique(lookup.context, c))) } } } diff --git a/sway-core/src/ir_generation/convert.rs b/sway-core/src/ir_generation/convert.rs index f95217fb2b4..10cdea70723 100644 --- a/sway-core/src/ir_generation/convert.rs +++ b/sway-core/src/ir_generation/convert.rs @@ -8,7 +8,7 @@ use crate::{ use super::types::{create_tagged_union_type, create_tuple_aggregate}; use sway_error::error::CompileError; -use sway_ir::{Constant, Context, Type, Value}; +use sway_ir::{Constant, ConstantContent, Context, Type, Value}; use sway_types::{integer_bits::IntegerBits, span::Span}; pub(super) fn convert_literal_to_value(context: &mut Context, ast_literal: &Literal) -> Value { @@ -22,15 +22,15 @@ pub(super) fn convert_literal_to_value(context: &mut Context, ast_literal: &Lite // // XXX The above isn't true for other targets. We need to improved this. // FIXME - Literal::U8(n) => Constant::get_uint(context, 8, *n as u64), - Literal::U16(n) => Constant::get_uint(context, 64, *n as u64), - Literal::U32(n) => Constant::get_uint(context, 64, *n as u64), - Literal::U64(n) => Constant::get_uint(context, 64, *n), - Literal::U256(n) => Constant::get_uint256(context, n.clone()), + Literal::U8(n) => ConstantContent::get_uint(context, 8, *n as u64), + Literal::U16(n) => ConstantContent::get_uint(context, 64, *n as u64), + Literal::U32(n) => ConstantContent::get_uint(context, 64, *n as u64), + Literal::U64(n) => ConstantContent::get_uint(context, 64, *n), + Literal::U256(n) => ConstantContent::get_uint256(context, n.clone()), Literal::Numeric(_) => unreachable!(), - Literal::String(s) => Constant::get_string(context, s.as_str().as_bytes().to_vec()), - Literal::Boolean(b) => Constant::get_bool(context, *b), - Literal::B256(bs) => Constant::get_b256(context, *bs), + Literal::String(s) => ConstantContent::get_string(context, s.as_str().as_bytes().to_vec()), + Literal::Boolean(b) => ConstantContent::get_bool(context, *b), + Literal::B256(bs) => ConstantContent::get_b256(context, *bs), } } @@ -38,18 +38,19 @@ pub(super) fn convert_literal_to_constant( context: &mut Context, ast_literal: &Literal, ) -> Constant { - match ast_literal { + let c = match ast_literal { // All integers are `u64`. See comment above. - Literal::U8(n) => Constant::new_uint(context, 8, *n as u64), - Literal::U16(n) => Constant::new_uint(context, 64, *n as u64), - Literal::U32(n) => Constant::new_uint(context, 64, *n as u64), - Literal::U64(n) => Constant::new_uint(context, 64, *n), - Literal::U256(n) => Constant::new_uint256(context, n.clone()), + Literal::U8(n) => ConstantContent::new_uint(context, 8, *n as u64), + Literal::U16(n) => ConstantContent::new_uint(context, 64, *n as u64), + Literal::U32(n) => ConstantContent::new_uint(context, 64, *n as u64), + Literal::U64(n) => ConstantContent::new_uint(context, 64, *n), + Literal::U256(n) => ConstantContent::new_uint256(context, n.clone()), Literal::Numeric(_) => unreachable!(), - Literal::String(s) => Constant::new_string(context, s.as_str().as_bytes().to_vec()), - Literal::Boolean(b) => Constant::new_bool(context, *b), - Literal::B256(bs) => Constant::new_b256(context, *bs), - } + Literal::String(s) => ConstantContent::new_string(context, s.as_str().as_bytes().to_vec()), + Literal::Boolean(b) => ConstantContent::new_bool(context, *b), + Literal::B256(bs) => ConstantContent::new_b256(context, *bs), + }; + Constant::unique(context, c) } pub(super) fn convert_resolved_type_id( diff --git a/sway-core/src/ir_generation/function.rs b/sway-core/src/ir_generation/function.rs index 33f16e8da57..90866d8cb26 100644 --- a/sway-core/src/ir_generation/function.rs +++ b/sway-core/src/ir_generation/function.rs @@ -105,8 +105,9 @@ pub(crate) struct FnCompiler<'eng> { } fn to_constant(_s: &mut FnCompiler<'_>, context: &mut Context, value: u64) -> Value { - let needed_size = Constant::new_uint(context, 64, value); - Value::new_constant(context, needed_size) + let needed_size = ConstantContent::new_uint(context, 64, value); + let c = Constant::unique(context, needed_size); + Value::new_constant(context, c) } fn save_to_local_return_ptr( @@ -210,7 +211,9 @@ impl<'eng> FnCompiler<'eng> { let v = loop { let ast_node = match ast_nodes.next() { Some(ast_node) => ast_node, - None => break TerminatorValue::new(Constant::get_unit(context), context), + None => { + break TerminatorValue::new(ConstantContent::get_unit(context), context) + } }; match fn_compiler.compile_ast_node(context, md_mgr, ast_node) { // 'Some' indicates an implicit return or a diverging expression, so break. @@ -400,7 +403,7 @@ impl<'eng> FnCompiler<'eng> { .append(context) .ptr_to_int(string_data, int_ty) .add_metadatum(context, span_md_idx); - let len_val = Constant::get_uint(context, 64, string_len); + let len_val = ConstantContent::get_uint(context, 64, string_len); // a slice is a pointer and a length let field_types = vec![int_ty, int_ty]; @@ -477,7 +480,8 @@ impl<'eng> FnCompiler<'eng> { 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 = Constant::get_string(context, s.as_str().as_bytes().to_vec()); + let string_data = + ConstantContent::get_string(context, s.as_str().as_bytes().to_vec()); let string_len = s.as_str().len() as u64; self.compile_string_slice(context, span_md_idx, string_data, string_len) } @@ -639,7 +643,7 @@ impl<'eng> FnCompiler<'eng> { ), ty::TyExpressionVariant::AbiCast { span, .. } => { let span_md_idx = md_mgr.span_to_md(context, span); - let val = Constant::get_unit(context).add_metadatum(context, span_md_idx); + let val = ConstantContent::get_unit(context).add_metadatum(context, span_md_idx); Ok(TerminatorValue::new(val, context)) } ty::TyExpressionVariant::StorageAccess(access) => { @@ -668,7 +672,9 @@ impl<'eng> FnCompiler<'eng> { ast_expr.return_type, ), ty::TyExpressionVariant::AbiName(_) => { - let val = Value::new_constant(context, Constant::new_unit(context)); + let c = ConstantContent::new_unit(context); + let c = Constant::unique(context, c); + let val = Value::new_constant(context, c); Ok(TerminatorValue::new(val, context)) } ty::TyExpressionVariant::UnsafeDowncast { @@ -924,7 +930,7 @@ impl<'eng> FnCompiler<'eng> { &exp.span, )?; self.compile_expression_to_value(context, md_mgr, exp)?; - let val = Constant::get_uint(context, 64, ir_type.size(context).in_bytes()); + let val = ConstantContent::get_uint(context, 64, ir_type.size(context).in_bytes()); Ok(TerminatorValue::new(val, context)) } Intrinsic::SizeOfType => { @@ -936,7 +942,7 @@ impl<'eng> FnCompiler<'eng> { targ.type_id, &targ.span, )?; - let val = Constant::get_uint(context, 64, ir_type.size(context).in_bytes()); + let val = ConstantContent::get_uint(context, 64, ir_type.size(context).in_bytes()); Ok(TerminatorValue::new(val, context)) } Intrinsic::SizeOfStr => { @@ -948,7 +954,7 @@ impl<'eng> FnCompiler<'eng> { targ.type_id, &targ.span, )?; - let val = Constant::get_uint( + let val = ConstantContent::get_uint( context, 64, ir_type.get_string_len(context).unwrap_or_default(), @@ -958,7 +964,7 @@ impl<'eng> FnCompiler<'eng> { Intrinsic::IsReferenceType => { let targ = type_arguments[0].clone(); let is_val = !engines.te().get_unaliased(targ.type_id).is_copy_type(); - let val = Constant::get_bool(context, is_val); + let val = ConstantContent::get_bool(context, is_val); Ok(TerminatorValue::new(val, context)) } Intrinsic::IsStrArray => { @@ -967,7 +973,7 @@ impl<'eng> FnCompiler<'eng> { &*engines.te().get_unaliased(targ.type_id), TypeInfo::StringArray(_) | TypeInfo::StringSlice ); - let val = Constant::get_bool(context, is_val); + let val = ConstantContent::get_bool(context, is_val); Ok(TerminatorValue::new(val, context)) } Intrinsic::AssertIsStrArray => { @@ -981,7 +987,7 @@ impl<'eng> FnCompiler<'eng> { )?; match ir_type.get_content(context) { TypeContent::StringSlice | TypeContent::StringArray(_) => { - let val = Constant::get_unit(context); + let val = ConstantContent::get_unit(context); Ok(TerminatorValue::new(val, context)) } _ => Err(CompileError::NonStrGenericType { @@ -991,7 +997,8 @@ impl<'eng> FnCompiler<'eng> { } Intrinsic::ToStrArray => match arguments[0].expression.extract_literal_value() { Some(Literal::String(span)) => { - let val = Constant::get_string(context, span.as_str().as_bytes().to_vec()); + let val = + ConstantContent::get_string(context, span.as_str().as_bytes().to_vec()); Ok(TerminatorValue::new(val, context)) } _ => unreachable!(), @@ -1037,7 +1044,7 @@ impl<'eng> FnCompiler<'eng> { None, &arguments[1], )?; - let tx_field_id = match tx_field_id_constant.value { + let tx_field_id = match tx_field_id_constant.get_content(context).value { ConstantValue::Uint(n) => n, _ => { return Err(CompileError::Internal( @@ -1342,7 +1349,8 @@ impl<'eng> FnCompiler<'eng> { len.type_id, &len.span, )?; - let len_value = Constant::get_uint(context, 64, ir_type.size(context).in_bytes()); + let len_value = + ConstantContent::get_uint(context, 64, ir_type.size(context).in_bytes()); let lhs = &arguments[0]; let count = &arguments[1]; @@ -1421,7 +1429,7 @@ impl<'eng> FnCompiler<'eng> { let message_id_val = self .messages_types_map .get(&arguments[1].return_type) - .map(|&msg_id| Constant::get_uint(context, 64, *msg_id as u64)) + .map(|&msg_id| ConstantContent::get_uint(context, 64, *msg_id as u64)) .ok_or_else(|| { CompileError::Internal( "Unable to determine ID for smo instance.", @@ -1450,7 +1458,8 @@ impl<'eng> FnCompiler<'eng> { .add_metadatum(context, span_md_idx); /* Third operand: the size of the message data */ - let user_message_size_val = Constant::get_uint(context, 64, user_message_size); + let user_message_size_val = + ConstantContent::get_uint(context, 64, user_message_size); /* Fourth operand: the amount of coins to send */ let coins = return_on_termination_or_extract!(self.compile_expression_to_value( @@ -1559,13 +1568,12 @@ impl<'eng> FnCompiler<'eng> { let uint64 = Type::get_uint64(context); // let cap = 1024; - let cap = Value::new_constant( - context, - Constant { - ty: uint64, - value: ConstantValue::Uint(1024), - }, - ); + let c = ConstantContent { + ty: uint64, + value: ConstantValue::Uint(1024), + }; + let c = Constant::unique(context, c); + let cap = Value::new_constant(context, c); // let ptr = asm(cap: cap) { // aloc cap; @@ -1591,8 +1599,9 @@ impl<'eng> FnCompiler<'eng> { let ptr_u8 = Type::new_ptr(context, Type::get_uint8(context)); let ptr = self.current_block.append(context).int_to_ptr(ptr, ptr_u8); - let len = Constant::new_uint(context, 64, 0); - let len = Value::new_constant(context, len); + let len = ConstantContent::new_uint(context, 64, 0); + let len_c = Constant::unique(context, len); + let len = Value::new_constant(context, len_c); let buffer = self.compile_to_encode_buffer(context, ptr, cap, len)?; Ok(TerminatorValue::new(buffer, context)) } @@ -1623,13 +1632,12 @@ impl<'eng> FnCompiler<'eng> { assert!(len.get_type(context).unwrap().is_uint64(context)); let uint64 = Type::get_uint64(context); - let step = Value::new_constant( - context, - Constant { - ty: uint64, - value: ConstantValue::Uint(step), - }, - ); + let step = ConstantContent { + ty: uint64, + value: ConstantValue::Uint(step), + }; + let step = Constant::unique(context, step); + let step = Value::new_constant(context, step); current_block .append(context) .binary_op(BinaryOpKind::Add, len, step) @@ -1653,13 +1661,12 @@ impl<'eng> FnCompiler<'eng> { let _ = current_block.append(context).store(addr, item); let uint64 = Type::get_uint64(context); - let step = Value::new_constant( - context, - Constant { - ty: uint64, - value: ConstantValue::Uint(1), - }, - ); + let step = ConstantContent { + ty: uint64, + value: ConstantValue::Uint(1), + }; + let step = Constant::unique(context, step); + let step = Value::new_constant(context, step); current_block .append(context) .binary_op(BinaryOpKind::Add, len, step) @@ -1685,13 +1692,12 @@ impl<'eng> FnCompiler<'eng> { let _ = current_block.append(context).store(addr, item); - let step = Value::new_constant( - context, - Constant { - ty: uint64, - value: ConstantValue::Uint(8), - }, - ); + let step = ConstantContent { + ty: uint64, + value: ConstantValue::Uint(8), + }; + let step = Constant::unique(context, step); + let step = Value::new_constant(context, step); current_block .append(context) .binary_op(BinaryOpKind::Add, len, step) @@ -1708,7 +1714,8 @@ impl<'eng> FnCompiler<'eng> { // save to local and offset let item_ptr = save_to_local_return_ptr(s, context, item)?; - let offset_value = Constant::new_uint(context, 64, offset); + let offset_value = ConstantContent::new_uint(context, 64, offset); + let offset_value = Constant::unique(context, offset_value); let offset_value = Value::new_constant(context, offset_value); let item_ptr = calc_addr_as_ptr( &mut s.current_block, @@ -1797,7 +1804,8 @@ impl<'eng> FnCompiler<'eng> { let u8 = Type::get_uint8(context); let ptr_u8 = Type::new_ptr(context, u8); - let two = Constant::new_uint(context, 64, 2); + let two = ConstantContent::new_uint(context, 64, 2); + let two = Constant::unique(context, two); let two = Value::new_constant(context, two); let new_cap_part = s.current_block @@ -2662,7 +2670,7 @@ impl<'eng> FnCompiler<'eng> { let u64_ty = Type::get_uint64(context); let user_args_val = match compiled_args.len() { - 0 => Constant::get_uint(context, 64, 0), + 0 => ConstantContent::get_uint(context, 64, 0), 1 => { // The single arg doesn't need to be put into a struct. let arg0 = compiled_args[0]; @@ -3015,7 +3023,7 @@ impl<'eng> FnCompiler<'eng> { 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(Constant::get_unit(context), context), + None => TerminatorValue::new(ConstantContent::get_unit(context), context), Some(expr) => self.compile_expression_to_value(context, md_mgr, expr)?, }; let false_block_end = self.current_block; @@ -3218,7 +3226,7 @@ impl<'eng> FnCompiler<'eng> { ); self.current_block = final_block; - let val = Constant::get_unit(context).add_metadatum(context, span_md_idx); + let val = ConstantContent::get_unit(context).add_metadatum(context, span_md_idx); Ok(TerminatorValue::new(val, context)) } @@ -3563,8 +3571,9 @@ impl<'eng> FnCompiler<'eng> { } Some((field_idx, field_type_id)) => { cur_type_id = field_type_id; - gep_indices - .push(Constant::get_uint(context, 64, field_idx)); + gep_indices.push(ConstantContent::get_uint( + context, 64, field_idx, + )); } } } @@ -3573,7 +3582,11 @@ impl<'eng> FnCompiler<'eng> { TypeInfo::Tuple(field_tys), ) => { cur_type_id = field_tys[*index].type_id; - gep_indices.push(Constant::get_uint(context, 64, *index as u64)); + gep_indices.push(ConstantContent::get_uint( + context, + 64, + *index as u64, + )); } ( ProjectionKind::ArrayIndex { index, .. }, @@ -3631,7 +3644,7 @@ impl<'eng> FnCompiler<'eng> { .store(lhs_ptr, rhs) .add_metadatum(context, span_md_idx); - let val = Constant::get_unit(context).add_metadatum(context, span_md_idx); + let val = ConstantContent::get_unit(context).add_metadatum(context, span_md_idx); Ok(TerminatorValue::new(val, context)) } @@ -3692,11 +3705,8 @@ impl<'eng> FnCompiler<'eng> { compiled_elems_iter.all(|elem| { elem.get_constant(context) .expect("Constant expression must evaluate to a constant IR value") - .eq( - context, - c.get_constant(context) - .expect("Constant expression must evaluate to a constant IR value"), - ) + == c.get_constant(context) + .expect("Constant expression must evaluate to a constant IR value") }) }); if let Some(const_initializer) = const_initialiser_opt { @@ -3705,7 +3715,8 @@ impl<'eng> FnCompiler<'eng> { .function .create_block(context, Some("array_init_loop".into())); // The loop begins with 0. - let zero = Constant::new_uint(context, 64, 0); + let zero = ConstantContent::new_uint(context, 64, 0); + let zero = Constant::unique(context, zero); let zero = Value::new_constant(context, zero); // Branch to the loop block, passing the initial iteration value. self.current_block @@ -3730,14 +3741,16 @@ impl<'eng> FnCompiler<'eng> { .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 = ConstantContent::new_uint(context, 64, 1); + let one = Constant::unique(context, one); 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 = ConstantContent::new_uint(context, 64, contents.len() as u64); + let len = Constant::unique(context, len); let len = Value::new_constant(context, len); let r#continue = self.current_block @@ -3815,7 +3828,7 @@ impl<'eng> FnCompiler<'eng> { let index_expr_span = index_expr.span.clone(); // Perform a bounds check if the array index is a constant int. - if let Ok(Constant { + if let Ok(ConstantContent { value: ConstantValue::Uint(constant_value), .. }) = compile_constant_expression_to_constant( @@ -3826,11 +3839,13 @@ impl<'eng> FnCompiler<'eng> { None, Some(self), index_expr, - ) { + ) + .map(|c| c.get_content(context)) + { let count = array_type.get_array_len(context).unwrap(); - if constant_value >= count { + if *constant_value >= count { return Err(CompileError::ArrayOutOfBounds { - index: constant_value, + index: *constant_value, count, span: index_expr_span, }); @@ -4002,7 +4017,7 @@ impl<'eng> FnCompiler<'eng> { &enum_decl.variants, )?; let tag_value = - Constant::get_uint(context, 64, tag as u64).add_metadatum(context, span_md_idx); + ConstantContent::get_uint(context, 64, tag as u64).add_metadatum(context, span_md_idx); // Start with a temporary local struct and insert the tag. let temp_name = self.lexical_map.insert_anon(); @@ -4124,7 +4139,7 @@ impl<'eng> FnCompiler<'eng> { if fields.is_empty() { // This is a Unit. We're still debating whether Unit should just be an empty tuple in // the IR or not... it is a special case for now. - let val = Constant::get_unit(context).add_metadatum(context, span_md_idx); + let val = ConstantContent::get_unit(context).add_metadatum(context, span_md_idx); Ok(TerminatorValue::new(val, context)) } else { let mut init_values = Vec::with_capacity(fields.len()); @@ -4424,7 +4439,7 @@ impl<'eng> FnCompiler<'eng> { .add_metadatum(context, span_md_idx); // Store the offset as the second field in the `StorageKey` struct - let offset_within_slot_val = Constant::get_uint(context, 64, offset_within_slot); + let offset_within_slot_val = ConstantContent::get_uint(context, 64, offset_within_slot); let gep_1_val = self.current_block .append(context) diff --git a/sway-core/src/ir_generation/storage.rs b/sway-core/src/ir_generation/storage.rs index 4f42a74a2e8..af4fdd9aaa4 100644 --- a/sway-core/src/ir_generation/storage.rs +++ b/sway-core/src/ir_generation/storage.rs @@ -5,9 +5,10 @@ use crate::fuel_prelude::{ }; use sway_features::ExperimentalFeatures; use sway_ir::{ - constant::{Constant, ConstantValue}, + constant::{ConstantContent, ConstantValue}, context::Context, irtype::Type, + Constant, }; use sway_types::u256::U256; @@ -140,7 +141,7 @@ pub fn serialize_to_storage_slots( ty: &Type, ) -> Vec { let experimental = context.experimental; - match &constant.value { + match &constant.get_content(context).value { ConstantValue::Undef => vec![], // If not being a part of an aggregate, single byte values like `bool`, `u8`, and unit // are stored as a byte at the beginning of the storage slot. @@ -231,7 +232,12 @@ pub fn serialize_to_storage_slots( // is a multiple of 4. This is useful because each storage slot is 4 words. // Regarding padding, the top level type in the call is either a string array, struct, or // a union. They will properly set the initial padding for the further recursive calls. - let mut packed = serialize_to_words(constant, context, ty, InByte8Padding::default()); + let mut packed = serialize_to_words( + constant.get_content(context), + context, + ty, + InByte8Padding::default(), + ); packed.extend(vec![ Bytes8::new([0; 8]); ((packed.len() + 3) / 4) * 4 - packed.len() @@ -276,7 +282,7 @@ pub fn serialize_to_storage_slots( /// Given a constant value `constant` and a type `ty`, serialize the constant into a vector of /// words and apply the requested padding if needed. fn serialize_to_words( - constant: &Constant, + constant: &ConstantContent, context: &Context, ty: &Type, padding: InByte8Padding, diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/storage.rs b/sway-core/src/semantic_analysis/ast_node/declaration/storage.rs index 7df2bb722d4..3112e5b2d87 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/storage.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/storage.rs @@ -119,7 +119,7 @@ impl ty::TyStorageField { .chain(vec![self.name.as_str().to_string()]) .collect(), key, - &constant.ty, + &constant.get_content(context).ty, ) }) } @@ -141,7 +141,7 @@ impl ty::TyStorageField { None, key_expression, )?; - if let ConstantValue::B256(key) = const_key.value { + if let ConstantValue::B256(key) = const_key.get_content(context).value.clone() { Ok(Some(key)) } else { Err(CompileError::Internal( diff --git a/sway-ir/src/constant.rs b/sway-ir/src/constant.rs index b3715b16803..13fd9dab0b5 100644 --- a/sway-ir/src/constant.rs +++ b/sway-ir/src/constant.rs @@ -1,11 +1,50 @@ //! [`Constant`] is a typed constant value. +use std::hash::{Hash, Hasher}; + use crate::{context::Context, irtype::Type, pretty::DebugWithContext, value::Value, Padding}; +use rustc_hash::FxHasher; use sway_types::u256::U256; +/// A wrapper around an [ECS](https://github.com/orlp/slotmap) handle into the +/// [`Context`]. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, DebugWithContext)] +pub struct Constant(#[in_context(values)] pub slotmap::DefaultKey); + +impl Constant { + /// Get or create a unique constant with given contents. + pub fn unique(context: &mut Context, constant: ConstantContent) -> Constant { + let mut hasher = FxHasher::default(); + constant.hash(&mut hasher); + let hash = hasher.finish(); + // Insert a new entry if it doesn't exist. + context.constants_map.entry(hash).or_insert_with(Vec::new); + let constants = context.constants_map.get(&hash).unwrap(); + // If the constant already exists, return it. + for c in constants.iter() { + if context.constants.get(c.0).unwrap().eq(context, &constant) { + return *c; + } + } + let constant = Constant(context.constants.insert(constant)); + // Re-borrow the constants map (mutably this time) to insert the new constant. + let constants = context.constants_map.get_mut(&hash).unwrap(); + constants.push(constant); + constant + } + + /// Get the contents of a unique constant + pub fn get_content<'a>(&self, context: &'a Context) -> &'a ConstantContent { + context + .constants + .get(self.0) + .expect("Constants are global immutable data, they must live through the context") + } +} + /// A [`Type`] and constant value, including [`ConstantValue::Undef`] for uninitialized constants. #[derive(Debug, Clone, DebugWithContext, Hash)] -pub struct Constant { +pub struct ConstantContent { pub ty: Type, pub value: ConstantValue, } @@ -22,28 +61,28 @@ pub enum ConstantValue { U256(U256), B256(B256), String(Vec), - Array(Vec), - Slice(Vec), - Struct(Vec), - Reference(Box), + Array(Vec), + Slice(Vec), + Struct(Vec), + Reference(Box), RawUntypedSlice(Vec), } /// A [Constant] with its required [Padding]. /// If the [Padding] is `None` the default [Padding] for the /// [Constant] type is expected. -type ConstantWithPadding<'a> = (&'a Constant, Option); +type ConstantWithPadding<'a> = (&'a ConstantContent, Option); -impl Constant { +impl ConstantContent { pub fn new_unit(context: &Context) -> Self { - Constant { + ConstantContent { ty: Type::get_unit(context), value: ConstantValue::Unit, } } pub fn new_bool(context: &Context, b: bool) -> Self { - Constant { + ConstantContent { ty: Type::get_bool(context), value: ConstantValue::Bool(b), } @@ -51,7 +90,7 @@ impl Constant { /// For numbers bigger than u64 see `new_uint256`. pub fn new_uint(context: &mut Context, nbits: u16, n: u64) -> Self { - Constant { + ConstantContent { ty: Type::new_uint(context, nbits), value: match nbits { 256 => ConstantValue::U256(n.into()), @@ -61,92 +100,107 @@ impl Constant { } pub fn new_uint256(context: &mut Context, n: U256) -> Self { - Constant { + ConstantContent { ty: Type::new_uint(context, 256), value: ConstantValue::U256(n), } } pub fn new_b256(context: &Context, bytes: [u8; 32]) -> Self { - Constant { + ConstantContent { ty: Type::get_b256(context), value: ConstantValue::B256(B256::from_be_bytes(&bytes)), } } pub fn new_string(context: &mut Context, string: Vec) -> Self { - Constant { + ConstantContent { ty: Type::new_string_array(context, string.len() as u64), value: ConstantValue::String(string), } } - pub fn new_array(context: &mut Context, elm_ty: Type, elems: Vec) -> Self { - Constant { + pub fn new_array(context: &mut Context, elm_ty: Type, elems: Vec) -> Self { + ConstantContent { ty: Type::new_array(context, elm_ty, elems.len() as u64), value: ConstantValue::Array(elems), } } - pub fn new_struct(context: &mut Context, field_tys: Vec, fields: Vec) -> Self { - Constant { + pub fn new_struct( + context: &mut Context, + field_tys: Vec, + fields: Vec, + ) -> Self { + ConstantContent { ty: Type::new_struct(context, field_tys), value: ConstantValue::Struct(fields), } } pub fn get_undef(ty: Type) -> Self { - Constant { + ConstantContent { ty, value: ConstantValue::Undef, } } pub fn get_unit(context: &mut Context) -> Value { - let new_const = Constant::new_unit(context); + let new_const_contents = ConstantContent::new_unit(context); + let new_const = Constant::unique(context, new_const_contents); Value::new_constant(context, new_const) } pub fn get_bool(context: &mut Context, value: bool) -> Value { - let new_const = Constant::new_bool(context, value); + let new_const_contents = ConstantContent::new_bool(context, value); + let new_const = Constant::unique(context, new_const_contents); Value::new_constant(context, new_const) } pub fn get_uint(context: &mut Context, nbits: u16, value: u64) -> Value { - let new_const = Constant::new_uint(context, nbits, value); + let new_const_contents = ConstantContent::new_uint(context, nbits, value); + let new_const = Constant::unique(context, new_const_contents); Value::new_constant(context, new_const) } pub fn get_uint256(context: &mut Context, value: U256) -> Value { - let new_const = Constant::new_uint256(context, value); + let new_const_contents = ConstantContent::new_uint256(context, value); + let new_const = Constant::unique(context, new_const_contents); Value::new_constant(context, new_const) } pub fn get_b256(context: &mut Context, value: [u8; 32]) -> Value { - let new_const = Constant::new_b256(context, value); + let new_const_contents = ConstantContent::new_b256(context, value); + let new_const = Constant::unique(context, new_const_contents); Value::new_constant(context, new_const) } pub fn get_string(context: &mut Context, value: Vec) -> Value { - let new_const = Constant::new_string(context, value); + let new_const_contents = ConstantContent::new_string(context, value); + let new_const = Constant::unique(context, new_const_contents); Value::new_constant(context, new_const) } /// `value` must be created as an array constant first, using [`Constant::new_array()`]. - pub fn get_array(context: &mut Context, value: Constant) -> Value { + pub fn get_array(context: &mut Context, value: ConstantContent) -> Value { assert!(value.ty.is_array(context)); - Value::new_constant(context, value) + let new_const = Constant::unique(context, value); + Value::new_constant(context, new_const) } /// `value` must be created as a struct constant first, using [`Constant::new_struct()`]. - pub fn get_struct(context: &mut Context, value: Constant) -> Value { + pub fn get_struct(context: &mut Context, value: ConstantContent) -> Value { assert!(value.ty.is_struct(context)); - Value::new_constant(context, value) + let new_const = Constant::unique(context, value); + Value::new_constant(context, new_const) } /// Returns the tag and the value of an enum constant if `self` is an enum constant, /// otherwise `None`. - fn extract_enum_tag_and_value(&self, context: &Context) -> Option<(&Constant, &Constant)> { + fn extract_enum_tag_and_value( + &self, + context: &Context, + ) -> Option<(&ConstantContent, &ConstantContent)> { if !self.ty.is_enum(context) { return None; } @@ -216,7 +270,7 @@ impl Constant { fn elements_of_aggregate_with_padding( &self, context: &Context, - ) -> Option)>> { + ) -> Option)>> { // We need a special handling in case of enums. if let Some((tag, value)) = self.extract_enum_tag_and_value(context) { let tag_with_padding = (tag, None); diff --git a/sway-ir/src/context.rs b/sway-ir/src/context.rs index a9e707336fd..b63a0c18b80 100644 --- a/sway-ir/src/context.rs +++ b/sway-ir/src/context.rs @@ -18,7 +18,7 @@ use crate::{ metadata::Metadatum, module::{Kind, ModuleContent, ModuleIterator}, value::ValueContent, - Type, TypeContent, + Constant, ConstantContent, Type, TypeContent, }; /// The main IR context handle. @@ -35,6 +35,10 @@ pub struct Context<'eng> { pub(crate) local_vars: SlotMap, pub(crate) types: SlotMap, pub(crate) type_map: FxHashMap, + pub(crate) constants: SlotMap, + // Maps the hash of a ConstantContent to the list of constants with that hash. + pub(crate) constants_map: FxHashMap>, + pub(crate) metadata: SlotMap, pub program_kind: Kind, @@ -55,6 +59,8 @@ impl<'eng> Context<'eng> { local_vars: Default::default(), types: Default::default(), type_map: Default::default(), + constants: Default::default(), + constants_map: Default::default(), metadata: Default::default(), next_unique_sym_tag: Default::default(), program_kind: Kind::Contract, diff --git a/sway-ir/src/function.rs b/sway-ir/src/function.rs index cedfee85930..4c19ee24a3d 100644 --- a/sway-ir/src/function.rs +++ b/sway-ir/src/function.rs @@ -11,10 +11,8 @@ use std::fmt::Write; use rustc_hash::{FxHashMap, FxHashSet}; -use crate::InstOp; use crate::{ block::{Block, BlockIterator, Label}, - constant::Constant, context::Context, error::IrError, irtype::Type, @@ -24,6 +22,7 @@ use crate::{ value::{Value, ValueDatum}, BlockArgument, BranchToWithArgs, }; +use crate::{Constant, InstOp}; /// A wrapper around an [ECS](https://github.com/orlp/slotmap) handle into the /// [`Context`]. diff --git a/sway-ir/src/instruction.rs b/sway-ir/src/instruction.rs index cd710a609c0..e62d9238d49 100644 --- a/sway-ir/src/instruction.rs +++ b/sway-ir/src/instruction.rs @@ -19,7 +19,7 @@ use crate::{ local_var::LocalVar, pretty::DebugWithContext, value::{Value, ValueDatum}, - AsmInstruction, Constant, Module, + AsmInstruction, ConstantContent, Module, }; #[derive(Debug, Clone, DebugWithContext)] @@ -288,7 +288,9 @@ impl InstOp { // Load needs to strip the pointer from the source type. InstOp::Load(ptr_val) => match &context.values[ptr_val.0].value { ValueDatum::Argument(arg) => arg.ty.get_pointee_type(context), - ValueDatum::Constant(cons) => cons.ty.get_pointee_type(context), + ValueDatum::Constant(cons) => { + cons.get_content(context).ty.get_pointee_type(context) + } ValueDatum::Instruction(ins) => ins .get_type(context) .and_then(|ty| ty.get_pointee_type(context)), @@ -1024,14 +1026,14 @@ impl<'a, 'eng> InstructionInserter<'a, 'eng> { } pub fn get_elem_ptr_with_idx(self, base: Value, elem_ty: Type, index: u64) -> Value { - let idx_val = Constant::get_uint(self.context, 64, index); + let idx_val = ConstantContent::get_uint(self.context, 64, index); self.get_elem_ptr(base, elem_ty, vec![idx_val]) } pub fn get_elem_ptr_with_idcs(self, base: Value, elem_ty: Type, indices: &[u64]) -> Value { let idx_vals = indices .iter() - .map(|idx| Constant::get_uint(self.context, 64, *idx)) + .map(|idx| ConstantContent::get_uint(self.context, 64, *idx)) .collect(); self.get_elem_ptr(base, elem_ty, idx_vals) } diff --git a/sway-ir/src/irtype.rs b/sway-ir/src/irtype.rs index dc96427d56a..40165fa91a4 100644 --- a/sway-ir/src/irtype.rs +++ b/sway-ir/src/irtype.rs @@ -9,7 +9,7 @@ //! [`Aggregate`] is an abstract collection of [`Type`]s used for structs, unions and arrays, //! though see below for future improvements around splitting arrays into a different construct. -use crate::{context::Context, pretty::DebugWithContext, Constant, ConstantValue, Value}; +use crate::{context::Context, pretty::DebugWithContext, ConstantContent, ConstantValue, Value}; #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub struct Type(pub slotmap::DefaultKey); @@ -426,10 +426,10 @@ impl Type { let const_indices: Vec<_> = indices .iter() .map_while(|idx| { - if let Some(Constant { + if let Some(ConstantContent { value: ConstantValue::Uint(idx), ty: _, - }) = idx.get_constant(context) + }) = idx.get_constant(context).map(|c| c.get_content(context)) { Some(*idx) } else { diff --git a/sway-ir/src/local_var.rs b/sway-ir/src/local_var.rs index f8f43cade42..56d91fdf6c8 100644 --- a/sway-ir/src/local_var.rs +++ b/sway-ir/src/local_var.rs @@ -1,10 +1,10 @@ //! A value representing a function-local variable. use crate::{ - constant::Constant, context::Context, irtype::{Type, TypeContent}, pretty::DebugWithContext, + Constant, }; /// A wrapper around an [ECS](https://github.com/orlp/slotmap) handle into the diff --git a/sway-ir/src/optimize/const_demotion.rs b/sway-ir/src/optimize/const_demotion.rs index a42a6932106..81b9cbfabf1 100644 --- a/sway-ir/src/optimize/const_demotion.rs +++ b/sway-ir/src/optimize/const_demotion.rs @@ -36,7 +36,7 @@ pub fn const_demotion( let operands = inst.get_instruction(context).unwrap().op.get_operands(); for val in operands.iter() { if let Some(c) = val.get_constant(context) { - if super::target_fuel::is_demotable_type(context, &c.ty) { + if super::target_fuel::is_demotable_type(context, &c.get_content(context).ty) { let dem = (*val, c.clone()); match candidate_values.entry(block) { indexmap::map::Entry::Occupied(mut occ) => { @@ -64,7 +64,7 @@ pub fn const_demotion( let var = function.new_unique_local_var( context, "__const".to_owned(), - c.ty, + c.get_content(context).ty, Some(c.clone()), false, ); diff --git a/sway-ir/src/optimize/constants.rs b/sway-ir/src/optimize/constants.rs index ec665bc1ff7..b0704708963 100644 --- a/sway-ir/src/optimize/constants.rs +++ b/sway-ir/src/optimize/constants.rs @@ -1,13 +1,14 @@ //! Optimization passes for manipulating constant values. use crate::{ - constant::{Constant, ConstantValue}, + constant::{ConstantContent, ConstantValue}, context::Context, error::IrError, function::Function, instruction::InstOp, value::ValueDatum, - AnalysisResults, BranchToWithArgs, Instruction, Pass, PassMutability, Predicate, ScopedPass, + AnalysisResults, BranchToWithArgs, Constant, Instruction, Pass, PassMutability, Predicate, + ScopedPass, }; use rustc_hash::FxHashMap; @@ -76,7 +77,12 @@ fn combine_cbr(context: &mut Context, function: &Function) -> Result { - match &cond_value.get_constant(context).unwrap().value { + match &cond_value + .get_constant(context) + .unwrap() + .get_content(context) + .value + { ConstantValue::Bool(true) => Some(Ok(( inst_val, in_block, @@ -140,9 +146,12 @@ fn combine_cmp(context: &mut Context, function: &Function) -> bool { use ConstantValue::*; match pred { - Predicate::Equal => Some((inst_val, block, val1.eq(context, val2))), + Predicate::Equal => Some((inst_val, block, val1 == val2)), Predicate::GreaterThan => { - let r = match (&val1.value, &val2.value) { + let r = match ( + &val1.get_content(context).value, + &val2.get_content(context).value, + ) { (Uint(val1), Uint(val2)) => val1 > val2, (U256(val1), U256(val2)) => val1 > val2, (B256(val1), B256(val2)) => val1 > val2, @@ -155,7 +164,10 @@ fn combine_cmp(context: &mut Context, function: &Function) -> bool { Some((inst_val, block, r)) } Predicate::LessThan => { - let r = match (&val1.value, &val2.value) { + let r = match ( + &val1.get_content(context).value, + &val2.get_content(context).value, + ) { (Uint(val1), Uint(val2)) => val1 < val2, (U256(val1), U256(val2)) => val1 < val2, (B256(val1), B256(val2)) => val1 < val2, @@ -174,11 +186,10 @@ fn combine_cmp(context: &mut Context, function: &Function) -> bool { ); candidate.map_or(false, |(inst_val, block, cn_replace)| { + let const_content = ConstantContent::new_bool(context, cn_replace); + let constant = crate::Constant::unique(context, const_content); // Replace this `cmp` instruction with a constant. - inst_val.replace( - context, - ValueDatum::Constant(Constant::new_bool(context, cn_replace)), - ); + inst_val.replace(context, ValueDatum::Constant(constant)); block.remove_instruction(context, inst_val); true }) @@ -193,8 +204,8 @@ fn combine_binary_op(context: &mut Context, function: &Function) -> bool { op: InstOp::BinaryOp { op, arg1, arg2 }, .. }) if arg1.is_constant(context) && arg2.is_constant(context) => { - let val1 = arg1.get_constant(context).unwrap(); - let val2 = arg2.get_constant(context).unwrap(); + let val1 = arg1.get_constant(context).unwrap().get_content(context); + let val2 = arg2.get_constant(context).unwrap().get_content(context); use crate::BinaryOpKind::*; use ConstantValue::*; let v = match (op, &val1.value, &val2.value) { @@ -233,7 +244,7 @@ fn combine_binary_op(context: &mut Context, function: &Function) -> bool { (Lsh, U256(l), Uint(r)) => l.checked_shl(r).map(U256), _ => None, }; - v.map(|value| (inst_val, block, Constant { ty: val1.ty, value })) + v.map(|value| (inst_val, block, ConstantContent { ty: val1.ty, value })) } _ => None, }, @@ -241,6 +252,7 @@ fn combine_binary_op(context: &mut Context, function: &Function) -> bool { // Replace this binary op instruction with a constant. candidate.map_or(false, |(inst_val, block, new_value)| { + let new_value = Constant::unique(context, new_value); inst_val.replace(context, ValueDatum::Constant(new_value)); block.remove_instruction(context, inst_val); true @@ -257,8 +269,12 @@ fn remove_useless_binary_op(context: &mut Context, function: &Function) -> bool op: InstOp::BinaryOp { op, arg1, arg2 }, .. }) if arg1.is_constant(context) || arg2.is_constant(context) => { - let val1 = arg1.get_constant(context).map(|x| &x.value); - let val2 = arg2.get_constant(context).map(|x| &x.value); + let val1 = arg1 + .get_constant(context) + .map(|x| &x.get_content(context).value); + let val2 = arg2 + .get_constant(context) + .map(|x| &x.get_content(context).value); use crate::BinaryOpKind::*; use ConstantValue::*; @@ -303,21 +319,34 @@ fn combine_unary_op(context: &mut Context, function: &Function) -> bool { let val = arg.get_constant(context).unwrap(); use crate::UnaryOpKind::*; use ConstantValue::*; - let v = match (op, &val.value) { - (Not, Uint(v)) => val.ty.get_uint_width(context).and_then(|width| { - let max = match width { - 8 => u8::MAX as u64, - 16 => u16::MAX as u64, - 32 => u32::MAX as u64, - 64 => u64::MAX, - _ => return None, - }; - Some(Uint((!v) & max)) - }), + let v = match (op, &val.get_content(context).value) { + (Not, Uint(v)) => val + .get_content(context) + .ty + .get_uint_width(context) + .and_then(|width| { + let max = match width { + 8 => u8::MAX as u64, + 16 => u16::MAX as u64, + 32 => u32::MAX as u64, + 64 => u64::MAX, + _ => return None, + }; + Some(Uint((!v) & max)) + }), (Not, U256(v)) => Some(U256(!v)), _ => None, }; - v.map(|value| (inst_val, block, Constant { ty: val.ty, value })) + v.map(|value| { + ( + inst_val, + block, + ConstantContent { + ty: val.get_content(context).ty, + value, + }, + ) + }) } _ => None, }, @@ -325,6 +354,7 @@ fn combine_unary_op(context: &mut Context, function: &Function) -> bool { // Replace this unary op instruction with a constant. candidate.map_or(false, |(inst_val, block, new_value)| { + let new_value = Constant::unique(context, new_value); inst_val.replace(context, ValueDatum::Constant(new_value)); block.remove_instruction(context, inst_val); true diff --git a/sway-ir/src/optimize/cse.rs b/sway-ir/src/optimize/cse.rs index 3eb03ac4ccb..cb5321aeeb1 100644 --- a/sway-ir/src/optimize/cse.rs +++ b/sway-ir/src/optimize/cse.rs @@ -242,7 +242,7 @@ pub fn cse( let c = val .get_constant(context) .expect("const_map can only contain consts"); - const_opd_const.eq(context, c) + const_opd_const == c }) }) { vntable diff --git a/sway-ir/src/optimize/mem2reg.rs b/sway-ir/src/optimize/mem2reg.rs index c7c8a5b847b..53fc88fe387 100644 --- a/sway-ir/src/optimize/mem2reg.rs +++ b/sway-ir/src/optimize/mem2reg.rs @@ -251,12 +251,11 @@ pub fn promote_to_registers( Some(val) => *val, None => { // Nothing on the stack, let's attempt to get the initializer - Value::new_constant( - context, - var.get_initializer(context) - .expect("We're dealing with an uninitialized value") - .clone(), - ) + let constant = var + .get_initializer(context) + .expect("We're dealing with an uninitialized value") + .clone(); + Value::new_constant(context, constant) } }; rewrites.insert(inst, new_val); @@ -304,12 +303,11 @@ pub fn promote_to_registers( Some(val) => *val, None => { // Nothing on the stack, let's attempt to get the initializer - Value::new_constant( - context, - ptr.get_initializer(context) - .expect("We're dealing with an uninitialized value") - .clone(), - ) + let constant = ptr + .get_initializer(context) + .expect("We're dealing with an uninitialized value") + .clone(); + Value::new_constant(context, constant) } }; let params = node.get_succ_params_mut(context, &succ).unwrap(); diff --git a/sway-ir/src/optimize/misc_demotion.rs b/sway-ir/src/optimize/misc_demotion.rs index b128b85197c..753c0e1f2b2 100644 --- a/sway-ir/src/optimize/misc_demotion.rs +++ b/sway-ir/src/optimize/misc_demotion.rs @@ -12,9 +12,9 @@ use std::ops::Not; /// values. /// - Fuel Wide binary operators: Demote binary operands bigger than 64 bits. use crate::{ - asm::AsmArg, AnalysisResults, BinaryOpKind, Constant, Context, FuelVmInstruction, Function, - InstOp, InstructionInserter, IrError, Pass, PassMutability, Predicate, ScopedPass, Type, - UnaryOpKind, Value, + asm::AsmArg, AnalysisResults, BinaryOpKind, Constant, ConstantContent, Context, + FuelVmInstruction, Function, InstOp, InstructionInserter, IrError, Pass, PassMutability, + Predicate, ScopedPass, Type, UnaryOpKind, Value, }; use rustc_hash::FxHashMap; @@ -477,7 +477,8 @@ fn wide_binary_op_demotion(context: &mut Context, function: Function) -> Result< // For MOD we need a local zero as RHS of the add operation let (wide_op, get_local_zero) = match op { BinaryOpKind::Mod => { - let initializer = Constant::new_uint(context, 256, 0); + let initializer = ConstantContent::new_uint(context, 256, 0); + let initializer = Constant::unique(context, initializer); let local_zero = function.new_unique_local_var( context, "__wide_zero".to_owned(), diff --git a/sway-ir/src/optimize/sroa.rs b/sway-ir/src/optimize/sroa.rs index 70c8160362c..5caa780fe8b 100644 --- a/sway-ir/src/optimize/sroa.rs +++ b/sway-ir/src/optimize/sroa.rs @@ -4,9 +4,9 @@ use rustc_hash::{FxHashMap, FxHashSet}; use crate::{ combine_indices, compute_escaped_symbols, get_gep_referred_symbols, get_loaded_ptr_values, - get_stored_ptr_values, pointee_size, AnalysisResults, Constant, ConstantValue, Context, - EscapedSymbols, Function, InstOp, IrError, LocalVar, Pass, PassMutability, ScopedPass, Symbol, - Type, Value, + get_stored_ptr_values, pointee_size, AnalysisResults, Constant, ConstantContent, ConstantValue, + Context, EscapedSymbols, Function, InstOp, IrError, LocalVar, Pass, PassMutability, ScopedPass, + Symbol, Type, Value, }; pub const SROA_NAME: &str = "sroa"; @@ -47,12 +47,14 @@ fn split_aggregate( initializer: Option, base_off: &mut u32, ) { - fn constant_index(c: &Constant, idx: usize) -> Constant { - match &c.value { - ConstantValue::Array(cs) | ConstantValue::Struct(cs) => cs - .get(idx) - .expect("Malformed initializer. Cannot index into sub-initializer") - .clone(), + fn constant_index(context: &mut Context, c: &Constant, idx: usize) -> Constant { + match &c.get_content(context).value { + ConstantValue::Array(cs) | ConstantValue::Struct(cs) => Constant::unique( + context, + cs.get(idx) + .expect("Malformed initializer. Cannot index into sub-initializer") + .clone(), + ), _ => panic!("Expected only array or struct const initializers"), } } @@ -67,13 +69,16 @@ fn split_aggregate( } else { let mut i = 0; while let Some(member_ty) = ty.get_indexed_type(context, &[i]) { + let initializer = initializer + .as_ref() + .map(|c| constant_index(context, c, i as usize)); split_type( context, function, aggr_base_name, map, member_ty, - initializer.as_ref().map(|c| constant_index(c, i as usize)), + initializer, base_off, ); @@ -247,7 +252,8 @@ pub fn sroa( let elm_index_values = indices .iter() .map(|&index| { - let c = Constant::new_uint(context, 64, index.into()); + let c = ConstantContent::new_uint(context, 64, index.into()); + let c = Constant::unique(context, c); Value::new_constant(context, c) }) .collect(); @@ -317,7 +323,8 @@ pub fn sroa( let elm_index_values = indices .iter() .map(|&index| { - let c = Constant::new_uint(context, 64, index.into()); + let c = ConstantContent::new_uint(context, 64, index.into()); + let c = Constant::unique(context, c); Value::new_constant(context, c) }) .collect(); diff --git a/sway-ir/src/parser.rs b/sway-ir/src/parser.rs index fb577d05e15..93065c5c265 100644 --- a/sway-ir/src/parser.rs +++ b/sway-ir/src/parser.rs @@ -678,7 +678,7 @@ mod ir_builder { use crate::{ asm::{AsmArg, AsmInstruction}, block::Block, - constant::{Constant, ConstantValue}, + constant::{ConstantContent, ConstantValue}, context::Context, error::IrError, function::Function, @@ -688,7 +688,7 @@ mod ir_builder { metadata::{MetadataIndex, Metadatum}, module::{Kind, Module}, value::Value, - BinaryOpKind, BlockArgument, ConfigContent, Instruction, UnaryOpKind, B256, + BinaryOpKind, BlockArgument, ConfigContent, Constant, Instruction, UnaryOpKind, B256, }; #[derive(Debug)] @@ -838,14 +838,24 @@ mod ir_builder { IrAstConstValue::Array(el_ty, els) => { let els: Vec<_> = els .iter() - .map(|cv| cv.value.as_constant(context, el_ty.clone())) + .map(|cv| { + cv.value + .as_constant(context, el_ty.clone()) + .get_content(context) + .clone() + }) .collect(); ConstantValue::Array(els) } IrAstConstValue::Struct(flds) => { let fields: Vec<_> = flds .iter() - .map(|(ty, cv)| cv.value.as_constant(context, ty.clone())) + .map(|(ty, cv)| { + cv.value + .as_constant(context, ty.clone()) + .get_content(context) + .clone() + }) .collect::>(); ConstantValue::Struct(fields) } @@ -853,38 +863,40 @@ mod ir_builder { } fn as_constant(&self, context: &mut Context, val_ty: IrAstTy) -> Constant { - Constant { + let value = self.as_constant_value(context, val_ty.clone()); + let constant = ConstantContent { ty: val_ty.to_ir_type(context), - value: self.as_constant_value(context, val_ty), - } + value, + }; + Constant::unique(context, constant) } fn as_value(&self, context: &mut Context, val_ty: IrAstTy) -> Value { match self { IrAstConstValue::Undef => unreachable!("Can't convert 'undef' to a value."), - IrAstConstValue::Unit => Constant::get_unit(context), - IrAstConstValue::Bool(b) => Constant::get_bool(context, *b), + IrAstConstValue::Unit => ConstantContent::get_unit(context), + IrAstConstValue::Bool(b) => ConstantContent::get_bool(context, *b), IrAstConstValue::Hex256(bs) => match val_ty { IrAstTy::U256 => { let n = U256::from_be_bytes(bs); - Constant::get_uint256(context, n) + ConstantContent::get_uint256(context, n) } - IrAstTy::B256 => Constant::get_b256(context, *bs), + IrAstTy::B256 => ConstantContent::get_b256(context, *bs), _ => unreachable!("invalid type for hex number"), }, IrAstConstValue::Number(n) => match val_ty { - IrAstTy::U8 => Constant::get_uint(context, 8, *n), - IrAstTy::U64 => Constant::get_uint(context, 64, *n), + IrAstTy::U8 => ConstantContent::get_uint(context, 8, *n), + IrAstTy::U64 => ConstantContent::get_uint(context, 64, *n), _ => unreachable!(), }, - IrAstConstValue::String(s) => Constant::get_string(context, s.clone()), + IrAstConstValue::String(s) => ConstantContent::get_string(context, s.clone()), IrAstConstValue::Array(..) => { let array_const = self.as_constant(context, val_ty); - Constant::get_array(context, array_const) + ConstantContent::get_array(context, array_const.get_content(context).clone()) } IrAstConstValue::Struct(_) => { let struct_const = self.as_constant(context, val_ty); - Constant::get_struct(context, struct_const) + ConstantContent::get_struct(context, struct_const.get_content(context).clone()) } } } diff --git a/sway-ir/src/printer.rs b/sway-ir/src/printer.rs index b589c329c3b..63461356c2d 100644 --- a/sway-ir/src/printer.rs +++ b/sway-ir/src/printer.rs @@ -11,7 +11,7 @@ use sway_types::SourceEngine; use crate::{ asm::*, block::Block, - constant::{Constant, ConstantValue}, + constant::{ConstantContent, ConstantValue}, context::Context, function::{Function, FunctionContent}, instruction::{FuelVmInstruction, InstOp, Predicate, Register}, @@ -251,7 +251,7 @@ fn config_to_doc( Doc::text(format!( "{} = config {}", name, - constant.as_lit_string(context) + constant.get_content(context).as_lit_string(context) )) .append(md_namer.md_idx_to_doc(context, opt_metadata)), ), @@ -374,7 +374,7 @@ fn function_to_doc<'a>( let init_doc = match &var_content.initializer { Some(const_val) => Doc::text(format!( " = const {}", - const_val.as_lit_string(context) + const_val.get_content(context).as_lit_string(context) )), None => Doc::Empty, }; @@ -455,7 +455,7 @@ fn constant_to_doc( Doc::text(format!( "{} = const {}", namer.name(context, const_val), - constant.as_lit_string(context) + constant.get_content(context).as_lit_string(context) )) .append(md_namer.md_idx_to_doc(context, metadata)), ) @@ -1137,7 +1137,7 @@ fn asm_block_to_doc( .append(Doc::text_line("}")) } -impl Constant { +impl ConstantContent { fn as_lit_string(&self, context: &Context) -> String { match &self.value { ConstantValue::Undef => format!("{} undef", self.ty.as_string(context)), diff --git a/sway-ir/src/value.rs b/sway-ir/src/value.rs index 42324aca351..27ec05e6f4d 100644 --- a/sway-ir/src/value.rs +++ b/sway-ir/src/value.rs @@ -10,13 +10,12 @@ use rustc_hash::FxHashMap; use crate::{ block::BlockArgument, - constant::Constant, context::Context, instruction::InstOp, irtype::Type, metadata::{combine, MetadataIndex}, pretty::DebugWithContext, - Block, Instruction, + Block, Constant, Instruction, }; /// A wrapper around an [ECS](https://github.com/orlp/slotmap) handle into the @@ -185,7 +184,7 @@ impl Value { pub fn get_type(&self, context: &Context) -> Option { match &context.values[self.0].value { ValueDatum::Argument(BlockArgument { ty, .. }) => Some(*ty), - ValueDatum::Constant(c) => Some(c.ty), + ValueDatum::Constant(c) => Some(c.get_content(context).ty), ValueDatum::Instruction(ins) => ins.get_type(context), } } diff --git a/sway-ir/src/verify.rs b/sway-ir/src/verify.rs index 7db2fd93dbe..99ed34b8217 100644 --- a/sway-ir/src/verify.rs +++ b/sway-ir/src/verify.rs @@ -795,7 +795,7 @@ impl InstructionVerifier<'_, '_> { idx_val .get_constant(self.context) .and_then(|const_ref| { - if let ConstantValue::Uint(n) = const_ref.value { + if let ConstantValue::Uint(n) = const_ref.get_content(self.context).value { Some(n) } else { None From 3e840cd5cb1662561a3ee87d90e4766432ada09c Mon Sep 17 00:00:00 2001 From: Vaivaswatha Nagaraj Date: Fri, 7 Feb 2025 12:47:46 +0530 Subject: [PATCH 02/21] clippy fixes --- sway-core/src/ir_generation/const_eval.rs | 14 +++++++------- sway-ir/src/constant.rs | 2 +- sway-ir/src/optimize/const_demotion.rs | 4 ++-- sway-ir/src/optimize/mem2reg.rs | 10 ++++------ 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/sway-core/src/ir_generation/const_eval.rs b/sway-core/src/ir_generation/const_eval.rs index 4545d58f833..067bb74319b 100644 --- a/sway-core/src/ir_generation/const_eval.rs +++ b/sway-core/src/ir_generation/const_eval.rs @@ -72,7 +72,7 @@ pub(crate) fn compile_const_decl( { found_local = true; if let Some(constant) = local_var.get_initializer(env.context) { - return Ok(Some(Value::new_constant(env.context, constant.clone()))); + return Ok(Some(Value::new_constant(env.context, *constant))); } // Check if a constant was stored to a local variable in the current block. @@ -99,7 +99,7 @@ pub(crate) fn compile_const_decl( } } if let Some(constant) = stored_const_opt { - return Ok(Some(Value::new_constant(env.context, constant.clone()))); + return Ok(Some(Value::new_constant(env.context, *constant))); } } @@ -314,7 +314,7 @@ fn const_eval_typed_expr( match known_consts.get(name) { // 1. Check if name/call_path is in known_consts. - Some(cvs) => Some(cvs.clone()), + Some(cvs) => Some(*cvs), None => { // 2. Check if name is a global constant. (lookup.lookup)(lookup, call_path, &Some(*const_decl.clone())) @@ -331,7 +331,7 @@ fn const_eval_typed_expr( name, call_path, .. } => match known_consts.get(name) { // 1. Check if name/call_path is in known_consts. - Some(cvs) => Some(cvs.clone()), + Some(cvs) => Some(*cvs), None => { let call_path = match call_path { Some(call_path) => call_path.clone(), @@ -713,7 +713,7 @@ fn const_eval_typed_expr( limit -= 1; let condition = const_eval_typed_expr(lookup, known_consts, condition)?; - match condition.map(|x| x.get_content(&lookup.context).value.clone()) { + match condition.map(|x| x.get_content(lookup.context).value.clone()) { Some(ConstantValue::Bool(true)) => { // Break and continue are not implemented, so there is need for flow control here let _ = const_eval_codeblock(lookup, known_consts, body)?; @@ -919,7 +919,7 @@ fn const_eval_intrinsic( use ConstantValue::*; let c = match ( - &args[0].get_content(&lookup.context).value, + &args[0].get_content(lookup.context).value, &args[1].get_content(lookup.context).value, ) { (Uint(arg1), Uint(ref arg2)) => { @@ -978,7 +978,7 @@ fn const_eval_intrinsic( use ConstantValue::*; let c = match ( &args[0].get_content(lookup.context).value, - &args[1].get_content(&lookup.context).value, + &args[1].get_content(lookup.context).value, ) { (Uint(arg1), Uint(ref arg2)) => { // All arithmetic is done as if it were u64 diff --git a/sway-ir/src/constant.rs b/sway-ir/src/constant.rs index 13fd9dab0b5..6b5bf62550c 100644 --- a/sway-ir/src/constant.rs +++ b/sway-ir/src/constant.rs @@ -18,7 +18,7 @@ impl Constant { constant.hash(&mut hasher); let hash = hasher.finish(); // Insert a new entry if it doesn't exist. - context.constants_map.entry(hash).or_insert_with(Vec::new); + context.constants_map.entry(hash).or_default(); let constants = context.constants_map.get(&hash).unwrap(); // If the constant already exists, return it. for c in constants.iter() { diff --git a/sway-ir/src/optimize/const_demotion.rs b/sway-ir/src/optimize/const_demotion.rs index 81b9cbfabf1..09298e7a7ac 100644 --- a/sway-ir/src/optimize/const_demotion.rs +++ b/sway-ir/src/optimize/const_demotion.rs @@ -37,7 +37,7 @@ pub fn const_demotion( for val in operands.iter() { if let Some(c) = val.get_constant(context) { if super::target_fuel::is_demotable_type(context, &c.get_content(context).ty) { - let dem = (*val, c.clone()); + let dem = (*val, *c); match candidate_values.entry(block) { indexmap::map::Entry::Occupied(mut occ) => { occ.get_mut().push(dem); @@ -65,7 +65,7 @@ pub fn const_demotion( context, "__const".to_owned(), c.get_content(context).ty, - Some(c.clone()), + Some(c), false, ); let var_val = Value::new_instruction(context, block, InstOp::GetLocal(var)); diff --git a/sway-ir/src/optimize/mem2reg.rs b/sway-ir/src/optimize/mem2reg.rs index 53fc88fe387..0b955b4ce07 100644 --- a/sway-ir/src/optimize/mem2reg.rs +++ b/sway-ir/src/optimize/mem2reg.rs @@ -251,10 +251,9 @@ pub fn promote_to_registers( Some(val) => *val, None => { // Nothing on the stack, let's attempt to get the initializer - let constant = var + let constant = *var .get_initializer(context) - .expect("We're dealing with an uninitialized value") - .clone(); + .expect("We're dealing with an uninitialized value"); Value::new_constant(context, constant) } }; @@ -303,10 +302,9 @@ pub fn promote_to_registers( Some(val) => *val, None => { // Nothing on the stack, let's attempt to get the initializer - let constant = ptr + let constant = *ptr .get_initializer(context) - .expect("We're dealing with an uninitialized value") - .clone(); + .expect("We're dealing with an uninitialized value"); Value::new_constant(context, constant) } }; From 9ed1a9907d88a017e89792c5d2137ce44a527f95 Mon Sep 17 00:00:00 2001 From: Vaivaswatha Nagaraj Date: Thu, 13 Feb 2025 21:05:51 +0530 Subject: [PATCH 03/21] Introduce global variables and a get_global IR instruction --- .../src/asm_generation/evm/evm_asm_builder.rs | 5 ++ .../asm_generation/fuel/fuel_asm_builder.rs | 35 +++++++++++ sway-core/src/ir_generation/const_eval.rs | 24 ++++++-- sway-core/src/ir_generation/function.rs | 11 +++- sway-ir/src/analysis/memory_utils.rs | 3 + sway-ir/src/context.rs | 6 +- sway-ir/src/function.rs | 2 +- sway-ir/src/instruction.rs | 17 +++++- sway-ir/src/lib.rs | 4 +- sway-ir/src/module.rs | 38 ++++++++---- sway-ir/src/optimize/cse.rs | 1 + sway-ir/src/optimize/fn_dedup.rs | 5 ++ sway-ir/src/optimize/inline.rs | 3 +- sway-ir/src/parser.rs | 2 +- sway-ir/src/printer.rs | 15 +++++ sway-ir/src/{local_var.rs => variable.rs} | 61 +++++++++++++++++++ sway-ir/src/verify.rs | 18 +++++- 17 files changed, 220 insertions(+), 30 deletions(-) rename sway-ir/src/{local_var.rs => variable.rs} (51%) diff --git a/sway-core/src/asm_generation/evm/evm_asm_builder.rs b/sway-core/src/asm_generation/evm/evm_asm_builder.rs index 6a94bbe7556..5a9cd73e870 100644 --- a/sway-core/src/asm_generation/evm/evm_asm_builder.rs +++ b/sway-core/src/asm_generation/evm/evm_asm_builder.rs @@ -342,6 +342,7 @@ impl<'ir, 'eng> EvmAsmBuilder<'ir, 'eng> { indices, } => self.compile_get_elem_ptr(instr_val, base, elem_ptr_ty, indices), InstOp::GetLocal(local_var) => self.compile_get_local(instr_val, local_var), + InstOp::GetGlobal(global_var) => self.compile_get_global(instr_val, global_var), InstOp::GetConfig(_, name) => self.compile_get_config(instr_val, name), InstOp::IntToPtr(val, _) => self.compile_int_to_ptr(instr_val, val), InstOp::Load(src_val) => self.compile_load(handler, instr_val, src_val)?, @@ -471,6 +472,10 @@ impl<'ir, 'eng> EvmAsmBuilder<'ir, 'eng> { todo!(); } + fn compile_get_global(&mut self, instr_val: &Value, global_var: &GlobalVar) { + todo!(); + } + fn compile_get_local(&mut self, instr_val: &Value, local_var: &LocalVar) { todo!(); } diff --git a/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs b/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs index 7e34d1ceb10..d21462b3e9a 100644 --- a/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs +++ b/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs @@ -484,6 +484,7 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> { indices, } => self.compile_get_elem_ptr(instr_val, base, elem_ptr_ty, indices), InstOp::GetLocal(local_var) => self.compile_get_local(instr_val, local_var), + InstOp::GetGlobal(global_var) => self.compile_get_global(instr_val, global_var), InstOp::GetConfig(_, name) => self.compile_get_config(instr_val, name), InstOp::IntToPtr(val, _) => self.compile_no_op_move(instr_val, val), InstOp::Load(src_val) => self.compile_load(instr_val, src_val), @@ -1249,6 +1250,40 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> { Ok(()) } + fn compile_get_global( + &mut self, + instr_val: &Value, + local_var: &GlobalVar, + ) -> Result<(), CompileError> { + let span = self + .md_mgr + .val_to_span(self.context, *instr_val) + .unwrap_or_else(Span::dummy); + let Some(constant) = local_var.get_initializer(self.context) else { + return Err(CompileError::Internal( + "Global variable must have an initializer.", + span, + )); + }; + let entry = Entry::from_constant( + self.context, + constant.get_content(self.context), + EntryName::NonConfigurable, + None, + ); + let data_id = self.data_section.insert_data_value(entry); + + // Allocate a register for it, and a load instruction. + let reg = self.reg_seqr.next(); + self.cur_bytecode.push(Op { + opcode: either::Either::Left(VirtualOp::LoadDataId(reg.clone(), data_id.clone())), + comment: "load constant from data section".into(), + owning_span: Some(span), + }); + self.reg_map.insert(*instr_val, reg); + Ok(()) + } + fn compile_get_local( &mut self, instr_val: &Value, diff --git a/sway-core/src/ir_generation/const_eval.rs b/sway-core/src/ir_generation/const_eval.rs index 067bb74319b..2ed408d3c74 100644 --- a/sway-core/src/ir_generation/const_eval.rs +++ b/sway-core/src/ir_generation/const_eval.rs @@ -27,7 +27,7 @@ use sway_ir::{ context::Context, module::Module, value::Value, - Constant, InstOp, Instruction, Type, TypeContent, + Constant, GlobalVar, InstOp, Instruction, Type, TypeContent, }; use sway_types::{ident::Ident, integer_bits::IntegerBits, span::Spanned, Named, Span}; use sway_utils::mapped_stack::MappedStack; @@ -118,10 +118,15 @@ pub(crate) fn compile_const_decl( // Check if it's a processed global constant. match ( env.module - .get_global_constant(env.context, &call_path.as_vec_string()), + .get_global_variable(env.context, &call_path.as_vec_string()), env.module_ns, ) { - (Some(const_val), _) => Ok(Some(const_val)), + (Some(global_var), _) => { + if let Some(constant) = global_var.get_initializer(env.context) { + return Ok(Some(Value::new_constant(env.context, *constant))); + } + Ok(None) + } (None, Some(module_ns)) => { // See if we it's a global const and whether we can compile it *now*. let decl = module_ns.root_items().check_symbol(&call_path.suffix); @@ -161,10 +166,19 @@ pub(crate) fn compile_const_decl( &value, )?; - env.module.add_global_constant( + let const_val_c = *const_val + .get_constant(env.context) + .expect("Must have been compiled to a constant"); + + let c_ty = const_val_c.get_content(env.context).ty; + let c_ty_ptr = Type::new_ptr(env.context, c_ty); + let const_global = + GlobalVar::new(env.context, c_ty_ptr, Some(const_val_c), false); + + env.module.add_global_variable( env.context, call_path.as_vec_string().to_vec(), - const_val, + const_global, ); Ok(Some(const_val)) diff --git a/sway-core/src/ir_generation/function.rs b/sway-core/src/ir_generation/function.rs index f2e7fa42986..3f7cf48ae83 100644 --- a/sway-core/src/ir_generation/function.rs +++ b/sway-core/src/ir_generation/function.rs @@ -3308,11 +3308,16 @@ impl<'eng> FnCompiler<'eng> { Ok(TerminatorValue::new(val, context)) } else if let Some(val) = self.function.get_arg(context, name.as_str()) { Ok(TerminatorValue::new(val, context)) - } else if let Some(const_val) = self + } else if let Some(global_val) = self .module - .get_global_constant(context, &call_path.as_vec_string()) + .get_global_variable(context, &call_path.as_vec_string()) { - Ok(TerminatorValue::new(const_val, context)) + let val = self + .current_block + .append(context) + .get_global(global_val) + .add_metadatum(context, span_md_idx); + Ok(TerminatorValue::new(val, context)) } else if self .module .get_config(context, &call_path.suffix.to_string()) diff --git a/sway-ir/src/analysis/memory_utils.rs b/sway-ir/src/analysis/memory_utils.rs index 1678eb3f101..391c4093a87 100644 --- a/sway-ir/src/analysis/memory_utils.rs +++ b/sway-ir/src/analysis/memory_utils.rs @@ -421,6 +421,7 @@ pub fn compute_escaped_symbols(context: &Context, function: &Function) -> Escape } InstOp::FuelVm(_) => (), InstOp::GetLocal(_) => (), + InstOp::GetGlobal(_) => (), InstOp::GetConfig(_, _) => (), InstOp::GetElemPtr { .. } => (), InstOp::IntToPtr(_, _) => (), @@ -453,6 +454,7 @@ pub fn get_loaded_ptr_values(context: &Context, inst: Value) -> Vec { | InstOp::Nop | InstOp::CastPtr(_, _) | InstOp::GetLocal(_) + | InstOp::GetGlobal(_) | InstOp::GetConfig(_, _) | InstOp::GetElemPtr { .. } | InstOp::IntToPtr(_, _) => vec![], @@ -540,6 +542,7 @@ pub fn get_stored_ptr_values(context: &Context, inst: Value) -> Vec { | InstOp::Ret(_, _) | InstOp::CastPtr(_, _) | InstOp::GetLocal(_) + | InstOp::GetGlobal(_) | InstOp::GetConfig(_, _) | InstOp::GetElemPtr { .. } | InstOp::IntToPtr(_, _) => vec![], diff --git a/sway-ir/src/context.rs b/sway-ir/src/context.rs index b63a0c18b80..8616e5a0e16 100644 --- a/sway-ir/src/context.rs +++ b/sway-ir/src/context.rs @@ -14,11 +14,11 @@ use sway_types::SourceEngine; use crate::{ block::BlockContent, function::FunctionContent, - local_var::LocalVarContent, metadata::Metadatum, module::{Kind, ModuleContent, ModuleIterator}, value::ValueContent, - Constant, ConstantContent, Type, TypeContent, + variable::LocalVarContent, + Constant, ConstantContent, GlobalVarContent, Type, TypeContent, }; /// The main IR context handle. @@ -33,6 +33,7 @@ pub struct Context<'eng> { pub(crate) blocks: SlotMap, pub(crate) values: SlotMap, pub(crate) local_vars: SlotMap, + pub(crate) global_vars: SlotMap, pub(crate) types: SlotMap, pub(crate) type_map: FxHashMap, pub(crate) constants: SlotMap, @@ -57,6 +58,7 @@ impl<'eng> Context<'eng> { blocks: Default::default(), values: Default::default(), local_vars: Default::default(), + global_vars: Default::default(), types: Default::default(), type_map: Default::default(), constants: Default::default(), diff --git a/sway-ir/src/function.rs b/sway-ir/src/function.rs index 4c19ee24a3d..b6d9dca05dc 100644 --- a/sway-ir/src/function.rs +++ b/sway-ir/src/function.rs @@ -16,10 +16,10 @@ use crate::{ context::Context, error::IrError, irtype::Type, - local_var::{LocalVar, LocalVarContent}, metadata::MetadataIndex, module::Module, value::{Value, ValueDatum}, + variable::{LocalVar, LocalVarContent}, BlockArgument, BranchToWithArgs, }; use crate::{Constant, InstOp}; diff --git a/sway-ir/src/instruction.rs b/sway-ir/src/instruction.rs index e62d9238d49..7283e6372c9 100644 --- a/sway-ir/src/instruction.rs +++ b/sway-ir/src/instruction.rs @@ -16,10 +16,10 @@ use crate::{ context::Context, function::Function, irtype::Type, - local_var::LocalVar, pretty::DebugWithContext, value::{Value, ValueDatum}, - AsmInstruction, ConstantContent, Module, + variable::LocalVar, + AsmInstruction, ConstantContent, GlobalVar, Module, }; #[derive(Debug, Clone, DebugWithContext)] @@ -85,6 +85,8 @@ pub enum InstOp { FuelVm(FuelVmInstruction), /// Return a local variable. GetLocal(LocalVar), + /// Return a global variable. + GetGlobal(GlobalVar), /// Return a ptr to a config GetConfig(Module, String), /// Translate a pointer from a base to a nested element in an aggregate type. @@ -299,6 +301,7 @@ impl InstOp { // These return pointer types. InstOp::GetElemPtr { elem_ptr_ty, .. } => Some(*elem_ptr_ty), InstOp::GetLocal(local_var) => Some(local_var.get_type(context)), + InstOp::GetGlobal(global_var) => Some(global_var.get_type(context)), InstOp::GetConfig(module, name) => Some(match module.get_config(context, name)? { crate::ConfigContent::V0 { ptr_ty, .. } => *ptr_ty, crate::ConfigContent::V1 { ptr_ty, .. } => *ptr_ty, @@ -390,6 +393,10 @@ impl InstOp { // `GetLocal` returns an SSA `Value` but does not take any as an operand. vec![] } + InstOp::GetGlobal(_global_var) => { + // `GetGlobal` returns an SSA `Value` but does not take any as an operand. + vec![] + } InstOp::GetConfig(_, _) => { // `GetConfig` returns an SSA `Value` but does not take any as an operand. vec![] @@ -523,6 +530,7 @@ impl InstOp { replace(gas); } InstOp::GetLocal(_) => (), + InstOp::GetGlobal(_) => (), InstOp::GetConfig(_, _) => (), InstOp::GetElemPtr { base, @@ -684,6 +692,7 @@ impl InstOp { | InstOp::FuelVm(FuelVmInstruction::StateLoadWord(_)) | InstOp::GetElemPtr { .. } | InstOp::GetLocal(_) + | InstOp::GetGlobal(_) | InstOp::GetConfig(_, _) | InstOp::IntToPtr(..) | InstOp::Load(_) @@ -1042,6 +1051,10 @@ impl<'a, 'eng> InstructionInserter<'a, 'eng> { insert_instruction!(self, InstOp::GetLocal(local_var)) } + pub fn get_global(self, global_var: GlobalVar) -> Value { + insert_instruction!(self, InstOp::GetGlobal(global_var)) + } + pub fn get_config(self, module: Module, name: String) -> Value { insert_instruction!(self, InstOp::GetConfig(module, name)) } diff --git a/sway-ir/src/lib.rs b/sway-ir/src/lib.rs index cf83260c29e..5d9ad4e391b 100644 --- a/sway-ir/src/lib.rs +++ b/sway-ir/src/lib.rs @@ -63,8 +63,8 @@ pub mod optimize; pub use optimize::*; pub mod parser; pub use parser::*; -pub mod local_var; -pub use local_var::*; +pub mod variable; +pub use variable::*; pub mod pass_manager; pub use pass_manager::*; pub mod pretty; diff --git a/sway-ir/src/module.rs b/sway-ir/src/module.rs index 34175c13a2b..eefad156501 100644 --- a/sway-ir/src/module.rs +++ b/sway-ir/src/module.rs @@ -10,8 +10,7 @@ use std::{ use crate::{ context::Context, function::{Function, FunctionIterator}, - value::Value, - Constant, MetadataIndex, Type, + Constant, GlobalVar, MetadataIndex, Type, }; /// A wrapper around an [ECS](https://github.com/orlp/slotmap) handle into the @@ -23,7 +22,7 @@ pub struct Module(pub slotmap::DefaultKey); pub struct ModuleContent { pub kind: Kind, pub functions: Vec, - pub global_constants: HashMap, Value>, + pub global_variables: HashMap, GlobalVar>, pub configs: BTreeMap, } @@ -61,7 +60,7 @@ impl Module { let content = ModuleContent { kind, functions: Vec::new(), - global_constants: HashMap::new(), + global_variables: HashMap::new(), configs: BTreeMap::new(), }; Module(context.modules.insert(content)) @@ -77,26 +76,43 @@ impl Module { FunctionIterator::new(context, self) } - /// Add a global constant value to this module. - pub fn add_global_constant( + /// Add a global variable value to this module. + pub fn add_global_variable( &self, context: &mut Context, call_path: Vec, - const_val: Value, + const_val: GlobalVar, ) { context.modules[self.0] - .global_constants + .global_variables .insert(call_path, const_val); } - /// Get a named global constant value from this module, if found. - pub fn get_global_constant(&self, context: &Context, call_path: &Vec) -> Option { + /// Get a named global variable from this module, if found. + pub fn get_global_variable( + &self, + context: &Context, + call_path: &Vec, + ) -> Option { context.modules[self.0] - .global_constants + .global_variables .get(call_path) .copied() } + /// Lookup global variable name + pub fn lookup_global_variable_name( + &self, + context: &Context, + global: &GlobalVar, + ) -> Option { + context.modules[self.0] + .global_variables + .iter() + .find(|(_key, val)| *val == global) + .map(|(key, _)| key.join("::")) + } + /// Add a config value to this module. pub fn add_config(&self, context: &mut Context, name: String, content: ConfigContent) { context.modules[self.0].configs.insert(name, content); diff --git a/sway-ir/src/optimize/cse.rs b/sway-ir/src/optimize/cse.rs index cb5321aeeb1..836b6848b12 100644 --- a/sway-ir/src/optimize/cse.rs +++ b/sway-ir/src/optimize/cse.rs @@ -102,6 +102,7 @@ fn instr_to_expr(context: &Context, vntable: &VNTable, instr: Value) -> Option None, InstOp::FuelVm(_) => None, InstOp::GetLocal(_) => None, + InstOp::GetGlobal(_) => None, InstOp::GetConfig(_, _) => None, InstOp::GetElemPtr { base, diff --git a/sway-ir/src/optimize/fn_dedup.rs b/sway-ir/src/optimize/fn_dedup.rs index c53aef4cb43..361e9418a0f 100644 --- a/sway-ir/src/optimize/fn_dedup.rs +++ b/sway-ir/src/optimize/fn_dedup.rs @@ -259,6 +259,11 @@ fn hash_fn( .lookup_local_name(context, local) .unwrap() .hash(state), + crate::InstOp::GetGlobal(global) => function + .get_module(context) + .lookup_global_variable_name(context, global) + .unwrap() + .hash(state), crate::InstOp::GetConfig(_, name) => name.hash(state), crate::InstOp::GetElemPtr { elem_ptr_ty, .. } => elem_ptr_ty.hash(state), crate::InstOp::IntToPtr(_, ty) => ty.hash(state), diff --git a/sway-ir/src/optimize/inline.rs b/sway-ir/src/optimize/inline.rs index 8c68b1f6167..7819f9993e3 100644 --- a/sway-ir/src/optimize/inline.rs +++ b/sway-ir/src/optimize/inline.rs @@ -15,9 +15,9 @@ use crate::{ function::Function, instruction::{FuelVmInstruction, InstOp}, irtype::Type, - local_var::LocalVar, metadata::{combine, MetadataIndex}, value::{Value, ValueContent, ValueDatum}, + variable::LocalVar, AnalysisResults, BlockArgument, Instruction, Module, Pass, PassMutability, ScopedPass, }; @@ -602,6 +602,7 @@ fn inline_instruction( InstOp::GetLocal(local_var) => { new_block.append(context).get_local(map_local(local_var)) } + InstOp::GetGlobal(global_var) => new_block.append(context).get_global(global_var), InstOp::GetConfig(module, name) => new_block.append(context).get_config(module, name), InstOp::IntToPtr(value, ty) => { new_block.append(context).int_to_ptr(map_value(value), ty) diff --git a/sway-ir/src/parser.rs b/sway-ir/src/parser.rs index 93065c5c265..fa064bfaad4 100644 --- a/sway-ir/src/parser.rs +++ b/sway-ir/src/parser.rs @@ -684,10 +684,10 @@ mod ir_builder { function::Function, instruction::{InstOp, Predicate, Register}, irtype::Type, - local_var::LocalVar, metadata::{MetadataIndex, Metadatum}, module::{Kind, Module}, value::Value, + variable::LocalVar, BinaryOpKind, BlockArgument, ConfigContent, Constant, Instruction, UnaryOpKind, B256, }; diff --git a/sway-ir/src/printer.rs b/sway-ir/src/printer.rs index 63461356c2d..05afaed992c 100644 --- a/sway-ir/src/printer.rs +++ b/sway-ir/src/printer.rs @@ -959,6 +959,21 @@ fn instruction_to_doc<'a>( .append(md_namer.md_idx_to_doc(context, metadata)), ) } + InstOp::GetGlobal(global_var) => { + let name = block + .get_function(context) + .get_module(context) + .lookup_global_variable_name(context, global_var) + .unwrap(); + Doc::line( + Doc::text(format!( + "{} = get_global {}, {name}", + namer.name(context, ins_value), + global_var.get_type(context).as_string(context), + )) + .append(md_namer.md_idx_to_doc(context, metadata)), + ) + } InstOp::GetConfig(_, name) => Doc::line( match block.get_module(context).get_config(context, name).unwrap() { ConfigContent::V0 { name, ptr_ty, .. } diff --git a/sway-ir/src/local_var.rs b/sway-ir/src/variable.rs similarity index 51% rename from sway-ir/src/local_var.rs rename to sway-ir/src/variable.rs index 56d91fdf6c8..15532b659d2 100644 --- a/sway-ir/src/local_var.rs +++ b/sway-ir/src/variable.rs @@ -67,3 +67,64 @@ impl LocalVar { context.local_vars[self.0].mutable = mutable; } } + +/// A wrapper around an [ECS](https://github.com/orlp/slotmap) handle into the +/// [`Context`]. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, DebugWithContext)] +pub struct GlobalVar(#[in_context(global_vars)] pub slotmap::DefaultKey); + +#[doc(hidden)] +#[derive(Clone, DebugWithContext)] +pub struct GlobalVarContent { + pub ptr_ty: Type, + pub initializer: Option, + pub mutable: bool, +} + +impl GlobalVar { + /// Return a new Global of a specific type with an optional [`Constant`] initializer. If a + /// Global is marked as mutable then it is guaranteed to be on the stack rather than in + /// read-only memory. + pub fn new( + context: &mut Context, + ty: Type, + initializer: Option, + mutable: bool, + ) -> Self { + let ptr_ty = Type::new_ptr(context, ty); + let content = GlobalVarContent { + ptr_ty, + initializer, + mutable, + }; + GlobalVar(context.global_vars.insert(content)) + } + + /// Return the type of this Global variable, which is always a pointer. + pub fn get_type(&self, context: &Context) -> Type { + context.global_vars[self.0].ptr_ty + } + + /// Return the inner (pointed to) type. + pub fn get_inner_type(&self, context: &Context) -> Type { + let TypeContent::Pointer(inner_ty) = self.get_type(context).get_content(context) else { + unreachable!("Global var type is always a pointer.") + }; + *inner_ty + } + + /// Return the initializer for this Global variable. + pub fn get_initializer<'a>(&self, context: &'a Context) -> Option<&'a Constant> { + context.global_vars[self.0].initializer.as_ref() + } + + /// Return whether this Global variable is mutable. + pub fn is_mutable(&self, context: &Context) -> bool { + context.global_vars[self.0].mutable + } + + /// Change this Global variable's mutability. + pub fn set_mutable(&self, context: &mut Context, mutable: bool) { + context.global_vars[self.0].mutable = mutable; + } +} diff --git a/sway-ir/src/verify.rs b/sway-ir/src/verify.rs index 29e00eb7c73..88a506f0812 100644 --- a/sway-ir/src/verify.rs +++ b/sway-ir/src/verify.rs @@ -11,12 +11,13 @@ use crate::{ function::Function, instruction::{FuelVmInstruction, InstOp, Predicate}, irtype::Type, - local_var::LocalVar, metadata::{MetadataIndex, Metadatum}, printer, value::{Value, ValueDatum}, + variable::LocalVar, AnalysisResult, AnalysisResultT, AnalysisResults, BinaryOpKind, Block, BlockArgument, - BranchToWithArgs, Doc, Module, Pass, PassMutability, ScopedPass, TypeOption, UnaryOpKind, + BranchToWithArgs, Doc, GlobalVar, Module, Pass, PassMutability, ScopedPass, TypeOption, + UnaryOpKind, }; pub struct ModuleVerifierResult; @@ -327,6 +328,7 @@ impl InstructionVerifier<'_, '_> { indices, } => self.verify_get_elem_ptr(&ins, base, elem_ptr_ty, indices)?, InstOp::GetLocal(local_var) => self.verify_get_local(local_var)?, + InstOp::GetGlobal(global_var) => self.verify_get_global(global_var)?, InstOp::GetConfig(_, name) => self.verify_get_config(self.cur_module, name)?, InstOp::IntToPtr(value, ty) => self.verify_int_to_ptr(value, ty)?, InstOp::Load(ptr) => self.verify_load(ptr)?, @@ -831,6 +833,18 @@ impl InstructionVerifier<'_, '_> { } } + fn verify_get_global(&self, global_var: &GlobalVar) -> Result<(), IrError> { + if !self.context.modules[self.cur_module.0] + .global_variables + .values() + .any(|var| var == global_var) + { + Err(IrError::VerifyGetNonExistentPointer) + } else { + Ok(()) + } + } + fn verify_get_config(&self, module: Module, name: &str) -> Result<(), IrError> { if !self.context.modules[module.0].configs.contains_key(name) { Err(IrError::VerifyGetNonExistentPointer) From 4129d3d2bde00286c22ce46e3dfd73f92c4c2fda Mon Sep 17 00:00:00 2001 From: Vaivaswatha Nagaraj Date: Fri, 14 Feb 2025 16:28:40 +0530 Subject: [PATCH 04/21] fix bugs --- sway-core/src/asm_generation/fuel/fuel_asm_builder.rs | 6 +++--- sway-core/src/ir_generation/const_eval.rs | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs b/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs index d21462b3e9a..c92fa12a841 100644 --- a/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs +++ b/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs @@ -1273,11 +1273,11 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> { ); let data_id = self.data_section.insert_data_value(entry); - // Allocate a register for it, and a load instruction. + // Allocate a register for it, and an address_of instruction. let reg = self.reg_seqr.next(); self.cur_bytecode.push(Op { - opcode: either::Either::Left(VirtualOp::LoadDataId(reg.clone(), data_id.clone())), - comment: "load constant from data section".into(), + opcode: either::Either::Left(VirtualOp::AddrDataId(reg.clone(), data_id.clone())), + comment: "constant address in data section".into(), owning_span: Some(span), }); self.reg_map.insert(*instr_val, reg); diff --git a/sway-core/src/ir_generation/const_eval.rs b/sway-core/src/ir_generation/const_eval.rs index 52775a4f923..0a9478768c3 100644 --- a/sway-core/src/ir_generation/const_eval.rs +++ b/sway-core/src/ir_generation/const_eval.rs @@ -171,9 +171,7 @@ pub(crate) fn compile_const_decl( .expect("Must have been compiled to a constant"); let c_ty = const_val_c.get_content(env.context).ty; - let c_ty_ptr = Type::new_ptr(env.context, c_ty); - let const_global = - GlobalVar::new(env.context, c_ty_ptr, Some(const_val_c), false); + let const_global = GlobalVar::new(env.context, c_ty, Some(const_val_c), false); env.module.add_global_variable( env.context, From 503cecf0e2afdee1d6205bab95b17977a92bf889 Mon Sep 17 00:00:00 2001 From: Vaivaswatha Nagaraj Date: Tue, 18 Feb 2025 14:28:32 +0530 Subject: [PATCH 05/21] IR printer and parser for the new IR insruction --- sway-ir/src/parser.rs | 62 +++++++++++++++++++++++++++++++++++++++--- sway-ir/src/printer.rs | 28 +++++++++++++++++++ 2 files changed, 86 insertions(+), 4 deletions(-) diff --git a/sway-ir/src/parser.rs b/sway-ir/src/parser.rs index fa064bfaad4..c0c7b7b93b6 100644 --- a/sway-ir/src/parser.rs +++ b/sway-ir/src/parser.rs @@ -43,11 +43,12 @@ mod ir_builder { } rule script_or_predicate() -> IrAstModule - = kind:module_kind() "{" _ configs:init_config()* _ fn_decls:fn_decl()* "}" _ + = kind:module_kind() "{" _ configs:init_config()* _ global_consts:global_const()* _ fn_decls:fn_decl()* "}" _ metadata:metadata_decls() { IrAstModule { kind, configs, + global_consts, fn_decls, metadata } @@ -59,16 +60,26 @@ mod ir_builder { rule contract() -> IrAstModule = "contract" _ "{" _ - configs:init_config()* fn_decls:fn_decl()* "}" _ + configs:init_config()* _ global_consts:global_const()* _ fn_decls:fn_decl()* "}" _ metadata:metadata_decls() { IrAstModule { kind: crate::module::Kind::Contract, configs, + global_consts, fn_decls, metadata } } + rule global_const() -> IrAstGlobalConst + = "global" _ name:path() _ ":" _ ty:ast_ty() _ "=" _ init:op_const() { + IrAstGlobalConst { + name, + ty, + init + } + } + rule config_encoded_bytes() -> Vec = "0x" s:$(hex_digit()*) _ { hex_string_to_vec(s) @@ -216,6 +227,7 @@ mod ir_builder { / op_contract_call() / op_get_elem_ptr() / op_get_local() + / op_get_global() / op_get_config() / op_gtf() / op_int_to_ptr() @@ -332,7 +344,10 @@ mod ir_builder { = "get_local" _ ast_ty() comma() name:id() { IrAstOperation::GetLocal(name) } - + rule op_get_global() -> IrAstOperation + = "get_global" _ ast_ty() comma() name:path() { + IrAstOperation::GetGlobal(name) + } rule op_get_config() -> IrAstOperation = "get_config" _ ast_ty() comma() name:id() { IrAstOperation::GetConfig(name) @@ -589,6 +604,9 @@ mod ir_builder { Ident::new(Span::new(id.into(), 0, id.len(), None).unwrap()) } + rule path() -> Vec + = (id() ** "::") + // Metadata decls are sensitive to the newlines since the assignee idx could belong to // the previous decl otherwise. e.g., // @@ -688,17 +706,26 @@ mod ir_builder { module::{Kind, Module}, value::Value, variable::LocalVar, - BinaryOpKind, BlockArgument, ConfigContent, Constant, Instruction, UnaryOpKind, B256, + BinaryOpKind, BlockArgument, ConfigContent, Constant, GlobalVar, Instruction, UnaryOpKind, + B256, }; #[derive(Debug)] pub(super) struct IrAstModule { kind: Kind, configs: Vec, + global_consts: Vec, fn_decls: Vec, metadata: Vec<(MdIdxRef, IrMetadatum)>, } + #[derive(Debug)] + pub(super) struct IrAstGlobalConst { + name: Vec, + ty: IrAstTy, + init: IrAstOperation, + } + #[derive(Debug)] struct IrAstFnDecl { name: String, @@ -749,6 +776,7 @@ mod ir_builder { ContractCall(IrAstTy, String, String, String, String, String), GetElemPtr(String, IrAstTy, Vec), GetLocal(String), + GetGlobal(Vec), GetConfig(String), Gtf(String, u64), IntToPtr(String, IrAstTy), @@ -982,6 +1010,7 @@ mod ir_builder { let mut builder = IrBuilder { module, configs_map: build_configs_map(&mut ctx, &module, ir_ast_mod.configs, &md_map), + global_map: build_global_consts_map(&mut ctx, &module, ir_ast_mod.global_consts), md_map, unresolved_calls: Vec::new(), }; @@ -998,6 +1027,7 @@ mod ir_builder { struct IrBuilder { module: Module, configs_map: BTreeMap, + global_map: BTreeMap, GlobalVar>, md_map: HashMap, unresolved_calls: Vec, } @@ -1322,6 +1352,10 @@ mod ir_builder { .append(context) .get_local(*local_map.get(&local_name).unwrap()) .add_metadatum(context, opt_metadata), + IrAstOperation::GetGlobal(global_name) => block + .append(context) + .get_global(*self.global_map.get(&global_name).unwrap()) + .add_metadatum(context, opt_metadata), IrAstOperation::GetConfig(name) => block .append(context) .get_config(self.module, name) @@ -1513,6 +1547,26 @@ mod ir_builder { } } + fn build_global_consts_map( + context: &mut Context, + module: &Module, + global_consts: Vec, + ) -> BTreeMap, GlobalVar> { + global_consts + .into_iter() + .map(|global_const| { + let ty = global_const.ty.to_ir_type(context); + let init = match global_const.init { + IrAstOperation::Const(ty, val) => val.value.as_constant(context, ty), + _ => unreachable!("Global const initializer must be a const value."), + }; + let global_var = GlobalVar::new(context, ty, Some(init), false); + module.add_global_variable(context, global_const.name.clone(), global_var); + (global_const.name, global_var) + }) + .collect() + } + fn build_configs_map( context: &mut Context, module: &Module, diff --git a/sway-ir/src/printer.rs b/sway-ir/src/printer.rs index 05afaed992c..f615114a885 100644 --- a/sway-ir/src/printer.rs +++ b/sway-ir/src/printer.rs @@ -214,6 +214,34 @@ fn module_to_doc<'a>( } else { Doc::Empty }) + .append(Doc::indent( + 4, + Doc::list_sep( + module + .global_variables + .iter() + .map(|(name, var)| { + let var_content = &context.global_vars[var.0]; + let init_doc = match &var_content.initializer { + Some(const_val) => Doc::text(format!( + " = const {}", + const_val.get_content(context).as_lit_string(context) + )), + None => Doc::Empty, + }; + Doc::line( + Doc::text(format!( + "global {} : {}", + name.join("::"), + var.get_inner_type(context).as_string(context), + )) + .append(init_doc), + ) + }) + .collect(), + Doc::line(Doc::Empty), + ), + )) .append(Doc::indent( 4, Doc::list_sep( From 8227ff6df52b18e86eb62d6e5cd0d21810029881 Mon Sep 17 00:00:00 2001 From: Vaivaswatha Nagaraj Date: Wed, 19 Feb 2025 09:21:54 +0530 Subject: [PATCH 06/21] use BTreeMap since we iterate over the entries --- sway-ir/src/module.rs | 9 +- .../configurable_dedup_decode/stdout.snap | 295 ++++++++++++++++++ 2 files changed, 298 insertions(+), 6 deletions(-) diff --git a/sway-ir/src/module.rs b/sway-ir/src/module.rs index eefad156501..0daaac84925 100644 --- a/sway-ir/src/module.rs +++ b/sway-ir/src/module.rs @@ -2,10 +2,7 @@ //! //! A module also has a 'kind' corresponding to the different Sway module types. -use std::{ - cell::Cell, - collections::{BTreeMap, HashMap}, -}; +use std::{cell::Cell, collections::BTreeMap}; use crate::{ context::Context, @@ -22,7 +19,7 @@ pub struct Module(pub slotmap::DefaultKey); pub struct ModuleContent { pub kind: Kind, pub functions: Vec, - pub global_variables: HashMap, GlobalVar>, + pub global_variables: BTreeMap, GlobalVar>, pub configs: BTreeMap, } @@ -60,7 +57,7 @@ impl Module { let content = ModuleContent { kind, functions: Vec::new(), - global_variables: HashMap::new(), + global_variables: BTreeMap::new(), configs: BTreeMap::new(), }; Module(context.modules.insert(content)) diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_dedup_decode/stdout.snap b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_dedup_decode/stdout.snap index eff418fc29d..caf219fab97 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_dedup_decode/stdout.snap +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_dedup_decode/stdout.snap @@ -1,5 +1,6 @@ --- source: test/tests/tests.rs +assertion_line: 115 --- > forc build --path test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_dedup_decode --release --ir final --asm final exit status: 0 @@ -9,6 +10,153 @@ output: Compiling library std (sway-lib-std) // IR: Final library { + global std::call_frames::FIRST_PARAMETER_OFFSET : u64 = const u64 73 + + global std::call_frames::PREV_FRAME_POINTER_OFFSET : u64 = const u64 6 + + global std::call_frames::SAVED_REGISTERS_OFFSET : u64 = const u64 8 + + global std::call_frames::SECOND_PARAMETER_OFFSET : u64 = const u64 74 + + global std::constants::DEFAULT_SUB_ID : b256 = const b256 0x0000000000000000000000000000000000000000000000000000000000000000 + + global std::constants::ZERO_B256 : b256 = const b256 0x0000000000000000000000000000000000000000000000000000000000000000 + + global std::constants::ZERO_U256 : u256 = const u256 0x0000000000000000000000000000000000000000000000000000000000000000 + + global std::error_signals::FAILED_ASSERT_EQ_SIGNAL : u64 = const u64 18446744073709486083 + + global std::error_signals::FAILED_ASSERT_NE_SIGNAL : u64 = const u64 18446744073709486085 + + global std::error_signals::FAILED_ASSERT_SIGNAL : u64 = const u64 18446744073709486084 + + global std::error_signals::FAILED_REQUIRE_SIGNAL : u64 = const u64 18446744073709486080 + + global std::error_signals::FAILED_TRANSFER_TO_ADDRESS_SIGNAL : u64 = const u64 18446744073709486081 + + global std::error_signals::REVERT_WITH_LOG_SIGNAL : u64 = const u64 18446744073709486086 + + global std::flags::F_UNSAFEMATH_DISABLE_MASK : u64 = const u64 1 + + global std::flags::F_UNSAFEMATH_ENABLE_MASK : u64 = const u64 18446744073709551614 + + global std::flags::F_WRAPPING_DISABLE_MASK : u64 = const u64 2 + + global std::flags::F_WRAPPING_ENABLE_MASK : u64 = const u64 18446744073709551613 + + global std::inputs::GTF_INPUT_COIN_AMOUNT : u64 = const u64 516 + + global std::inputs::GTF_INPUT_COIN_ASSET_ID : u64 = const u64 517 + + global std::inputs::GTF_INPUT_COIN_OWNER : u64 = const u64 515 + + global std::inputs::GTF_INPUT_COIN_PREDICATE : u64 = const u64 523 + + global std::inputs::GTF_INPUT_COIN_PREDICATE_DATA : u64 = const u64 524 + + global std::inputs::GTF_INPUT_COIN_PREDICATE_DATA_LENGTH : u64 = const u64 522 + + global std::inputs::GTF_INPUT_COIN_PREDICATE_LENGTH : u64 = const u64 521 + + global std::inputs::GTF_INPUT_COIN_WITNESS_INDEX : u64 = const u64 519 + + global std::inputs::GTF_INPUT_MESSAGE_AMOUNT : u64 = const u64 578 + + global std::inputs::GTF_INPUT_MESSAGE_DATA : u64 = const u64 584 + + global std::inputs::GTF_INPUT_MESSAGE_DATA_LENGTH : u64 = const u64 581 + + global std::inputs::GTF_INPUT_MESSAGE_NONCE : u64 = const u64 579 + + global std::inputs::GTF_INPUT_MESSAGE_PREDICATE : u64 = const u64 585 + + global std::inputs::GTF_INPUT_MESSAGE_PREDICATE_DATA : u64 = const u64 586 + + global std::inputs::GTF_INPUT_MESSAGE_PREDICATE_DATA_LENGTH : u64 = const u64 583 + + global std::inputs::GTF_INPUT_MESSAGE_PREDICATE_LENGTH : u64 = const u64 582 + + global std::inputs::GTF_INPUT_MESSAGE_RECIPIENT : u64 = const u64 577 + + global std::inputs::GTF_INPUT_MESSAGE_SENDER : u64 = const u64 576 + + global std::inputs::GTF_INPUT_MESSAGE_WITNESS_INDEX : u64 = const u64 580 + + global std::inputs::GTF_INPUT_TYPE : u64 = const u64 512 + + global std::outputs::GTF_OUTPUT_COIN_AMOUNT : u64 = const u64 770 + + global std::outputs::GTF_OUTPUT_COIN_ASSET_ID : u64 = const u64 771 + + global std::outputs::GTF_OUTPUT_COIN_TO : u64 = const u64 769 + + global std::outputs::GTF_OUTPUT_TYPE : u64 = const u64 768 + + global std::outputs::OUTPUT_VARIABLE_ASSET_ID_OFFSET : u64 = const u64 48 + + global std::outputs::OUTPUT_VARIABLE_TO_OFFSET : u64 = const u64 8 + + global std::tx::GTF_CREATE_INPUTS_COUNT : u64 = const u64 259 + + global std::tx::GTF_CREATE_INPUT_AT_INDEX : u64 = const u64 264 + + global std::tx::GTF_CREATE_OUTPUTS_COUNT : u64 = const u64 260 + + global std::tx::GTF_CREATE_OUTPUT_AT_INDEX : u64 = const u64 265 + + global std::tx::GTF_CREATE_WITNESSES_COUNT : u64 = const u64 261 + + global std::tx::GTF_CREATE_WITNESS_AT_INDEX : u64 = const u64 266 + + global std::tx::GTF_POLICY_MATURITY : u64 = const u64 1283 + + global std::tx::GTF_POLICY_MAX_FEE : u64 = const u64 1284 + + global std::tx::GTF_POLICY_TIP : u64 = const u64 1281 + + global std::tx::GTF_POLICY_TYPES : u64 = const u64 1280 + + global std::tx::GTF_POLICY_WITNESS_LIMIT : u64 = const u64 1282 + + global std::tx::GTF_SCRIPT_GAS_LIMIT : u64 = const u64 2 + + global std::tx::GTF_SCRIPT_INPUTS_COUNT : u64 = const u64 5 + + global std::tx::GTF_SCRIPT_INPUT_AT_INDEX : u64 = const u64 11 + + global std::tx::GTF_SCRIPT_OUTPUTS_COUNT : u64 = const u64 6 + + global std::tx::GTF_SCRIPT_OUTPUT_AT_INDEX : u64 = const u64 12 + + global std::tx::GTF_SCRIPT_SCRIPT : u64 = const u64 9 + + global std::tx::GTF_SCRIPT_SCRIPT_DATA : u64 = const u64 10 + + global std::tx::GTF_SCRIPT_SCRIPT_DATA_LENGTH : u64 = const u64 4 + + global std::tx::GTF_SCRIPT_SCRIPT_LENGTH : u64 = const u64 3 + + global std::tx::GTF_SCRIPT_WITNESSES_COUNT : u64 = const u64 7 + + global std::tx::GTF_SCRIPT_WITNESS_AT_INDEX : u64 = const u64 13 + + global std::tx::GTF_TX_LENGTH : u64 = const u64 14 + + global std::tx::GTF_TYPE : u64 = const u64 1 + + global std::tx::GTF_WITNESS_DATA : u64 = const u64 1025 + + global std::tx::GTF_WITNESS_DATA_LENGTH : u64 = const u64 1024 + + global std::tx::MATURITY_POLICY : u64 = const u64 4 + + global std::tx::MAX_FEE_POLICY : u64 = const u64 8 + + global std::tx::TIP_POLICY : u64 = const u64 1 + + global std::tx::TX_ID_OFFSET : u64 = const u64 0 + + global std::tx::WITNESS_LIMIT_POLICY : u64 = const u64 2 } Compiling script configurable_dedup_decode (test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_dedup_decode) @@ -17,6 +165,153 @@ script { TUPLE = config { u64 }, abi_decode_in_place_0, 0x0000000000000002, !1 WRAPPED = config { u64 }, abi_decode_in_place_0, 0x0000000000000001, !2 + global std::call_frames::FIRST_PARAMETER_OFFSET : u64 = const u64 73 + + global std::call_frames::PREV_FRAME_POINTER_OFFSET : u64 = const u64 6 + + global std::call_frames::SAVED_REGISTERS_OFFSET : u64 = const u64 8 + + global std::call_frames::SECOND_PARAMETER_OFFSET : u64 = const u64 74 + + global std::constants::DEFAULT_SUB_ID : b256 = const b256 0x0000000000000000000000000000000000000000000000000000000000000000 + + global std::constants::ZERO_B256 : b256 = const b256 0x0000000000000000000000000000000000000000000000000000000000000000 + + global std::constants::ZERO_U256 : u256 = const u256 0x0000000000000000000000000000000000000000000000000000000000000000 + + global std::error_signals::FAILED_ASSERT_EQ_SIGNAL : u64 = const u64 18446744073709486083 + + global std::error_signals::FAILED_ASSERT_NE_SIGNAL : u64 = const u64 18446744073709486085 + + global std::error_signals::FAILED_ASSERT_SIGNAL : u64 = const u64 18446744073709486084 + + global std::error_signals::FAILED_REQUIRE_SIGNAL : u64 = const u64 18446744073709486080 + + global std::error_signals::FAILED_TRANSFER_TO_ADDRESS_SIGNAL : u64 = const u64 18446744073709486081 + + global std::error_signals::REVERT_WITH_LOG_SIGNAL : u64 = const u64 18446744073709486086 + + global std::flags::F_UNSAFEMATH_DISABLE_MASK : u64 = const u64 1 + + global std::flags::F_UNSAFEMATH_ENABLE_MASK : u64 = const u64 18446744073709551614 + + global std::flags::F_WRAPPING_DISABLE_MASK : u64 = const u64 2 + + global std::flags::F_WRAPPING_ENABLE_MASK : u64 = const u64 18446744073709551613 + + global std::inputs::GTF_INPUT_COIN_AMOUNT : u64 = const u64 516 + + global std::inputs::GTF_INPUT_COIN_ASSET_ID : u64 = const u64 517 + + global std::inputs::GTF_INPUT_COIN_OWNER : u64 = const u64 515 + + global std::inputs::GTF_INPUT_COIN_PREDICATE : u64 = const u64 523 + + global std::inputs::GTF_INPUT_COIN_PREDICATE_DATA : u64 = const u64 524 + + global std::inputs::GTF_INPUT_COIN_PREDICATE_DATA_LENGTH : u64 = const u64 522 + + global std::inputs::GTF_INPUT_COIN_PREDICATE_LENGTH : u64 = const u64 521 + + global std::inputs::GTF_INPUT_COIN_WITNESS_INDEX : u64 = const u64 519 + + global std::inputs::GTF_INPUT_MESSAGE_AMOUNT : u64 = const u64 578 + + global std::inputs::GTF_INPUT_MESSAGE_DATA : u64 = const u64 584 + + global std::inputs::GTF_INPUT_MESSAGE_DATA_LENGTH : u64 = const u64 581 + + global std::inputs::GTF_INPUT_MESSAGE_NONCE : u64 = const u64 579 + + global std::inputs::GTF_INPUT_MESSAGE_PREDICATE : u64 = const u64 585 + + global std::inputs::GTF_INPUT_MESSAGE_PREDICATE_DATA : u64 = const u64 586 + + global std::inputs::GTF_INPUT_MESSAGE_PREDICATE_DATA_LENGTH : u64 = const u64 583 + + global std::inputs::GTF_INPUT_MESSAGE_PREDICATE_LENGTH : u64 = const u64 582 + + global std::inputs::GTF_INPUT_MESSAGE_RECIPIENT : u64 = const u64 577 + + global std::inputs::GTF_INPUT_MESSAGE_SENDER : u64 = const u64 576 + + global std::inputs::GTF_INPUT_MESSAGE_WITNESS_INDEX : u64 = const u64 580 + + global std::inputs::GTF_INPUT_TYPE : u64 = const u64 512 + + global std::outputs::GTF_OUTPUT_COIN_AMOUNT : u64 = const u64 770 + + global std::outputs::GTF_OUTPUT_COIN_ASSET_ID : u64 = const u64 771 + + global std::outputs::GTF_OUTPUT_COIN_TO : u64 = const u64 769 + + global std::outputs::GTF_OUTPUT_TYPE : u64 = const u64 768 + + global std::outputs::OUTPUT_VARIABLE_ASSET_ID_OFFSET : u64 = const u64 48 + + global std::outputs::OUTPUT_VARIABLE_TO_OFFSET : u64 = const u64 8 + + global std::tx::GTF_CREATE_INPUTS_COUNT : u64 = const u64 259 + + global std::tx::GTF_CREATE_INPUT_AT_INDEX : u64 = const u64 264 + + global std::tx::GTF_CREATE_OUTPUTS_COUNT : u64 = const u64 260 + + global std::tx::GTF_CREATE_OUTPUT_AT_INDEX : u64 = const u64 265 + + global std::tx::GTF_CREATE_WITNESSES_COUNT : u64 = const u64 261 + + global std::tx::GTF_CREATE_WITNESS_AT_INDEX : u64 = const u64 266 + + global std::tx::GTF_POLICY_MATURITY : u64 = const u64 1283 + + global std::tx::GTF_POLICY_MAX_FEE : u64 = const u64 1284 + + global std::tx::GTF_POLICY_TIP : u64 = const u64 1281 + + global std::tx::GTF_POLICY_TYPES : u64 = const u64 1280 + + global std::tx::GTF_POLICY_WITNESS_LIMIT : u64 = const u64 1282 + + global std::tx::GTF_SCRIPT_GAS_LIMIT : u64 = const u64 2 + + global std::tx::GTF_SCRIPT_INPUTS_COUNT : u64 = const u64 5 + + global std::tx::GTF_SCRIPT_INPUT_AT_INDEX : u64 = const u64 11 + + global std::tx::GTF_SCRIPT_OUTPUTS_COUNT : u64 = const u64 6 + + global std::tx::GTF_SCRIPT_OUTPUT_AT_INDEX : u64 = const u64 12 + + global std::tx::GTF_SCRIPT_SCRIPT : u64 = const u64 9 + + global std::tx::GTF_SCRIPT_SCRIPT_DATA : u64 = const u64 10 + + global std::tx::GTF_SCRIPT_SCRIPT_DATA_LENGTH : u64 = const u64 4 + + global std::tx::GTF_SCRIPT_SCRIPT_LENGTH : u64 = const u64 3 + + global std::tx::GTF_SCRIPT_WITNESSES_COUNT : u64 = const u64 7 + + global std::tx::GTF_SCRIPT_WITNESS_AT_INDEX : u64 = const u64 13 + + global std::tx::GTF_TX_LENGTH : u64 = const u64 14 + + global std::tx::GTF_TYPE : u64 = const u64 1 + + global std::tx::GTF_WITNESS_DATA : u64 = const u64 1025 + + global std::tx::GTF_WITNESS_DATA_LENGTH : u64 = const u64 1024 + + global std::tx::MATURITY_POLICY : u64 = const u64 4 + + global std::tx::MAX_FEE_POLICY : u64 = const u64 8 + + global std::tx::TIP_POLICY : u64 = const u64 1 + + global std::tx::TX_ID_OFFSET : u64 = const u64 0 + + global std::tx::WITNESS_LIMIT_POLICY : u64 = const u64 2 pub fn abi_decode_in_place_0(ptr !4: u64, len !5: u64, target !6: u64) -> (), !10 { local { u64 } __ret_val local mut { u64 } buffer From a15fa810045f1a52ed115cbee8d78941519b02de Mon Sep 17 00:00:00 2001 From: Vaivaswatha Nagaraj Date: Wed, 19 Feb 2025 09:50:57 +0530 Subject: [PATCH 07/21] test updates --- .../configurable_consts/json_abi_oracle.json | 304 +++++++----------- .../json_abi_oracle.partial_eq.json | 32 +- .../json_abi_oracle_new_encoding.json | 32 +- .../asset_ops_test/src/main.sw | 2 +- .../call_basic_storage/src/main.sw | 2 +- .../call_storage_enum/src/main.sw | 2 +- 6 files changed, 159 insertions(+), 215 deletions(-) diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle.json index 096b1d487c9..1b9d2328683 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle.json +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle.json @@ -1,284 +1,228 @@ { + "concreteTypes": [ + { + "concreteTypeId": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d", + "type": "()" + }, + { + "concreteTypeId": "c998ca9a5f221fe7b5c66ae70c8a9562b86d964408b00d17f883c906bc1fe4be", + "metadataTypeId": 0, + "type": "(bool, u64)" + }, + { + "concreteTypeId": "4926d35d1a5157936b0a29bc126b8aace6d911209a5c130e9b716b0c73643ea6", + "metadataTypeId": 1, + "type": "[bool; 3]" + }, + { + "concreteTypeId": "776fb5a3824169d6736138565fdc20aad684d9111266a5ff6d5c675280b7e199", + "metadataTypeId": 2, + "type": "[u64; 3]" + }, + { + "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b", + "type": "b256" + }, + { + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", + "type": "bool" + }, + { + "concreteTypeId": "a2922861f03be8a650595dd76455b95383a61b46dd418f02607fa2e00dc39d5c", + "metadataTypeId": 3, + "type": "enum ConfigurableEnum" + }, + { + "concreteTypeId": "94f0fa95c830be5e4f711963e83259fe7e8bc723278ab6ec34449e791a99b53a", + "type": "str[4]" + }, + { + "concreteTypeId": "81fc10c4681a3271cf2d66b2ec6fbc8ed007a442652930844fcf11818c295bff", + "metadataTypeId": 4, + "type": "struct ConfigurableStruct" + }, + { + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef", + "type": "u16" + }, + { + "concreteTypeId": "1b5759d94094368cfd443019e7ca5ec4074300e544e5ea993a979f5da627261e", + "type": "u256" + }, + { + "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc", + "type": "u32" + }, + { + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", + "type": "u8" + } + ], "configurables": [ { - "configurableType": { - "name": "", - "type": 5, - "typeArguments": null - }, + "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", "name": "BOOL", - "offset": 3392 + "offset": 6800 }, { - "configurableType": { - "name": "", - "type": 13, - "typeArguments": null - }, + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", "name": "U8", - "offset": 3528 + "offset": 6992 }, { - "configurableType": { - "name": "", - "type": 13, - "typeArguments": null - }, + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", "name": "ANOTHER_U8", - "offset": 3320 + "offset": 6728 }, { - "configurableType": { - "name": "", - "type": 9, - "typeArguments": null - }, + "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef", "name": "U16", - "offset": 3472 + "offset": 6936 }, { - "configurableType": { - "name": "", - "type": 11, - "typeArguments": null - }, + "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc", "name": "U32", - "offset": 3512 + "offset": 6976 }, { - "configurableType": { - "name": "", - "type": 11, - "typeArguments": null - }, + "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc", "name": "U64", - "offset": 3520 + "offset": 6984 }, { - "configurableType": { - "name": "", - "type": 10, - "typeArguments": null - }, + "concreteTypeId": "1b5759d94094368cfd443019e7ca5ec4074300e544e5ea993a979f5da627261e", "name": "U256", - "offset": 3480 + "offset": 6944 }, { - "configurableType": { - "name": "", - "type": 4, - "typeArguments": null - }, + "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b", "name": "B256", - "offset": 3360 + "offset": 6768 }, { - "configurableType": { - "name": "", - "type": 8, - "typeArguments": [] - }, + "concreteTypeId": "81fc10c4681a3271cf2d66b2ec6fbc8ed007a442652930844fcf11818c295bff", "name": "CONFIGURABLE_STRUCT", - "offset": 3432 + "offset": 6888 }, { - "configurableType": { - "name": "", - "type": 6, - "typeArguments": [] - }, + "concreteTypeId": "a2922861f03be8a650595dd76455b95383a61b46dd418f02607fa2e00dc39d5c", "name": "CONFIGURABLE_ENUM_A", - "offset": 3400 + "offset": 6808 }, { - "configurableType": { - "name": "", - "type": 6, - "typeArguments": [] - }, + "concreteTypeId": "a2922861f03be8a650595dd76455b95383a61b46dd418f02607fa2e00dc39d5c", "name": "CONFIGURABLE_ENUM_B", - "offset": 3416 + "offset": 6848 }, { - "configurableType": { - "name": "", - "type": 2, - "typeArguments": null - }, + "concreteTypeId": "4926d35d1a5157936b0a29bc126b8aace6d911209a5c130e9b716b0c73643ea6", "name": "ARRAY_BOOL", - "offset": 3328 + "offset": 6736 }, { - "configurableType": { - "name": "", - "type": 3, - "typeArguments": null - }, + "concreteTypeId": "776fb5a3824169d6736138565fdc20aad684d9111266a5ff6d5c675280b7e199", "name": "ARRAY_U64", - "offset": 3336 + "offset": 6744 }, { - "configurableType": { - "name": "", - "type": 1, - "typeArguments": null - }, + "concreteTypeId": "c998ca9a5f221fe7b5c66ae70c8a9562b86d964408b00d17f883c906bc1fe4be", "name": "TUPLE_BOOL_U64", - "offset": 3456 + "offset": 6920 }, { - "configurableType": { - "name": "", - "type": 7, - "typeArguments": null - }, + "concreteTypeId": "94f0fa95c830be5e4f711963e83259fe7e8bc723278ab6ec34449e791a99b53a", "name": "STR_4", - "offset": 3448 + "offset": 6912 + }, + { + "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", + "name": "NOT_USED", + "offset": 6904 } ], + "encodingVersion": "1", "functions": [ { "attributes": null, "inputs": [], "name": "main", - "output": { - "name": "", - "type": 0, - "typeArguments": null - } + "output": "2e38e77b22c314a449e91fafed92a43826ac6aa403ae6a8acb6cf58239fbaf5d" } ], "loggedTypes": [], "messagesTypes": [], - "types": [ - { - "components": [], - "type": "()", - "typeId": 0, - "typeParameters": null - }, + "metadataTypes": [ { "components": [ { "name": "__tuple_element", - "type": 5, - "typeArguments": null + "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" }, { "name": "__tuple_element", - "type": 12, - "typeArguments": null + "typeId": 5 } ], - "type": "(_, _)", - "typeId": 1, - "typeParameters": null + "metadataTypeId": 0, + "type": "(_, _)" }, { "components": [ { "name": "__array_element", - "type": 5, - "typeArguments": null + "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" } ], - "type": "[_; 3]", - "typeId": 2, - "typeParameters": null + "metadataTypeId": 1, + "type": "[_; 3]" }, { "components": [ { "name": "__array_element", - "type": 12, - "typeArguments": null + "typeId": 5 } ], - "type": "[_; 3]", - "typeId": 3, - "typeParameters": null - }, - { - "components": null, - "type": "b256", - "typeId": 4, - "typeParameters": null - }, - { - "components": null, - "type": "bool", - "typeId": 5, - "typeParameters": null + "metadataTypeId": 2, + "type": "[_; 3]" }, { "components": [ { "name": "A", - "type": 5, - "typeArguments": null + "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" }, { "name": "B", - "type": 12, - "typeArguments": null + "typeId": 5 + }, + { + "name": "C", + "typeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b" } ], - "type": "enum ConfigurableEnum", - "typeId": 6, - "typeParameters": null - }, - { - "components": null, - "type": "str[4]", - "typeId": 7, - "typeParameters": null + "metadataTypeId": 3, + "type": "enum ConfigurableEnum" }, { "components": [ { "name": "a", - "type": 5, - "typeArguments": null + "typeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903" }, { "name": "b", - "type": 12, - "typeArguments": null + "typeId": 5 } ], - "type": "struct ConfigurableStruct", - "typeId": 8, - "typeParameters": null - }, - { - "components": null, - "type": "u16", - "typeId": 9, - "typeParameters": null - }, - { - "components": null, - "type": "u256", - "typeId": 10, - "typeParameters": null - }, - { - "components": null, - "type": "u32", - "typeId": 11, - "typeParameters": null + "metadataTypeId": 4, + "type": "struct ConfigurableStruct" }, { - "components": null, - "type": "u64", - "typeId": 12, - "typeParameters": null - }, - { - "components": null, - "type": "u8", - "typeId": 13, - "typeParameters": null + "metadataTypeId": 5, + "type": "u64" } - ] + ], + "programType": "script", + "specVersion": "1" } \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle.partial_eq.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle.partial_eq.json index d8c52d54b8c..1b9d2328683 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle.partial_eq.json +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle.partial_eq.json @@ -62,82 +62,82 @@ { "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", "name": "BOOL", - "offset": 6640 + "offset": 6800 }, { "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", "name": "U8", - "offset": 6832 + "offset": 6992 }, { "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", "name": "ANOTHER_U8", - "offset": 6568 + "offset": 6728 }, { "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef", "name": "U16", - "offset": 6776 + "offset": 6936 }, { "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc", "name": "U32", - "offset": 6816 + "offset": 6976 }, { "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc", "name": "U64", - "offset": 6824 + "offset": 6984 }, { "concreteTypeId": "1b5759d94094368cfd443019e7ca5ec4074300e544e5ea993a979f5da627261e", "name": "U256", - "offset": 6784 + "offset": 6944 }, { "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b", "name": "B256", - "offset": 6608 + "offset": 6768 }, { "concreteTypeId": "81fc10c4681a3271cf2d66b2ec6fbc8ed007a442652930844fcf11818c295bff", "name": "CONFIGURABLE_STRUCT", - "offset": 6728 + "offset": 6888 }, { "concreteTypeId": "a2922861f03be8a650595dd76455b95383a61b46dd418f02607fa2e00dc39d5c", "name": "CONFIGURABLE_ENUM_A", - "offset": 6648 + "offset": 6808 }, { "concreteTypeId": "a2922861f03be8a650595dd76455b95383a61b46dd418f02607fa2e00dc39d5c", "name": "CONFIGURABLE_ENUM_B", - "offset": 6688 + "offset": 6848 }, { "concreteTypeId": "4926d35d1a5157936b0a29bc126b8aace6d911209a5c130e9b716b0c73643ea6", "name": "ARRAY_BOOL", - "offset": 6576 + "offset": 6736 }, { "concreteTypeId": "776fb5a3824169d6736138565fdc20aad684d9111266a5ff6d5c675280b7e199", "name": "ARRAY_U64", - "offset": 6584 + "offset": 6744 }, { "concreteTypeId": "c998ca9a5f221fe7b5c66ae70c8a9562b86d964408b00d17f883c906bc1fe4be", "name": "TUPLE_BOOL_U64", - "offset": 6760 + "offset": 6920 }, { "concreteTypeId": "94f0fa95c830be5e4f711963e83259fe7e8bc723278ab6ec34449e791a99b53a", "name": "STR_4", - "offset": 6752 + "offset": 6912 }, { "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", "name": "NOT_USED", - "offset": 6744 + "offset": 6904 } ], "encodingVersion": "1", diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle_new_encoding.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle_new_encoding.json index d8c52d54b8c..1b9d2328683 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle_new_encoding.json +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle_new_encoding.json @@ -62,82 +62,82 @@ { "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", "name": "BOOL", - "offset": 6640 + "offset": 6800 }, { "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", "name": "U8", - "offset": 6832 + "offset": 6992 }, { "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", "name": "ANOTHER_U8", - "offset": 6568 + "offset": 6728 }, { "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef", "name": "U16", - "offset": 6776 + "offset": 6936 }, { "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc", "name": "U32", - "offset": 6816 + "offset": 6976 }, { "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc", "name": "U64", - "offset": 6824 + "offset": 6984 }, { "concreteTypeId": "1b5759d94094368cfd443019e7ca5ec4074300e544e5ea993a979f5da627261e", "name": "U256", - "offset": 6784 + "offset": 6944 }, { "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b", "name": "B256", - "offset": 6608 + "offset": 6768 }, { "concreteTypeId": "81fc10c4681a3271cf2d66b2ec6fbc8ed007a442652930844fcf11818c295bff", "name": "CONFIGURABLE_STRUCT", - "offset": 6728 + "offset": 6888 }, { "concreteTypeId": "a2922861f03be8a650595dd76455b95383a61b46dd418f02607fa2e00dc39d5c", "name": "CONFIGURABLE_ENUM_A", - "offset": 6648 + "offset": 6808 }, { "concreteTypeId": "a2922861f03be8a650595dd76455b95383a61b46dd418f02607fa2e00dc39d5c", "name": "CONFIGURABLE_ENUM_B", - "offset": 6688 + "offset": 6848 }, { "concreteTypeId": "4926d35d1a5157936b0a29bc126b8aace6d911209a5c130e9b716b0c73643ea6", "name": "ARRAY_BOOL", - "offset": 6576 + "offset": 6736 }, { "concreteTypeId": "776fb5a3824169d6736138565fdc20aad684d9111266a5ff6d5c675280b7e199", "name": "ARRAY_U64", - "offset": 6584 + "offset": 6744 }, { "concreteTypeId": "c998ca9a5f221fe7b5c66ae70c8a9562b86d964408b00d17f883c906bc1fe4be", "name": "TUPLE_BOOL_U64", - "offset": 6760 + "offset": 6920 }, { "concreteTypeId": "94f0fa95c830be5e4f711963e83259fe7e8bc723278ab6ec34449e791a99b53a", "name": "STR_4", - "offset": 6752 + "offset": 6912 }, { "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", "name": "NOT_USED", - "offset": 6744 + "offset": 6904 } ], "encodingVersion": "1", diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/asset_ops_test/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/asset_ops_test/src/main.sw index e8d797723ee..a48a169b480 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/asset_ops_test/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/asset_ops_test/src/main.sw @@ -9,7 +9,7 @@ use test_fuel_coin_abi::*; #[cfg(experimental_new_encoding = false)] const FUEL_COIN_CONTRACT_ID = 0xec2277ebe007ade87e3d797c3b1e070dcd542d5ef8f038b471f262ef9cebc87c; #[cfg(experimental_new_encoding = true)] -const FUEL_COIN_CONTRACT_ID = 0xd8ca4eaa438f31a1c0e0ec2e74b75b27d97df77714b3fb0b789d22d6b57d459f; // AUTO-CONTRACT-ID ../../test_contracts/test_fuel_coin_contract --release +const FUEL_COIN_CONTRACT_ID = 0x7779557446cb040b539d00bdcae7a677ce05e3f3c2886f4aa2b41bc4ba953840; // AUTO-CONTRACT-ID ../../test_contracts/test_fuel_coin_contract --release #[cfg(experimental_new_encoding = false)] const BALANCE_CONTRACT_ID = 0xf6cd545152ac83225e8e7df2efb5c6fa6e37bc9b9e977b5ea8103d28668925df; diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_basic_storage/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_basic_storage/src/main.sw index df7a9614177..88d9cd5e115 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_basic_storage/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_basic_storage/src/main.sw @@ -4,7 +4,7 @@ use basic_storage_abi::{BasicStorage, Quad}; #[cfg(experimental_new_encoding = false)] const CONTRACT_ID = 0x94db39f409a31b9f2ebcadeea44378e419208c20de90f5d8e1e33dc1523754cb; #[cfg(experimental_new_encoding = true)] -const CONTRACT_ID = 0x2214048d9ec349ce0d65cc0d23b5e80eeb1d1c34ae3794cca8c375ae6d804468; // AUTO-CONTRACT-ID ../../test_contracts/basic_storage --release +const CONTRACT_ID = 0x729d5678b0d1a5623c931c5903ebfdb7bd3e0cb3750758b75b46fa9588bd7e78; // AUTO-CONTRACT-ID ../../test_contracts/basic_storage --release fn main() -> u64 { let addr = abi(BasicStorage, CONTRACT_ID); diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_storage_enum/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_storage_enum/src/main.sw index 62e646a53f0..14586cc5f4e 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_storage_enum/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_storage_enum/src/main.sw @@ -5,7 +5,7 @@ use storage_enum_abi::*; #[cfg(experimental_new_encoding = false)] const CONTRACT_ID = 0xc601d11767195485a6654d566c67774134668863d8c797a8c69e8778fb1f89e9; #[cfg(experimental_new_encoding = true)] -const CONTRACT_ID = 0x0a56d4c7db5c58271451d87e6f288982ce706bc568af53b790d303584f08e601; // AUTO-CONTRACT-ID ../../test_contracts/storage_enum_contract --release +const CONTRACT_ID = 0x4bca7e0ed458b427f8c563a56785c93021b64c358f64b3a093cc0c32c27b6cfd; // AUTO-CONTRACT-ID ../../test_contracts/storage_enum_contract --release fn main() -> u64 { let caller = abi(StorageEnum, CONTRACT_ID); From 0e359eb1dc2c03dee31c88f20a7d2a10e97a4736 Mon Sep 17 00:00:00 2001 From: Vaivaswatha Nagaraj Date: Wed, 19 Feb 2025 14:33:27 +0530 Subject: [PATCH 08/21] remove unused globals --- sway-ir/src/optimize/dce.rs | 68 +++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 22 deletions(-) diff --git a/sway-ir/src/optimize/dce.rs b/sway-ir/src/optimize/dce.rs index 7883a8b74bc..92707843fec 100644 --- a/sway-ir/src/optimize/dce.rs +++ b/sway-ir/src/optimize/dce.rs @@ -11,8 +11,8 @@ use rustc_hash::FxHashSet; use crate::{ get_gep_referred_symbols, get_referred_symbols, memory_utils, AnalysisResults, Context, - EscapedSymbols, Function, InstOp, Instruction, IrError, LocalVar, Module, Pass, PassMutability, - ReferredSymbols, ScopedPass, Symbol, Value, ValueDatum, ESCAPED_SYMBOLS_NAME, + EscapedSymbols, Function, GlobalVar, InstOp, Instruction, IrError, LocalVar, Module, Pass, + PassMutability, ReferredSymbols, ScopedPass, Symbol, Value, ValueDatum, ESCAPED_SYMBOLS_NAME, }; use std::collections::{HashMap, HashSet}; @@ -35,7 +35,7 @@ pub fn create_fn_dce_pass() -> Pass { name: FN_DCE_NAME, descr: "Dead function elimination", deps: vec![], - runner: ScopedPass::ModulePass(PassMutability::Transform(fn_dce)), + runner: ScopedPass::ModulePass(PassMutability::Transform(globals_dce)), } } @@ -349,18 +349,28 @@ pub fn dce( Ok(modified) } -/// Remove entire functions from a module based on whether they are called or not, using a list of -/// root 'entry' functions to perform a search. +/// Remove entire functions and globals from a module based on whether they are called / used or not, +/// using a list of root 'entry' functions to perform a search. /// /// Functions which are `pub` will not be removed and only functions within the passed [`Module`] /// are considered for removal. -pub fn fn_dce(context: &mut Context, _: &AnalysisResults, module: Module) -> Result { +pub fn globals_dce( + context: &mut Context, + _: &AnalysisResults, + module: Module, +) -> Result { let mut called_fns: HashSet = HashSet::new(); + let mut used_globals: HashSet = HashSet::new(); // config decode fns for config in context.modules[module.0].configs.iter() { if let crate::ConfigContent::V1 { decode_fn, .. } = config.1 { - grow_called_function_set(context, decode_fn.get(), &mut called_fns); + grow_called_function_used_globals_set( + context, + decode_fn.get(), + &mut called_fns, + &mut used_globals, + ); } } @@ -372,7 +382,12 @@ pub fn fn_dce(context: &mut Context, _: &AnalysisResults, module: Module) -> Res // expand all called fns for entry_fn in entry_fns { - grow_called_function_set(context, entry_fn, &mut called_fns); + grow_called_function_used_globals_set( + context, + entry_fn, + &mut called_fns, + &mut used_globals, + ); } // Gather the functions in the module which aren't called. It's better to collect them @@ -382,7 +397,12 @@ pub fn fn_dce(context: &mut Context, _: &AnalysisResults, module: Module) -> Res .filter(|f| !called_fns.contains(f)) .collect::>(); - let modified = !dead_fns.is_empty(); + let m = &mut context.modules[module.0]; + let cur_num_globals = m.global_variables.len(); + m.global_variables.retain(|_, g| used_globals.contains(g)); + + let mut modified = !dead_fns.is_empty(); + modified |= cur_num_globals != m.global_variables.len(); for dead_fn in dead_fns { module.remove_function(context, &dead_fn); } @@ -391,25 +411,29 @@ pub fn fn_dce(context: &mut Context, _: &AnalysisResults, module: Module) -> Res } // Recursively find all the functions called by an entry function. -fn grow_called_function_set( +fn grow_called_function_used_globals_set( context: &Context, caller: Function, called_set: &mut HashSet, + used_globals: &mut HashSet, ) { if called_set.insert(caller) { // We haven't seen caller before. Iterate for all that it calls. - for func in caller - .instruction_iter(context) - .filter_map(|(_block, ins_value)| { - ins_value - .get_instruction(context) - .and_then(|ins| match &ins.op { - InstOp::Call(f, _args) => Some(f), - _otherwise => None, - }) - }) - { - grow_called_function_set(context, *func, called_set); + let mut callees = HashSet::new(); + for (_block, value) in caller.instruction_iter(context) { + let inst = value.get_instruction(context).unwrap(); + match &inst.op { + InstOp::Call(f, _args) => { + callees.insert(*f); + } + InstOp::GetGlobal(g) => { + used_globals.insert(*g); + } + _otherwise => (), + } } + callees.into_iter().for_each(|func| { + grow_called_function_used_globals_set(context, func, called_set, used_globals); + }); } } From 15b342cbdb5066b67f4735fb9a3f738a2a0bc8e3 Mon Sep 17 00:00:00 2001 From: Vaivaswatha Nagaraj Date: Wed, 19 Feb 2025 14:40:08 +0530 Subject: [PATCH 09/21] fix a test --- .../configurable_dedup_decode/stdout.snap | 294 ------------------ 1 file changed, 294 deletions(-) diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_dedup_decode/stdout.snap b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_dedup_decode/stdout.snap index caf219fab97..931294c8287 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_dedup_decode/stdout.snap +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_dedup_decode/stdout.snap @@ -10,153 +10,6 @@ output: Compiling library std (sway-lib-std) // IR: Final library { - global std::call_frames::FIRST_PARAMETER_OFFSET : u64 = const u64 73 - - global std::call_frames::PREV_FRAME_POINTER_OFFSET : u64 = const u64 6 - - global std::call_frames::SAVED_REGISTERS_OFFSET : u64 = const u64 8 - - global std::call_frames::SECOND_PARAMETER_OFFSET : u64 = const u64 74 - - global std::constants::DEFAULT_SUB_ID : b256 = const b256 0x0000000000000000000000000000000000000000000000000000000000000000 - - global std::constants::ZERO_B256 : b256 = const b256 0x0000000000000000000000000000000000000000000000000000000000000000 - - global std::constants::ZERO_U256 : u256 = const u256 0x0000000000000000000000000000000000000000000000000000000000000000 - - global std::error_signals::FAILED_ASSERT_EQ_SIGNAL : u64 = const u64 18446744073709486083 - - global std::error_signals::FAILED_ASSERT_NE_SIGNAL : u64 = const u64 18446744073709486085 - - global std::error_signals::FAILED_ASSERT_SIGNAL : u64 = const u64 18446744073709486084 - - global std::error_signals::FAILED_REQUIRE_SIGNAL : u64 = const u64 18446744073709486080 - - global std::error_signals::FAILED_TRANSFER_TO_ADDRESS_SIGNAL : u64 = const u64 18446744073709486081 - - global std::error_signals::REVERT_WITH_LOG_SIGNAL : u64 = const u64 18446744073709486086 - - global std::flags::F_UNSAFEMATH_DISABLE_MASK : u64 = const u64 1 - - global std::flags::F_UNSAFEMATH_ENABLE_MASK : u64 = const u64 18446744073709551614 - - global std::flags::F_WRAPPING_DISABLE_MASK : u64 = const u64 2 - - global std::flags::F_WRAPPING_ENABLE_MASK : u64 = const u64 18446744073709551613 - - global std::inputs::GTF_INPUT_COIN_AMOUNT : u64 = const u64 516 - - global std::inputs::GTF_INPUT_COIN_ASSET_ID : u64 = const u64 517 - - global std::inputs::GTF_INPUT_COIN_OWNER : u64 = const u64 515 - - global std::inputs::GTF_INPUT_COIN_PREDICATE : u64 = const u64 523 - - global std::inputs::GTF_INPUT_COIN_PREDICATE_DATA : u64 = const u64 524 - - global std::inputs::GTF_INPUT_COIN_PREDICATE_DATA_LENGTH : u64 = const u64 522 - - global std::inputs::GTF_INPUT_COIN_PREDICATE_LENGTH : u64 = const u64 521 - - global std::inputs::GTF_INPUT_COIN_WITNESS_INDEX : u64 = const u64 519 - - global std::inputs::GTF_INPUT_MESSAGE_AMOUNT : u64 = const u64 578 - - global std::inputs::GTF_INPUT_MESSAGE_DATA : u64 = const u64 584 - - global std::inputs::GTF_INPUT_MESSAGE_DATA_LENGTH : u64 = const u64 581 - - global std::inputs::GTF_INPUT_MESSAGE_NONCE : u64 = const u64 579 - - global std::inputs::GTF_INPUT_MESSAGE_PREDICATE : u64 = const u64 585 - - global std::inputs::GTF_INPUT_MESSAGE_PREDICATE_DATA : u64 = const u64 586 - - global std::inputs::GTF_INPUT_MESSAGE_PREDICATE_DATA_LENGTH : u64 = const u64 583 - - global std::inputs::GTF_INPUT_MESSAGE_PREDICATE_LENGTH : u64 = const u64 582 - - global std::inputs::GTF_INPUT_MESSAGE_RECIPIENT : u64 = const u64 577 - - global std::inputs::GTF_INPUT_MESSAGE_SENDER : u64 = const u64 576 - - global std::inputs::GTF_INPUT_MESSAGE_WITNESS_INDEX : u64 = const u64 580 - - global std::inputs::GTF_INPUT_TYPE : u64 = const u64 512 - - global std::outputs::GTF_OUTPUT_COIN_AMOUNT : u64 = const u64 770 - - global std::outputs::GTF_OUTPUT_COIN_ASSET_ID : u64 = const u64 771 - - global std::outputs::GTF_OUTPUT_COIN_TO : u64 = const u64 769 - - global std::outputs::GTF_OUTPUT_TYPE : u64 = const u64 768 - - global std::outputs::OUTPUT_VARIABLE_ASSET_ID_OFFSET : u64 = const u64 48 - - global std::outputs::OUTPUT_VARIABLE_TO_OFFSET : u64 = const u64 8 - - global std::tx::GTF_CREATE_INPUTS_COUNT : u64 = const u64 259 - - global std::tx::GTF_CREATE_INPUT_AT_INDEX : u64 = const u64 264 - - global std::tx::GTF_CREATE_OUTPUTS_COUNT : u64 = const u64 260 - - global std::tx::GTF_CREATE_OUTPUT_AT_INDEX : u64 = const u64 265 - - global std::tx::GTF_CREATE_WITNESSES_COUNT : u64 = const u64 261 - - global std::tx::GTF_CREATE_WITNESS_AT_INDEX : u64 = const u64 266 - - global std::tx::GTF_POLICY_MATURITY : u64 = const u64 1283 - - global std::tx::GTF_POLICY_MAX_FEE : u64 = const u64 1284 - - global std::tx::GTF_POLICY_TIP : u64 = const u64 1281 - - global std::tx::GTF_POLICY_TYPES : u64 = const u64 1280 - - global std::tx::GTF_POLICY_WITNESS_LIMIT : u64 = const u64 1282 - - global std::tx::GTF_SCRIPT_GAS_LIMIT : u64 = const u64 2 - - global std::tx::GTF_SCRIPT_INPUTS_COUNT : u64 = const u64 5 - - global std::tx::GTF_SCRIPT_INPUT_AT_INDEX : u64 = const u64 11 - - global std::tx::GTF_SCRIPT_OUTPUTS_COUNT : u64 = const u64 6 - - global std::tx::GTF_SCRIPT_OUTPUT_AT_INDEX : u64 = const u64 12 - - global std::tx::GTF_SCRIPT_SCRIPT : u64 = const u64 9 - - global std::tx::GTF_SCRIPT_SCRIPT_DATA : u64 = const u64 10 - - global std::tx::GTF_SCRIPT_SCRIPT_DATA_LENGTH : u64 = const u64 4 - - global std::tx::GTF_SCRIPT_SCRIPT_LENGTH : u64 = const u64 3 - - global std::tx::GTF_SCRIPT_WITNESSES_COUNT : u64 = const u64 7 - - global std::tx::GTF_SCRIPT_WITNESS_AT_INDEX : u64 = const u64 13 - - global std::tx::GTF_TX_LENGTH : u64 = const u64 14 - - global std::tx::GTF_TYPE : u64 = const u64 1 - - global std::tx::GTF_WITNESS_DATA : u64 = const u64 1025 - - global std::tx::GTF_WITNESS_DATA_LENGTH : u64 = const u64 1024 - - global std::tx::MATURITY_POLICY : u64 = const u64 4 - - global std::tx::MAX_FEE_POLICY : u64 = const u64 8 - - global std::tx::TIP_POLICY : u64 = const u64 1 - - global std::tx::TX_ID_OFFSET : u64 = const u64 0 - - global std::tx::WITNESS_LIMIT_POLICY : u64 = const u64 2 } Compiling script configurable_dedup_decode (test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_dedup_decode) @@ -165,153 +18,6 @@ script { TUPLE = config { u64 }, abi_decode_in_place_0, 0x0000000000000002, !1 WRAPPED = config { u64 }, abi_decode_in_place_0, 0x0000000000000001, !2 - global std::call_frames::FIRST_PARAMETER_OFFSET : u64 = const u64 73 - - global std::call_frames::PREV_FRAME_POINTER_OFFSET : u64 = const u64 6 - - global std::call_frames::SAVED_REGISTERS_OFFSET : u64 = const u64 8 - - global std::call_frames::SECOND_PARAMETER_OFFSET : u64 = const u64 74 - - global std::constants::DEFAULT_SUB_ID : b256 = const b256 0x0000000000000000000000000000000000000000000000000000000000000000 - - global std::constants::ZERO_B256 : b256 = const b256 0x0000000000000000000000000000000000000000000000000000000000000000 - - global std::constants::ZERO_U256 : u256 = const u256 0x0000000000000000000000000000000000000000000000000000000000000000 - - global std::error_signals::FAILED_ASSERT_EQ_SIGNAL : u64 = const u64 18446744073709486083 - - global std::error_signals::FAILED_ASSERT_NE_SIGNAL : u64 = const u64 18446744073709486085 - - global std::error_signals::FAILED_ASSERT_SIGNAL : u64 = const u64 18446744073709486084 - - global std::error_signals::FAILED_REQUIRE_SIGNAL : u64 = const u64 18446744073709486080 - - global std::error_signals::FAILED_TRANSFER_TO_ADDRESS_SIGNAL : u64 = const u64 18446744073709486081 - - global std::error_signals::REVERT_WITH_LOG_SIGNAL : u64 = const u64 18446744073709486086 - - global std::flags::F_UNSAFEMATH_DISABLE_MASK : u64 = const u64 1 - - global std::flags::F_UNSAFEMATH_ENABLE_MASK : u64 = const u64 18446744073709551614 - - global std::flags::F_WRAPPING_DISABLE_MASK : u64 = const u64 2 - - global std::flags::F_WRAPPING_ENABLE_MASK : u64 = const u64 18446744073709551613 - - global std::inputs::GTF_INPUT_COIN_AMOUNT : u64 = const u64 516 - - global std::inputs::GTF_INPUT_COIN_ASSET_ID : u64 = const u64 517 - - global std::inputs::GTF_INPUT_COIN_OWNER : u64 = const u64 515 - - global std::inputs::GTF_INPUT_COIN_PREDICATE : u64 = const u64 523 - - global std::inputs::GTF_INPUT_COIN_PREDICATE_DATA : u64 = const u64 524 - - global std::inputs::GTF_INPUT_COIN_PREDICATE_DATA_LENGTH : u64 = const u64 522 - - global std::inputs::GTF_INPUT_COIN_PREDICATE_LENGTH : u64 = const u64 521 - - global std::inputs::GTF_INPUT_COIN_WITNESS_INDEX : u64 = const u64 519 - - global std::inputs::GTF_INPUT_MESSAGE_AMOUNT : u64 = const u64 578 - - global std::inputs::GTF_INPUT_MESSAGE_DATA : u64 = const u64 584 - - global std::inputs::GTF_INPUT_MESSAGE_DATA_LENGTH : u64 = const u64 581 - - global std::inputs::GTF_INPUT_MESSAGE_NONCE : u64 = const u64 579 - - global std::inputs::GTF_INPUT_MESSAGE_PREDICATE : u64 = const u64 585 - - global std::inputs::GTF_INPUT_MESSAGE_PREDICATE_DATA : u64 = const u64 586 - - global std::inputs::GTF_INPUT_MESSAGE_PREDICATE_DATA_LENGTH : u64 = const u64 583 - - global std::inputs::GTF_INPUT_MESSAGE_PREDICATE_LENGTH : u64 = const u64 582 - - global std::inputs::GTF_INPUT_MESSAGE_RECIPIENT : u64 = const u64 577 - - global std::inputs::GTF_INPUT_MESSAGE_SENDER : u64 = const u64 576 - - global std::inputs::GTF_INPUT_MESSAGE_WITNESS_INDEX : u64 = const u64 580 - - global std::inputs::GTF_INPUT_TYPE : u64 = const u64 512 - - global std::outputs::GTF_OUTPUT_COIN_AMOUNT : u64 = const u64 770 - - global std::outputs::GTF_OUTPUT_COIN_ASSET_ID : u64 = const u64 771 - - global std::outputs::GTF_OUTPUT_COIN_TO : u64 = const u64 769 - - global std::outputs::GTF_OUTPUT_TYPE : u64 = const u64 768 - - global std::outputs::OUTPUT_VARIABLE_ASSET_ID_OFFSET : u64 = const u64 48 - - global std::outputs::OUTPUT_VARIABLE_TO_OFFSET : u64 = const u64 8 - - global std::tx::GTF_CREATE_INPUTS_COUNT : u64 = const u64 259 - - global std::tx::GTF_CREATE_INPUT_AT_INDEX : u64 = const u64 264 - - global std::tx::GTF_CREATE_OUTPUTS_COUNT : u64 = const u64 260 - - global std::tx::GTF_CREATE_OUTPUT_AT_INDEX : u64 = const u64 265 - - global std::tx::GTF_CREATE_WITNESSES_COUNT : u64 = const u64 261 - - global std::tx::GTF_CREATE_WITNESS_AT_INDEX : u64 = const u64 266 - - global std::tx::GTF_POLICY_MATURITY : u64 = const u64 1283 - - global std::tx::GTF_POLICY_MAX_FEE : u64 = const u64 1284 - - global std::tx::GTF_POLICY_TIP : u64 = const u64 1281 - - global std::tx::GTF_POLICY_TYPES : u64 = const u64 1280 - - global std::tx::GTF_POLICY_WITNESS_LIMIT : u64 = const u64 1282 - - global std::tx::GTF_SCRIPT_GAS_LIMIT : u64 = const u64 2 - - global std::tx::GTF_SCRIPT_INPUTS_COUNT : u64 = const u64 5 - - global std::tx::GTF_SCRIPT_INPUT_AT_INDEX : u64 = const u64 11 - - global std::tx::GTF_SCRIPT_OUTPUTS_COUNT : u64 = const u64 6 - - global std::tx::GTF_SCRIPT_OUTPUT_AT_INDEX : u64 = const u64 12 - - global std::tx::GTF_SCRIPT_SCRIPT : u64 = const u64 9 - - global std::tx::GTF_SCRIPT_SCRIPT_DATA : u64 = const u64 10 - - global std::tx::GTF_SCRIPT_SCRIPT_DATA_LENGTH : u64 = const u64 4 - - global std::tx::GTF_SCRIPT_SCRIPT_LENGTH : u64 = const u64 3 - - global std::tx::GTF_SCRIPT_WITNESSES_COUNT : u64 = const u64 7 - - global std::tx::GTF_SCRIPT_WITNESS_AT_INDEX : u64 = const u64 13 - - global std::tx::GTF_TX_LENGTH : u64 = const u64 14 - - global std::tx::GTF_TYPE : u64 = const u64 1 - - global std::tx::GTF_WITNESS_DATA : u64 = const u64 1025 - - global std::tx::GTF_WITNESS_DATA_LENGTH : u64 = const u64 1024 - - global std::tx::MATURITY_POLICY : u64 = const u64 4 - - global std::tx::MAX_FEE_POLICY : u64 = const u64 8 - - global std::tx::TIP_POLICY : u64 = const u64 1 - - global std::tx::TX_ID_OFFSET : u64 = const u64 0 - - global std::tx::WITNESS_LIMIT_POLICY : u64 = const u64 2 pub fn abi_decode_in_place_0(ptr !4: u64, len !5: u64, target !6: u64) -> (), !10 { local { u64 } __ret_val local mut { u64 } buffer From 5c7f1e4c1bbd8006bddc7170020414e7bb524d49 Mon Sep 17 00:00:00 2001 From: Vaivaswatha Nagaraj Date: Wed, 19 Feb 2025 15:44:38 +0530 Subject: [PATCH 10/21] add a unit test that checks addresses of global consts --- .../references/addr_of_consts/Forc.lock | 13 ++++++ .../references/addr_of_consts/Forc.toml | 8 ++++ .../references/addr_of_consts/src/main.sw | 40 +++++++++++++++++++ .../references/addr_of_consts/test.toml | 3 ++ 4 files changed, 64 insertions(+) create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/references/addr_of_consts/Forc.lock create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/references/addr_of_consts/Forc.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/references/addr_of_consts/src/main.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/references/addr_of_consts/test.toml diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/addr_of_consts/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/addr_of_consts/Forc.lock new file mode 100644 index 00000000000..2d5dc5f57d1 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/addr_of_consts/Forc.lock @@ -0,0 +1,13 @@ +[[package]] +name = "addr_of_consts" +source = "member" +dependencies = ["std"] + +[[package]] +name = "core" +source = "path+from-root-EAA0F05534A380BD" + +[[package]] +name = "std" +source = "path+from-root-EAA0F05534A380BD" +dependencies = ["core"] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/addr_of_consts/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/addr_of_consts/Forc.toml new file mode 100644 index 00000000000..3c35b59e4fb --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/addr_of_consts/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "addr_of_consts" + +[dependencies] +std = { path = "../../../../../reduced_std_libs/sway-lib-std-assert" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/addr_of_consts/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/addr_of_consts/src/main.sw new file mode 100644 index 00000000000..3d83fae5edc --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/addr_of_consts/src/main.sw @@ -0,0 +1,40 @@ +script; + +struct S1 { + x: u64, + y: u64, +} + +const TEST: S1 = S1 { x: 101, y: 111 }; + +fn reference_to_int(ptr: &u64) -> u64 { + asm(ptr:ptr) { + ptr: u64 + } +} + +#[inline(never)] +fn get_addr_x() -> u64 { + let x_addr = &TEST.x; + reference_to_int(x_addr) +} + +#[inline(never)] +fn get_addr_y() -> u64 { + let y_addr = &TEST.y; + reference_to_int(y_addr) +} + +#[inline(never)] +fn sum_x_y_addresses() -> u64 { + get_addr_x() + get_addr_y() +} + +fn main() -> u64 { + 0 +} + +#[test] +fn test_x_y_addr() { + assert(get_addr_x() + get_addr_y() == sum_x_y_addresses()); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/addr_of_consts/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/addr_of_consts/test.toml new file mode 100644 index 00000000000..ad1782559e7 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/addr_of_consts/test.toml @@ -0,0 +1,3 @@ +category = "unit_tests_pass" +validate_abi = false +expected_warnings = 0 From 39e30195c8adc80b05df3d0e0491b8b3e86542ab Mon Sep 17 00:00:00 2001 From: Vaivaswatha Nagaraj Date: Thu, 20 Feb 2025 13:48:56 +0530 Subject: [PATCH 11/21] pass to promote global consts to immediate constants when possible --- sway-ir/src/optimize/mem2reg.rs | 69 +++++++++++++++++++++++++++------ sway-ir/src/pass_manager.rs | 1 + 2 files changed, 59 insertions(+), 11 deletions(-) diff --git a/sway-ir/src/optimize/mem2reg.rs b/sway-ir/src/optimize/mem2reg.rs index 0b955b4ce07..3a53fdf37d9 100644 --- a/sway-ir/src/optimize/mem2reg.rs +++ b/sway-ir/src/optimize/mem2reg.rs @@ -9,9 +9,9 @@ use std::collections::HashSet; use sway_utils::mapped_stack::MappedStack; use crate::{ - AnalysisResults, Block, BranchToWithArgs, Context, DomFronts, DomTree, Function, InstOp, - Instruction, IrError, LocalVar, Pass, PassMutability, PostOrder, ScopedPass, Type, Value, - ValueDatum, DOMINATORS_NAME, DOM_FRONTS_NAME, POSTORDER_NAME, + AnalysisResults, Block, BranchToWithArgs, Constant, Context, DomFronts, DomTree, Function, + InstOp, Instruction, IrError, LocalVar, Pass, PassMutability, PostOrder, ScopedPass, Type, + Value, ValueDatum, DOMINATORS_NAME, DOM_FRONTS_NAME, POSTORDER_NAME, }; pub const MEM2REG_NAME: &str = "mem2reg"; @@ -19,7 +19,7 @@ pub const MEM2REG_NAME: &str = "mem2reg"; pub fn create_mem2reg_pass() -> Pass { Pass { name: MEM2REG_NAME, - descr: "Promotion of local memory to SSA registers", + descr: "Promotion of local / global memory to SSA registers", deps: vec![POSTORDER_NAME, DOMINATORS_NAME, DOM_FRONTS_NAME], runner: ScopedPass::FunctionPass(PassMutability::Transform(promote_to_registers)), } @@ -43,6 +43,12 @@ fn get_validate_local_var( } } +fn is_promotable_type(context: &Context, ty: Type) -> bool { + ty.is_unit(context) + || ty.is_bool(context) + || (ty.is_uint(context) && ty.get_uint_width(context).unwrap() <= 64) +} + // Returns those locals that can be promoted to SSA registers. fn filter_usable_locals(context: &mut Context, function: &Function) -> HashSet { // The size of an SSA register is target specific. Here we're going to just stick with atomic @@ -51,10 +57,7 @@ fn filter_usable_locals(context: &mut Context, function: &Function) -> HashSet Result { + let mut replacements = FxHashMap::::default(); + for (_, inst) in function.instruction_iter(context) { + if let ValueDatum::Instruction(Instruction { + op: InstOp::Load(ptr), + .. + }) = context.values[inst.0].value + { + if let ValueDatum::Instruction(Instruction { + op: InstOp::GetGlobal(global_var), + .. + }) = context.values[ptr.0].value + { + if !global_var.is_mutable(context) + && is_promotable_type(context, global_var.get_inner_type(context)) + { + let constant = *global_var + .get_initializer(context) + .expect("We're dealing with an uninitialized value"); + replacements.insert(inst, constant); + } + } + } + } + + if replacements.is_empty() { + return Ok(false); + } + + let replacements = replacements + .into_iter() + .map(|(k, v)| (k, Value::new_constant(context, v))) + .collect::>(); + + function.replace_values(context, &replacements, None); + + Ok(true) +} + +/// Promote memory values that are accessed via load/store to SSA registers. +/// Globals: We promote only non-mutable globals of non-copy types +/// Locals: We promote only locals of non-copy type, whose every use is in a `get_local` /// without offsets, and the result of such a `get_local` is used only in a load /// or a store. pub fn promote_to_registers( @@ -157,10 +201,13 @@ pub fn promote_to_registers( analyses: &AnalysisResults, function: Function, ) -> Result { + let promoted_globals = promote_globals(context, &function)?; + let safe_locals = filter_usable_locals(context, &function); if safe_locals.is_empty() { - return Ok(false); + return Ok(promoted_globals); } + let po: &PostOrder = analyses.get_analysis_result(function); let dom_tree: &DomTree = analyses.get_analysis_result(function); let dom_fronts: &DomFronts = analyses.get_analysis_result(function); diff --git a/sway-ir/src/pass_manager.rs b/sway-ir/src/pass_manager.rs index b1c069a39dd..b2b830a61d4 100644 --- a/sway-ir/src/pass_manager.rs +++ b/sway-ir/src/pass_manager.rs @@ -418,6 +418,7 @@ pub fn create_o1_pass_group() -> PassGroup { o1.append_pass(FN_INLINE_NAME); o1.append_pass(SIMPLIFY_CFG_NAME); o1.append_pass(FN_DCE_NAME); + o1.append_pass(DCE_NAME); o1.append_pass(FN_INLINE_NAME); o1.append_pass(CCP_NAME); o1.append_pass(CONST_FOLDING_NAME); From 3ec3f9001e33e92776d7c2d57ec1fb93228277bd Mon Sep 17 00:00:00 2001 From: Vaivaswatha Nagaraj Date: Thu, 20 Feb 2025 14:29:52 +0530 Subject: [PATCH 12/21] update tests --- .../json_abi_oracle.partial_eq.json | 32 +++++++++---------- .../json_abi_oracle_new_encoding.json | 32 +++++++++---------- .../asset_ops_test/src/main.sw | 2 +- .../call_basic_storage/src/main.sw | 2 +- .../call_storage_enum/src/main.sw | 2 +- 5 files changed, 35 insertions(+), 35 deletions(-) diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle.partial_eq.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle.partial_eq.json index 1b9d2328683..d8c52d54b8c 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle.partial_eq.json +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle.partial_eq.json @@ -62,82 +62,82 @@ { "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", "name": "BOOL", - "offset": 6800 + "offset": 6640 }, { "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", "name": "U8", - "offset": 6992 + "offset": 6832 }, { "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", "name": "ANOTHER_U8", - "offset": 6728 + "offset": 6568 }, { "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef", "name": "U16", - "offset": 6936 + "offset": 6776 }, { "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc", "name": "U32", - "offset": 6976 + "offset": 6816 }, { "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc", "name": "U64", - "offset": 6984 + "offset": 6824 }, { "concreteTypeId": "1b5759d94094368cfd443019e7ca5ec4074300e544e5ea993a979f5da627261e", "name": "U256", - "offset": 6944 + "offset": 6784 }, { "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b", "name": "B256", - "offset": 6768 + "offset": 6608 }, { "concreteTypeId": "81fc10c4681a3271cf2d66b2ec6fbc8ed007a442652930844fcf11818c295bff", "name": "CONFIGURABLE_STRUCT", - "offset": 6888 + "offset": 6728 }, { "concreteTypeId": "a2922861f03be8a650595dd76455b95383a61b46dd418f02607fa2e00dc39d5c", "name": "CONFIGURABLE_ENUM_A", - "offset": 6808 + "offset": 6648 }, { "concreteTypeId": "a2922861f03be8a650595dd76455b95383a61b46dd418f02607fa2e00dc39d5c", "name": "CONFIGURABLE_ENUM_B", - "offset": 6848 + "offset": 6688 }, { "concreteTypeId": "4926d35d1a5157936b0a29bc126b8aace6d911209a5c130e9b716b0c73643ea6", "name": "ARRAY_BOOL", - "offset": 6736 + "offset": 6576 }, { "concreteTypeId": "776fb5a3824169d6736138565fdc20aad684d9111266a5ff6d5c675280b7e199", "name": "ARRAY_U64", - "offset": 6744 + "offset": 6584 }, { "concreteTypeId": "c998ca9a5f221fe7b5c66ae70c8a9562b86d964408b00d17f883c906bc1fe4be", "name": "TUPLE_BOOL_U64", - "offset": 6920 + "offset": 6760 }, { "concreteTypeId": "94f0fa95c830be5e4f711963e83259fe7e8bc723278ab6ec34449e791a99b53a", "name": "STR_4", - "offset": 6912 + "offset": 6752 }, { "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", "name": "NOT_USED", - "offset": 6904 + "offset": 6744 } ], "encodingVersion": "1", diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle_new_encoding.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle_new_encoding.json index 1b9d2328683..d8c52d54b8c 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle_new_encoding.json +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_consts/json_abi_oracle_new_encoding.json @@ -62,82 +62,82 @@ { "concreteTypeId": "b760f44fa5965c2474a3b471467a22c43185152129295af588b022ae50b50903", "name": "BOOL", - "offset": 6800 + "offset": 6640 }, { "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", "name": "U8", - "offset": 6992 + "offset": 6832 }, { "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", "name": "ANOTHER_U8", - "offset": 6728 + "offset": 6568 }, { "concreteTypeId": "29881aad8730c5ab11d275376323d8e4ff4179aae8ccb6c13fe4902137e162ef", "name": "U16", - "offset": 6936 + "offset": 6776 }, { "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc", "name": "U32", - "offset": 6976 + "offset": 6816 }, { "concreteTypeId": "d7649d428b9ff33d188ecbf38a7e4d8fd167fa01b2e10fe9a8f9308e52f1d7cc", "name": "U64", - "offset": 6984 + "offset": 6824 }, { "concreteTypeId": "1b5759d94094368cfd443019e7ca5ec4074300e544e5ea993a979f5da627261e", "name": "U256", - "offset": 6944 + "offset": 6784 }, { "concreteTypeId": "7c5ee1cecf5f8eacd1284feb5f0bf2bdea533a51e2f0c9aabe9236d335989f3b", "name": "B256", - "offset": 6768 + "offset": 6608 }, { "concreteTypeId": "81fc10c4681a3271cf2d66b2ec6fbc8ed007a442652930844fcf11818c295bff", "name": "CONFIGURABLE_STRUCT", - "offset": 6888 + "offset": 6728 }, { "concreteTypeId": "a2922861f03be8a650595dd76455b95383a61b46dd418f02607fa2e00dc39d5c", "name": "CONFIGURABLE_ENUM_A", - "offset": 6808 + "offset": 6648 }, { "concreteTypeId": "a2922861f03be8a650595dd76455b95383a61b46dd418f02607fa2e00dc39d5c", "name": "CONFIGURABLE_ENUM_B", - "offset": 6848 + "offset": 6688 }, { "concreteTypeId": "4926d35d1a5157936b0a29bc126b8aace6d911209a5c130e9b716b0c73643ea6", "name": "ARRAY_BOOL", - "offset": 6736 + "offset": 6576 }, { "concreteTypeId": "776fb5a3824169d6736138565fdc20aad684d9111266a5ff6d5c675280b7e199", "name": "ARRAY_U64", - "offset": 6744 + "offset": 6584 }, { "concreteTypeId": "c998ca9a5f221fe7b5c66ae70c8a9562b86d964408b00d17f883c906bc1fe4be", "name": "TUPLE_BOOL_U64", - "offset": 6920 + "offset": 6760 }, { "concreteTypeId": "94f0fa95c830be5e4f711963e83259fe7e8bc723278ab6ec34449e791a99b53a", "name": "STR_4", - "offset": 6912 + "offset": 6752 }, { "concreteTypeId": "c89951a24c6ca28c13fd1cfdc646b2b656d69e61a92b91023be7eb58eb914b6b", "name": "NOT_USED", - "offset": 6904 + "offset": 6744 } ], "encodingVersion": "1", diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/asset_ops_test/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/asset_ops_test/src/main.sw index a48a169b480..e8d797723ee 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/asset_ops_test/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/asset_ops_test/src/main.sw @@ -9,7 +9,7 @@ use test_fuel_coin_abi::*; #[cfg(experimental_new_encoding = false)] const FUEL_COIN_CONTRACT_ID = 0xec2277ebe007ade87e3d797c3b1e070dcd542d5ef8f038b471f262ef9cebc87c; #[cfg(experimental_new_encoding = true)] -const FUEL_COIN_CONTRACT_ID = 0x7779557446cb040b539d00bdcae7a677ce05e3f3c2886f4aa2b41bc4ba953840; // AUTO-CONTRACT-ID ../../test_contracts/test_fuel_coin_contract --release +const FUEL_COIN_CONTRACT_ID = 0xd8ca4eaa438f31a1c0e0ec2e74b75b27d97df77714b3fb0b789d22d6b57d459f; // AUTO-CONTRACT-ID ../../test_contracts/test_fuel_coin_contract --release #[cfg(experimental_new_encoding = false)] const BALANCE_CONTRACT_ID = 0xf6cd545152ac83225e8e7df2efb5c6fa6e37bc9b9e977b5ea8103d28668925df; diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_basic_storage/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_basic_storage/src/main.sw index 88d9cd5e115..54837ccb258 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_basic_storage/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_basic_storage/src/main.sw @@ -4,7 +4,7 @@ use basic_storage_abi::{BasicStorage, Quad}; #[cfg(experimental_new_encoding = false)] const CONTRACT_ID = 0x94db39f409a31b9f2ebcadeea44378e419208c20de90f5d8e1e33dc1523754cb; #[cfg(experimental_new_encoding = true)] -const CONTRACT_ID = 0x729d5678b0d1a5623c931c5903ebfdb7bd3e0cb3750758b75b46fa9588bd7e78; // AUTO-CONTRACT-ID ../../test_contracts/basic_storage --release +const CONTRACT_ID = 0x96b99bf6194794b0a60788027a30e4713c3ed0ba8b3a63ba9b8a70f838cffa71; // AUTO-CONTRACT-ID ../../test_contracts/basic_storage --release fn main() -> u64 { let addr = abi(BasicStorage, CONTRACT_ID); diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_storage_enum/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_storage_enum/src/main.sw index 14586cc5f4e..62e646a53f0 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_storage_enum/src/main.sw +++ b/test/src/e2e_vm_tests/test_programs/should_pass/require_contract_deployment/call_storage_enum/src/main.sw @@ -5,7 +5,7 @@ use storage_enum_abi::*; #[cfg(experimental_new_encoding = false)] const CONTRACT_ID = 0xc601d11767195485a6654d566c67774134668863d8c797a8c69e8778fb1f89e9; #[cfg(experimental_new_encoding = true)] -const CONTRACT_ID = 0x4bca7e0ed458b427f8c563a56785c93021b64c358f64b3a093cc0c32c27b6cfd; // AUTO-CONTRACT-ID ../../test_contracts/storage_enum_contract --release +const CONTRACT_ID = 0x0a56d4c7db5c58271451d87e6f288982ce706bc568af53b790d303584f08e601; // AUTO-CONTRACT-ID ../../test_contracts/storage_enum_contract --release fn main() -> u64 { let caller = abi(StorageEnum, CONTRACT_ID); From c29eaeab58d699420dea05d292349903ff704b06 Mon Sep 17 00:00:00 2001 From: Vaivaswatha Nagaraj Date: Wed, 26 Feb 2025 07:05:33 +0530 Subject: [PATCH 13/21] address review comments --- .../asm_generation/fuel/fuel_asm_builder.rs | 2 +- sway-core/src/ir_generation/const_eval.rs | 8 ++-- sway-ir/src/parser.rs | 38 +++++++++---------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs b/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs index c92fa12a841..4bc34d9a501 100644 --- a/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs +++ b/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs @@ -1277,7 +1277,7 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> { let reg = self.reg_seqr.next(); self.cur_bytecode.push(Op { opcode: either::Either::Left(VirtualOp::AddrDataId(reg.clone(), data_id.clone())), - comment: "constant address in data section".into(), + comment: "get constant's address in data section".into(), owning_span: Some(span), }); self.reg_map.insert(*instr_val, reg); diff --git a/sway-core/src/ir_generation/const_eval.rs b/sway-core/src/ir_generation/const_eval.rs index fe831f72723..bb3ef92fbba 100644 --- a/sway-core/src/ir_generation/const_eval.rs +++ b/sway-core/src/ir_generation/const_eval.rs @@ -122,10 +122,10 @@ pub(crate) fn compile_const_decl( env.module_ns, ) { (Some(global_var), _) => { - if let Some(constant) = global_var.get_initializer(env.context) { - return Ok(Some(Value::new_constant(env.context, *constant))); - } - Ok(None) + let constant = global_var + .get_initializer(env.context) + .expect("const decl without initializer, should've been detected way early"); + return Ok(Some(Value::new_constant(env.context, *constant))); } (None, Some(module_ns)) => { // See if we it's a global const and whether we can compile it *now*. diff --git a/sway-ir/src/parser.rs b/sway-ir/src/parser.rs index c0c7b7b93b6..fbf471e61c3 100644 --- a/sway-ir/src/parser.rs +++ b/sway-ir/src/parser.rs @@ -43,12 +43,12 @@ mod ir_builder { } rule script_or_predicate() -> IrAstModule - = kind:module_kind() "{" _ configs:init_config()* _ global_consts:global_const()* _ fn_decls:fn_decl()* "}" _ + = kind:module_kind() "{" _ configs:init_config()* _ global_vars:global_var()* _ fn_decls:fn_decl()* "}" _ metadata:metadata_decls() { IrAstModule { kind, configs, - global_consts, + global_vars, fn_decls, metadata } @@ -60,20 +60,20 @@ mod ir_builder { rule contract() -> IrAstModule = "contract" _ "{" _ - configs:init_config()* _ global_consts:global_const()* _ fn_decls:fn_decl()* "}" _ + configs:init_config()* _ global_vars:global_var()* _ fn_decls:fn_decl()* "}" _ metadata:metadata_decls() { IrAstModule { kind: crate::module::Kind::Contract, configs, - global_consts, + global_vars, fn_decls, metadata } } - rule global_const() -> IrAstGlobalConst + rule global_var() -> IrAstGlobalVar = "global" _ name:path() _ ":" _ ty:ast_ty() _ "=" _ init:op_const() { - IrAstGlobalConst { + IrAstGlobalVar { name, ty, init @@ -714,13 +714,13 @@ mod ir_builder { pub(super) struct IrAstModule { kind: Kind, configs: Vec, - global_consts: Vec, + global_vars: Vec, fn_decls: Vec, metadata: Vec<(MdIdxRef, IrMetadatum)>, } #[derive(Debug)] - pub(super) struct IrAstGlobalConst { + pub(super) struct IrAstGlobalVar { name: Vec, ty: IrAstTy, init: IrAstOperation, @@ -1010,7 +1010,7 @@ mod ir_builder { let mut builder = IrBuilder { module, configs_map: build_configs_map(&mut ctx, &module, ir_ast_mod.configs, &md_map), - global_map: build_global_consts_map(&mut ctx, &module, ir_ast_mod.global_consts), + globals_map: build_global_vars_map(&mut ctx, &module, ir_ast_mod.global_vars), md_map, unresolved_calls: Vec::new(), }; @@ -1027,7 +1027,7 @@ mod ir_builder { struct IrBuilder { module: Module, configs_map: BTreeMap, - global_map: BTreeMap, GlobalVar>, + globals_map: BTreeMap, GlobalVar>, md_map: HashMap, unresolved_calls: Vec, } @@ -1354,7 +1354,7 @@ mod ir_builder { .add_metadatum(context, opt_metadata), IrAstOperation::GetGlobal(global_name) => block .append(context) - .get_global(*self.global_map.get(&global_name).unwrap()) + .get_global(*self.globals_map.get(&global_name).unwrap()) .add_metadatum(context, opt_metadata), IrAstOperation::GetConfig(name) => block .append(context) @@ -1547,22 +1547,22 @@ mod ir_builder { } } - fn build_global_consts_map( + fn build_global_vars_map( context: &mut Context, module: &Module, - global_consts: Vec, + global_vars: Vec, ) -> BTreeMap, GlobalVar> { - global_consts + global_vars .into_iter() - .map(|global_const| { - let ty = global_const.ty.to_ir_type(context); - let init = match global_const.init { + .map(|global_var_node| { + let ty = global_var_node.ty.to_ir_type(context); + let init = match global_var_node.init { IrAstOperation::Const(ty, val) => val.value.as_constant(context, ty), _ => unreachable!("Global const initializer must be a const value."), }; let global_var = GlobalVar::new(context, ty, Some(init), false); - module.add_global_variable(context, global_const.name.clone(), global_var); - (global_const.name, global_var) + module.add_global_variable(context, global_var_node.name.clone(), global_var); + (global_var_node.name, global_var) }) .collect() } From c0f6b7e7d8bc38e78823de3d8ac872e853b2f413 Mon Sep 17 00:00:00 2001 From: Vaivaswatha Nagaraj Date: Wed, 26 Feb 2025 07:50:30 +0530 Subject: [PATCH 14/21] clippy fix --- sway-core/src/ir_generation/const_eval.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway-core/src/ir_generation/const_eval.rs b/sway-core/src/ir_generation/const_eval.rs index bb3ef92fbba..8e83995f261 100644 --- a/sway-core/src/ir_generation/const_eval.rs +++ b/sway-core/src/ir_generation/const_eval.rs @@ -125,7 +125,7 @@ pub(crate) fn compile_const_decl( let constant = global_var .get_initializer(env.context) .expect("const decl without initializer, should've been detected way early"); - return Ok(Some(Value::new_constant(env.context, *constant))); + Ok(Some(Value::new_constant(env.context, *constant))) } (None, Some(module_ns)) => { // See if we it's a global const and whether we can compile it *now*. From a86bc225a93069b7177c6ea2d73fd4ec8fefb115 Mon Sep 17 00:00:00 2001 From: Vaivaswatha Nagaraj Date: Sat, 1 Mar 2025 11:30:39 +0530 Subject: [PATCH 15/21] address review comments, tests pending --- .../asm_generation/fuel/fuel_asm_builder.rs | 10 +++- sway-core/src/lib.rs | 6 +- sway-ir/src/error.rs | 60 ++++++++++++------- sway-ir/src/optimize/dce.rs | 38 ++++++------ sway-ir/src/optimize/mem2reg.rs | 27 ++++++--- sway-ir/src/parser.rs | 14 +++-- sway-ir/src/pass_manager.rs | 18 +++--- sway-ir/src/printer.rs | 4 +- sway-ir/src/verify.rs | 20 +++++++ sway-ir/tests/tests.rs | 6 +- 10 files changed, 128 insertions(+), 75 deletions(-) diff --git a/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs b/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs index 4bc34d9a501..73ff703abd9 100644 --- a/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs +++ b/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs @@ -1253,15 +1253,19 @@ impl<'ir, 'eng> FuelAsmBuilder<'ir, 'eng> { fn compile_get_global( &mut self, instr_val: &Value, - local_var: &GlobalVar, + global_var: &GlobalVar, ) -> Result<(), CompileError> { + if global_var.is_mutable(self.context) { + todo!("Implement mutable global variables"); + } + let span = self .md_mgr .val_to_span(self.context, *instr_val) .unwrap_or_else(Span::dummy); - let Some(constant) = local_var.get_initializer(self.context) else { + let Some(constant) = global_var.get_initializer(self.context) else { return Err(CompileError::Internal( - "Global variable must have an initializer.", + "Global constants (immutable variables) must have an initializer.", span, )); }; diff --git a/sway-core/src/lib.rs b/sway-core/src/lib.rs index 9d572d5d8e7..89fdbe672e1 100644 --- a/sway-core/src/lib.rs +++ b/sway-core/src/lib.rs @@ -48,8 +48,8 @@ use sway_error::handler::{ErrorEmitted, Handler}; use sway_features::ExperimentalFeatures; use sway_ir::{ create_o1_pass_group, register_known_passes, Context, Kind, Module, PassGroup, PassManager, - PrintPassesOpts, ARG_DEMOTION_NAME, CONST_DEMOTION_NAME, DCE_NAME, FN_DCE_NAME, - FN_DEDUP_DEBUG_PROFILE_NAME, FN_INLINE_NAME, MEM2REG_NAME, MEMCPYOPT_NAME, MISC_DEMOTION_NAME, + PrintPassesOpts, ARG_DEMOTION_NAME, CONST_DEMOTION_NAME, DCE_NAME, FN_DEDUP_DEBUG_PROFILE_NAME, + FN_INLINE_NAME, GLOBALS_DCE_NAME, MEM2REG_NAME, MEMCPYOPT_NAME, MISC_DEMOTION_NAME, RET_DEMOTION_NAME, SIMPLIFY_CFG_NAME, SROA_NAME, }; use sway_types::constants::DOC_COMMENT_ATTRIBUTE_NAME; @@ -964,7 +964,7 @@ pub(crate) fn compile_ast_to_ir_to_asm( pass_group.append_pass(FN_INLINE_NAME); // Do DCE so other optimizations run faster. - pass_group.append_pass(FN_DCE_NAME); + pass_group.append_pass(GLOBALS_DCE_NAME); pass_group.append_pass(DCE_NAME); } } diff --git a/sway-ir/src/error.rs b/sway-ir/src/error.rs index 80b0894fe16..2a9febc02d0 100644 --- a/sway-ir/src/error.rs +++ b/sway-ir/src/error.rs @@ -37,6 +37,7 @@ pub enum IrError { VerifyGepInconsistentTypes(String, Option), VerifyGepOnNonAggregate, VerifyGetNonExistentPointer, + VerifyGlobalMissingInitializer(String), VerifyInsertElementOfIncorrectType, VerifyInsertValueOfIncorrectType, VerifyIntToPtrFromNonIntegerType(String), @@ -44,6 +45,7 @@ pub enum IrError { VerifyIntToPtrUnknownSourceType, VerifyInvalidGtfIndexType, VerifyLoadFromNonPointer(String), + VerifyLocalMissingInitializer(String, String), VerifyLogId, VerifyLogMismatchedTypes, VerifyMemcopyNonPointer(String), @@ -121,10 +123,10 @@ impl fmt::Display for IrError { } IrError::InconsistentParent(entity, expected_parent, found_parent) => { write!( - f, - "For IR Entity (module/function/block) {entity}, expected parent to be {expected_parent}, \ + f, + "For IR Entity (module/function/block) {entity}, expected parent to be {expected_parent}, \ but found {found_parent}." - ) + ) } IrError::VerifyArgumentValueIsNotArgument(callee) => write!( f, @@ -160,9 +162,9 @@ impl fmt::Display for IrError { } IrError::VerifyCallArgTypeMismatch(callee, caller_ty, callee_ty) => { write!( - f, - "Verification failed: Type mismatch found for call to '{callee}': {caller_ty} is not a {callee_ty}." - ) + f, + "Verification failed: Type mismatch found for call to '{callee}': {caller_ty} is not a {callee_ty}." + ) } IrError::VerifyCallToMissingFunction(callee) => { write!( @@ -282,23 +284,23 @@ impl fmt::Display for IrError { IrError::VerifyEntryBlockHasPredecessors(function_name, predecessors) => { let plural_s = if predecessors.len() == 1 { "" } else { "s" }; write!( - f, - "Verification failed: Entry block of the function \"{function_name}\" has {}predecessor{}. \ + f, + "Verification failed: Entry block of the function \"{function_name}\" has {}predecessor{}. \ The predecessor{} {} {}.", - if predecessors.len() == 1 { - "a " - } else { - "" - }, - plural_s, - plural_s, - if predecessors.len() == 1 { - "is" - } else { - "are" - }, - predecessors.iter().map(|block_label| format!("\"{block_label}\"")).collect_vec().join(", ") - ) + if predecessors.len() == 1 { + "a " + } else { + "" + }, + plural_s, + plural_s, + if predecessors.len() == 1 { + "is" + } else { + "are" + }, + predecessors.iter().map(|block_label| format!("\"{block_label}\"")).collect_vec().join(", ") + ) } IrError::VerifyBlockArgMalformed => { write!(f, "Verification failed: Block argument is malformed") @@ -420,6 +422,20 @@ impl fmt::Display for IrError { "Verification failed: smo coins value must be an integer." ) } + IrError::VerifyGlobalMissingInitializer(global_name) => { + write!( + f, + "Verification failed: Immutable global variable {global_name}\ + is missing an initializer." + ) + } + IrError::VerifyLocalMissingInitializer(local_name, func_name) => { + write!( + f, + "Verification failed: Immutable local variable {local_name} in function \ + {func_name} is missing an initializer." + ) + } } } } diff --git a/sway-ir/src/optimize/dce.rs b/sway-ir/src/optimize/dce.rs index 92707843fec..21fd6eb152e 100644 --- a/sway-ir/src/optimize/dce.rs +++ b/sway-ir/src/optimize/dce.rs @@ -28,12 +28,12 @@ pub fn create_dce_pass() -> Pass { } } -pub const FN_DCE_NAME: &str = "fn-dce"; +pub const GLOBALS_DCE_NAME: &str = "globals-dce"; -pub fn create_fn_dce_pass() -> Pass { +pub fn create_globals_dce_pass() -> Pass { Pass { - name: FN_DCE_NAME, - descr: "Dead function elimination", + name: GLOBALS_DCE_NAME, + descr: "Dead globals (functions and variables) elimination", deps: vec![], runner: ScopedPass::ModulePass(PassMutability::Transform(globals_dce)), } @@ -374,14 +374,11 @@ pub fn globals_dce( } } - // entry fns and fallback - let entry_fns = module + // expand all called fns + for entry_fn in module .function_iter(context) .filter(|func| func.is_entry(context) || func.is_fallback(context)) - .collect::>(); - - // expand all called fns - for entry_fn in entry_fns { + { grow_called_function_used_globals_set( context, entry_fn, @@ -390,23 +387,26 @@ pub fn globals_dce( ); } + let mut modified = false; + + // Remove dead globals + let m = &mut context.modules[module.0]; + let cur_num_globals = m.global_variables.len(); + m.global_variables.retain(|_, g| used_globals.contains(g)); + modified |= cur_num_globals != m.global_variables.len(); + // Gather the functions in the module which aren't called. It's better to collect them // separately first so as to avoid any issues with invalidating the function iterator. let dead_fns = module .function_iter(context) .filter(|f| !called_fns.contains(f)) .collect::>(); - - let m = &mut context.modules[module.0]; - let cur_num_globals = m.global_variables.len(); - m.global_variables.retain(|_, g| used_globals.contains(g)); - - let mut modified = !dead_fns.is_empty(); - modified |= cur_num_globals != m.global_variables.len(); - for dead_fn in dead_fns { - module.remove_function(context, &dead_fn); + for dead_fn in &dead_fns { + module.remove_function(context, dead_fn); } + modified |= !dead_fns.is_empty(); + Ok(modified) } diff --git a/sway-ir/src/optimize/mem2reg.rs b/sway-ir/src/optimize/mem2reg.rs index 3a53fdf37d9..e22cf33eeef 100644 --- a/sway-ir/src/optimize/mem2reg.rs +++ b/sway-ir/src/optimize/mem2reg.rs @@ -19,7 +19,7 @@ pub const MEM2REG_NAME: &str = "mem2reg"; pub fn create_mem2reg_pass() -> Pass { Pass { name: MEM2REG_NAME, - descr: "Promotion of local / global memory to SSA registers", + descr: "Promotion of memory to SSA registers", deps: vec![POSTORDER_NAME, DOMINATORS_NAME, DOM_FRONTS_NAME], runner: ScopedPass::FunctionPass(PassMutability::Transform(promote_to_registers)), } @@ -151,7 +151,8 @@ pub fn compute_livein( result } -// Promote loads of globals constants to SSA registers +/// Promote loads of globals constants to SSA registers +/// We promote only non-mutable globals of non-copy types fn promote_globals(context: &mut Context, function: &Function) -> Result { let mut replacements = FxHashMap::::default(); for (_, inst) in function.instruction_iter(context) { @@ -170,7 +171,7 @@ fn promote_globals(context: &mut Context, function: &Function) -> Result Result Result { - let promoted_globals = promote_globals(context, &function)?; + let mut modified = false; + modified |= promote_globals(context, &function)?; + modified |= promote_locals(context, analyses, function)?; + Ok(modified) +} +/// Promote locals to registers. We promote only locals of copy type, +/// whose every use is in a `get_local` without offsets, and the result of +/// such a `get_local` is used only in a load or a store. +pub fn promote_locals( + context: &mut Context, + analyses: &AnalysisResults, + function: Function, +) -> Result { let safe_locals = filter_usable_locals(context, &function); if safe_locals.is_empty() { - return Ok(promoted_globals); + return Ok(false); } let po: &PostOrder = analyses.get_analysis_result(function); diff --git a/sway-ir/src/parser.rs b/sway-ir/src/parser.rs index fbf471e61c3..68a489cada0 100644 --- a/sway-ir/src/parser.rs +++ b/sway-ir/src/parser.rs @@ -72,11 +72,12 @@ mod ir_builder { } rule global_var() -> IrAstGlobalVar - = "global" _ name:path() _ ":" _ ty:ast_ty() _ "=" _ init:op_const() { + = m:("mut" _)? _ "global" _ name:path() _ ":" _ ty:ast_ty() _ "=" _ init:(op_const())? { IrAstGlobalVar { name, ty, - init + init, + mutable: m.is_some(), } } @@ -723,7 +724,8 @@ mod ir_builder { pub(super) struct IrAstGlobalVar { name: Vec, ty: IrAstTy, - init: IrAstOperation, + init: Option, + mutable: bool, } #[derive(Debug)] @@ -1556,11 +1558,11 @@ mod ir_builder { .into_iter() .map(|global_var_node| { let ty = global_var_node.ty.to_ir_type(context); - let init = match global_var_node.init { + let init = global_var_node.init.map(|init| match init { IrAstOperation::Const(ty, val) => val.value.as_constant(context, ty), _ => unreachable!("Global const initializer must be a const value."), - }; - let global_var = GlobalVar::new(context, ty, Some(init), false); + }); + let global_var = GlobalVar::new(context, ty, init, global_var_node.mutable); module.add_global_variable(context, global_var_node.name.clone(), global_var); (global_var_node.name, global_var) }) diff --git a/sway-ir/src/pass_manager.rs b/sway-ir/src/pass_manager.rs index b2b830a61d4..0bcdd218f58 100644 --- a/sway-ir/src/pass_manager.rs +++ b/sway-ir/src/pass_manager.rs @@ -1,14 +1,14 @@ use crate::{ create_arg_demotion_pass, create_ccp_pass, create_const_demotion_pass, create_const_folding_pass, create_cse_pass, create_dce_pass, create_dom_fronts_pass, - create_dominators_pass, create_escaped_symbols_pass, create_fn_dce_pass, - create_fn_dedup_debug_profile_pass, create_fn_dedup_release_profile_pass, - create_fn_inline_pass, create_mem2reg_pass, create_memcpyopt_pass, create_misc_demotion_pass, + create_dominators_pass, create_escaped_symbols_pass, create_fn_dedup_debug_profile_pass, + create_fn_dedup_release_profile_pass, create_fn_inline_pass, create_globals_dce_pass, + create_mem2reg_pass, create_memcpyopt_pass, create_misc_demotion_pass, create_module_printer_pass, create_module_verifier_pass, create_postorder_pass, create_ret_demotion_pass, create_simplify_cfg_pass, create_sroa_pass, Context, Function, IrError, Module, ARG_DEMOTION_NAME, CCP_NAME, CONST_DEMOTION_NAME, CONST_FOLDING_NAME, - CSE_NAME, DCE_NAME, FN_DCE_NAME, FN_DEDUP_DEBUG_PROFILE_NAME, FN_DEDUP_RELEASE_PROFILE_NAME, - FN_INLINE_NAME, MEM2REG_NAME, MEMCPYOPT_NAME, MISC_DEMOTION_NAME, RET_DEMOTION_NAME, + CSE_NAME, DCE_NAME, FN_DEDUP_DEBUG_PROFILE_NAME, FN_DEDUP_RELEASE_PROFILE_NAME, FN_INLINE_NAME, + GLOBALS_DCE_NAME, MEM2REG_NAME, MEMCPYOPT_NAME, MISC_DEMOTION_NAME, RET_DEMOTION_NAME, SIMPLIFY_CFG_NAME, SROA_NAME, }; use downcast_rs::{impl_downcast, Downcast}; @@ -169,7 +169,7 @@ impl PassManager { SIMPLIFY_CFG_NAME, SROA_NAME, DCE_NAME, - FN_DCE_NAME, + GLOBALS_DCE_NAME, FN_DEDUP_RELEASE_PROFILE_NAME, FN_DEDUP_DEBUG_PROFILE_NAME, MEM2REG_NAME, @@ -399,7 +399,7 @@ pub fn register_known_passes(pm: &mut PassManager) { pm.register(create_const_folding_pass()); pm.register(create_ccp_pass()); pm.register(create_simplify_cfg_pass()); - pm.register(create_fn_dce_pass()); + pm.register(create_globals_dce_pass()); pm.register(create_dce_pass()); pm.register(create_cse_pass()); pm.register(create_arg_demotion_pass()); @@ -417,7 +417,7 @@ pub fn create_o1_pass_group() -> PassGroup { o1.append_pass(FN_DEDUP_RELEASE_PROFILE_NAME); o1.append_pass(FN_INLINE_NAME); o1.append_pass(SIMPLIFY_CFG_NAME); - o1.append_pass(FN_DCE_NAME); + o1.append_pass(GLOBALS_DCE_NAME); o1.append_pass(DCE_NAME); o1.append_pass(FN_INLINE_NAME); o1.append_pass(CCP_NAME); @@ -426,7 +426,7 @@ pub fn create_o1_pass_group() -> PassGroup { o1.append_pass(CSE_NAME); o1.append_pass(CONST_FOLDING_NAME); o1.append_pass(SIMPLIFY_CFG_NAME); - o1.append_pass(FN_DCE_NAME); + o1.append_pass(GLOBALS_DCE_NAME); o1.append_pass(DCE_NAME); o1.append_pass(FN_DEDUP_RELEASE_PROFILE_NAME); diff --git a/sway-ir/src/printer.rs b/sway-ir/src/printer.rs index f615114a885..53f421f3afe 100644 --- a/sway-ir/src/printer.rs +++ b/sway-ir/src/printer.rs @@ -229,9 +229,11 @@ fn module_to_doc<'a>( )), None => Doc::Empty, }; + let mut_string = if var_content.mutable { "mut " } else { "" }; Doc::line( Doc::text(format!( - "global {} : {}", + "{}global {} : {}", + mut_string, name.join("::"), var.get_inner_type(context).as_string(context), )) diff --git a/sway-ir/src/verify.rs b/sway-ir/src/verify.rs index 88a506f0812..8ba7103c0b0 100644 --- a/sway-ir/src/verify.rs +++ b/sway-ir/src/verify.rs @@ -58,6 +58,16 @@ impl Context<'_> { for function in module.function_iter(self) { self.verify_function(module, function)?; } + + // Check that globals have initializers if they are not mutable. + for global in &self.modules[module.0].global_variables { + if !global.1.is_mutable(self) && global.1.get_initializer(self).is_none() { + let global_name = module.lookup_global_variable_name(self, global.1); + return Err(IrError::VerifyGlobalMissingInitializer( + global_name.unwrap_or_else(|| "".to_owned()), + )); + } + } Ok(()) } @@ -92,6 +102,16 @@ impl Context<'_> { } } + // Check that locals have initializers if they aren't mutable. + for local in &self.functions[function.0].local_storage { + if !local.1.is_mutable(self) && local.1.get_initializer(self).is_none() { + return Err(IrError::VerifyLocalMissingInitializer( + local.0.to_string(), + function.get_name(self).to_string(), + )); + } + } + for block in function.block_iter(self) { self.verify_block(cur_module, function, block)?; } diff --git a/sway-ir/tests/tests.rs b/sway-ir/tests/tests.rs index 5e6ccaeaa8f..16e10589b7b 100644 --- a/sway-ir/tests/tests.rs +++ b/sway-ir/tests/tests.rs @@ -9,7 +9,7 @@ use sway_ir::{ create_memcpyopt_pass, create_misc_demotion_pass, create_postorder_pass, create_ret_demotion_pass, create_simplify_cfg_pass, metadata_to_inline, optimize as opt, register_known_passes, Context, Function, IrError, PassGroup, PassManager, Value, DCE_NAME, - FN_DCE_NAME, FN_DEDUP_DEBUG_PROFILE_NAME, FN_DEDUP_RELEASE_PROFILE_NAME, MEM2REG_NAME, + FN_DEDUP_DEBUG_PROFILE_NAME, FN_DEDUP_RELEASE_PROFILE_NAME, GLOBALS_DCE_NAME, MEM2REG_NAME, SROA_NAME, }; use sway_types::SourceEngine; @@ -408,7 +408,7 @@ fn fndedup_debug() { let mut pass_group = PassGroup::default(); register_known_passes(&mut pass_mgr); pass_group.append_pass(FN_DEDUP_DEBUG_PROFILE_NAME); - pass_group.append_pass(FN_DCE_NAME); + pass_group.append_pass(GLOBALS_DCE_NAME); pass_mgr.run(ir, &pass_group).unwrap() }) } @@ -421,7 +421,7 @@ fn fndedup_release() { let mut pass_group = PassGroup::default(); register_known_passes(&mut pass_mgr); pass_group.append_pass(FN_DEDUP_RELEASE_PROFILE_NAME); - pass_group.append_pass(FN_DCE_NAME); + pass_group.append_pass(GLOBALS_DCE_NAME); pass_mgr.run(ir, &pass_group).unwrap() }) } From 8dedba4014c653ceb07d3285381917f9c2a09b5c Mon Sep 17 00:00:00 2001 From: Vaivaswatha Nagaraj Date: Sat, 1 Mar 2025 11:40:46 +0530 Subject: [PATCH 16/21] disable immutable-initializer check for locals --- sway-ir/src/verify.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/sway-ir/src/verify.rs b/sway-ir/src/verify.rs index 8ba7103c0b0..c23999ce933 100644 --- a/sway-ir/src/verify.rs +++ b/sway-ir/src/verify.rs @@ -103,14 +103,16 @@ impl Context<'_> { } // Check that locals have initializers if they aren't mutable. - for local in &self.functions[function.0].local_storage { - if !local.1.is_mutable(self) && local.1.get_initializer(self).is_none() { - return Err(IrError::VerifyLocalMissingInitializer( - local.0.to_string(), - function.get_name(self).to_string(), - )); - } - } + // TODO: This check is disabled because we incorrect create + // immutable locals without initializers at many places. + // for local in &self.functions[function.0].local_storage { + // if !local.1.is_mutable(self) && local.1.get_initializer(self).is_none() { + // return Err(IrError::VerifyLocalMissingInitializer( + // local.0.to_string(), + // function.get_name(self).to_string(), + // )); + // } + // } for block in function.block_iter(self) { self.verify_block(cur_module, function, block)?; From 94dec149186843dded1735197fd0edfcbd21711f Mon Sep 17 00:00:00 2001 From: Vaivaswatha Nagaraj Date: Sat, 1 Mar 2025 12:47:18 +0530 Subject: [PATCH 17/21] add unit tests for mem2reg and dce for globals --- sway-ir/tests/globals_dce/dce_dead_global.ir | 14 ++++++++++++++ sway-ir/tests/mem2reg/global_simple.ir | 14 ++++++++++++++ sway-ir/tests/tests.rs | 14 ++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 sway-ir/tests/globals_dce/dce_dead_global.ir create mode 100644 sway-ir/tests/mem2reg/global_simple.ir diff --git a/sway-ir/tests/globals_dce/dce_dead_global.ir b/sway-ir/tests/globals_dce/dce_dead_global.ir new file mode 100644 index 00000000000..7e9625989ed --- /dev/null +++ b/sway-ir/tests/globals_dce/dce_dead_global.ir @@ -0,0 +1,14 @@ +contract { + global X : u64 = const u64 11 + + pub entry fn main() -> u64 { + local u64 x + + entry(): + v0 = get_local u64, x + v1 = load v0 + ret u64 v1 + } +} + +// not: global X diff --git a/sway-ir/tests/mem2reg/global_simple.ir b/sway-ir/tests/mem2reg/global_simple.ir new file mode 100644 index 00000000000..f0f76671d8a --- /dev/null +++ b/sway-ir/tests/mem2reg/global_simple.ir @@ -0,0 +1,14 @@ +// regex: VAR=v\d+ +// regex: ID=[[:alpha:]0-9_]+ + +script { + global sway3::F1 : u64 = const u64 11 + pub entry fn main_0() -> u64, !15 { + entry(): + v0 = get_global ptr u64, sway3::F1, !16 + v1 = load v0 + // check: $(res=$VAR) = const u64 11 + // check ret u64 $res + ret u64 v1 + } +} \ No newline at end of file diff --git a/sway-ir/tests/tests.rs b/sway-ir/tests/tests.rs index 16e10589b7b..7b8ef859dd8 100644 --- a/sway-ir/tests/tests.rs +++ b/sway-ir/tests/tests.rs @@ -400,6 +400,20 @@ fn sroa() { // ------------------------------------------------------------------------------------------------- +#[allow(clippy::needless_collect)] +#[test] +fn globals_dce() { + run_tests("globals_dce", |_first_line, ir: &mut Context| { + let mut pass_mgr = PassManager::default(); + let mut pass_group = PassGroup::default(); + register_known_passes(&mut pass_mgr); + pass_group.append_pass(GLOBALS_DCE_NAME); + pass_mgr.run(ir, &pass_group).unwrap() + }) +} + +// ------------------------------------------------------------------------------------------------- + #[allow(clippy::needless_collect)] #[test] fn fndedup_debug() { From 4d3f865a19ed3e3ae8e4c1a5212adad2e42884cb Mon Sep 17 00:00:00 2001 From: Vaivaswatha Nagaraj Date: Sat, 1 Mar 2025 19:50:26 +0530 Subject: [PATCH 18/21] Update test as per review comments --- .../references/addr_of_consts/src/main.sw | 40 -------- .../Forc.lock | 12 +-- .../Forc.toml | 2 +- .../referencing_global_constants/src/lib.sw | 92 +++++++++++++++++++ .../referencing_global_constants/src/main.sw | 47 ++++++++++ .../test.toml | 0 6 files changed, 146 insertions(+), 47 deletions(-) delete mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/references/addr_of_consts/src/main.sw rename test/src/e2e_vm_tests/test_programs/should_pass/language/references/{addr_of_consts => referencing_global_constants}/Forc.lock (51%) rename test/src/e2e_vm_tests/test_programs/should_pass/language/references/{addr_of_consts => referencing_global_constants}/Forc.toml (82%) create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_global_constants/src/lib.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_global_constants/src/main.sw rename test/src/e2e_vm_tests/test_programs/should_pass/language/references/{addr_of_consts => referencing_global_constants}/test.toml (100%) diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/addr_of_consts/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/addr_of_consts/src/main.sw deleted file mode 100644 index 3d83fae5edc..00000000000 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/addr_of_consts/src/main.sw +++ /dev/null @@ -1,40 +0,0 @@ -script; - -struct S1 { - x: u64, - y: u64, -} - -const TEST: S1 = S1 { x: 101, y: 111 }; - -fn reference_to_int(ptr: &u64) -> u64 { - asm(ptr:ptr) { - ptr: u64 - } -} - -#[inline(never)] -fn get_addr_x() -> u64 { - let x_addr = &TEST.x; - reference_to_int(x_addr) -} - -#[inline(never)] -fn get_addr_y() -> u64 { - let y_addr = &TEST.y; - reference_to_int(y_addr) -} - -#[inline(never)] -fn sum_x_y_addresses() -> u64 { - get_addr_x() + get_addr_y() -} - -fn main() -> u64 { - 0 -} - -#[test] -fn test_x_y_addr() { - assert(get_addr_x() + get_addr_y() == sum_x_y_addresses()); -} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/addr_of_consts/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_global_constants/Forc.lock similarity index 51% rename from test/src/e2e_vm_tests/test_programs/should_pass/language/references/addr_of_consts/Forc.lock rename to test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_global_constants/Forc.lock index 2d5dc5f57d1..00735550dc1 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/addr_of_consts/Forc.lock +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_global_constants/Forc.lock @@ -1,13 +1,13 @@ [[package]] -name = "addr_of_consts" -source = "member" -dependencies = ["std"] +name = "core" +source = "path+from-root-1B1094E02C4E7AA3" [[package]] -name = "core" -source = "path+from-root-EAA0F05534A380BD" +name = "referencing_global_constants" +source = "member" +dependencies = ["std"] [[package]] name = "std" -source = "path+from-root-EAA0F05534A380BD" +source = "path+from-root-1B1094E02C4E7AA3" dependencies = ["core"] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/addr_of_consts/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_global_constants/Forc.toml similarity index 82% rename from test/src/e2e_vm_tests/test_programs/should_pass/language/references/addr_of_consts/Forc.toml rename to test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_global_constants/Forc.toml index 3c35b59e4fb..9eb24132f21 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/addr_of_consts/Forc.toml +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_global_constants/Forc.toml @@ -2,7 +2,7 @@ authors = ["Fuel Labs "] entry = "main.sw" license = "Apache-2.0" -name = "addr_of_consts" +name = "referencing_global_constants" [dependencies] std = { path = "../../../../../reduced_std_libs/sway-lib-std-assert" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_global_constants/src/lib.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_global_constants/src/lib.sw new file mode 100644 index 00000000000..829480ee808 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_global_constants/src/lib.sw @@ -0,0 +1,92 @@ +library; + +pub struct S1 { + pub x: u64, + pub y: u64, +} + +pub const TEST: S1 = S1 { x: 101, y: 111 }; +pub const TEST_U64: u64 = 202; +pub const TEST_BOOL: bool = true; +pub const TEST_ARRAY: [u64; 3] = [1, 2, 3]; +pub const TEST_TUPLE: (u64, bool, [u64; 3]) = (303, false, [4, 5, 6]); + +#[inline(never)] +pub fn get_addr_tuple() -> u64 { + let tuple_addr = &TEST_TUPLE; + reference_to_int(tuple_addr) +} + +#[test] +fn test_tuple_addr() { + assert(get_addr_tuple() == reference_to_int(&TEST_TUPLE)); +} +#[inline(never)] +pub fn get_addr_u64() -> u64 { + let u64_addr = &TEST_U64; + reference_to_int(u64_addr) +} + +#[inline(never)] +pub fn get_addr_bool() -> u64 { + let bool_addr = &TEST_BOOL; + reference_to_int(bool_addr) +} + +#[inline(never)] +pub fn get_addr_array() -> u64 { + let array_addr = &TEST_ARRAY; + reference_to_int(array_addr) +} + +#[test] +fn test_u64_addr() { + assert(get_addr_u64() == reference_to_int(&TEST_U64)); +} + +#[test] +fn test_bool_addr() { + assert(get_addr_bool() == reference_to_int(&TEST_BOOL)); +} + +#[test] +fn test_array_addr() { + assert(get_addr_array() == reference_to_int(&TEST_ARRAY)); +} +impl S1 { + const ASSOCIATED_CONST: u64 = 123; +} + +pub fn reference_to_int(ptr: &T) -> u64 { + asm(ptr:ptr) { + ptr: u64 + } +} + +#[inline(never)] +pub fn get_addr_x() -> u64 { + let x_addr = &TEST.x; + reference_to_int(x_addr) +} + +#[inline(never)] +pub fn get_addr_y() -> u64 { + let y_addr = &TEST.y; + reference_to_int(y_addr) +} + +#[inline(never)] +pub fn sum_x_y_addresses() -> u64 { + get_addr_x() + get_addr_y() +} + +#[inline(never)] +pub fn get_associated_const() -> u64 { + let assoc_const = &S1::ASSOCIATED_CONST; + reference_to_int(assoc_const) +} + +#[test] +fn test_x_y_addr() { + assert(get_addr_x() + get_addr_y() == sum_x_y_addresses()); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_global_constants/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_global_constants/src/main.sw new file mode 100644 index 00000000000..ccf764d8ff3 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_global_constants/src/main.sw @@ -0,0 +1,47 @@ +script; + +mod lib; + +#[inline(never)] +fn get_addr_x() -> u64 { + let x_addr = &lib::TEST.x; + lib::reference_to_int(x_addr) +} + +#[inline(never)] +fn get_addr_y() -> u64 { + let y_addr = &lib::TEST.y; + lib::reference_to_int(y_addr) +} + +#[inline(never)] +fn sum_x_y_addresses() -> u64 { + get_addr_x() + get_addr_y() +} + +fn main() -> u64 { + 0 +} + +#[test] +fn test_x_y_addr() { + assert(get_addr_x() + get_addr_y() == sum_x_y_addresses()); +} + +#[test] +fn test_across_modules() { + assert(lib::get_addr_x() == get_addr_x()); + assert(lib::get_addr_y() == get_addr_y()); + assert(lib::get_addr_x() + lib::get_addr_y() == sum_x_y_addresses()); + + assert(lib::get_addr_u64() == lib::reference_to_int(&lib::TEST_U64)); + assert(lib::get_addr_bool() == lib::reference_to_int(&lib::TEST_BOOL)); + assert(lib::get_addr_array() == lib::reference_to_int(&lib::TEST_ARRAY)); + assert(lib::get_addr_tuple() == lib::reference_to_int(&lib::TEST_TUPLE)); +} + +#[test] +fn test_associated_const() { + // TODO: Associated consts don't work right yet. + // assert(lib::get_associated_const() == lib::reference_to_int(&lib::S1::ASSOCIATED_CONST)); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/references/addr_of_consts/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_global_constants/test.toml similarity index 100% rename from test/src/e2e_vm_tests/test_programs/should_pass/language/references/addr_of_consts/test.toml rename to test/src/e2e_vm_tests/test_programs/should_pass/language/references/referencing_global_constants/test.toml From 14b4adae50e5869078330bdf3fbc32c38c122a91 Mon Sep 17 00:00:00 2001 From: Vaivaswatha Nagaraj Date: Sat, 1 Mar 2025 20:27:39 +0530 Subject: [PATCH 19/21] typo fix to please CI --- sway-core/src/ir_generation/const_eval.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sway-core/src/ir_generation/const_eval.rs b/sway-core/src/ir_generation/const_eval.rs index 795054d9a4f..93b8532f419 100644 --- a/sway-core/src/ir_generation/const_eval.rs +++ b/sway-core/src/ir_generation/const_eval.rs @@ -262,13 +262,13 @@ pub(crate) fn compile_constant_expression_to_constant( fn create_array_from_vec( lookup: &mut LookupEnv, elem_type: crate::TypeId, - element_typs: Vec, + element_tys: Vec, element_vals: Vec, ) -> Option { let te = lookup.engines.te(); assert!({ let unify_check = UnifyCheck::coercion(lookup.engines); - element_typs + element_tys .iter() .all(|tid| unify_check.check(*tid, elem_type)) }); @@ -278,7 +278,7 @@ fn create_array_from_vec( lookup.engines.de(), lookup.context, elem_type, - element_typs.len().try_into().unwrap(), + element_tys.len().try_into().unwrap(), ) .map_or(None, |array_ty| { Some(ConstantContent::new_array( From 39561883f59815acceb3d34eec319d479baf589f Mon Sep 17 00:00:00 2001 From: Vaivaswatha Nagaraj Date: Sat, 1 Mar 2025 20:30:24 +0530 Subject: [PATCH 20/21] more typo fix --- sway-core/src/ir_generation/const_eval.rs | 30 +++++++++++------------ 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/sway-core/src/ir_generation/const_eval.rs b/sway-core/src/ir_generation/const_eval.rs index 93b8532f419..e0ec0e6d465 100644 --- a/sway-core/src/ir_generation/const_eval.rs +++ b/sway-core/src/ir_generation/const_eval.rs @@ -396,13 +396,13 @@ fn const_eval_typed_expr( instantiation_span, .. } => { - let (mut field_typs, mut field_vals): (Vec<_>, Vec<_>) = (vec![], vec![]); + let (mut field_tys, mut field_vals): (Vec<_>, Vec<_>) = (vec![], vec![]); for field in fields { let ty::TyStructExpressionField { name: _, value, .. } = field; let eval_expr_opt = const_eval_typed_expr(lookup, known_consts, value)?; if let Some(cv) = eval_expr_opt { - field_typs.push(value.return_type); + field_tys.push(value.return_type); field_vals.push(cv); } else { return Err(ConstEvalError::CannotBeEvaluatedToConst { @@ -411,14 +411,14 @@ fn const_eval_typed_expr( } } - assert!(field_typs.len() == fields.len()); + assert!(field_tys.len() == fields.len()); assert!(field_vals.len() == fields.len()); get_struct_for_types( lookup.engines.te(), lookup.engines.de(), lookup.context, - &field_typs, + &field_tys, ) .map_or(None, |struct_ty| { let c = ConstantContent::new_struct( @@ -434,12 +434,12 @@ fn const_eval_typed_expr( }) } ty::TyExpressionVariant::Tuple { fields } => { - let (mut field_typs, mut field_vals): (Vec<_>, Vec<_>) = (vec![], vec![]); + let (mut field_tys, mut field_vals): (Vec<_>, Vec<_>) = (vec![], vec![]); for value in fields { let eval_expr_opt = const_eval_typed_expr(lookup, known_consts, value)?; if let Some(cv) = eval_expr_opt { - field_typs.push(value.return_type); + field_tys.push(value.return_type); field_vals.push(cv); } else { return Err(ConstEvalError::CannotBeEvaluatedToConst { @@ -448,14 +448,14 @@ fn const_eval_typed_expr( } } - assert!(field_typs.len() == fields.len()); + assert!(field_tys.len() == fields.len()); assert!(field_vals.len() == fields.len()); create_tuple_aggregate( lookup.engines.te(), lookup.engines.de(), lookup.context, - &field_typs, + &field_tys, ) .map_or(None, |tuple_ty| { let c = ConstantContent::new_struct( @@ -474,12 +474,12 @@ fn const_eval_typed_expr( elem_type, contents, } => { - let (mut element_typs, mut element_vals): (Vec<_>, Vec<_>) = (vec![], vec![]); + let (mut element_tys, mut element_vals): (Vec<_>, Vec<_>) = (vec![], vec![]); for value in contents { let eval_expr_opt = const_eval_typed_expr(lookup, known_consts, value)?; if let Some(cv) = eval_expr_opt { - element_typs.push(value.return_type); + element_tys.push(value.return_type); element_vals.push(cv); } else { return Err(ConstEvalError::CannotBeEvaluatedToConst { @@ -488,10 +488,10 @@ fn const_eval_typed_expr( } } - assert!(element_typs.len() == contents.len()); + assert!(element_tys.len() == contents.len()); assert!(element_vals.len() == contents.len()); - create_array_from_vec(lookup, *elem_type, element_typs, element_vals) + create_array_from_vec(lookup, *elem_type, element_tys, element_vals) } ty::TyExpressionVariant::ArrayRepeat { elem_type, @@ -505,12 +505,12 @@ fn const_eval_typed_expr( .as_uint() .unwrap() as usize; let element_vals = (0..length).map(|_| constant).collect::>(); - let element_typs = (0..length).map(|_| value.return_type).collect::>(); + let element_tys = (0..length).map(|_| value.return_type).collect::>(); - assert!(element_typs.len() == length); + assert!(element_tys.len() == length); assert!(element_vals.len() == length); - create_array_from_vec(lookup, *elem_type, element_typs, element_vals) + create_array_from_vec(lookup, *elem_type, element_tys, element_vals) } ty::TyExpressionVariant::EnumInstantiation { enum_ref, From d13cfd8eec54a229b0edc4c091f1df65a753255d Mon Sep 17 00:00:00 2001 From: Vaivaswatha Nagaraj Date: Mon, 3 Mar 2025 17:27:51 +0530 Subject: [PATCH 21/21] fixes related to review comments --- sway-ir/src/optimize/mem2reg.rs | 4 ++-- sway-ir/src/parser.rs | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/sway-ir/src/optimize/mem2reg.rs b/sway-ir/src/optimize/mem2reg.rs index e22cf33eeef..06df23a281c 100644 --- a/sway-ir/src/optimize/mem2reg.rs +++ b/sway-ir/src/optimize/mem2reg.rs @@ -152,7 +152,7 @@ pub fn compute_livein( } /// Promote loads of globals constants to SSA registers -/// We promote only non-mutable globals of non-copy types +/// We promote only non-mutable globals of copy types fn promote_globals(context: &mut Context, function: &Function) -> Result { let mut replacements = FxHashMap::::default(); for (_, inst) in function.instruction_iter(context) { @@ -204,7 +204,7 @@ pub fn promote_to_registers( Ok(modified) } -/// Promote locals to registers. We promote only locals of copy type, +/// Promote locals to registers. We promote only locals of copy types, /// whose every use is in a `get_local` without offsets, and the result of /// such a `get_local` is used only in a load or a store. pub fn promote_locals( diff --git a/sway-ir/src/parser.rs b/sway-ir/src/parser.rs index 68a489cada0..614e3436c61 100644 --- a/sway-ir/src/parser.rs +++ b/sway-ir/src/parser.rs @@ -72,7 +72,7 @@ mod ir_builder { } rule global_var() -> IrAstGlobalVar - = m:("mut" _)? _ "global" _ name:path() _ ":" _ ty:ast_ty() _ "=" _ init:(op_const())? { + = "global" _ m:("mut" _)? name:path() _ ":" _ ty:ast_ty() init:global_init()? { IrAstGlobalVar { name, ty, @@ -81,6 +81,11 @@ mod ir_builder { } } + rule global_init() -> IrAstOperation + = "=" _ cv:op_const() { + cv + } + rule config_encoded_bytes() -> Vec = "0x" s:$(hex_digit()*) _ { hex_string_to_vec(s)