Skip to content

Commit

Permalink
signed right shift for brillig
Browse files Browse the repository at this point in the history
  • Loading branch information
guipublic committed Nov 4, 2024
1 parent 2a03854 commit 28deb05
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 95 deletions.
156 changes: 61 additions & 95 deletions compiler/noirc_evaluator/src/brillig/brillig_gen/brillig_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::brillig::brillig_ir::artifact::Label;
use crate::brillig::brillig_ir::brillig_variable::{
type_to_heap_value_type, BrilligArray, BrilligVariable, SingleAddrVariable,
};

use crate::brillig::brillig_ir::registers::Stack;
use crate::brillig::brillig_ir::{
BrilligBinaryOp, BrilligContext, ReservedRegisters, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE,
Expand Down Expand Up @@ -1202,7 +1203,7 @@ impl<'block> BrilligBlock<'block> {
let brillig_binary_op = match binary.operator {
BinaryOp::Div => {
if is_signed {
self.convert_signed_division(left, right, result_variable);
self.brillig_context.convert_signed_division(left, right, result_variable);
return;
} else if is_field {
BrilligBinaryOp::FieldDiv
Expand Down Expand Up @@ -1234,7 +1235,14 @@ impl<'block> BrilligBlock<'block> {
BinaryOp::Or => BrilligBinaryOp::Or,
BinaryOp::Xor => BrilligBinaryOp::Xor,
BinaryOp::Shl => BrilligBinaryOp::Shl,
BinaryOp::Shr => BrilligBinaryOp::Shr,
BinaryOp::Shr => {
if is_signed {
self.convert_signed_shr(left, right, result_variable);
return;
} else {
BrilligBinaryOp::Shr
}
}
};

self.brillig_context.binary_instruction(left, right, result_variable, brillig_binary_op);
Expand All @@ -1250,98 +1258,6 @@ impl<'block> BrilligBlock<'block> {
);
}

/// Splits a two's complement signed integer in the sign bit and the absolute value.
/// For example, -6 i8 (11111010) is split to 00000110 (6, absolute value) and 1 (is_negative).
fn absolute_value(
&mut self,
num: SingleAddrVariable,
absolute_value: SingleAddrVariable,
result_is_negative: SingleAddrVariable,
) {
let max_positive = self
.brillig_context
.make_constant_instruction(((1_u128 << (num.bit_size - 1)) - 1).into(), num.bit_size);

// Compute if num is negative
self.brillig_context.binary_instruction(
max_positive,
num,
result_is_negative,
BrilligBinaryOp::LessThan,
);

// Two's complement of num
let zero = self.brillig_context.make_constant_instruction(0_usize.into(), num.bit_size);
let twos_complement =
SingleAddrVariable::new(self.brillig_context.allocate_register(), num.bit_size);
self.brillig_context.binary_instruction(zero, num, twos_complement, BrilligBinaryOp::Sub);

// absolute_value = result_is_negative ? twos_complement : num
self.brillig_context.codegen_branch(result_is_negative.address, |ctx, is_negative| {
if is_negative {
ctx.mov_instruction(absolute_value.address, twos_complement.address);
} else {
ctx.mov_instruction(absolute_value.address, num.address);
}
});

self.brillig_context.deallocate_single_addr(zero);
self.brillig_context.deallocate_single_addr(max_positive);
self.brillig_context.deallocate_single_addr(twos_complement);
}

fn convert_signed_division(
&mut self,
left: SingleAddrVariable,
right: SingleAddrVariable,
result: SingleAddrVariable,
) {
let left_is_negative = SingleAddrVariable::new(self.brillig_context.allocate_register(), 1);
let left_abs_value =
SingleAddrVariable::new(self.brillig_context.allocate_register(), left.bit_size);

let right_is_negative =
SingleAddrVariable::new(self.brillig_context.allocate_register(), 1);
let right_abs_value =
SingleAddrVariable::new(self.brillig_context.allocate_register(), right.bit_size);

let result_is_negative =
SingleAddrVariable::new(self.brillig_context.allocate_register(), 1);

// Compute both absolute values
self.absolute_value(left, left_abs_value, left_is_negative);
self.absolute_value(right, right_abs_value, right_is_negative);

// Perform the division on the absolute values
self.brillig_context.binary_instruction(
left_abs_value,
right_abs_value,
result,
BrilligBinaryOp::UnsignedDiv,
);

// Compute result sign
self.brillig_context.binary_instruction(
left_is_negative,
right_is_negative,
result_is_negative,
BrilligBinaryOp::Xor,
);

// If result has to be negative, perform two's complement
self.brillig_context.codegen_if(result_is_negative.address, |ctx| {
let zero = ctx.make_constant_instruction(0_usize.into(), result.bit_size);
ctx.binary_instruction(zero, result, result, BrilligBinaryOp::Sub);
ctx.deallocate_single_addr(zero);
});

self.brillig_context.deallocate_single_addr(left_is_negative);
self.brillig_context.deallocate_single_addr(left_abs_value);
self.brillig_context.deallocate_single_addr(right_is_negative);
self.brillig_context.deallocate_single_addr(right_abs_value);
self.brillig_context.deallocate_single_addr(result_is_negative);
}

fn convert_signed_modulo(
&mut self,
left: SingleAddrVariable,
Expand All @@ -1354,7 +1270,7 @@ impl<'block> BrilligBlock<'block> {
SingleAddrVariable::new(self.brillig_context.allocate_register(), left.bit_size);

// i = left / right
self.convert_signed_division(left, right, scratch_var_i);
self.brillig_context.convert_signed_division(left, right, scratch_var_i);

// j = i * right
self.brillig_context.binary_instruction(
Expand Down Expand Up @@ -1401,6 +1317,56 @@ impl<'block> BrilligBlock<'block> {
self.brillig_context.deallocate_single_addr(bias);
}

fn convert_signed_shr(
&mut self,
left: SingleAddrVariable,
right: SingleAddrVariable,
result: SingleAddrVariable,
) {
// Check if left is negative
let left_is_negative = SingleAddrVariable::new(self.brillig_context.allocate_register(), 1);
let max_positive = self
.brillig_context
.make_constant_instruction(((1_u128 << (left.bit_size - 1)) - 1).into(), left.bit_size);
self.brillig_context.binary_instruction(
max_positive,
left,
left_is_negative,
BrilligBinaryOp::LessThan,
);

self.brillig_context.codegen_branch(left_is_negative.address, |ctx, is_negative| {
if is_negative {
let one = ctx.make_constant_instruction(1_u128.into(), left.bit_size);

// computes 2^right
let two = ctx.make_constant_instruction(2_u128.into(), left.bit_size);
let two_pow = ctx.make_constant_instruction(1_u128.into(), left.bit_size);
let right_u32 = SingleAddrVariable::new(ctx.allocate_register(), 32);
ctx.cast(right_u32, right);
let pow_body = |ctx: &mut BrilligContext<_, _>, _: SingleAddrVariable| {
ctx.binary_instruction(two_pow, two, two_pow, BrilligBinaryOp::Mul);
};
ctx.codegen_for_loop(None, right_u32.address, None, pow_body);

// Right shift using division on 1-complement
ctx.binary_instruction(left, one, result, BrilligBinaryOp::Add);
ctx.convert_signed_division(result, two_pow, result);
ctx.binary_instruction(result, one, result, BrilligBinaryOp::Sub);

// Clean-up
ctx.deallocate_single_addr(one);
ctx.deallocate_single_addr(two);
ctx.deallocate_single_addr(two_pow);
ctx.deallocate_single_addr(right_u32);
} else {
ctx.binary_instruction(left, right, result, BrilligBinaryOp::Shr);
}
});

self.brillig_context.deallocate_single_addr(left_is_negative);
}

#[allow(clippy::too_many_arguments)]
fn add_overflow_check(
&mut self,
Expand Down
82 changes: 82 additions & 0 deletions compiler/noirc_evaluator/src/brillig/brillig_ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ mod entry_point;
mod instructions;

use artifact::Label;
use brillig_variable::SingleAddrVariable;
pub(crate) use instructions::BrilligBinaryOp;
use registers::{RegisterAllocator, ScratchSpace};

Expand Down Expand Up @@ -109,6 +110,87 @@ impl<F: AcirField + DebugToString> BrilligContext<F, Stack> {
can_call_procedures: true,
}
}

/// Splits a two's complement signed integer in the sign bit and the absolute value.
/// For example, -6 i8 (11111010) is split to 00000110 (6, absolute value) and 1 (is_negative).
pub(crate) fn absolute_value(
&mut self,
num: SingleAddrVariable,
absolute_value: SingleAddrVariable,
result_is_negative: SingleAddrVariable,
) {
let max_positive = self
.make_constant_instruction(((1_u128 << (num.bit_size - 1)) - 1).into(), num.bit_size);

// Compute if num is negative
self.binary_instruction(max_positive, num, result_is_negative, BrilligBinaryOp::LessThan);

// Two's complement of num
let zero = self.make_constant_instruction(0_usize.into(), num.bit_size);
let twos_complement = SingleAddrVariable::new(self.allocate_register(), num.bit_size);
self.binary_instruction(zero, num, twos_complement, BrilligBinaryOp::Sub);

// absolute_value = result_is_negative ? twos_complement : num
self.codegen_branch(result_is_negative.address, |ctx, is_negative| {
if is_negative {
ctx.mov_instruction(absolute_value.address, twos_complement.address);
} else {
ctx.mov_instruction(absolute_value.address, num.address);
}
});

self.deallocate_single_addr(zero);
self.deallocate_single_addr(max_positive);
self.deallocate_single_addr(twos_complement);
}

pub(crate) fn convert_signed_division(
&mut self,
left: SingleAddrVariable,
right: SingleAddrVariable,
result: SingleAddrVariable,
) {
let left_is_negative = SingleAddrVariable::new(self.allocate_register(), 1);
let left_abs_value = SingleAddrVariable::new(self.allocate_register(), left.bit_size);

let right_is_negative = SingleAddrVariable::new(self.allocate_register(), 1);
let right_abs_value = SingleAddrVariable::new(self.allocate_register(), right.bit_size);

let result_is_negative = SingleAddrVariable::new(self.allocate_register(), 1);

// Compute both absolute values
self.absolute_value(left, left_abs_value, left_is_negative);
self.absolute_value(right, right_abs_value, right_is_negative);

// Perform the division on the absolute values
self.binary_instruction(
left_abs_value,
right_abs_value,
result,
BrilligBinaryOp::UnsignedDiv,
);

// Compute result sign
self.binary_instruction(
left_is_negative,
right_is_negative,
result_is_negative,
BrilligBinaryOp::Xor,
);

// If result has to be negative, perform two's complement
self.codegen_if(result_is_negative.address, |ctx| {
let zero = ctx.make_constant_instruction(0_usize.into(), result.bit_size);
ctx.binary_instruction(zero, result, result, BrilligBinaryOp::Sub);
ctx.deallocate_single_addr(zero);
});

self.deallocate_single_addr(left_is_negative);
self.deallocate_single_addr(left_abs_value);
self.deallocate_single_addr(right_is_negative);
self.deallocate_single_addr(right_abs_value);
self.deallocate_single_addr(result_is_negative);
}
}

/// Special brillig context to codegen compiler intrinsic shared procedures
Expand Down

0 comments on commit 28deb05

Please sign in to comment.