From c318607380b33a312b2e9842fe58300e418eeaaa Mon Sep 17 00:00:00 2001 From: Vaivaswatha Nagaraj Date: Thu, 6 Feb 2025 17:01:36 +0530 Subject: [PATCH 1/2] 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 2/2] 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) } };