diff --git a/compiler/noirc_evaluator/src/acir/mod.rs b/compiler/noirc_evaluator/src/acir/mod.rs index cfba6ccf3a6..fe46fbe72a1 100644 --- a/compiler/noirc_evaluator/src/acir/mod.rs +++ b/compiler/noirc_evaluator/src/acir/mod.rs @@ -1872,7 +1872,8 @@ impl<'a> Context<'a> { let acir_value = match value { Value::NumericConstant { constant, typ } => { - AcirValue::Var(self.acir_context.add_constant(*constant), typ.into()) + let typ = AcirType::from(Type::Numeric(*typ)); + AcirValue::Var(self.acir_context.add_constant(*constant), typ) } Value::Intrinsic(..) => todo!(), Value::Function(function_id) => { @@ -2902,7 +2903,12 @@ mod test { brillig::Brillig, ssa::{ function_builder::FunctionBuilder, - ir::{function::FunctionId, instruction::BinaryOp, map::Id, types::Type}, + ir::{ + function::FunctionId, + instruction::BinaryOp, + map::Id, + types::{NumericType, Type}, + }, }, }; @@ -2930,7 +2936,7 @@ mod test { let foo_v1 = builder.add_parameter(Type::field()); let foo_equality_check = builder.insert_binary(foo_v0, BinaryOp::Eq, foo_v1); - let zero = builder.numeric_constant(0u128, Type::unsigned(1)); + let zero = builder.numeric_constant(0u128, NumericType::unsigned(1)); builder.insert_constrain(foo_equality_check, zero, None); builder.terminate_with_return(vec![foo_v0]); } @@ -3374,7 +3380,7 @@ mod test { // Call the same primitive operation again let v1_div_v2 = builder.insert_binary(main_v1, BinaryOp::Div, main_v2); - let one = builder.numeric_constant(1u128, Type::unsigned(32)); + let one = builder.numeric_constant(1u128, NumericType::unsigned(32)); builder.insert_constrain(v1_div_v2, one, None); builder.terminate_with_return(vec![]); @@ -3447,7 +3453,7 @@ mod test { // Call the same primitive operation again let v1_div_v2 = builder.insert_binary(main_v1, BinaryOp::Div, main_v2); - let one = builder.numeric_constant(1u128, Type::unsigned(32)); + let one = builder.numeric_constant(1u128, NumericType::unsigned(32)); builder.insert_constrain(v1_div_v2, one, None); builder.terminate_with_return(vec![]); @@ -3533,7 +3539,7 @@ mod test { // Call the same primitive operation again let v1_div_v2 = builder.insert_binary(main_v1, BinaryOp::Div, main_v2); - let one = builder.numeric_constant(1u128, Type::unsigned(32)); + let one = builder.numeric_constant(1u128, NumericType::unsigned(32)); builder.insert_constrain(v1_div_v2, one, None); builder.terminate_with_return(vec![]); diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs index 7b4cdb2b8ce..56511127da8 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs @@ -226,16 +226,14 @@ impl<'block> BrilligBlock<'block> { dfg.get_numeric_constant_with_type(*rhs), ) { // If the constraint is of the form `x == u1 1` then we can simply constrain `x` directly - ( - Some((constant, Type::Numeric(NumericType::Unsigned { bit_size: 1 }))), - None, - ) if constant == FieldElement::one() => { + (Some((constant, NumericType::Unsigned { bit_size: 1 })), None) + if constant == FieldElement::one() => + { (self.convert_ssa_single_addr_value(*rhs, dfg), false) } - ( - None, - Some((constant, Type::Numeric(NumericType::Unsigned { bit_size: 1 }))), - ) if constant == FieldElement::one() => { + (None, Some((constant, NumericType::Unsigned { bit_size: 1 }))) + if constant == FieldElement::one() => + { (self.convert_ssa_single_addr_value(*lhs, dfg), false) } @@ -1285,8 +1283,8 @@ impl<'block> BrilligBlock<'block> { result_variable: SingleAddrVariable, ) { let binary_type = type_of_binary_operation( - dfg[binary.lhs].get_type(), - dfg[binary.rhs].get_type(), + dfg[binary.lhs].get_type().as_ref(), + dfg[binary.rhs].get_type().as_ref(), binary.operator, ); @@ -1795,7 +1793,7 @@ impl<'block> BrilligBlock<'block> { dfg: &DataFlowGraph, ) -> BrilligVariable { let typ = dfg[result].get_type(); - match typ { + match typ.as_ref() { Type::Numeric(_) => self.variables.define_variable( self.function_context, self.brillig_context, @@ -1811,7 +1809,7 @@ impl<'block> BrilligBlock<'block> { dfg, ); let array = variable.extract_array(); - self.allocate_foreign_call_result_array(typ, array); + self.allocate_foreign_call_result_array(typ.as_ref(), array); variable } diff --git a/compiler/noirc_evaluator/src/brillig/brillig_gen/variable_liveness.rs b/compiler/noirc_evaluator/src/brillig/brillig_gen/variable_liveness.rs index 87165c36dff..d6851a9ecf9 100644 --- a/compiler/noirc_evaluator/src/brillig/brillig_gen/variable_liveness.rs +++ b/compiler/noirc_evaluator/src/brillig/brillig_gen/variable_liveness.rs @@ -372,7 +372,7 @@ mod test { let v3 = builder.insert_allocate(Type::field()); - let zero = builder.numeric_constant(0u128, Type::field()); + let zero = builder.field_constant(0u128); builder.insert_store(v3, zero); let v4 = builder.insert_binary(v0, BinaryOp::Eq, zero); @@ -381,7 +381,7 @@ mod test { builder.switch_to_block(b2); - let twenty_seven = builder.numeric_constant(27u128, Type::field()); + let twenty_seven = builder.field_constant(27u128); let v7 = builder.insert_binary(v0, BinaryOp::Add, twenty_seven); builder.insert_store(v3, v7); @@ -487,7 +487,7 @@ mod test { let v3 = builder.insert_allocate(Type::field()); - let zero = builder.numeric_constant(0u128, Type::field()); + let zero = builder.field_constant(0u128); builder.insert_store(v3, zero); builder.terminate_with_jmp(b1, vec![zero]); @@ -515,7 +515,7 @@ mod test { builder.switch_to_block(b5); - let twenty_seven = builder.numeric_constant(27u128, Type::field()); + let twenty_seven = builder.field_constant(27u128); let v10 = builder.insert_binary(v7, BinaryOp::Eq, twenty_seven); let v11 = builder.insert_not(v10); @@ -534,7 +534,7 @@ mod test { builder.switch_to_block(b8); - let one = builder.numeric_constant(1u128, Type::field()); + let one = builder.field_constant(1u128); let v15 = builder.insert_binary(v7, BinaryOp::Add, one); builder.terminate_with_jmp(b4, vec![v15]); diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs b/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs index bd2585a3bfa..1d18683ee9e 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/data_bus.rs @@ -1,6 +1,10 @@ use std::{collections::BTreeMap, sync::Arc}; -use crate::ssa::ir::{function::RuntimeType, types::Type, value::ValueId}; +use crate::ssa::ir::{ + function::RuntimeType, + types::{NumericType, Type}, + value::ValueId, +}; use acvm::FieldElement; use fxhash::FxHashMap as HashMap; use noirc_frontend::ast; @@ -115,7 +119,7 @@ impl FunctionBuilder { /// Insert a value into a data bus builder fn add_to_data_bus(&mut self, value: ValueId, databus: &mut DataBusBuilder) { assert!(databus.databus.is_none(), "initializing finalized call data"); - let typ = self.current_function.dfg[value].get_type().clone(); + let typ = self.current_function.dfg[value].get_type().into_owned(); match typ { Type::Numeric(_) => { databus.values.push_back(value); @@ -128,10 +132,10 @@ impl FunctionBuilder { for _i in 0..len { for subitem_typ in typ.iter() { // load each element of the array, and add it to the databus - let index_var = self - .current_function - .dfg - .make_constant(FieldElement::from(index as i128), Type::length_type()); + let length_type = NumericType::length_type(); + let index_var = FieldElement::from(index as i128); + let index_var = + self.current_function.dfg.make_constant(index_var, length_type); let element = self.insert_array_get(value, index_var, subitem_typ.clone()); index += match subitem_typ { Type::Array(_, _) | Type::Slice(_) => subitem_typ.element_size(), diff --git a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs index 0ae61404442..80329ea4483 100644 --- a/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/function_builder/mod.rs @@ -21,6 +21,7 @@ use super::{ dfg::{CallStack, InsertInstructionResult}, function::RuntimeType, instruction::{ConstrainError, InstructionId, Intrinsic}, + types::NumericType, }, ssa_gen::Ssa, }; @@ -122,19 +123,19 @@ impl FunctionBuilder { pub(crate) fn numeric_constant( &mut self, value: impl Into, - typ: Type, + typ: NumericType, ) -> ValueId { self.current_function.dfg.make_constant(value.into(), typ) } /// Insert a numeric constant into the current function of type Field pub(crate) fn field_constant(&mut self, value: impl Into) -> ValueId { - self.numeric_constant(value.into(), Type::field()) + self.numeric_constant(value.into(), NumericType::NativeField) } /// Insert a numeric constant into the current function of type Type::length_type() pub(crate) fn length_constant(&mut self, value: impl Into) -> ValueId { - self.numeric_constant(value.into(), Type::length_type()) + self.numeric_constant(value.into(), NumericType::length_type()) } /// Returns the type of the given value. @@ -251,7 +252,7 @@ impl FunctionBuilder { /// Insert a cast instruction at the end of the current block. /// Returns the result of the cast instruction. - pub(crate) fn insert_cast(&mut self, value: ValueId, typ: Type) -> ValueId { + pub(crate) fn insert_cast(&mut self, value: ValueId, typ: NumericType) -> ValueId { self.insert_instruction(Instruction::Cast(value, typ), None).first() } @@ -526,7 +527,7 @@ mod tests { use crate::ssa::ir::{ instruction::{Endian, Intrinsic}, map::Id, - types::Type, + types::{NumericType, Type}, }; use super::FunctionBuilder; @@ -538,12 +539,12 @@ mod tests { // let bits: [u1; 8] = x.to_le_bits(); let func_id = Id::test_new(0); let mut builder = FunctionBuilder::new("func".into(), func_id); - let one = builder.numeric_constant(FieldElement::one(), Type::bool()); - let zero = builder.numeric_constant(FieldElement::zero(), Type::bool()); + let one = builder.numeric_constant(FieldElement::one(), NumericType::bool()); + let zero = builder.numeric_constant(FieldElement::zero(), NumericType::bool()); let to_bits_id = builder.import_intrinsic_id(Intrinsic::ToBits(Endian::Little)); - let input = builder.numeric_constant(FieldElement::from(7_u128), Type::field()); - let length = builder.numeric_constant(FieldElement::from(8_u128), Type::field()); + let input = builder.field_constant(FieldElement::from(7_u128)); + let length = builder.field_constant(FieldElement::from(8_u128)); let result_types = vec![Type::Array(Arc::new(vec![Type::bool()]), 8)]; let call_results = builder.insert_call(to_bits_id, vec![input, length], result_types).into_owned(); diff --git a/compiler/noirc_evaluator/src/ssa/ir/dfg.rs b/compiler/noirc_evaluator/src/ssa/ir/dfg.rs index 827944e22d1..94074eb3854 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/dfg.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/dfg.rs @@ -9,7 +9,7 @@ use super::{ Instruction, InstructionId, InstructionResultType, Intrinsic, TerminatorInstruction, }, map::DenseMap, - types::Type, + types::{NumericType, Type}, value::{Value, ValueId}, }; @@ -50,7 +50,7 @@ pub(crate) struct DataFlowGraph { /// Each constant is unique, attempting to insert the same constant /// twice will return the same ValueId. #[serde(skip)] - constants: HashMap<(FieldElement, Type), ValueId>, + constants: HashMap<(FieldElement, NumericType), ValueId>, /// Contains each function that has been imported into the current function. /// A unique `ValueId` for each function's [`Value::Function`] is stored so any given FunctionId @@ -119,7 +119,7 @@ impl DataFlowGraph { let parameters = self.blocks[block].parameters(); let parameters = vecmap(parameters.iter().enumerate(), |(position, param)| { - let typ = self.values[*param].get_type().clone(); + let typ = self.values[*param].get_type().into_owned(); self.values.insert(Value::Param { block: new_block, position, typ }) }); @@ -233,11 +233,12 @@ impl DataFlowGraph { pub(crate) fn set_type_of_value(&mut self, value_id: ValueId, target_type: Type) { let value = &mut self.values[value_id]; match value { - Value::Instruction { typ, .. } - | Value::Param { typ, .. } - | Value::NumericConstant { typ, .. } => { + Value::Instruction { typ, .. } | Value::Param { typ, .. } => { *typ = target_type; } + Value::NumericConstant { typ, .. } => { + *typ = target_type.unwrap_numeric(); + } _ => { unreachable!("ICE: Cannot set type of {:?}", value); } @@ -257,11 +258,11 @@ impl DataFlowGraph { /// Creates a new constant value, or returns the Id to an existing one if /// one already exists. - pub(crate) fn make_constant(&mut self, constant: FieldElement, typ: Type) -> ValueId { - if let Some(id) = self.constants.get(&(constant, typ.clone())) { + pub(crate) fn make_constant(&mut self, constant: FieldElement, typ: NumericType) -> ValueId { + if let Some(id) = self.constants.get(&(constant, typ)) { return *id; } - let id = self.values.insert(Value::NumericConstant { constant, typ: typ.clone() }); + let id = self.values.insert(Value::NumericConstant { constant, typ }); self.constants.insert((constant, typ), id); id } @@ -342,7 +343,7 @@ impl DataFlowGraph { /// Returns the type of a given value pub(crate) fn type_of_value(&self, value: ValueId) -> Type { - self.values[value].get_type().clone() + self.values[value].get_type().into_owned() } /// Returns the maximum possible number of bits that `value` can potentially be. @@ -367,7 +368,7 @@ impl DataFlowGraph { /// True if the type of this value is Type::Reference. /// Using this method over type_of_value avoids cloning the value's type. pub(crate) fn value_is_reference(&self, value: ValueId) -> bool { - matches!(self.values[value].get_type(), Type::Reference(_)) + matches!(self.values[value].get_type().as_ref(), Type::Reference(_)) } /// Replaces an instruction result with a fresh id. @@ -425,9 +426,9 @@ impl DataFlowGraph { pub(crate) fn get_numeric_constant_with_type( &self, value: ValueId, - ) -> Option<(FieldElement, Type)> { + ) -> Option<(FieldElement, NumericType)> { match &self.values[self.resolve(value)] { - Value::NumericConstant { constant, typ } => Some((*constant, typ.clone())), + Value::NumericConstant { constant, typ } => Some((*constant, *typ)), _ => None, } } diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs index ba212fdad96..0e9f6e31a09 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction.rs @@ -252,8 +252,8 @@ pub(crate) enum Instruction { /// Binary Operations like +, -, *, /, ==, != Binary(Binary), - /// Converts `Value` into Typ - Cast(ValueId, Type), + /// Converts `Value` into the given NumericType + Cast(ValueId, NumericType), /// Computes a bit wise not Not(ValueId), @@ -355,9 +355,8 @@ impl Instruction { pub(crate) fn result_type(&self) -> InstructionResultType { match self { Instruction::Binary(binary) => binary.result_type(), - Instruction::Cast(_, typ) | Instruction::MakeArray { typ, .. } => { - InstructionResultType::Known(typ.clone()) - } + Instruction::Cast(_, typ) => InstructionResultType::Known(Type::Numeric(*typ)), + Instruction::MakeArray { typ, .. } => InstructionResultType::Known(typ.clone()), Instruction::Not(value) | Instruction::Truncate { value, .. } | Instruction::ArraySet { array: value, .. } @@ -603,7 +602,7 @@ impl Instruction { rhs: f(binary.rhs), operator: binary.operator, }), - Instruction::Cast(value, typ) => Instruction::Cast(f(*value), typ.clone()), + Instruction::Cast(value, typ) => Instruction::Cast(f(*value), *typ), Instruction::Not(value) => Instruction::Not(f(*value)), Instruction::Truncate { value, bit_size, max_bit_size } => Instruction::Truncate { value: f(*value), @@ -751,7 +750,7 @@ impl Instruction { use SimplifyResult::*; match self { Instruction::Binary(binary) => binary.simplify(dfg), - Instruction::Cast(value, typ) => simplify_cast(*value, typ, dfg), + Instruction::Cast(value, typ) => simplify_cast(*value, *typ, dfg), Instruction::Not(value) => { match &dfg[dfg.resolve(*value)] { // Limit optimizing ! on constants to only booleans. If we tried it on fields, @@ -760,7 +759,7 @@ impl Instruction { Value::NumericConstant { constant, typ } if typ.is_unsigned() => { // As we're casting to a `u128`, we need to clear out any upper bits that the NOT fills. let value = !constant.to_u128() % (1 << typ.bit_size()); - SimplifiedTo(dfg.make_constant(value.into(), typ.clone())) + SimplifiedTo(dfg.make_constant(value.into(), *typ)) } Value::Instruction { instruction, .. } => { // !!v => v diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs index 487370488b9..0f52168a30d 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/binary.rs @@ -88,7 +88,7 @@ impl Binary { pub(super) fn simplify(&self, dfg: &mut DataFlowGraph) -> SimplifyResult { let lhs = dfg.get_numeric_constant(self.lhs); let rhs = dfg.get_numeric_constant(self.rhs); - let operand_type = dfg.type_of_value(self.lhs); + let operand_type = dfg.type_of_value(self.lhs).unwrap_numeric(); if let (Some(lhs), Some(rhs)) = (lhs, rhs) { return match eval_constant_binary_op(lhs, rhs, self.operator, operand_type) { @@ -168,11 +168,11 @@ impl Binary { } BinaryOp::Eq => { if dfg.resolve(self.lhs) == dfg.resolve(self.rhs) { - let one = dfg.make_constant(FieldElement::one(), Type::bool()); + let one = dfg.make_constant(FieldElement::one(), NumericType::bool()); return SimplifyResult::SimplifiedTo(one); } - if operand_type == Type::bool() { + if operand_type == NumericType::bool() { // Simplify forms of `(boolean == true)` into `boolean` if lhs_is_one { return SimplifyResult::SimplifiedTo(self.rhs); @@ -191,13 +191,13 @@ impl Binary { } BinaryOp::Lt => { if dfg.resolve(self.lhs) == dfg.resolve(self.rhs) { - let zero = dfg.make_constant(FieldElement::zero(), Type::bool()); + let zero = dfg.make_constant(FieldElement::zero(), NumericType::bool()); return SimplifyResult::SimplifiedTo(zero); } if operand_type.is_unsigned() { if rhs_is_zero { // Unsigned values cannot be less than zero. - let zero = dfg.make_constant(FieldElement::zero(), Type::bool()); + let zero = dfg.make_constant(FieldElement::zero(), NumericType::bool()); return SimplifyResult::SimplifiedTo(zero); } else if rhs_is_one { let zero = dfg.make_constant(FieldElement::zero(), operand_type); @@ -217,7 +217,7 @@ impl Binary { if dfg.resolve(self.lhs) == dfg.resolve(self.rhs) { return SimplifyResult::SimplifiedTo(self.lhs); } - if operand_type == Type::bool() { + if operand_type == NumericType::bool() { // Boolean AND is equivalent to multiplication, which is a cheaper operation. let instruction = Instruction::binary(BinaryOp::Mul, self.lhs, self.rhs); return SimplifyResult::SimplifiedToInstruction(instruction); @@ -294,10 +294,10 @@ fn eval_constant_binary_op( lhs: FieldElement, rhs: FieldElement, operator: BinaryOp, - mut operand_type: Type, -) -> Option<(FieldElement, Type)> { - let value = match &operand_type { - Type::Numeric(NumericType::NativeField) => { + mut operand_type: NumericType, +) -> Option<(FieldElement, NumericType)> { + let value = match operand_type { + NumericType::NativeField => { // If the rhs of a division is zero, attempting to evaluate the division will cause a compiler panic. // Thus, we do not evaluate the division in this method, as we want to avoid triggering a panic, // and the operation should be handled by ACIR generation. @@ -306,11 +306,11 @@ fn eval_constant_binary_op( } operator.get_field_function()?(lhs, rhs) } - Type::Numeric(NumericType::Unsigned { bit_size }) => { + NumericType::Unsigned { bit_size } => { let function = operator.get_u128_function(); - let lhs = truncate(lhs.try_into_u128()?, *bit_size); - let rhs = truncate(rhs.try_into_u128()?, *bit_size); + let lhs = truncate(lhs.try_into_u128()?, bit_size); + let rhs = truncate(rhs.try_into_u128()?, bit_size); // The divisor is being truncated into the type of the operand, which can potentially // lead to the rhs being zero. @@ -322,16 +322,16 @@ fn eval_constant_binary_op( } let result = function(lhs, rhs)?; // Check for overflow - if result >= 1 << *bit_size { + if result >= 1 << bit_size { return None; } result.into() } - Type::Numeric(NumericType::Signed { bit_size }) => { + NumericType::Signed { bit_size } => { let function = operator.get_i128_function(); - let lhs = try_convert_field_element_to_signed_integer(lhs, *bit_size)?; - let rhs = try_convert_field_element_to_signed_integer(rhs, *bit_size)?; + let lhs = try_convert_field_element_to_signed_integer(lhs, bit_size)?; + let rhs = try_convert_field_element_to_signed_integer(rhs, bit_size)?; // The divisor is being truncated into the type of the operand, which can potentially // lead to the rhs being zero. // If the rhs of a division is zero, attempting to evaluate the division will cause a compiler panic. @@ -343,17 +343,16 @@ fn eval_constant_binary_op( let result = function(lhs, rhs)?; // Check for overflow - let two_pow_bit_size_minus_one = 1i128 << (*bit_size - 1); + let two_pow_bit_size_minus_one = 1i128 << (bit_size - 1); if result >= two_pow_bit_size_minus_one || result < -two_pow_bit_size_minus_one { return None; } - convert_signed_integer_to_field_element(result, *bit_size) + convert_signed_integer_to_field_element(result, bit_size) } - _ => return None, }; if matches!(operator, BinaryOp::Eq | BinaryOp::Lt) { - operand_type = Type::bool(); + operand_type = NumericType::bool(); } Some((value, operand_type)) diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs index 40dd0bca41a..1daa1af7907 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/call.rs @@ -15,7 +15,7 @@ use crate::ssa::{ dfg::{CallStack, DataFlowGraph}, instruction::Intrinsic, map::Id, - types::Type, + types::{NumericType, Type}, value::{Value, ValueId}, }, opt::flatten_cfg::value_merger::ValueMerger, @@ -61,7 +61,13 @@ pub(super) fn simplify_call( unreachable!("ICE: Intrinsic::ToRadix return type must be array") }; constant_to_radix(endian, field, 2, limb_count, |values| { - make_constant_array(dfg, values.into_iter(), Type::bool(), block, call_stack) + make_constant_array( + dfg, + values.into_iter(), + NumericType::bool(), + block, + call_stack, + ) }) } else { SimplifyResult::None @@ -81,7 +87,7 @@ pub(super) fn simplify_call( make_constant_array( dfg, values.into_iter(), - Type::unsigned(8), + NumericType::Unsigned { bit_size: 8 }, block, call_stack, ) @@ -93,7 +99,7 @@ pub(super) fn simplify_call( Intrinsic::ArrayLen => { if let Some(length) = dfg.try_get_array_length(arguments[0]) { let length = FieldElement::from(length as u128); - SimplifyResult::SimplifiedTo(dfg.make_constant(length, Type::length_type())) + SimplifyResult::SimplifiedTo(dfg.make_constant(length, NumericType::length_type())) } else if matches!(dfg.type_of_value(arguments[1]), Type::Slice(_)) { SimplifyResult::SimplifiedTo(arguments[0]) } else { @@ -116,7 +122,7 @@ pub(super) fn simplify_call( ); let slice_length_value = array.len() / elements_size; let slice_length = - dfg.make_constant(slice_length_value.into(), Type::length_type()); + dfg.make_constant(slice_length_value.into(), NumericType::length_type()); let new_slice = make_array(dfg, array, Type::Slice(inner_element_types), block, call_stack); SimplifyResult::SimplifiedToMultiple(vec![slice_length, new_slice]) @@ -331,10 +337,7 @@ pub(super) fn simplify_call( simplify_black_box_func(bb_func, arguments, dfg, block, call_stack) } Intrinsic::AsField => { - let instruction = Instruction::Cast( - arguments[0], - Type::Numeric(crate::ssa::ir::types::NumericType::NativeField), - ); + let instruction = Instruction::Cast(arguments[0], NumericType::NativeField); SimplifyResult::SimplifiedToInstruction(instruction) } Intrinsic::FromField => { @@ -355,7 +358,7 @@ pub(super) fn simplify_call( ) .first(); - let instruction = Instruction::Cast(truncated_value, target_type); + let instruction = Instruction::Cast(truncated_value, target_type.unwrap_numeric()); SimplifyResult::SimplifiedToInstruction(instruction) } Intrinsic::AsWitness => SimplifyResult::None, @@ -371,7 +374,7 @@ pub(super) fn simplify_call( if let Some(constants) = constant_args { let lhs = constants[0]; let rhs = constants[1]; - let result = dfg.make_constant((lhs < rhs).into(), Type::bool()); + let result = dfg.make_constant((lhs < rhs).into(), NumericType::bool()); SimplifyResult::SimplifiedTo(result) } else { SimplifyResult::None @@ -407,7 +410,7 @@ fn update_slice_length( operator: BinaryOp, block: BasicBlockId, ) -> ValueId { - let one = dfg.make_constant(FieldElement::one(), Type::length_type()); + let one = dfg.make_constant(FieldElement::one(), NumericType::length_type()); let instruction = Instruction::Binary(Binary { lhs: slice_len, operator, rhs: one }); let call_stack = dfg.get_value_call_stack(slice_len); dfg.insert_instruction_and_results(instruction, block, None, call_stack).first() @@ -422,7 +425,7 @@ fn simplify_slice_push_back( call_stack: CallStack, ) -> SimplifyResult { // The capacity must be an integer so that we can compare it against the slice length - let capacity = dfg.make_constant((slice.len() as u128).into(), Type::length_type()); + let capacity = dfg.make_constant((slice.len() as u128).into(), NumericType::length_type()); let len_equals_capacity_instr = Instruction::Binary(Binary { lhs: arguments[0], operator: BinaryOp::Eq, rhs: capacity }); let len_equals_capacity = dfg @@ -495,7 +498,8 @@ fn simplify_slice_pop_back( let new_slice_length = update_slice_length(arguments[0], dfg, BinaryOp::Sub, block); - let element_size = dfg.make_constant((element_count as u128).into(), Type::length_type()); + let element_size = + dfg.make_constant((element_count as u128).into(), NumericType::length_type()); let flattened_len_instr = Instruction::binary(BinaryOp::Mul, arguments[0], element_size); let mut flattened_len = dfg .insert_instruction_and_results(flattened_len_instr, block, None, call_stack.clone()) @@ -569,7 +573,7 @@ fn simplify_black_box_func( let result_array = make_constant_array( dfg, state_values, - Type::unsigned(64), + NumericType::Unsigned { bit_size: 64 }, block, call_stack, ); @@ -629,14 +633,14 @@ fn simplify_black_box_func( fn make_constant_array( dfg: &mut DataFlowGraph, results: impl Iterator, - typ: Type, + typ: NumericType, block: BasicBlockId, call_stack: &CallStack, ) -> ValueId { let result_constants: im::Vector<_> = - results.map(|element| dfg.make_constant(element, typ.clone())).collect(); + results.map(|element| dfg.make_constant(element, typ)).collect(); - let typ = Type::Array(Arc::new(vec![typ]), result_constants.len() as u32); + let typ = Type::Array(Arc::new(vec![Type::Numeric(typ)]), result_constants.len() as u32); make_array(dfg, result_constants, typ, block, call_stack) } @@ -652,23 +656,6 @@ fn make_array( dfg.insert_instruction_and_results(instruction, block, None, call_stack).first() } -fn make_constant_slice( - dfg: &mut DataFlowGraph, - results: Vec, - typ: Type, - block: BasicBlockId, - call_stack: &CallStack, -) -> (ValueId, ValueId) { - let result_constants = vecmap(results, |element| dfg.make_constant(element, typ.clone())); - - let typ = Type::Slice(Arc::new(vec![typ])); - let length = FieldElement::from(result_constants.len() as u128); - let length = dfg.make_constant(length, Type::length_type()); - - let slice = make_array(dfg, result_constants.into(), typ, block, call_stack); - (length, slice) -} - /// Returns a slice (represented by a tuple (len, slice)) of constants corresponding to the limbs of the radix decomposition. fn constant_to_radix( endian: Endian, @@ -733,8 +720,8 @@ fn simplify_hash( let hash_values = hash.iter().map(|byte| FieldElement::from_be_bytes_reduce(&[*byte])); - let result_array = - make_constant_array(dfg, hash_values, Type::unsigned(8), block, call_stack); + let u8_type = NumericType::Unsigned { bit_size: 8 }; + let result_array = make_constant_array(dfg, hash_values, u8_type, block, call_stack); SimplifyResult::SimplifiedTo(result_array) } _ => SimplifyResult::None, @@ -782,7 +769,7 @@ fn simplify_signature( signature_verifier(&hashed_message, &public_key_x, &public_key_y, &signature) .expect("Rust solvable black box function should not fail"); - let valid_signature = dfg.make_constant(valid_signature.into(), Type::bool()); + let valid_signature = dfg.make_constant(valid_signature.into(), NumericType::bool()); SimplifyResult::SimplifiedTo(valid_signature) } _ => SimplifyResult::None, @@ -812,15 +799,15 @@ fn simplify_derive_generators( num_generators, starting_index.try_to_u32().expect("argument is declared as u32"), ); - let is_infinite = dfg.make_constant(FieldElement::zero(), Type::bool()); + let is_infinite = dfg.make_constant(FieldElement::zero(), NumericType::bool()); let mut results = Vec::new(); for gen in generators { let x_big: BigUint = gen.x.into(); let x = FieldElement::from_be_bytes_reduce(&x_big.to_bytes_be()); let y_big: BigUint = gen.y.into(); let y = FieldElement::from_be_bytes_reduce(&y_big.to_bytes_be()); - results.push(dfg.make_constant(x, Type::field())); - results.push(dfg.make_constant(y, Type::field())); + results.push(dfg.make_constant(x, NumericType::NativeField)); + results.push(dfg.make_constant(y, NumericType::NativeField)); results.push(is_infinite); } let len = results.len() as u32; diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs index 016d7ffa25b..c58264dbe84 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/call/blackbox.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use acvm::{acir::AcirField, BlackBoxFunctionSolver, BlackBoxResolutionError, FieldElement}; use crate::ssa::ir::instruction::BlackBoxFunc; +use crate::ssa::ir::types::NumericType; use crate::ssa::ir::{ basic_block::BasicBlockId, dfg::{CallStack, DataFlowGraph}, @@ -47,9 +48,10 @@ pub(super) fn simplify_ec_add( return SimplifyResult::None; }; - let result_x = dfg.make_constant(result_x, Type::field()); - let result_y = dfg.make_constant(result_y, Type::field()); - let result_is_infinity = dfg.make_constant(result_is_infinity, Type::field()); + let result_x = dfg.make_constant(result_x, NumericType::NativeField); + let result_y = dfg.make_constant(result_y, NumericType::NativeField); + let result_is_infinity = + dfg.make_constant(result_is_infinity, NumericType::NativeField); let typ = Type::Array(Arc::new(vec![Type::field()]), 3); @@ -142,9 +144,10 @@ pub(super) fn simplify_msm( // If there are no variable term, we can directly return the constant result if var_scalars.is_empty() { - let result_x = dfg.make_constant(result_x, Type::field()); - let result_y = dfg.make_constant(result_y, Type::field()); - let result_is_infinity = dfg.make_constant(result_is_infinity, Type::field()); + let result_x = dfg.make_constant(result_x, NumericType::NativeField); + let result_y = dfg.make_constant(result_y, NumericType::NativeField); + let result_is_infinity = + dfg.make_constant(result_is_infinity, NumericType::NativeField); let elements = im::vector![result_x, result_y, result_is_infinity]; let typ = Type::Array(Arc::new(vec![Type::field()]), 3); @@ -163,14 +166,14 @@ pub(super) fn simplify_msm( return SimplifyResult::None; } // Add the constant part back to the non-constant part, if it is not null - let one = dfg.make_constant(FieldElement::one(), Type::field()); - let zero = dfg.make_constant(FieldElement::zero(), Type::field()); + let one = dfg.make_constant(FieldElement::one(), NumericType::NativeField); + let zero = dfg.make_constant(FieldElement::zero(), NumericType::NativeField); if result_is_infinity.is_zero() { var_scalars.push(one); var_scalars.push(zero); - let result_x = dfg.make_constant(result_x, Type::field()); - let result_y = dfg.make_constant(result_y, Type::field()); - let result_is_infinity = dfg.make_constant(result_is_infinity, Type::bool()); + let result_x = dfg.make_constant(result_x, NumericType::NativeField); + let result_y = dfg.make_constant(result_y, NumericType::NativeField); + let result_is_infinity = dfg.make_constant(result_is_infinity, NumericType::bool()); var_points.push(result_x); var_points.push(result_y); var_points.push(result_is_infinity); @@ -221,7 +224,7 @@ pub(super) fn simplify_poseidon2_permutation( }; let new_state = new_state.into_iter(); - let typ = Type::field(); + let typ = NumericType::NativeField; let result_array = make_constant_array(dfg, new_state, typ, block, call_stack); SimplifyResult::SimplifiedTo(result_array) @@ -246,7 +249,7 @@ pub(super) fn simplify_hash( let hash_values = hash.iter().map(|byte| FieldElement::from_be_bytes_reduce(&[*byte])); - let u8_type = Type::unsigned(8); + let u8_type = NumericType::Unsigned { bit_size: 8 }; let result_array = make_constant_array(dfg, hash_values, u8_type, block, call_stack); SimplifyResult::SimplifiedTo(result_array) } @@ -296,7 +299,7 @@ pub(super) fn simplify_signature( signature_verifier(&hashed_message, &public_key_x, &public_key_y, &signature) .expect("Rust solvable black box function should not fail"); - let valid_signature = dfg.make_constant(valid_signature.into(), Type::bool()); + let valid_signature = dfg.make_constant(valid_signature.into(), NumericType::bool()); SimplifyResult::SimplifiedTo(valid_signature) } _ => SimplifyResult::None, diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/cast.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/cast.rs index ed588def1d7..ee2ab43aa5d 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/cast.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/cast.rs @@ -7,7 +7,7 @@ use super::{DataFlowGraph, Instruction, NumericType, SimplifyResult, Type, Value /// that value is returned. Otherwise None is returned. pub(super) fn simplify_cast( value: ValueId, - dst_typ: &Type, + dst_typ: NumericType, dfg: &mut DataFlowGraph, ) -> SimplifyResult { use SimplifyResult::*; @@ -15,60 +15,55 @@ pub(super) fn simplify_cast( if let Value::Instruction { instruction, .. } = &dfg[value] { if let Instruction::Cast(original_value, _) = &dfg[*instruction] { - return SimplifiedToInstruction(Instruction::Cast(*original_value, dst_typ.clone())); + return SimplifiedToInstruction(Instruction::Cast(*original_value, dst_typ)); } } if let Some(constant) = dfg.get_numeric_constant(value) { - let src_typ = dfg.type_of_value(value); + let src_typ = dfg.type_of_value(value).unwrap_numeric(); match (src_typ, dst_typ) { - (Type::Numeric(NumericType::NativeField), Type::Numeric(NumericType::NativeField)) => { + (NumericType::NativeField, NumericType::NativeField) => { // Field -> Field: use src value SimplifiedTo(value) } ( - Type::Numeric(NumericType::Unsigned { .. } | NumericType::Signed { .. }), - Type::Numeric(NumericType::NativeField), + NumericType::Unsigned { .. } | NumericType::Signed { .. }, + NumericType::NativeField, ) => { // Unsigned/Signed -> Field: redefine same constant as Field - SimplifiedTo(dfg.make_constant(constant, dst_typ.clone())) + SimplifiedTo(dfg.make_constant(constant, dst_typ)) } ( - Type::Numeric( - NumericType::NativeField - | NumericType::Unsigned { .. } - | NumericType::Signed { .. }, - ), - Type::Numeric(NumericType::Unsigned { bit_size }), + NumericType::NativeField + | NumericType::Unsigned { .. } + | NumericType::Signed { .. }, + NumericType::Unsigned { bit_size }, ) => { // Field/Unsigned -> unsigned: truncate - let integer_modulus = BigUint::from(2u128).pow(*bit_size); + let integer_modulus = BigUint::from(2u128).pow(bit_size); let constant: BigUint = BigUint::from_bytes_be(&constant.to_be_bytes()); let truncated = constant % integer_modulus; let truncated = FieldElement::from_be_bytes_reduce(&truncated.to_bytes_be()); - SimplifiedTo(dfg.make_constant(truncated, dst_typ.clone())) + SimplifiedTo(dfg.make_constant(truncated, dst_typ)) } ( - Type::Numeric( - NumericType::NativeField - | NumericType::Unsigned { .. } - | NumericType::Signed { .. }, - ), - Type::Numeric(NumericType::Signed { bit_size }), + NumericType::NativeField + | NumericType::Unsigned { .. } + | NumericType::Signed { .. }, + NumericType::Signed { bit_size }, ) => { // Field/Unsigned -> signed // We only simplify to signed when we are below the maximum signed integer of the destination type. - let integer_modulus = BigUint::from(2u128).pow(*bit_size - 1); + let integer_modulus = BigUint::from(2u128).pow(bit_size - 1); let constant_uint: BigUint = BigUint::from_bytes_be(&constant.to_be_bytes()); if constant_uint < integer_modulus { - SimplifiedTo(dfg.make_constant(constant, dst_typ.clone())) + SimplifiedTo(dfg.make_constant(constant, dst_typ)) } else { None } } - _ => None, } - } else if *dst_typ == dfg.type_of_value(value) { + } else if Type::Numeric(dst_typ) == dfg.type_of_value(value) { SimplifiedTo(value) } else { None diff --git a/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs b/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs index 66f50440d64..5ae6a642a57 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/instruction/constrain.rs @@ -1,5 +1,7 @@ use acvm::{acir::AcirField, FieldElement}; +use crate::ssa::ir::types::NumericType; + use super::{Binary, BinaryOp, ConstrainError, DataFlowGraph, Instruction, Type, Value, ValueId}; /// Try to decompose this constrain instruction. This constraint will be broken down such that it instead constrains @@ -20,7 +22,7 @@ pub(super) fn decompose_constrain( match (&dfg[lhs], &dfg[rhs]) { (Value::NumericConstant { constant, typ }, Value::Instruction { instruction, .. }) | (Value::Instruction { instruction, .. }, Value::NumericConstant { constant, typ }) - if *typ == Type::bool() => + if *typ == NumericType::bool() => { match dfg[*instruction] { Instruction::Binary(Binary { lhs, rhs, operator: BinaryOp::Eq }) @@ -61,7 +63,7 @@ pub(super) fn decompose_constrain( // Note that this doesn't remove the value `v2` as it may be used in other instructions, but it // will likely be removed through dead instruction elimination. let one = FieldElement::one(); - let one = dfg.make_constant(one, Type::bool()); + let one = dfg.make_constant(one, NumericType::bool()); [ decompose_constrain(lhs, one, msg, dfg), @@ -89,7 +91,7 @@ pub(super) fn decompose_constrain( // Note that this doesn't remove the value `v2` as it may be used in other instructions, but it // will likely be removed through dead instruction elimination. let zero = FieldElement::zero(); - let zero = dfg.make_constant(zero, dfg.type_of_value(lhs)); + let zero = dfg.make_constant(zero, dfg.type_of_value(lhs).unwrap_numeric()); [ decompose_constrain(lhs, zero, msg, dfg), @@ -112,7 +114,8 @@ pub(super) fn decompose_constrain( // Note that this doesn't remove the value `v1` as it may be used in other instructions, but it // will likely be removed through dead instruction elimination. let reversed_constant = FieldElement::from(!constant.is_one()); - let reversed_constant = dfg.make_constant(reversed_constant, Type::bool()); + let reversed_constant = + dfg.make_constant(reversed_constant, NumericType::bool()); decompose_constrain(value, reversed_constant, msg, dfg) } diff --git a/compiler/noirc_evaluator/src/ssa/ir/types.rs b/compiler/noirc_evaluator/src/ssa/ir/types.rs index 4e4f7e8aa62..0dd7fd92ee5 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/types.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/types.rs @@ -30,6 +30,31 @@ impl NumericType { } } + /// Creates a NumericType::Signed type + pub(crate) fn signed(bit_size: u32) -> NumericType { + NumericType::Signed { bit_size } + } + + /// Creates a NumericType::Unsigned type + pub(crate) fn unsigned(bit_size: u32) -> NumericType { + NumericType::Unsigned { bit_size } + } + + /// Creates the u1 type + pub(crate) fn bool() -> NumericType { + NumericType::Unsigned { bit_size: 1 } + } + + /// Creates the char type, represented as u8. + pub(crate) fn char() -> NumericType { + NumericType::Unsigned { bit_size: 8 } + } + + /// Creates the type of an array's length. + pub(crate) fn length_type() -> NumericType { + NumericType::Unsigned { bit_size: SSA_WORD_SIZE } + } + /// Returns None if the given Field value is within the numeric limits /// for the current NumericType. Otherwise returns a string describing /// the limits, as a range. @@ -63,6 +88,10 @@ impl NumericType { NumericType::NativeField => None, } } + + pub(crate) fn is_unsigned(&self) -> bool { + matches!(self, NumericType::Unsigned { .. }) + } } /// All types representable in the IR. @@ -125,6 +154,14 @@ impl Type { Type::unsigned(SSA_WORD_SIZE) } + /// Returns the inner NumericType if this is one, or panics otherwise + pub(crate) fn unwrap_numeric(&self) -> NumericType { + match self { + Type::Numeric(numeric) => *numeric, + other => panic!("Expected NumericType, found {other}"), + } + } + /// Returns the bit size of the provided numeric type. /// /// # Panics diff --git a/compiler/noirc_evaluator/src/ssa/ir/value.rs b/compiler/noirc_evaluator/src/ssa/ir/value.rs index ef494200308..ec7a8e25246 100644 --- a/compiler/noirc_evaluator/src/ssa/ir/value.rs +++ b/compiler/noirc_evaluator/src/ssa/ir/value.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + use acvm::FieldElement; use serde::{Deserialize, Serialize}; @@ -7,7 +9,7 @@ use super::{ function::FunctionId, instruction::{InstructionId, Intrinsic}, map::Id, - types::Type, + types::{NumericType, Type}, }; pub(crate) type ValueId = Id; @@ -34,7 +36,7 @@ pub(crate) enum Value { Param { block: BasicBlockId, position: usize, typ: Type }, /// This Value originates from a numeric constant - NumericConstant { constant: FieldElement, typ: Type }, + NumericConstant { constant: FieldElement, typ: NumericType }, /// This Value refers to a function in the IR. /// Functions always have the type Type::Function. @@ -55,14 +57,13 @@ pub(crate) enum Value { impl Value { /// Retrieves the type of this Value - pub(crate) fn get_type(&self) -> &Type { + pub(crate) fn get_type(&self) -> Cow { match self { - Value::Instruction { typ, .. } => typ, - Value::Param { typ, .. } => typ, - Value::NumericConstant { typ, .. } => typ, - Value::Function { .. } => &Type::Function, - Value::Intrinsic { .. } => &Type::Function, - Value::ForeignFunction { .. } => &Type::Function, + Value::Instruction { typ, .. } | Value::Param { typ, .. } => Cow::Borrowed(typ), + Value::NumericConstant { typ, .. } => Cow::Owned(Type::Numeric(*typ)), + Value::Function { .. } | Value::Intrinsic { .. } | Value::ForeignFunction { .. } => { + Cow::Owned(Type::Function) + } } } } diff --git a/compiler/noirc_evaluator/src/ssa/opt/as_slice_length.rs b/compiler/noirc_evaluator/src/ssa/opt/as_slice_length.rs index 75cdea349b7..c6cdffd3bc3 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/as_slice_length.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/as_slice_length.rs @@ -2,7 +2,7 @@ use crate::ssa::{ ir::{ function::Function, instruction::{Instruction, InstructionId, Intrinsic}, - types::Type, + types::{NumericType, Type}, value::Value, }, ssa_gen::Ssa, @@ -71,7 +71,7 @@ fn replace_known_slice_lengths( // This isn't strictly necessary as a new result will be defined the next time for which the instruction // is reinserted but this avoids leaving the program in an invalid state. func.dfg.replace_result(instruction_id, original_slice_length); - let known_length = func.dfg.make_constant(known_length.into(), Type::length_type()); + let known_length = func.dfg.make_constant(known_length.into(), NumericType::length_type()); func.dfg.set_value_from_id(original_slice_length, known_length); }); } diff --git a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs index 56029a8fbd4..e2379043541 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/constant_folding.rs @@ -43,7 +43,7 @@ use crate::{ dom::DominatorTree, function::{Function, FunctionId, RuntimeType}, instruction::{Instruction, InstructionId}, - types::Type, + types::{NumericType, Type}, value::{Value, ValueId}, }, ssa_gen::Ssa, @@ -276,7 +276,7 @@ impl<'brillig> Context<'brillig> { // Default side effect condition variable with an enabled state. let mut side_effects_enabled_var = - function.dfg.make_constant(FieldElement::one(), Type::bool()); + function.dfg.make_constant(FieldElement::one(), NumericType::bool()); for instruction_id in instructions { self.fold_constants_into_instruction( @@ -657,7 +657,7 @@ impl<'brillig> Context<'brillig> { dfg: &mut DataFlowGraph, ) -> ValueId { match typ { - Type::Numeric(_) => { + Type::Numeric(typ) => { let memory = memory_values[*memory_index]; *memory_index += 1; @@ -831,7 +831,10 @@ mod test { use crate::ssa::{ function_builder::FunctionBuilder, - ir::{map::Id, types::Type}, + ir::{ + map::Id, + types::{NumericType, Type}, + }, opt::assert_normalized_ssa_equals, Ssa, }; @@ -855,7 +858,7 @@ mod test { assert_eq!(instructions.len(), 2); // The final return is not counted let v0 = main.parameters()[0]; - let two = main.dfg.make_constant(2_u128.into(), Type::field()); + let two = main.dfg.make_constant(2_u128.into(), NumericType::NativeField); main.dfg.set_value_from_id(v0, two); @@ -891,7 +894,7 @@ mod test { // Note that this constant guarantees that `v0/constant < 2^8`. We then do not need to truncate the result. let constant = 2_u128.pow(8); - let constant = main.dfg.make_constant(constant.into(), Type::unsigned(16)); + let constant = main.dfg.make_constant(constant.into(), NumericType::unsigned(16)); main.dfg.set_value_from_id(v1, constant); @@ -929,7 +932,7 @@ mod test { // Note that this constant does not guarantee that `v0/constant < 2^8`. We must then truncate the result. let constant = 2_u128.pow(8) - 1; - let constant = main.dfg.make_constant(constant.into(), Type::unsigned(16)); + let constant = main.dfg.make_constant(constant.into(), NumericType::unsigned(16)); main.dfg.set_value_from_id(v1, constant); @@ -1150,7 +1153,7 @@ mod test { // Compiling main let mut builder = FunctionBuilder::new("main".into(), main_id); let v0 = builder.add_parameter(Type::unsigned(64)); - let zero = builder.numeric_constant(0u128, Type::unsigned(64)); + let zero = builder.numeric_constant(0u128, NumericType::unsigned(64)); let typ = Type::Array(Arc::new(vec![Type::unsigned(64)]), 25); let array_contents = im::vector![ diff --git a/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs b/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs index cfeb8751f25..ded1f52d529 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/defunctionalize.rs @@ -130,13 +130,14 @@ impl DefunctionalizationContext { // Change the type of all the values that are not call targets to NativeField let value_ids = vecmap(func.dfg.values_iter(), |(id, _)| id); for value_id in value_ids { - if let Type::Function = &func.dfg[value_id].get_type() { + if let Type::Function = func.dfg[value_id].get_type().as_ref() { match &func.dfg[value_id] { // If the value is a static function, transform it to the function id Value::Function(id) => { if !call_target_values.contains(&value_id) { + let field = NumericType::NativeField; let new_value = - func.dfg.make_constant(function_id_to_field(*id), Type::field()); + func.dfg.make_constant(function_id_to_field(*id), field); func.dfg.set_value_from_id(value_id, new_value); } } @@ -287,10 +288,8 @@ fn create_apply_function( let is_last = index == function_ids.len() - 1; let mut next_function_block = None; - let function_id_constant = function_builder.numeric_constant( - function_id_to_field(*function_id), - Type::Numeric(NumericType::NativeField), - ); + let function_id_constant = function_builder + .numeric_constant(function_id_to_field(*function_id), NumericType::NativeField); // If it's not the last function to dispatch, create an if statement if !is_last { diff --git a/compiler/noirc_evaluator/src/ssa/opt/die.rs b/compiler/noirc_evaluator/src/ssa/opt/die.rs index f7ac6f7b313..de3ac44652f 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/die.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/die.rs @@ -12,10 +12,10 @@ use crate::ssa::{ function::Function, instruction::{BinaryOp, Instruction, InstructionId, Intrinsic}, post_order::PostOrder, - types::Type, + types::NumericType, value::{Value, ValueId}, }, - ssa_gen::{Ssa, SSA_WORD_SIZE}, + ssa_gen::Ssa, }; impl Ssa { @@ -285,25 +285,25 @@ impl Context { let (lhs, rhs) = if function.dfg.get_numeric_constant(*index).is_some() { // If we are here it means the index is known but out of bounds. That's always an error! - let false_const = function.dfg.make_constant(false.into(), Type::bool()); - let true_const = function.dfg.make_constant(true.into(), Type::bool()); + let false_const = function.dfg.make_constant(false.into(), NumericType::bool()); + let true_const = function.dfg.make_constant(true.into(), NumericType::bool()); (false_const, true_const) } else { // `index` will be relative to the flattened array length, so we need to take that into account let array_length = function.dfg.type_of_value(*array).flattened_size(); // If we are here it means the index is dynamic, so let's add a check that it's less than length + let length_type = NumericType::length_type(); let index = function.dfg.insert_instruction_and_results( - Instruction::Cast(*index, Type::unsigned(SSA_WORD_SIZE)), + Instruction::Cast(*index, length_type), block_id, None, call_stack.clone(), ); let index = index.first(); - let array_typ = Type::unsigned(SSA_WORD_SIZE); let array_length = - function.dfg.make_constant((array_length as u128).into(), array_typ); + function.dfg.make_constant((array_length as u128).into(), length_type); let is_index_out_of_bounds = function.dfg.insert_instruction_and_results( Instruction::binary(BinaryOp::Lt, index, array_length), block_id, @@ -311,7 +311,7 @@ impl Context { call_stack.clone(), ); let is_index_out_of_bounds = is_index_out_of_bounds.first(); - let true_const = function.dfg.make_constant(true.into(), Type::bool()); + let true_const = function.dfg.make_constant(true.into(), NumericType::bool()); (is_index_out_of_bounds, true_const) }; @@ -495,12 +495,9 @@ fn apply_side_effects( // Condition needs to be cast to argument type in order to multiply them together. // In our case, lhs is always a boolean. - let casted_condition = dfg.insert_instruction_and_results( - Instruction::Cast(condition, Type::bool()), - block_id, - None, - call_stack.clone(), - ); + let cast = Instruction::Cast(condition, NumericType::bool()); + let casted_condition = + dfg.insert_instruction_and_results(cast, block_id, None, call_stack.clone()); let casted_condition = casted_condition.first(); let lhs = dfg.insert_instruction_and_results( @@ -530,7 +527,10 @@ mod test { use crate::ssa::{ function_builder::FunctionBuilder, - ir::{map::Id, types::Type}, + ir::{ + map::Id, + types::{NumericType, Type}, + }, opt::assert_normalized_ssa_equals, Ssa, }; @@ -639,7 +639,7 @@ mod test { // Compiling main let mut builder = FunctionBuilder::new("main".into(), main_id); - let zero = builder.numeric_constant(0u128, Type::unsigned(32)); + let zero = builder.numeric_constant(0u128, NumericType::unsigned(32)); let array_type = Type::Array(Arc::new(vec![Type::unsigned(32)]), 2); let v1 = builder.insert_make_array(vector![zero, zero], array_type.clone()); let v2 = builder.insert_allocate(array_type.clone()); @@ -652,7 +652,7 @@ mod test { builder.switch_to_block(b1); let v3 = builder.insert_load(v2, array_type); - let one = builder.numeric_constant(1u128, Type::unsigned(32)); + let one = builder.numeric_constant(1u128, NumericType::unsigned(32)); let v5 = builder.insert_array_set(v3, zero, one); builder.terminate_with_return(vec![v5]); diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs index 3fbccf93ec9..dc7952979e5 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg.rs @@ -144,7 +144,7 @@ use crate::ssa::{ function::{Function, FunctionId, RuntimeType}, function_inserter::FunctionInserter, instruction::{BinaryOp, Instruction, InstructionId, Intrinsic, TerminatorInstruction}, - types::Type, + types::{NumericType, Type}, value::{Value, ValueId}, }, ssa_gen::Ssa, @@ -332,11 +332,8 @@ impl<'f> Context<'f> { for instruction in instructions.iter() { if self.is_no_predicate(no_predicates, instruction) { // disable side effect for no_predicate functions - let one = self - .inserter - .function - .dfg - .make_constant(FieldElement::one(), Type::unsigned(1)); + let bool_type = NumericType::bool(); + let one = self.inserter.function.dfg.make_constant(FieldElement::one(), bool_type); self.insert_instruction_with_typevars( Instruction::EnableSideEffectsIf { condition: one }, None, @@ -540,7 +537,7 @@ impl<'f> Context<'f> { let else_condition = if let Some(branch) = cond_context.else_branch { branch.condition } else { - self.inserter.function.dfg.make_constant(FieldElement::zero(), Type::bool()) + self.inserter.function.dfg.make_constant(FieldElement::zero(), NumericType::bool()) }; let block = self.inserter.function.entry_block(); @@ -606,7 +603,7 @@ impl<'f> Context<'f> { let condition = match self.get_last_condition() { Some(cond) => cond, None => { - self.inserter.function.dfg.make_constant(FieldElement::one(), Type::unsigned(1)) + self.inserter.function.dfg.make_constant(FieldElement::one(), NumericType::bool()) } }; let enable_side_effects = Instruction::EnableSideEffectsIf { condition }; @@ -653,13 +650,9 @@ impl<'f> Context<'f> { // Condition needs to be cast to argument type in order to multiply them together. let argument_type = self.inserter.function.dfg.type_of_value(lhs); - // Sanity check that we're not constraining non-primitive types - assert!(matches!(argument_type, Type::Numeric(_))); - let casted_condition = self.insert_instruction( - Instruction::Cast(condition, argument_type), - call_stack.clone(), - ); + let cast = Instruction::Cast(condition, argument_type.unwrap_numeric()); + let casted_condition = self.insert_instruction(cast, call_stack.clone()); let lhs = self.insert_instruction( Instruction::binary(BinaryOp::Mul, lhs, casted_condition), @@ -708,10 +701,8 @@ impl<'f> Context<'f> { // Condition needs to be cast to argument type in order to multiply them together. let argument_type = self.inserter.function.dfg.type_of_value(value); - let casted_condition = self.insert_instruction( - Instruction::Cast(condition, argument_type), - call_stack.clone(), - ); + let cast = Instruction::Cast(condition, argument_type.unwrap_numeric()); + let casted_condition = self.insert_instruction(cast, call_stack.clone()); let value = self.insert_instruction( Instruction::binary(BinaryOp::Mul, value, casted_condition), @@ -725,10 +716,8 @@ impl<'f> Context<'f> { let field = arguments[0]; let argument_type = self.inserter.function.dfg.type_of_value(field); - let casted_condition = self.insert_instruction( - Instruction::Cast(condition, argument_type), - call_stack.clone(), - ); + let cast = Instruction::Cast(condition, argument_type.unwrap_numeric()); + let casted_condition = self.insert_instruction(cast, call_stack.clone()); let field = self.insert_instruction( Instruction::binary(BinaryOp::Mul, field, casted_condition), call_stack.clone(), diff --git a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs index 6ea235b9414..c2b071a9c9a 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/flatten_cfg/value_merger.rs @@ -5,7 +5,7 @@ use crate::ssa::ir::{ basic_block::BasicBlockId, dfg::{CallStack, DataFlowGraph, InsertInstructionResult}, instruction::{BinaryOp, Instruction}, - types::Type, + types::{NumericType, Type}, value::{Value, ValueId}, }; @@ -95,8 +95,8 @@ impl<'a> ValueMerger<'a> { then_value: ValueId, else_value: ValueId, ) -> ValueId { - let then_type = dfg.type_of_value(then_value); - let else_type = dfg.type_of_value(else_value); + let then_type = dfg.type_of_value(then_value).unwrap_numeric(); + let else_type = dfg.type_of_value(else_value).unwrap_numeric(); assert_eq!( then_type, else_type, "Expected values merged to be of the same type but found {then_type} and {else_type}" @@ -112,22 +112,13 @@ impl<'a> ValueMerger<'a> { let call_stack = if then_call_stack.is_empty() { else_call_stack } else { then_call_stack }; // We must cast the bool conditions to the actual numeric type used by each value. - let then_condition = dfg - .insert_instruction_and_results( - Instruction::Cast(then_condition, then_type), - block, - None, - call_stack.clone(), - ) - .first(); - let else_condition = dfg - .insert_instruction_and_results( - Instruction::Cast(else_condition, else_type), - block, - None, - call_stack.clone(), - ) - .first(); + let cast = Instruction::Cast(then_condition, then_type); + let then_condition = + dfg.insert_instruction_and_results(cast, block, None, call_stack.clone()).first(); + + let cast = Instruction::Cast(else_condition, else_type); + let else_condition = + dfg.insert_instruction_and_results(cast, block, None, call_stack.clone()).first(); let mul = Instruction::binary(BinaryOp::Mul, then_condition, then_value); let then_value = @@ -175,7 +166,7 @@ impl<'a> ValueMerger<'a> { for (element_index, element_type) in element_types.iter().enumerate() { let index = ((i * element_types.len() as u32 + element_index as u32) as u128).into(); - let index = self.dfg.make_constant(index, Type::field()); + let index = self.dfg.make_constant(index, NumericType::NativeField); let typevars = Some(vec![element_type.clone()]); @@ -243,7 +234,7 @@ impl<'a> ValueMerger<'a> { for (element_index, element_type) in element_types.iter().enumerate() { let index_u32 = i * element_types.len() as u32 + element_index as u32; let index_value = (index_u32 as u128).into(); - let index = self.dfg.make_constant(index_value, Type::field()); + let index = self.dfg.make_constant(index_value, NumericType::NativeField); let typevars = Some(vec![element_type.clone()]); @@ -295,7 +286,7 @@ impl<'a> ValueMerger<'a> { match typ { Type::Numeric(numeric_type) => { let zero = FieldElement::zero(); - self.dfg.make_constant(zero, Type::Numeric(*numeric_type)) + self.dfg.make_constant(zero, *numeric_type) } Type::Array(element_types, len) => { let mut array = im::Vector::new(); diff --git a/compiler/noirc_evaluator/src/ssa/opt/inlining.rs b/compiler/noirc_evaluator/src/ssa/opt/inlining.rs index f91487fd73e..37659ec7c98 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/inlining.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/inlining.rs @@ -469,7 +469,7 @@ impl<'function> PerFunctionContext<'function> { unreachable!("All Value::Params should already be known from previous calls to translate_block. Unknown value {id} = {value:?}") } Value::NumericConstant { constant, typ } => { - self.context.builder.numeric_constant(*constant, typ.clone()) + self.context.builder.numeric_constant(*constant, *typ) } Value::Function(function) => self.context.builder.import_function(*function), Value::Intrinsic(intrinsic) => self.context.builder.import_intrinsic_id(*intrinsic), @@ -1062,10 +1062,10 @@ mod test { let join_block = builder.insert_block(); builder.terminate_with_jmpif(inner2_cond, then_block, else_block); builder.switch_to_block(then_block); - let one = builder.numeric_constant(FieldElement::one(), Type::field()); + let one = builder.field_constant(FieldElement::one()); builder.terminate_with_jmp(join_block, vec![one]); builder.switch_to_block(else_block); - let two = builder.numeric_constant(FieldElement::from(2_u128), Type::field()); + let two = builder.field_constant(FieldElement::from(2_u128)); builder.terminate_with_jmp(join_block, vec![two]); let join_param = builder.add_block_parameter(join_block, Type::field()); builder.switch_to_block(join_block); @@ -1177,17 +1177,16 @@ mod test { builder.terminate_with_return(v0); builder.new_brillig_function("bar".into(), bar_id, InlineType::default()); - let bar_v0 = - builder.numeric_constant(1_usize, Type::Numeric(NumericType::Unsigned { bit_size: 1 })); + let bar_v0 = builder.numeric_constant(1_usize, NumericType::bool()); let then_block = builder.insert_block(); let else_block = builder.insert_block(); let join_block = builder.insert_block(); builder.terminate_with_jmpif(bar_v0, then_block, else_block); builder.switch_to_block(then_block); - let one = builder.numeric_constant(FieldElement::one(), Type::field()); + let one = builder.field_constant(FieldElement::one()); builder.terminate_with_jmp(join_block, vec![one]); builder.switch_to_block(else_block); - let two = builder.numeric_constant(FieldElement::from(2_u128), Type::field()); + let two = builder.field_constant(FieldElement::from(2_u128)); builder.terminate_with_jmp(join_block, vec![two]); let join_param = builder.add_block_parameter(join_block, Type::field()); builder.switch_to_block(join_block); diff --git a/compiler/noirc_evaluator/src/ssa/opt/normalize_value_ids.rs b/compiler/noirc_evaluator/src/ssa/opt/normalize_value_ids.rs index a5b60fb5fcd..f5e96224260 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/normalize_value_ids.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/normalize_value_ids.rs @@ -177,7 +177,7 @@ impl IdMaps { } Value::NumericConstant { constant, typ } => { - new_function.dfg.make_constant(*constant, typ.clone()) + new_function.dfg.make_constant(*constant, *typ) } Value::Intrinsic(intrinsic) => new_function.dfg.import_intrinsic(*intrinsic), Value::ForeignFunction(name) => new_function.dfg.import_foreign_function(name), diff --git a/compiler/noirc_evaluator/src/ssa/opt/rc.rs b/compiler/noirc_evaluator/src/ssa/opt/rc.rs index ffe4ada39b7..64f6e2ddfea 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/rc.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/rc.rs @@ -160,8 +160,12 @@ mod test { use crate::ssa::{ function_builder::FunctionBuilder, ir::{ - basic_block::BasicBlockId, dfg::DataFlowGraph, function::RuntimeType, - instruction::Instruction, map::Id, types::Type, + basic_block::BasicBlockId, + dfg::DataFlowGraph, + function::RuntimeType, + instruction::Instruction, + map::Id, + types::{NumericType, Type}, }, }; @@ -251,7 +255,7 @@ mod test { builder.insert_inc_rc(v0); let v2 = builder.insert_load(v1, array_type); - let zero = builder.numeric_constant(0u128, Type::unsigned(64)); + let zero = builder.numeric_constant(0u128, NumericType::unsigned(64)); let five = builder.field_constant(5u128); let v7 = builder.insert_array_set(v2, zero, five); @@ -302,7 +306,7 @@ mod test { builder.insert_store(v0, v1); let v2 = builder.insert_load(v1, array_type.clone()); - let zero = builder.numeric_constant(0u128, Type::unsigned(64)); + let zero = builder.numeric_constant(0u128, NumericType::unsigned(64)); let five = builder.field_constant(5u128); let v7 = builder.insert_array_set(v2, zero, five); diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs index ccf5bd9d9f8..872c7920a77 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_bit_shifts.rs @@ -100,7 +100,7 @@ impl Context<'_> { bit_size: u32, ) -> ValueId { let base = self.field_constant(FieldElement::from(2_u128)); - let typ = self.function.dfg.type_of_value(lhs); + let typ = self.function.dfg.type_of_value(lhs).unwrap_numeric(); let (max_bit, pow) = if let Some(rhs_constant) = self.function.dfg.get_numeric_constant(rhs) { // Happy case is that we know precisely by how many bits the integer will @@ -115,29 +115,29 @@ impl Context<'_> { return InsertInstructionResult::SimplifiedTo(zero).first(); } } - let pow = self.numeric_constant(FieldElement::from(rhs_bit_size_pow_2), typ.clone()); + let pow = self.numeric_constant(FieldElement::from(rhs_bit_size_pow_2), typ); let max_lhs_bits = self.function.dfg.get_value_max_num_bits(lhs); (max_lhs_bits + bit_shift_size, pow) } else { // we use a predicate to nullify the result in case of overflow - let bit_size_var = - self.numeric_constant(FieldElement::from(bit_size as u128), Type::unsigned(8)); + let u8_type = NumericType::unsigned(8); + let bit_size_var = self.numeric_constant(FieldElement::from(bit_size as u128), u8_type); let overflow = self.insert_binary(rhs, BinaryOp::Lt, bit_size_var); - let predicate = self.insert_cast(overflow, typ.clone()); + let predicate = self.insert_cast(overflow, typ); // we can safely cast to unsigned because overflow_checks prevent bit-shift with a negative value - let rhs_unsigned = self.insert_cast(rhs, Type::unsigned(bit_size)); + let rhs_unsigned = self.insert_cast(rhs, NumericType::unsigned(bit_size)); let pow = self.pow(base, rhs_unsigned); - let pow = self.insert_cast(pow, typ.clone()); + let pow = self.insert_cast(pow, typ); (FieldElement::max_num_bits(), self.insert_binary(predicate, BinaryOp::Mul, pow)) }; if max_bit <= bit_size { self.insert_binary(lhs, BinaryOp::Mul, pow) } else { - let lhs_field = self.insert_cast(lhs, Type::field()); - let pow_field = self.insert_cast(pow, Type::field()); + let lhs_field = self.insert_cast(lhs, NumericType::NativeField); + let pow_field = self.insert_cast(pow, NumericType::NativeField); let result = self.insert_binary(lhs_field, BinaryOp::Mul, pow_field); let result = self.insert_truncate(result, bit_size, max_bit); self.insert_cast(result, typ) @@ -153,7 +153,7 @@ impl Context<'_> { rhs: ValueId, bit_size: u32, ) -> ValueId { - let lhs_typ = self.function.dfg.type_of_value(lhs); + let lhs_typ = self.function.dfg.type_of_value(lhs).unwrap_numeric(); let base = self.field_constant(FieldElement::from(2_u128)); let pow = self.pow(base, rhs); if lhs_typ.is_unsigned() { @@ -161,14 +161,14 @@ impl Context<'_> { self.insert_binary(lhs, BinaryOp::Div, pow) } else { // Get the sign of the operand; positive signed operand will just do a division as well - let zero = self.numeric_constant(FieldElement::zero(), Type::signed(bit_size)); + let zero = self.numeric_constant(FieldElement::zero(), NumericType::signed(bit_size)); let lhs_sign = self.insert_binary(lhs, BinaryOp::Lt, zero); - let lhs_sign_as_field = self.insert_cast(lhs_sign, Type::field()); - let lhs_as_field = self.insert_cast(lhs, Type::field()); + let lhs_sign_as_field = self.insert_cast(lhs_sign, NumericType::NativeField); + let lhs_as_field = self.insert_cast(lhs, NumericType::NativeField); // For negative numbers, convert to 1-complement using wrapping addition of a + 1 let one_complement = self.insert_binary(lhs_sign_as_field, BinaryOp::Add, lhs_as_field); let one_complement = self.insert_truncate(one_complement, bit_size, bit_size + 1); - let one_complement = self.insert_cast(one_complement, Type::signed(bit_size)); + let one_complement = self.insert_cast(one_complement, NumericType::signed(bit_size)); // Performs the division on the 1-complement (or the operand if positive) let shifted_complement = self.insert_binary(one_complement, BinaryOp::Div, pow); // Convert back to 2-complement representation if operand is negative @@ -203,8 +203,8 @@ impl Context<'_> { let idx = self.field_constant(FieldElement::from((bit_size - i) as i128)); let b = self.insert_array_get(rhs_bits, idx, Type::bool()); let not_b = self.insert_not(b); - let b = self.insert_cast(b, Type::field()); - let not_b = self.insert_cast(not_b, Type::field()); + let b = self.insert_cast(b, NumericType::NativeField); + let not_b = self.insert_cast(not_b, NumericType::NativeField); let r1 = self.insert_binary(a, BinaryOp::Mul, b); let r2 = self.insert_binary(r_squared, BinaryOp::Mul, not_b); r = self.insert_binary(r1, BinaryOp::Add, r2); @@ -216,14 +216,14 @@ impl Context<'_> { } pub(crate) fn field_constant(&mut self, constant: FieldElement) -> ValueId { - self.function.dfg.make_constant(constant, Type::field()) + self.function.dfg.make_constant(constant, NumericType::NativeField) } /// Insert a numeric constant into the current function pub(crate) fn numeric_constant( &mut self, value: impl Into, - typ: Type, + typ: NumericType, ) -> ValueId { self.function.dfg.make_constant(value.into(), typ) } @@ -260,7 +260,7 @@ impl Context<'_> { /// Insert a cast instruction at the end of the current block. /// Returns the result of the cast instruction. - pub(crate) fn insert_cast(&mut self, value: ValueId, typ: Type) -> ValueId { + pub(crate) fn insert_cast(&mut self, value: ValueId, typ: NumericType) -> ValueId { self.insert_instruction(Instruction::Cast(value, typ), None).first() } diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs index ce5534ecc7a..e85e2c4a441 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_enable_side_effects.rs @@ -18,7 +18,7 @@ use crate::ssa::{ dfg::DataFlowGraph, function::{Function, RuntimeType}, instruction::{BinaryOp, Hint, Instruction, Intrinsic}, - types::Type, + types::NumericType, value::Value, }, ssa_gen::Ssa, @@ -70,7 +70,8 @@ impl Context { ) { let instructions = function.dfg[block].take_instructions(); - let mut active_condition = function.dfg.make_constant(FieldElement::one(), Type::bool()); + let one = FieldElement::one(); + let mut active_condition = function.dfg.make_constant(one, NumericType::bool()); let mut last_side_effects_enabled_instruction = None; let mut new_instructions = Vec::with_capacity(instructions.len()); @@ -203,7 +204,7 @@ mod test { ir::{ instruction::{BinaryOp, Instruction}, map::Id, - types::Type, + types::{NumericType, Type}, }, }; @@ -234,9 +235,9 @@ mod test { let mut builder = FunctionBuilder::new("main".into(), main_id); let v0 = builder.add_parameter(Type::field()); - let two = builder.numeric_constant(2u128, Type::field()); + let two = builder.field_constant(2u128); - let one = builder.numeric_constant(1u128, Type::bool()); + let one = builder.numeric_constant(1u128, NumericType::bool()); builder.insert_enable_side_effects_if(one); builder.insert_binary(v0, BinaryOp::Mul, two); diff --git a/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs b/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs index f93f63c1fbb..45b7f9072d8 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/remove_if_else.rs @@ -5,6 +5,7 @@ use fxhash::FxHashMap as HashMap; use crate::ssa::ir::function::RuntimeType; use crate::ssa::ir::instruction::Hint; +use crate::ssa::ir::types::NumericType; use crate::ssa::ir::value::ValueId; use crate::ssa::{ ir::{ @@ -63,7 +64,8 @@ impl Context { fn remove_if_else(&mut self, function: &mut Function) { let block = function.entry_block(); let instructions = function.dfg[block].take_instructions(); - let mut current_conditional = function.dfg.make_constant(FieldElement::one(), Type::bool()); + let one = FieldElement::one(); + let mut current_conditional = function.dfg.make_constant(one, NumericType::bool()); for instruction in instructions { match &function.dfg[instruction] { diff --git a/compiler/noirc_evaluator/src/ssa/opt/resolve_is_unconstrained.rs b/compiler/noirc_evaluator/src/ssa/opt/resolve_is_unconstrained.rs index 3d40c88d704..87e680932c6 100644 --- a/compiler/noirc_evaluator/src/ssa/opt/resolve_is_unconstrained.rs +++ b/compiler/noirc_evaluator/src/ssa/opt/resolve_is_unconstrained.rs @@ -2,12 +2,11 @@ use crate::ssa::{ ir::{ function::{Function, RuntimeType}, instruction::{Instruction, Intrinsic}, - types::Type, + types::NumericType, value::Value, }, ssa_gen::Ssa, }; -use acvm::FieldElement; use fxhash::FxHashSet as HashSet; impl Ssa { @@ -47,10 +46,9 @@ impl Function { // We replace the result with a fresh id. This will be unused, so the DIE pass will remove the leftover intrinsic call. self.dfg.replace_result(instruction_id, original_return_id); - let is_within_unconstrained = self.dfg.make_constant( - FieldElement::from(matches!(self.runtime(), RuntimeType::Brillig(_))), - Type::bool(), - ); + let is_unconstrained = matches!(self.runtime(), RuntimeType::Brillig(_)).into(); + let is_within_unconstrained = + self.dfg.make_constant(is_unconstrained, NumericType::bool()); // Replace all uses of the original return value with the constant self.dfg.set_value_from_id(original_return_id, is_within_unconstrained); } diff --git a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs index e78cbbd75a1..b0003aa5f0f 100644 --- a/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs +++ b/compiler/noirc_evaluator/src/ssa/parser/into_ssa.rs @@ -207,7 +207,7 @@ impl Translator { } ParsedInstruction::Cast { target, lhs, typ } => { let lhs = self.translate_value(lhs)?; - let value_id = self.builder.insert_cast(lhs, typ); + let value_id = self.builder.insert_cast(lhs, typ.unwrap_numeric()); self.define_variable(target, value_id)?; } ParsedInstruction::Constrain { lhs, rhs, assert_message } => { @@ -290,7 +290,7 @@ impl Translator { fn translate_value(&mut self, value: ParsedValue) -> Result { match value { ParsedValue::NumericConstant { constant, typ } => { - Ok(self.builder.numeric_constant(constant, typ)) + Ok(self.builder.numeric_constant(constant, typ.unwrap_numeric())) } ParsedValue::Variable(identifier) => self.lookup_variable(identifier), } diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs index 116e0de4ecd..7807658dabb 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/context.rs @@ -19,7 +19,6 @@ use crate::ssa::ir::types::{NumericType, Type}; use crate::ssa::ir::value::ValueId; use super::value::{Tree, Value, Values}; -use super::SSA_WORD_SIZE; use fxhash::{FxHashMap as HashMap, FxHashSet as HashSet}; /// The FunctionContext is the main context object for translating a @@ -281,37 +280,33 @@ impl<'a> FunctionContext<'a> { &mut self, value: impl Into, negative: bool, - typ: Type, + numeric_type: NumericType, ) -> Result { let value = value.into(); - if let Type::Numeric(numeric_type) = typ { - if let Some(range) = numeric_type.value_is_outside_limits(value, negative) { - let call_stack = self.builder.get_call_stack(); - return Err(RuntimeError::IntegerOutOfBounds { - value: if negative { -value } else { value }, - typ: numeric_type, - range, - call_stack, - }); - } + if let Some(range) = numeric_type.value_is_outside_limits(value, negative) { + let call_stack = self.builder.get_call_stack(); + return Err(RuntimeError::IntegerOutOfBounds { + value: if negative { -value } else { value }, + typ: numeric_type, + range, + call_stack, + }); + } - let value = if negative { - match numeric_type { - NumericType::NativeField => -value, - NumericType::Signed { bit_size } | NumericType::Unsigned { bit_size } => { - let base = 1_u128 << bit_size; - FieldElement::from(base) - value - } + let value = if negative { + match numeric_type { + NumericType::NativeField => -value, + NumericType::Signed { bit_size } | NumericType::Unsigned { bit_size } => { + let base = 1_u128 << bit_size; + FieldElement::from(base) - value } - } else { - value - }; - - Ok(self.builder.numeric_constant(value, typ)) + } } else { - panic!("Expected type for numeric constant to be a numeric type, found {typ}"); - } + value + }; + + Ok(self.builder.numeric_constant(value, numeric_type)) } /// helper function which add instructions to the block computing the absolute value of the @@ -320,16 +315,16 @@ impl<'a> FunctionContext<'a> { assert_eq!(self.builder.type_of_value(sign), Type::bool()); // We compute the absolute value of lhs - let bit_width = - self.builder.numeric_constant(FieldElement::from(2_i128.pow(bit_size)), Type::field()); + let bit_width = FieldElement::from(2_i128.pow(bit_size)); + let bit_width = self.builder.numeric_constant(bit_width, NumericType::NativeField); let sign_not = self.builder.insert_not(sign); // We use unsafe casts here, this is fine as we're casting to a `field` type. - let as_field = self.builder.insert_cast(input, Type::field()); - let sign_field = self.builder.insert_cast(sign, Type::field()); + let as_field = self.builder.insert_cast(input, NumericType::NativeField); + let sign_field = self.builder.insert_cast(sign, NumericType::NativeField); let positive_predicate = self.builder.insert_binary(sign_field, BinaryOp::Mul, as_field); let two_complement = self.builder.insert_binary(bit_width, BinaryOp::Sub, as_field); - let sign_not_field = self.builder.insert_cast(sign_not, Type::field()); + let sign_not_field = self.builder.insert_cast(sign_not, NumericType::NativeField); let negative_predicate = self.builder.insert_binary(sign_not_field, BinaryOp::Mul, two_complement); self.builder.insert_binary(positive_predicate, BinaryOp::Add, negative_predicate) @@ -354,15 +349,18 @@ impl<'a> FunctionContext<'a> { operator: BinaryOpKind, location: Location, ) -> ValueId { - let result_type = self.builder.current_function.dfg.type_of_value(result); + let result_type = self.builder.current_function.dfg.type_of_value(result).unwrap_numeric(); match result_type { - Type::Numeric(NumericType::Signed { bit_size }) => { + NumericType::Signed { bit_size } => { match operator { BinaryOpKind::Add | BinaryOpKind::Subtract => { // Result is computed modulo the bit size let result = self.builder.insert_truncate(result, bit_size, bit_size + 1); - let result = - self.insert_safe_cast(result, Type::unsigned(bit_size), location); + let result = self.insert_safe_cast( + result, + NumericType::unsigned(bit_size), + location, + ); self.check_signed_overflow(result, lhs, rhs, operator, bit_size, location); self.insert_safe_cast(result, result_type, location) @@ -370,7 +368,7 @@ impl<'a> FunctionContext<'a> { BinaryOpKind::Multiply => { // Result is computed modulo the bit size let mut result = - self.builder.insert_cast(result, Type::unsigned(2 * bit_size)); + self.builder.insert_cast(result, NumericType::unsigned(2 * bit_size)); result = self.builder.insert_truncate(result, bit_size, 2 * bit_size); self.check_signed_overflow(result, lhs, rhs, operator, bit_size, location); @@ -382,7 +380,7 @@ impl<'a> FunctionContext<'a> { _ => unreachable!("operator {} should not overflow", operator), } } - Type::Numeric(NumericType::Unsigned { bit_size }) => { + NumericType::Unsigned { bit_size } => { let dfg = &self.builder.current_function.dfg; let max_lhs_bits = dfg.get_value_max_num_bits(lhs); @@ -410,7 +408,7 @@ impl<'a> FunctionContext<'a> { result } - _ => result, + NumericType::NativeField => result, } } @@ -425,11 +423,11 @@ impl<'a> FunctionContext<'a> { bit_size: u32, location: Location, ) -> ValueId { - let one = self.builder.numeric_constant(FieldElement::one(), Type::bool()); + let one = self.builder.numeric_constant(FieldElement::one(), NumericType::bool()); assert!(self.builder.current_function.dfg.type_of_value(rhs) == Type::unsigned(8)); - let max = - self.builder.numeric_constant(FieldElement::from(bit_size as i128), Type::unsigned(8)); + let bit_size_field = FieldElement::from(bit_size as i128); + let max = self.builder.numeric_constant(bit_size_field, NumericType::unsigned(8)); let overflow = self.builder.insert_binary(rhs, BinaryOp::Lt, max); self.builder.set_location(location).insert_constrain( overflow, @@ -463,11 +461,11 @@ impl<'a> FunctionContext<'a> { let is_sub = operator == BinaryOpKind::Subtract; let half_width = self.builder.numeric_constant( FieldElement::from(2_i128.pow(bit_size - 1)), - Type::unsigned(bit_size), + NumericType::unsigned(bit_size), ); // We compute the sign of the operands. The overflow checks for signed integers depends on these signs - let lhs_as_unsigned = self.insert_safe_cast(lhs, Type::unsigned(bit_size), location); - let rhs_as_unsigned = self.insert_safe_cast(rhs, Type::unsigned(bit_size), location); + let lhs_as_unsigned = self.insert_safe_cast(lhs, NumericType::unsigned(bit_size), location); + let rhs_as_unsigned = self.insert_safe_cast(rhs, NumericType::unsigned(bit_size), location); let lhs_sign = self.builder.insert_binary(lhs_as_unsigned, BinaryOp::Lt, half_width); let mut rhs_sign = self.builder.insert_binary(rhs_as_unsigned, BinaryOp::Lt, half_width); let message = if is_sub { @@ -505,18 +503,19 @@ impl<'a> FunctionContext<'a> { bit_size, Some("attempt to multiply with overflow".to_string()), ); - let product = self.builder.insert_cast(product_field, Type::unsigned(bit_size)); + let product = + self.builder.insert_cast(product_field, NumericType::unsigned(bit_size)); // Then we check the signed product fits in a signed integer of bit_size-bits let not_same = self.builder.insert_not(same_sign); let not_same_sign_field = - self.insert_safe_cast(not_same, Type::unsigned(bit_size), location); + self.insert_safe_cast(not_same, NumericType::unsigned(bit_size), location); let positive_maximum_with_offset = self.builder.insert_binary(half_width, BinaryOp::Add, not_same_sign_field); let product_overflow_check = self.builder.insert_binary(product, BinaryOp::Lt, positive_maximum_with_offset); - let one = self.builder.numeric_constant(FieldElement::one(), Type::bool()); + let one = self.builder.numeric_constant(FieldElement::one(), NumericType::bool()); self.builder.set_location(location).insert_constrain( product_overflow_check, one, @@ -595,7 +594,7 @@ impl<'a> FunctionContext<'a> { pub(super) fn insert_safe_cast( &mut self, mut value: ValueId, - typ: Type, + typ: NumericType, location: Location, ) -> ValueId { self.builder.set_location(location); @@ -614,7 +613,8 @@ impl<'a> FunctionContext<'a> { /// Create a const offset of an address for an array load or store pub(super) fn make_offset(&mut self, mut address: ValueId, offset: u128) -> ValueId { if offset != 0 { - let offset = self.builder.numeric_constant(offset, self.builder.type_of_value(address)); + let typ = self.builder.type_of_value(address).unwrap_numeric(); + let offset = self.builder.numeric_constant(offset, typ); address = self.builder.insert_binary(address, BinaryOp::Add, offset); } address @@ -622,7 +622,7 @@ impl<'a> FunctionContext<'a> { /// Array indexes are u32. This function casts values used as indexes to u32. pub(super) fn make_array_index(&mut self, index: ValueId) -> ValueId { - self.builder.insert_cast(index, Type::unsigned(SSA_WORD_SIZE)) + self.builder.insert_cast(index, NumericType::length_type()) } /// Define a local variable to be some Values that can later be retrieved @@ -870,12 +870,12 @@ impl<'a> FunctionContext<'a> { ) -> ValueId { let index = self.make_array_index(index); let element_size = - self.builder.numeric_constant(self.element_size(array), Type::unsigned(SSA_WORD_SIZE)); + self.builder.numeric_constant(self.element_size(array), NumericType::length_type()); // The actual base index is the user's index * the array element type's size let mut index = self.builder.set_location(location).insert_binary(index, BinaryOp::Mul, element_size); - let one = self.builder.numeric_constant(FieldElement::one(), Type::unsigned(SSA_WORD_SIZE)); + let one = self.builder.numeric_constant(FieldElement::one(), NumericType::length_type()); new_value.for_each(|value| { let value = value.eval(self); diff --git a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs index 91a49018f76..d341e5e9c4c 100644 --- a/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs +++ b/compiler/noirc_evaluator/src/ssa/ssa_gen/mod.rs @@ -23,6 +23,7 @@ use self::{ }; use super::ir::instruction::ErrorType; +use super::ir::types::NumericType; use super::{ function_builder::data_bus::DataBus, ir::{ @@ -223,12 +224,12 @@ impl<'a> FunctionContext<'a> { } ast::Literal::Integer(value, negative, typ, location) => { self.builder.set_location(*location); - let typ = Self::convert_non_tuple_type(typ); + let typ = Self::convert_non_tuple_type(typ).unwrap_numeric(); self.checked_numeric_constant(*value, *negative, typ).map(Into::into) } ast::Literal::Bool(value) => { // Don't need to call checked_numeric_constant here since `value` can only be true or false - Ok(self.builder.numeric_constant(*value as u128, Type::bool()).into()) + Ok(self.builder.numeric_constant(*value as u128, NumericType::bool()).into()) } ast::Literal::Str(string) => Ok(self.codegen_string(string)), ast::Literal::FmtStr(fragments, number_of_fields, fields) => { @@ -272,7 +273,7 @@ impl<'a> FunctionContext<'a> { fn codegen_string(&mut self, string: &str) -> Values { let elements = vecmap(string.as_bytes(), |byte| { - let char = self.builder.numeric_constant(*byte as u128, Type::unsigned(8)); + let char = self.builder.numeric_constant(*byte as u128, NumericType::char()); (char.into(), false) }); let typ = Self::convert_non_tuple_type(&ast::Type::String(elements.len() as u32)); @@ -349,7 +350,7 @@ impl<'a> FunctionContext<'a> { UnaryOp::Minus => { let rhs = self.codegen_expression(&unary.rhs)?; let rhs = rhs.into_leaf().eval(self); - let typ = self.builder.type_of_value(rhs); + let typ = self.builder.type_of_value(rhs).unwrap_numeric(); let zero = self.builder.numeric_constant(0u128, typ); Ok(self.insert_binary( zero, @@ -443,7 +444,7 @@ impl<'a> FunctionContext<'a> { let index = self.make_array_index(index); let type_size = Self::convert_type(element_type).size_of_type(); let type_size = - self.builder.numeric_constant(type_size as u128, Type::unsigned(SSA_WORD_SIZE)); + self.builder.numeric_constant(type_size as u128, NumericType::length_type()); let base_index = self.builder.set_location(location).insert_binary(index, BinaryOp::Mul, type_size); @@ -482,7 +483,7 @@ impl<'a> FunctionContext<'a> { .make_array_index(length.expect("ICE: a length must be supplied for indexing slices")); let is_offset_out_of_bounds = self.builder.insert_binary(index, BinaryOp::Lt, array_len); - let true_const = self.builder.numeric_constant(true, Type::bool()); + let true_const = self.builder.numeric_constant(true, NumericType::bool()); self.builder.insert_constrain( is_offset_out_of_bounds, @@ -493,7 +494,7 @@ impl<'a> FunctionContext<'a> { fn codegen_cast(&mut self, cast: &ast::Cast) -> Result { let lhs = self.codegen_non_tuple_expression(&cast.lhs)?; - let typ = Self::convert_non_tuple_type(&cast.r#type); + let typ = Self::convert_non_tuple_type(&cast.r#type).unwrap_numeric(); Ok(self.insert_safe_cast(lhs, typ, cast.location).into()) } @@ -730,7 +731,7 @@ impl<'a> FunctionContext<'a> { assert_payload: &Option>, ) -> Result { let expr = self.codegen_non_tuple_expression(expr)?; - let true_literal = self.builder.numeric_constant(true, Type::bool()); + let true_literal = self.builder.numeric_constant(true, NumericType::bool()); // Set the location here for any errors that may occur when we codegen the assert message self.builder.set_location(location);