Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(avm-simulator): euclidean and field div #5181

Merged
merged 1 commit into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions avm-transpiler/src/opcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub enum AvmOpcode {
SUB,
MUL,
DIV,
FDIV,
EQ,
LT,
LTE,
Expand Down Expand Up @@ -82,6 +83,7 @@ impl AvmOpcode {
AvmOpcode::SUB => "SUB",
AvmOpcode::MUL => "MUL",
AvmOpcode::DIV => "DIV",
AvmOpcode::FDIV => "FDIV",
// Compute - Comparators
AvmOpcode::EQ => "EQ",
AvmOpcode::LT => "LT",
Expand Down
51 changes: 34 additions & 17 deletions avm-transpiler/src/transpile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ pub fn brillig_to_avm(brillig: &Brillig) -> Vec<u8> {
BinaryFieldOp::Add => AvmOpcode::ADD,
BinaryFieldOp::Sub => AvmOpcode::SUB,
BinaryFieldOp::Mul => AvmOpcode::MUL,
BinaryFieldOp::Div => AvmOpcode::DIV,
BinaryFieldOp::Div => AvmOpcode::FDIV,
BinaryFieldOp::Equals => AvmOpcode::EQ,
};
avm_instrs.push(AvmInstruction {
Expand All @@ -62,29 +62,42 @@ pub fn brillig_to_avm(brillig: &Brillig) -> Vec<u8> {
lhs,
rhs,
} => {
assert!(is_integral_bit_size(*bit_size), "BinaryIntOp::{:?} bit_size must be integral, got {:?}", op, bit_size);
let is_integral = is_integral_bit_size(*bit_size);
let avm_opcode = match op {
BinaryIntOp::Add => AvmOpcode::ADD,
BinaryIntOp::Sub => AvmOpcode::SUB,
BinaryIntOp::Mul => AvmOpcode::MUL,
BinaryIntOp::UnsignedDiv => AvmOpcode::DIV,
BinaryIntOp::Equals => AvmOpcode::EQ,
BinaryIntOp::LessThan => AvmOpcode::LT,
BinaryIntOp::LessThanEquals => AvmOpcode::LTE,
BinaryIntOp::And => AvmOpcode::AND,
BinaryIntOp::Or => AvmOpcode::OR,
BinaryIntOp::Xor => AvmOpcode::XOR,
BinaryIntOp::Shl => AvmOpcode::SHL,
BinaryIntOp::Shr => AvmOpcode::SHR,
BinaryIntOp::Add if is_integral => AvmOpcode::ADD,
BinaryIntOp::Sub if is_integral => AvmOpcode::SUB,
BinaryIntOp::Mul if is_integral => AvmOpcode::MUL,
BinaryIntOp::UnsignedDiv if is_integral => AvmOpcode::DIV,
BinaryIntOp::UnsignedDiv if is_field_bit_size(*bit_size) => AvmOpcode::FDIV,
BinaryIntOp::Equals if is_integral => AvmOpcode::EQ,
BinaryIntOp::LessThan if is_integral => AvmOpcode::LT,
BinaryIntOp::LessThanEquals if is_integral => AvmOpcode::LTE,
BinaryIntOp::And if is_integral => AvmOpcode::AND,
BinaryIntOp::Or if is_integral => AvmOpcode::OR,
BinaryIntOp::Xor if is_integral => AvmOpcode::XOR,
BinaryIntOp::Shl if is_integral => AvmOpcode::SHL,
BinaryIntOp::Shr if is_integral => AvmOpcode::SHR,
// https://github.com/noir-lang/noir/issues/4543
// Using Field for now, until the bug is fixed.
BinaryIntOp::Mul if is_field_bit_size(*bit_size) => AvmOpcode::MUL,
BinaryIntOp::Sub if is_field_bit_size(*bit_size) => AvmOpcode::SUB,
// https://github.com/noir-lang/noir/issues/4544
// These are implemented on our side, but Brillig does not have LT(E) in BinaryFieldOp
// So they use BinaryIntOp.
BinaryIntOp::LessThan if is_field_bit_size(*bit_size) => AvmOpcode::LT,
BinaryIntOp::LessThanEquals if is_field_bit_size(*bit_size) => AvmOpcode::LTE,
_ => panic!(
"Transpiler doesn't know how to process BinaryIntOp {:?}",
brillig_instr
"Transpiler doesn't know how to process {:?}", brillig_instr
),
};
avm_instrs.push(AvmInstruction {
opcode: avm_opcode,
indirect: Some(ALL_DIRECT),
tag: Some(tag_from_bit_size(*bit_size)),
tag: if is_integral {
Some(tag_from_bit_size(*bit_size))
} else {
None
},
operands: vec![
AvmOperand::U32 {
value: lhs.to_usize() as u32,
Expand Down Expand Up @@ -997,6 +1010,10 @@ fn map_brillig_pcs_to_avm_pcs(initial_offset: usize, brillig: &Brillig) -> Vec<u
pc_map
}

fn is_field_bit_size(bit_size: u32) -> bool {
bit_size == 254
}

fn is_integral_bit_size(bit_size: u32) -> bool {
match bit_size {
1 | 8 | 16 | 32 | 64 | 128 => true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const std::unordered_map<OpCode, size_t> Bytecode::OPERANDS_NUM = {
{ OpCode::SUB, 3 },
{ OpCode::MUL, 3 },
{ OpCode::DIV, 3 },
{ OpCode::FDIV, 3 },
//// Compute - Comparators
//{OpCode::EQ, },
//{OpCode::LT, },
Expand Down Expand Up @@ -154,6 +155,7 @@ bool Bytecode::has_in_tag(OpCode const op_code)
case OpCode::STATICCALL:
case OpCode::RETURN:
case OpCode::REVERT:
case OpCode::FDIV:
return false;
default:
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ enum class OpCode : uint8_t {
SUB,
MUL,
DIV,
FDIV,
// Compute - Comparators
EQ,
LT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ TEST_F(AvmExecutionTests, simpleInternalCall)
"03" // U32
"0D3D2518" // val 222111000 = 0xD3D2518
"00000004" // dst_offset 4
"25" // INTERNALCALL 37
+ to_hex(OpCode::INTERNALCALL) + // opcode INTERNALCALL
"00000004" // jmp_dest
+ to_hex(OpCode::ADD) + // opcode ADD
"00" // Indirect flag
Expand Down
9 changes: 9 additions & 0 deletions yarn-project/foundation/src/fields/fields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,15 @@ export class Fr extends BaseField {
const bInv = modInverse(rhs.toBigInt());
return this.mul(bInv);
}

// Integer division.
ediv(rhs: Fr) {
if (rhs.isZero()) {
throw new Error('Division by zero');
}

return new Fr(this.toBigInt() / rhs.toBigInt());
}
}

/**
Expand Down
6 changes: 6 additions & 0 deletions yarn-project/simulator/src/avm/avm_memory_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,13 @@ export class Field extends MemoryValue {
return new Field(this.rep.mul(rhs.rep));
}

// Euclidean division.
public div(rhs: Field): Field {
return new Field(this.rep.ediv(rhs.rep));
}

// Field division.
public fdiv(rhs: Field): Field {
return new Field(this.rep.div(rhs.rep));
}

Expand Down
42 changes: 41 additions & 1 deletion yarn-project/simulator/src/avm/opcodes/arithmetic.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AvmContext } from '../avm_context.js';
import { Field, TypeTag } from '../avm_memory_types.js';
import { initContext } from '../fixtures/index.js';
import { Add, Div, Mul, Sub } from './arithmetic.js';
import { Add, Div, FieldDiv, Mul, Sub } from './arithmetic.js';

describe('Arithmetic Instructions', () => {
let context: AvmContext;
Expand Down Expand Up @@ -201,6 +201,46 @@ describe('Arithmetic Instructions', () => {
expect(inst.serialize()).toEqual(buf);
});

it('Should perform integer division', async () => {
const a = new Field(100n);
const b = new Field(5n);

context.machineState.memory.set(0, a);
context.machineState.memory.set(1, b);

await new Div(
/*indirect=*/ 0,
/*inTag=*/ TypeTag.FIELD,
/*aOffset=*/ 0,
/*bOffset=*/ 1,
/*dstOffset=*/ 2,
).execute(context);

const actual = context.machineState.memory.get(2);
expect(actual).toEqual(new Field(20));
});
});

describe('FDiv', () => {
it('Should (de)serialize correctly', () => {
const buf = Buffer.from([
FieldDiv.opcode, // opcode
0x01, // indirect
...Buffer.from('12345678', 'hex'), // aOffset
...Buffer.from('23456789', 'hex'), // bOffset
...Buffer.from('3456789a', 'hex'), // dstOffset
]);
const inst = new FieldDiv(
/*indirect=*/ 0x01,
/*aOffset=*/ 0x12345678,
/*bOffset=*/ 0x23456789,
/*dstOffset=*/ 0x3456789a,
);

expect(FieldDiv.deserialize(buf)).toEqual(inst);
expect(inst.serialize()).toEqual(buf);
});

it('Should perform field division', async () => {
const a = new Field(10n);
const b = new Field(5n);
Expand Down
34 changes: 33 additions & 1 deletion yarn-project/simulator/src/avm/opcodes/arithmetic.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type { AvmContext } from '../avm_context.js';
import { Opcode } from '../serialization/instruction_serialization.js';
import { Field, TypeTag } from '../avm_memory_types.js';
import { Opcode, OperandType } from '../serialization/instruction_serialization.js';
import { Instruction } from './instruction.js';
import { ThreeOperandInstruction } from './instruction_impl.js';

export class Add extends ThreeOperandInstruction {
Expand Down Expand Up @@ -79,3 +81,33 @@ export class Div extends ThreeOperandInstruction {
context.machineState.incrementPc();
}
}

export class FieldDiv extends Instruction {
static type: string = 'FDIV';
static readonly opcode = Opcode.FDIV;

// Informs (de)serialization. See Instruction.deserialize.
static readonly wireFormat: OperandType[] = [
OperandType.UINT8,
OperandType.UINT8,
OperandType.UINT32,
OperandType.UINT32,
OperandType.UINT32,
];

constructor(private indirect: number, private aOffset: number, private bOffset: number, private dstOffset: number) {
super();
}

async execute(context: AvmContext): Promise<void> {
context.machineState.memory.checkTags(TypeTag.FIELD, this.aOffset, this.bOffset);

const a = context.machineState.memory.getAs<Field>(this.aOffset);
const b = context.machineState.memory.getAs<Field>(this.bOffset);

const dest = a.fdiv(b);
context.machineState.memory.set(this.dstOffset, dest);

context.machineState.incrementPc();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
FeePerDAGas,
FeePerL1Gas,
FeePerL2Gas,
FieldDiv,
InternalCall,
InternalReturn,
Jump,
Expand Down Expand Up @@ -66,6 +67,7 @@ const INSTRUCTION_SET = () =>
[Sub.opcode, Sub],
[Mul.opcode, Mul],
[Div.opcode, Div],
[FieldDiv.opcode, FieldDiv],
[Eq.opcode, Eq],
[Lt.opcode, Lt],
[Lte.opcode, Lte],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export enum Opcode {
SUB,
MUL,
DIV,
FDIV,
EQ,
LT,
LTE,
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/simulator/src/public/avm_executor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ describe('AVM WitGen and Proof Generation', () => {
// new Add(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 2),
// new Return(/*indirect=*/ 0, /*returnOffset=*/ 2, /*copySize=*/ 1),
// ]);
const bytecode: Buffer = Buffer.from('HwAAAAAAAAAAAgAAAAAAAAYAAAAAAAAAAQAAAAI3AAAAAAIAAAAB', 'base64');
const bytecode: Buffer = Buffer.from('IAAAAAAAAAAAAgAAAAAAAAYAAAAAAAAAAQAAAAI4AAAAAAIAAAAB', 'base64');
publicContracts.getBytecode.mockResolvedValue(bytecode);
const executor = new PublicExecutor(publicState, publicContracts, commitmentsDb, header);
const functionData = FunctionData.empty();
Expand Down
Loading
Loading