diff --git a/crates/noirc_evaluator/src/lib.rs b/crates/noirc_evaluator/src/lib.rs index 9f39dc3baa4..157dc19fb49 100644 --- a/crates/noirc_evaluator/src/lib.rs +++ b/crates/noirc_evaluator/src/lib.rs @@ -15,7 +15,7 @@ use errors::{RuntimeError, RuntimeErrorKind}; use iter_extended::btree_map; use noirc_abi::{Abi, AbiType, AbiVisibility, MAIN_RETURN_NAME}; use noirc_frontend::monomorphization::ast::*; -use ssa::{node, ssa_gen::IrGenerator}; +use ssa::{node::ObjectType, ssa_gen::IrGenerator}; use std::collections::{BTreeMap, BTreeSet}; #[derive(Default)] @@ -165,7 +165,7 @@ impl Evaluator { ir_gen.create_new_variable( name.to_owned(), Some(def), - node::ObjectType::NativeField, + ObjectType::native_field(), Some(witness), ); vec![witness] @@ -187,7 +187,7 @@ impl Evaluator { AbiType::Boolean => { let witness = self.add_witness_to_cs(); ssa::acir_gen::range_constraint(witness, 1, self)?; - let obj_type = node::ObjectType::Boolean; + let obj_type = ObjectType::boolean(); ir_gen.create_new_variable(name.to_owned(), Some(def), obj_type, Some(witness)); vec![witness] diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/internal_var_cache.rs b/crates/noirc_evaluator/src/ssa/acir_gen/internal_var_cache.rs index 2e37da15cd0..e2ea1dba2cf 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/internal_var_cache.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/internal_var_cache.rs @@ -65,10 +65,7 @@ impl InternalVarCache { NodeObject::Variable(variable) => { let variable_type = variable.get_type(); match variable_type { - ObjectType::Boolean - | ObjectType::NativeField - | ObjectType::Signed(..) - | ObjectType::Unsigned(..) => { + ObjectType::Numeric(..) => { let witness = variable.witness.unwrap_or_else(|| evaluator.add_witness_to_cs()); InternalVar::from_witness(witness) diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/operations/binary.rs b/crates/noirc_evaluator/src/ssa/acir_gen/operations/binary.rs index b0f31f88ec1..bf1f59391f9 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/operations/binary.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/operations/binary.rs @@ -52,7 +52,7 @@ pub(crate) fn evaluate( BinaryOp::Sub { max_rhs_value } | BinaryOp::SafeSub { max_rhs_value } => { let l_c = acir_gen.var_cache.get_or_compute_internal_var_unwrap(binary.lhs, evaluator, ctx); let r_c = acir_gen.var_cache.get_or_compute_internal_var_unwrap(binary.rhs, evaluator, ctx); - if res_type == ObjectType::NativeField { + if res_type == ObjectType::native_field() { InternalVar::from(constraints::subtract( l_c.expression(), FieldElement::one(), diff --git a/crates/noirc_evaluator/src/ssa/anchor.rs b/crates/noirc_evaluator/src/ssa/anchor.rs index 5ca3f9b9064..53edc19ec4d 100644 --- a/crates/noirc_evaluator/src/ssa/anchor.rs +++ b/crates/noirc_evaluator/src/ssa/anchor.rs @@ -25,7 +25,7 @@ pub(super) enum CseAction { #[derive(Default, Clone)] pub(super) struct Anchor { map: HashMap>, //standard anchor - cast_map: HashMap>, //cast anchor + cast_map: HashMap>, //cast anchor mem_map: HashMap>>, //Memory anchor: one Vec for each array where Vec[i] contains the list of load and store instructions having index i, and the mem_item position in which they appear mem_list: HashMap>, // list of the memory instructions, per array, and grouped into MemItems } diff --git a/crates/noirc_evaluator/src/ssa/builtin.rs b/crates/noirc_evaluator/src/ssa/builtin.rs index 2ca9effe5df..8248322c488 100644 --- a/crates/noirc_evaluator/src/ssa/builtin.rs +++ b/crates/noirc_evaluator/src/ssa/builtin.rs @@ -80,7 +80,7 @@ impl Opcode { BlackBoxFunc::SchnorrVerify | BlackBoxFunc::EcdsaSecp256k1 | BlackBoxFunc::MerkleMembership => BigUint::one(), - BlackBoxFunc::HashToField128Security => ObjectType::NativeField.max_size(), + BlackBoxFunc::HashToField128Security => ObjectType::native_field().max_size(), BlackBoxFunc::AES => { todo!("ICE: AES is unimplemented") } @@ -108,21 +108,23 @@ impl Opcode { BlackBoxFunc::Keccak256 => { todo!("ICE: Keccak256 is unimplemented") } - BlackBoxFunc::SHA256 | BlackBoxFunc::Blake2s => (32, ObjectType::Unsigned(8)), - BlackBoxFunc::HashToField128Security => (1, ObjectType::NativeField), + BlackBoxFunc::SHA256 | BlackBoxFunc::Blake2s => { + (32, ObjectType::unsigned_integer(8)) + } + BlackBoxFunc::HashToField128Security => (1, ObjectType::native_field()), // See issue #775 on changing this to return a boolean BlackBoxFunc::MerkleMembership | BlackBoxFunc::SchnorrVerify - | BlackBoxFunc::EcdsaSecp256k1 => (1, ObjectType::NativeField), - BlackBoxFunc::Pedersen => (2, ObjectType::NativeField), - BlackBoxFunc::FixedBaseScalarMul => (2, ObjectType::NativeField), + | BlackBoxFunc::EcdsaSecp256k1 => (1, ObjectType::native_field()), + BlackBoxFunc::Pedersen => (2, ObjectType::native_field()), + BlackBoxFunc::FixedBaseScalarMul => (2, ObjectType::native_field()), BlackBoxFunc::RANGE | BlackBoxFunc::AND | BlackBoxFunc::XOR => { unreachable!("ICE: these opcodes do not have Noir builtin functions") } } } - Opcode::ToBits(_) => (FieldElement::max_num_bits(), ObjectType::Boolean), - Opcode::ToRadix(_) => (FieldElement::max_num_bits(), ObjectType::NativeField), + Opcode::ToBits(_) => (FieldElement::max_num_bits(), ObjectType::boolean()), + Opcode::ToRadix(_) => (FieldElement::max_num_bits(), ObjectType::native_field()), Opcode::Println(_) => (0, ObjectType::NotAnObject), Opcode::Sort => { let a = super::mem::Memory::deref(ctx, args[0]).unwrap(); @@ -135,7 +137,7 @@ impl Opcode { #[derive(Clone, Debug, Hash, Copy, PartialEq, Eq)] pub(crate) struct PrintlnInfo { // We store strings as arrays and there is no differentiation between them in the SSA. - // This bool simply states whether an array that is to be printed should be outputted as a utf8 string + // This bool simply states whether an array that is to be printed should be outputted as a utf8 string. pub(crate) is_string_output: bool, // This is a flag used during `nargo test` to determine whether to display println output. pub(crate) show_output: bool, diff --git a/crates/noirc_evaluator/src/ssa/conditional.rs b/crates/noirc_evaluator/src/ssa/conditional.rs index c3904d1c633..c70217bd920 100644 --- a/crates/noirc_evaluator/src/ssa/conditional.rs +++ b/crates/noirc_evaluator/src/ssa/conditional.rs @@ -5,7 +5,7 @@ use crate::{ context::SsaContext, flatten::UnrollContext, inline::StackFrame, - node::{BinaryOp, Instruction, Mark, NodeId, ObjectType, Opcode, Operation}, + node::{Binary, BinaryOp, Instruction, Mark, NodeId, ObjectType, Opcode, Operation}, {block, flatten, node, optimizations}, }, }; @@ -171,7 +171,7 @@ impl DecisionTree { BinaryOp::Mul, parent_value, condition, - ObjectType::Boolean, + ObjectType::boolean(), ) } else { let not_condition = DecisionTree::new_instruction_after_phi( @@ -180,7 +180,7 @@ impl DecisionTree { BinaryOp::Sub { max_rhs_value: BigUint::one() }, ctx.one(), condition, - ObjectType::Boolean, + ObjectType::boolean(), ); DecisionTree::new_instruction_after( ctx, @@ -188,7 +188,7 @@ impl DecisionTree { BinaryOp::Mul, parent_value, not_condition, - ObjectType::Boolean, + ObjectType::boolean(), not_condition, ) }; @@ -480,7 +480,7 @@ impl DecisionTree { Operation::Cond { condition, val_true: ctx.zero(), val_false: ctx.one() }; let cond = ctx.add_instruction(Instruction::new( operation, - ObjectType::Boolean, + ObjectType::boolean(), Some(stack.block), )); stack.push(cond); @@ -595,7 +595,7 @@ impl DecisionTree { }); cond = ctx.add_instruction(Instruction::new( op, - ObjectType::Boolean, + ObjectType::boolean(), Some(stack.block), )); optimizations::simplify_id(ctx, cond).unwrap(); @@ -624,7 +624,7 @@ impl DecisionTree { } if ctx.under_assumption(cond) { let ins2 = ctx.instruction_mut(ins_id); - ins2.operation = Operation::Binary(crate::node::Binary { + ins2.operation = Operation::Binary(Binary { lhs: binary_op.lhs, rhs: binary_op.rhs, operator: binary_op.operator.clone(), @@ -724,7 +724,7 @@ impl DecisionTree { } let cond = ctx.add_instruction(Instruction::new( operation, - ObjectType::Boolean, + ObjectType::boolean(), Some(stack.block), )); stack.push(cond); @@ -775,7 +775,7 @@ impl DecisionTree { }); let cond = ctx.add_instruction(Instruction::new( op, - ObjectType::Boolean, + ObjectType::boolean(), Some(stack_frame.block), )); optimizations::simplify_id(ctx, cond).unwrap(); @@ -808,7 +808,7 @@ impl DecisionTree { }); let cond = ctx.add_instruction(Instruction::new( op, - ObjectType::Boolean, + ObjectType::boolean(), Some(stack_frame.block), )); optimizations::simplify_id(ctx, cond).unwrap(); diff --git a/crates/noirc_evaluator/src/ssa/context.rs b/crates/noirc_evaluator/src/ssa/context.rs index 13baef1718f..236565b2536 100644 --- a/crates/noirc_evaluator/src/ssa/context.rs +++ b/crates/noirc_evaluator/src/ssa/context.rs @@ -65,19 +65,19 @@ impl Default for SsaContext { constants: HashMap::new(), }; block::create_first_block(&mut pc); - pc.one_with_type(node::ObjectType::Boolean); - pc.zero_with_type(node::ObjectType::Boolean); + pc.one_with_type(ObjectType::boolean()); + pc.zero_with_type(ObjectType::boolean()); pc } } impl SsaContext { pub(crate) fn zero(&self) -> NodeId { - self.find_const_with_type(&FieldElement::zero(), node::ObjectType::Boolean).unwrap() + self.find_const_with_type(&FieldElement::zero(), ObjectType::boolean()).unwrap() } pub(crate) fn one(&self) -> NodeId { - self.find_const_with_type(&FieldElement::one(), node::ObjectType::Boolean).unwrap() + self.find_const_with_type(&FieldElement::one(), ObjectType::boolean()).unwrap() } pub(crate) fn zero_with_type(&mut self, obj_type: ObjectType) -> NodeId { @@ -139,7 +139,7 @@ impl SsaContext { predicate: None, location: None, }; - let dummy_store = node::Instruction::new(op_a, node::ObjectType::NotAnObject, None); + let dummy_store = node::Instruction::new(op_a, ObjectType::NotAnObject, None); let id = self.add_instruction(dummy_store); self.dummy_store.insert(a, id); } @@ -621,7 +621,7 @@ impl SsaContext { | Binary(node::Binary { operator: Slt, .. }) | Binary(node::Binary { operator: Sle, .. }) | Binary(node::Binary { operator: Lt, .. }) - | Binary(node::Binary { operator: Lte, .. }) => ObjectType::Boolean, + | Binary(node::Binary { operator: Lte, .. }) => ObjectType::boolean(), Operation::Jne(_, _) | Operation::Jeq(_, _) | Operation::Jmp(_) @@ -649,7 +649,7 @@ impl SsaContext { //we create a variable pointing to this MemArray let new_var = node::Variable { id: NodeId::dummy(), - obj_type: node::ObjectType::Pointer(array_index), + obj_type: ObjectType::Pointer(array_index), name: name.to_string(), root: None, def: def.clone(), @@ -786,10 +786,14 @@ impl SsaContext { let len = self.mem[a].len; let e_type = self.mem[b].element_type; for i in 0..len { - let idx_b = self - .get_or_create_const(FieldElement::from(i as i128), ObjectType::Unsigned(32)); - let idx_a = self - .get_or_create_const(FieldElement::from(i as i128), ObjectType::Unsigned(32)); + let idx_b = self.get_or_create_const( + FieldElement::from(i as i128), + ObjectType::unsigned_integer(32), + ); + let idx_a = self.get_or_create_const( + FieldElement::from(i as i128), + ObjectType::unsigned_integer(32), + ); let op_b = Operation::Load { array_id: b, index: idx_b, location: None }; let load = self.new_instruction(op_b, e_type)?; let op_a = Operation::Store { @@ -957,8 +961,10 @@ impl SsaContext { let e_type = self.mem[array_id].element_type; assert_eq!(len, values.len()); for (i, v) in values.iter().enumerate() { - let index = - self.get_or_create_const(FieldElement::from(i as i128), ObjectType::Unsigned(32)); + let index = self.get_or_create_const( + FieldElement::from(i as i128), + ObjectType::unsigned_integer(32), + ); let op_a = Operation::Store { array_id, index, value: *v, predicate: None, location: None }; self.new_instruction_inline(op_a, e_type, stack_frame); @@ -979,10 +985,14 @@ impl SsaContext { let len = self.mem[a].len; let e_type = self.mem[b].element_type; for i in 0..len { - let idx_b = self - .get_or_create_const(FieldElement::from(i as i128), ObjectType::Unsigned(32)); - let idx_a = self - .get_or_create_const(FieldElement::from(i as i128), ObjectType::Unsigned(32)); + let idx_b = self.get_or_create_const( + FieldElement::from(i as i128), + ObjectType::unsigned_integer(32), + ); + let idx_a = self.get_or_create_const( + FieldElement::from(i as i128), + ObjectType::unsigned_integer(32), + ); let op_b = Operation::Load { array_id: b, index: idx_b, location: None }; let load = self.new_instruction_inline(op_b, e_type, stack_frame); let op_a = Operation::Store { @@ -1071,13 +1081,13 @@ impl SsaContext { let name = format!("if_{}_ret{c}", exit_block.0.into_raw_parts().0); *c += 1; - if let node::ObjectType::Pointer(adr1) = a_type { + if let ObjectType::Pointer(adr1) = a_type { let len = self.mem[adr1].len; let el_type = self.mem[adr1].element_type; let (id, array_id) = self.new_array(&name, el_type, len, None); for i in 0..len { let index = self - .get_or_create_const(FieldElement::from(i as u128), ObjectType::NativeField); + .get_or_create_const(FieldElement::from(i as u128), ObjectType::native_field()); self.current_block = block1; let op = Operation::Load { array_id: adr1, index, location: None }; let v1 = self.new_instruction(op, el_type).unwrap(); @@ -1178,16 +1188,16 @@ impl SsaContext { pub(crate) fn convert_type(&mut self, t: &Type) -> ObjectType { use noirc_frontend::Signedness; match t { - Type::Bool => ObjectType::Boolean, - Type::Field => ObjectType::NativeField, + Type::Bool => ObjectType::boolean(), + Type::Field => ObjectType::native_field(), Type::Integer(sign, bit_size) => { assert!( *bit_size < super::integer::short_integer_max_bit_size(), "long integers are not yet supported" ); match sign { - Signedness::Signed => ObjectType::Signed(*bit_size), - Signedness::Unsigned => ObjectType::Unsigned(*bit_size), + Signedness::Signed => ObjectType::signed_integer(*bit_size), + Signedness::Unsigned => ObjectType::unsigned_integer(*bit_size), } } Type::Array(..) => panic!("Cannot convert an array type {t} into an ObjectType since it is unknown which array it refers to"), @@ -1222,7 +1232,7 @@ impl SsaContext { }); let cond = self.add_instruction(Instruction::new( op, - ObjectType::Boolean, + ObjectType::boolean(), Some(stack.block), )); optimizations::simplify_id(self, cond).unwrap(); @@ -1239,7 +1249,7 @@ impl SsaContext { Operation::Cond { condition: pred, val_true: *cond, val_false: self.one() }; let c_ins = self.add_instruction(Instruction::new( operation, - ObjectType::Boolean, + ObjectType::boolean(), Some(stack.block), )); stack.push(c_ins); diff --git a/crates/noirc_evaluator/src/ssa/integer.rs b/crates/noirc_evaluator/src/ssa/integer.rs index 826076af0db..9a48286f42f 100644 --- a/crates/noirc_evaluator/src/ssa/integer.rs +++ b/crates/noirc_evaluator/src/ssa/integer.rs @@ -54,7 +54,8 @@ fn get_instruction_max_operand( Operation::Binary(node::Binary { operator, lhs, rhs, .. }) => { if let BinaryOp::Sub { .. } = operator { //TODO uses interval analysis instead - if matches!(ins.res_type, ObjectType::Unsigned(_) | ObjectType::Boolean) { + // Note that a boolean is also handled as an unsigned integer + if ins.res_type.is_unsigned_integer() { if let Some(lhs_const) = ctx.get_as_constant(*lhs) { let lhs_big = BigUint::from_bytes_be(&lhs_const.to_be_bytes()); if max_map[rhs] <= lhs_big { @@ -266,14 +267,14 @@ fn block_overflow( let ins_max_bits = get_instruction_max(ctx, &ins, max_map, &value_map).bits(); let res_type = ins.res_type; - let too_many_bits = ins_max_bits > FieldElement::max_num_bits() as u64 - && res_type != ObjectType::NativeField; + let too_many_bits = + ins_max_bits > FieldElement::max_num_bits() as u64 && !res_type.is_native_field(); ins.operation.for_each_id(|id| { get_obj_max_value(ctx, id, max_map, &value_map); let arg = ctx.try_get_node(id); let should_truncate_arg = - should_truncate_ins && arg.is_some() && get_type(arg) != ObjectType::NativeField; + should_truncate_ins && arg.is_some() && !get_type(arg).is_native_field(); if should_truncate_arg || too_many_bits { add_to_truncate(ctx, id, get_size_in_bits(arg), &mut truncate_map, max_map); @@ -312,7 +313,7 @@ fn block_overflow( } } Operation::Binary(node::Binary { operator: BinaryOp::Shr(loc), lhs, rhs, .. }) => { - if !matches!(ins.res_type, node::ObjectType::Unsigned(_)) { + if !ins.res_type.is_unsigned_integer() { todo!("Right shift is only implemented for unsigned integers"); } if let Some(r_const) = ctx.get_as_constant(rhs) { @@ -494,7 +495,7 @@ fn get_max_value(ins: &Instruction, max_map: &mut HashMap) -> B Operation::Intrinsic(opcode, _) => opcode.get_max_value(), }; - if ins.res_type == ObjectType::NativeField { + if ins.res_type.is_native_field() { let field_max = BigUint::from_bytes_be(&FieldElement::one().neg().to_be_bytes()); //Native Field operations cannot overflow so they will not be truncated diff --git a/crates/noirc_evaluator/src/ssa/node.rs b/crates/noirc_evaluator/src/ssa/node.rs index 9e6881aca7e..31c497b4e9e 100644 --- a/crates/noirc_evaluator/src/ssa/node.rs +++ b/crates/noirc_evaluator/src/ssa/node.rs @@ -177,28 +177,23 @@ impl Variable { #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub(crate) enum ObjectType { - NativeField, - Boolean, - Unsigned(u32), //bit size - Signed(u32), //bit size + Numeric(NumericType), Pointer(ArrayId), Function, NotAnObject, //not an object } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub(crate) enum NumericType { - Signed(u32), - Unsigned(u32), + Signed(u32), // bit size + Unsigned(u32), // bit size NativeField, } impl From for NumericType { fn from(object_type: ObjectType) -> NumericType { match object_type { - ObjectType::Signed(x) => NumericType::Signed(x), - ObjectType::Unsigned(x) => NumericType::Unsigned(x), - ObjectType::NativeField => NumericType::NativeField, + ObjectType::Numeric(numeric_type) => numeric_type, _ => unreachable!("failed to convert an object type into a numeric type"), } } @@ -207,34 +202,71 @@ impl From for NumericType { impl ObjectType { pub(crate) fn bits(&self) -> u32 { match self { - ObjectType::Boolean => 1, - ObjectType::NativeField => FieldElement::max_num_bits(), ObjectType::NotAnObject => 0, - ObjectType::Signed(c) => *c, - ObjectType::Unsigned(c) => *c, ObjectType::Pointer(_) => 0, ObjectType::Function => 0, + ObjectType::Numeric(numeric_type) => match numeric_type { + NumericType::Signed(c) | NumericType::Unsigned(c) => *c, + NumericType::NativeField => FieldElement::max_num_bits(), + }, } } + /// Returns the type that represents + /// a field element. + /// The most basic/fundamental type in the language + pub(crate) fn native_field() -> ObjectType { + ObjectType::Numeric(NumericType::NativeField) + } + /// Returns a type that represents an unsigned integer + pub(crate) fn unsigned_integer(bit_size: u32) -> ObjectType { + ObjectType::Numeric(NumericType::Unsigned(bit_size)) + } + /// Returns a type that represents an boolean + /// Booleans are just seen as an unsigned integer + /// with a bit size of 1. + pub(crate) fn boolean() -> ObjectType { + ObjectType::unsigned_integer(1) + } + /// Returns a type that represents an signed integer + pub(crate) fn signed_integer(bit_size: u32) -> ObjectType { + ObjectType::Numeric(NumericType::Signed(bit_size)) + } + + /// Returns true, if the `ObjectType` + /// represents a field element + pub(crate) fn is_native_field(&self) -> bool { + matches!(self, ObjectType::Numeric(NumericType::NativeField)) + } + /// Returns true, if the `ObjectType` + /// represents an unsigned integer + pub(crate) fn is_unsigned_integer(&self) -> bool { + matches!(self, ObjectType::Numeric(NumericType::Unsigned(_))) + } + //maximum size of the representation (e.g. signed(8).max_size() return 255, not 128.) pub(crate) fn max_size(&self) -> BigUint { match self { - &ObjectType::NativeField => { + ObjectType::Numeric(NumericType::NativeField) => { BigUint::from_bytes_be(&FieldElement::from(-1_i128).to_be_bytes()) } _ => (BigUint::one() << self.bits()) - BigUint::one(), } } + // TODO: the name of this function is misleading + // TODO since the type is not being returned pub(crate) fn field_to_type(&self, f: FieldElement) -> FieldElement { match self { + // TODO: document why this is unreachable ObjectType::NotAnObject | ObjectType::Pointer(_) => { unreachable!() } - ObjectType::NativeField => f, - ObjectType::Signed(_) => todo!(), - _ => { + ObjectType::Numeric(NumericType::NativeField) => f, + // TODO: document why this is a TODO and create an issue + ObjectType::Numeric(NumericType::Signed(_)) => todo!(), + ObjectType::Function | ObjectType::Numeric(NumericType::Unsigned(_)) => { + // TODO: document where this 128 comes from assert!(self.bits() < 128); FieldElement::from(f.to_u128() % (1_u128 << self.bits())) } @@ -382,7 +414,7 @@ impl Instruction { } Operation::Cast(value) => { if let Some(l_const) = eval_fn(ctx, *value)?.into_const_value() { - if self.res_type == ObjectType::NativeField { + if self.res_type.is_native_field() { return Ok(NodeEval::Const(l_const, self.res_type)); } else if let Some(l_const) = l_const.try_into_u128() { return Ok(NodeEval::Const( @@ -879,63 +911,69 @@ impl Binary { } BinaryOp::Ult => { if r_is_zero { - return Ok(NodeEval::Const(FieldElement::zero(), ObjectType::Boolean)); + return Ok(NodeEval::Const(FieldElement::zero(), ObjectType::boolean())); //n.b we assume the type of lhs and rhs is unsigned because of the opcode, we could also verify this } else if let (Some(lhs), Some(rhs)) = (lhs, rhs) { - assert_ne!(res_type, ObjectType::NativeField); //comparisons are not implemented for field elements + assert!( + !res_type.is_native_field(), + "ICE: comparisons are not implemented for field elements" + ); let res = if lhs < rhs { FieldElement::one() } else { FieldElement::zero() }; - return Ok(NodeEval::Const(res, ObjectType::Boolean)); + return Ok(NodeEval::Const(res, ObjectType::boolean())); } } BinaryOp::Ule => { if l_is_zero { - return Ok(NodeEval::Const(FieldElement::one(), ObjectType::Boolean)); + return Ok(NodeEval::Const(FieldElement::one(), ObjectType::boolean())); //n.b we assume the type of lhs and rhs is unsigned because of the opcode, we could also verify this } else if let (Some(lhs), Some(rhs)) = (lhs, rhs) { - assert_ne!(res_type, ObjectType::NativeField); //comparisons are not implemented for field elements + assert!( + !res_type.is_native_field(), + "ICE: comparisons are not implemented for field elements" + ); let res = if lhs <= rhs { FieldElement::one() } else { FieldElement::zero() }; - return Ok(NodeEval::Const(res, ObjectType::Boolean)); + return Ok(NodeEval::Const(res, ObjectType::boolean())); } } BinaryOp::Slt => (), BinaryOp::Sle => (), BinaryOp::Lt => { if r_is_zero { - return Ok(NodeEval::Const(FieldElement::zero(), ObjectType::Boolean)); + return Ok(NodeEval::Const(FieldElement::zero(), ObjectType::boolean())); //n.b we assume the type of lhs and rhs is unsigned because of the opcode, we could also verify this } else if let (Some(lhs), Some(rhs)) = (lhs, rhs) { let res = if lhs < rhs { FieldElement::one() } else { FieldElement::zero() }; - return Ok(NodeEval::Const(res, ObjectType::Boolean)); + return Ok(NodeEval::Const(res, ObjectType::boolean())); } } BinaryOp::Lte => { if l_is_zero { - return Ok(NodeEval::Const(FieldElement::one(), ObjectType::Boolean)); + return Ok(NodeEval::Const(FieldElement::one(), ObjectType::boolean())); //n.b we assume the type of lhs and rhs is unsigned because of the opcode, we could also verify this } else if let (Some(lhs), Some(rhs)) = (lhs, rhs) { let res = if lhs <= rhs { FieldElement::one() } else { FieldElement::zero() }; - return Ok(NodeEval::Const(res, ObjectType::Boolean)); + return Ok(NodeEval::Const(res, ObjectType::boolean())); } } BinaryOp::Eq => { if self.lhs == self.rhs { - return Ok(NodeEval::Const(FieldElement::one(), ObjectType::Boolean)); + return Ok(NodeEval::Const(FieldElement::one(), ObjectType::boolean())); } else if let (Some(lhs), Some(rhs)) = (lhs, rhs) { if lhs == rhs { - return Ok(NodeEval::Const(FieldElement::one(), ObjectType::Boolean)); + return Ok(NodeEval::Const(FieldElement::one(), ObjectType::boolean())); } else { - return Ok(NodeEval::Const(FieldElement::zero(), ObjectType::Boolean)); + return Ok(NodeEval::Const(FieldElement::zero(), ObjectType::boolean())); } } } BinaryOp::Ne => { if self.lhs == self.rhs { - return Ok(NodeEval::Const(FieldElement::zero(), ObjectType::Boolean)); + return Ok(NodeEval::Const(FieldElement::zero(), ObjectType::boolean())); } else if let (Some(lhs), Some(rhs)) = (lhs, rhs) { if lhs != rhs { - return Ok(NodeEval::Const(FieldElement::one(), ObjectType::Boolean)); + return Ok(NodeEval::Const(FieldElement::one(), ObjectType::boolean())); } else { - return Ok(NodeEval::Const(FieldElement::zero(), ObjectType::Boolean)); + return Ok(NodeEval::Const(FieldElement::zero(), ObjectType::boolean())); } } } @@ -1081,7 +1119,7 @@ fn wrapping( u128_op: impl FnOnce(u128, u128) -> u128, field_op: impl FnOnce(FieldElement, FieldElement) -> FieldElement, ) -> NodeEval { - if res_type != ObjectType::NativeField { + if !res_type.is_native_field() { let type_modulo = 1_u128 << res_type.bits(); let lhs = lhs.to_u128() % type_modulo; let rhs = rhs.to_u128() % type_modulo; @@ -1299,7 +1337,8 @@ impl BinaryOp { ) } } - +// TODO: We should create a constant and explain where the 127 and 126 constants +// TODO are from fn field_to_signed(f: FieldElement, n: u32) -> i128 { assert!(n < 127); let a = f.to_u128(); diff --git a/crates/noirc_evaluator/src/ssa/optimizations.rs b/crates/noirc_evaluator/src/ssa/optimizations.rs index 1afac20b866..985a53cf754 100644 --- a/crates/noirc_evaluator/src/ssa/optimizations.rs +++ b/crates/noirc_evaluator/src/ssa/optimizations.rs @@ -87,7 +87,7 @@ fn evaluate_intrinsic( for i in 0..bit_count { let index = ctx.get_or_create_const( FieldElement::from(i as i128), - ObjectType::NativeField, + ObjectType::native_field(), ); let op = if args[0] & (1 << i) != 0 { Operation::Store { @@ -136,11 +136,11 @@ fn evaluate_intrinsic( for (i, item) in element.iter().enumerate() { let index = ctx.get_or_create_const( FieldElement::from(i as i128), - ObjectType::NativeField, + ObjectType::native_field(), ); let value = ctx.get_or_create_const( FieldElement::from(*item as i128), - ObjectType::NativeField, + ObjectType::native_field(), ); let op = Operation::Store { array_id: *a, @@ -369,7 +369,7 @@ fn cse_block_with_anchor( }); let pred_id = ctx.add_instruction(Instruction::new( or_op, - ObjectType::Boolean, + ObjectType::boolean(), Some(block_id), )); new_list.push(pred_id); diff --git a/crates/noirc_evaluator/src/ssa/ssa_gen.rs b/crates/noirc_evaluator/src/ssa/ssa_gen.rs index ac4b9b297b7..82d0cadde0d 100644 --- a/crates/noirc_evaluator/src/ssa/ssa_gen.rs +++ b/crates/noirc_evaluator/src/ssa/ssa_gen.rs @@ -74,12 +74,12 @@ impl IrGenerator { pub(crate) fn get_object_type_from_abi(&self, el_type: &noirc_abi::AbiType) -> ObjectType { match el_type { - noirc_abi::AbiType::Field => ObjectType::NativeField, + noirc_abi::AbiType::Field => ObjectType::native_field(), noirc_abi::AbiType::Integer { sign, width, .. } => match sign { - noirc_abi::Sign::Unsigned => ObjectType::Unsigned(*width), - noirc_abi::Sign::Signed => ObjectType::Signed(*width), + noirc_abi::Sign::Unsigned => ObjectType::unsigned_integer(*width), + noirc_abi::Sign::Signed => ObjectType::signed_integer(*width), }, - noirc_abi::AbiType::Boolean => ObjectType::Boolean, + noirc_abi::AbiType::Boolean => ObjectType::boolean(), noirc_abi::AbiType::Array { .. } => { unreachable!("array of arrays are not supported for now") } @@ -319,7 +319,8 @@ impl IrGenerator { Value::Node(v_id) } Type::String(len) => { - let obj_type = ObjectType::Unsigned(8); + // Strings are a packed array of utf-8 encoded bytes + let obj_type = ObjectType::unsigned_integer(8); let len = *len; let (v_id, _) = self.new_array(base_name, obj_type, len.try_into().unwrap(), def); Value::Node(v_id) @@ -594,7 +595,7 @@ impl IrGenerator { for (pos, object) in elements.into_iter().enumerate() { let lhs_adr = self.context.get_or_create_const( FieldElement::from((pos as u32) as u128), - ObjectType::NativeField, + ObjectType::native_field(), ); let store = Operation::Store { array_id, @@ -687,7 +688,7 @@ impl IrGenerator { self.update_variable_id(iter_id, iter_id, phi); //is it still needed? let not_equal = Operation::binary(BinaryOp::Ne, phi, end_idx); - let cond = self.context.new_instruction(not_equal, ObjectType::Boolean)?; + let cond = self.context.new_instruction(not_equal, ObjectType::boolean())?; let to_fix = self.context.new_instruction(Operation::Nop, ObjectType::NotAnObject)?;