Skip to content

Commit

Permalink
feat: remove truncation from brillig casts
Browse files Browse the repository at this point in the history
  • Loading branch information
TomAFrench committed Jan 10, 2024
1 parent 5be049e commit 621b22f
Show file tree
Hide file tree
Showing 3 changed files with 12 additions and 81 deletions.
47 changes: 12 additions & 35 deletions compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -541,12 +541,7 @@ impl<'block> BrilligBlock<'block> {
dfg,
);
let source_register = self.convert_ssa_register_value(*value, dfg);
self.convert_cast(
destination_register,
source_register,
target_type,
&dfg.type_of_value(*value),
);
self.convert_cast(destination_register, source_register, target_type);
}
Instruction::ArrayGet { array, index } => {
let result_ids = dfg.instruction_results(instruction_id);
Expand Down Expand Up @@ -1097,38 +1092,20 @@ impl<'block> BrilligBlock<'block> {
destination: RegisterIndex,
source: RegisterIndex,
target_type: &Type,
source_type: &Type,
) {
fn numeric_to_bit_size(typ: &NumericType) -> u32 {
match typ {
NumericType::Signed { bit_size } | NumericType::Unsigned { bit_size } => *bit_size,
NumericType::NativeField => FieldElement::max_num_bits(),
}
}
// Casting is only valid for numeric types
// This should be checked by the frontend, so we panic if this is the case
let (source_numeric_type, target_numeric_type) = match (source_type, target_type) {
(Type::Numeric(source_numeric_type), Type::Numeric(target_numeric_type)) => {
(source_numeric_type, target_numeric_type)
}
_ => unimplemented!("The cast operation is only valid for integers."),
};
let source_bit_size = numeric_to_bit_size(source_numeric_type);
let target_bit_size = numeric_to_bit_size(target_numeric_type);
// Casting from a larger bit size to a smaller bit size (narrowing cast)
// requires a cast instruction.
// If its a widening cast, ie casting from a smaller bit size to a larger bit size
// we simply put a mov instruction as a no-op
//
// Field elements by construction always have the largest bit size
// This means that casting to a Field element, will always be a widening cast
// and therefore a no-op. Conversely, casting from a Field element
// will always be a narrowing cast and therefore a cast instruction
if source_bit_size > target_bit_size {
self.brillig_context.cast_instruction(destination, source, target_bit_size);
} else {
self.brillig_context.mov_instruction(destination, source);
}
let target_bit_size = target_type.bit_size();

assert!(
target_bit_size <= BRILLIG_INTEGER_ARITHMETIC_BIT_SIZE,
"tried to cast to a bit size greater than allowed {target_bit_size}"
);

// We assume that `source` is a valid `target_type` as it's expected that a truncate instruction was emitted
// to ensure this is the case.

self.brillig_context.mov_instruction(destination, source);
}

/// Converts the Binary instruction into a sequence of Brillig opcodes.
Expand Down
30 changes: 0 additions & 30 deletions compiler/noirc_evaluator/src/brillig/brillig_ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -761,36 +761,6 @@ impl BrilligContext {
self.deallocate_register(scratch_register_j);
}

/// Emits a modulo instruction against 2**target_bit_size
///
/// Integer arithmetic in Brillig is currently constrained to 127 bit integers.
/// We restrict the cast operation, so that integer types over 127 bits
/// cannot be created.
pub(crate) fn cast_instruction(
&mut self,
destination: RegisterIndex,
source: RegisterIndex,
target_bit_size: u32,
) {
self.debug_show.cast_instruction(destination, source, target_bit_size);
assert!(
target_bit_size <= BRILLIG_INTEGER_ARITHMETIC_BIT_SIZE,
"tried to cast to a bit size greater than allowed {target_bit_size}"
);

// The brillig VM performs all arithmetic operations modulo 2**bit_size
// So to cast any value to a target bit size we can just issue a no-op arithmetic operation
// With bit size equal to target_bit_size
let zero_register = self.make_constant(Value::from(FieldElement::zero()));
self.binary_instruction(
source,
zero_register,
destination,
BrilligBinaryOp::Integer { op: BinaryIntOp::Add, bit_size: target_bit_size },
);
self.deallocate_register(zero_register);
}

/// Adds a unresolved external `Call` instruction to the bytecode.
/// This calls into another function compiled into this brillig artifact.
pub(crate) fn add_external_call_instruction<T: ToString>(&mut self, func_label: T) {
Expand Down
16 changes: 0 additions & 16 deletions compiler/noirc_evaluator/src/brillig/brillig_ir/debug_show.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,22 +325,6 @@ impl DebugShow {
);
}

/// Debug function for cast_instruction
pub(crate) fn cast_instruction(
&self,
destination: RegisterIndex,
source: RegisterIndex,
target_bit_size: u32,
) {
debug_println!(
self.enable_debug_trace,
" CAST {} FROM {} TO {} BITS",
destination,
source,
target_bit_size
);
}

/// Debug function for black_box_op
pub(crate) fn black_box_op_instruction(&self, op: BlackBoxOp) {
match op {
Expand Down

0 comments on commit 621b22f

Please sign in to comment.