From 64d0fdb7af8e80e6b3724662f327a929b50d0750 Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Wed, 24 Jan 2024 17:48:01 +0000 Subject: [PATCH 1/8] WIP prototyping memory types interfaces --- .../src/avm/avm_memory_types.test.ts | 21 ++ .../src/avm/avm_memory_types.ts | 195 ++++++++++++++++++ 2 files changed, 216 insertions(+) create mode 100644 yarn-project/acir-simulator/src/avm/avm_memory_types.test.ts create mode 100644 yarn-project/acir-simulator/src/avm/avm_memory_types.ts diff --git a/yarn-project/acir-simulator/src/avm/avm_memory_types.test.ts b/yarn-project/acir-simulator/src/avm/avm_memory_types.test.ts new file mode 100644 index 00000000000..d887d0c753c --- /dev/null +++ b/yarn-project/acir-simulator/src/avm/avm_memory_types.test.ts @@ -0,0 +1,21 @@ +import { Uint8, FieldValue } from './avm_memory_types.js'; + +describe('Uint8', () => { + it('Unsigned 8 max value', () => { + expect(new Uint8(255).toBigInt()).toEqual(255n); + }); + + it('Unsigned 8 bit add', () => { + expect(new Uint8(50).add(new Uint8(20))).toEqual(new Uint8(70)); + }); + + it('Unsigned 8 bit add wraps', () => { + expect(new Uint8(200).add(new Uint8(100))).toEqual(new Uint8(44)); + }); +}); + +describe('FieldValue', () => { + it('xxx', () => { + expect(new FieldValue(27).add(new FieldValue(48))).toEqual(new FieldValue(75)); + }); +}); \ No newline at end of file diff --git a/yarn-project/acir-simulator/src/avm/avm_memory_types.ts b/yarn-project/acir-simulator/src/avm/avm_memory_types.ts new file mode 100644 index 00000000000..91d4874ffcb --- /dev/null +++ b/yarn-project/acir-simulator/src/avm/avm_memory_types.ts @@ -0,0 +1,195 @@ +import { strict as assert } from 'assert'; +import { Fr } from '@aztec/foundation/fields'; + +export interface FieldValueType { + add(rhs: FieldValueType): FieldValueType; + sub(rhs: FieldValueType): FieldValueType; + mul(rhs: FieldValueType): FieldValueType; + div(rhs: FieldValueType): FieldValueType; + + // Use sparingly. + toBigInt(): bigint; +} +export interface IntegralValueType extends FieldValueType { + shl(rhs: IntegralValueType): IntegralValueType; + shr(rhs: IntegralValueType): IntegralValueType; + and(rhs: IntegralValueType): IntegralValueType; + or(rhs: IntegralValueType): IntegralValueType; + xor(rhs: IntegralValueType): IntegralValueType; + not(): IntegralValueType; +} + +// TODO: optimize calculation of mod, etc. Can only 1 per class? +// TODO: optimize return new UnsignedInteger. +class UnsignedInteger implements IntegralValueType { + private readonly bitmask: bigint; + private readonly mod: bigint; + + protected constructor(private n: bigint, private bits: bigint) { + assert(bits > 0); + // x % 2^n == x & (2^n - 1) + this.mod = 2n**bits; + this.bitmask = this.mod - 1n; + assert(n < this.mod); + } + + public add(rhs: UnsignedInteger): UnsignedInteger { + assert(this.bits == rhs.bits); + return new UnsignedInteger((this.n + rhs.n) & this.bitmask, this.bits); + } + + public sub(rhs: UnsignedInteger): UnsignedInteger { + assert(this.bits == rhs.bits); + const res: bigint = this.n - rhs.n; + return new UnsignedInteger(res >= 0 ? res : res + this.mod, this.bits); + } + + public mul(rhs: UnsignedInteger): UnsignedInteger { + assert(this.bits == rhs.bits); + return new UnsignedInteger((this.n * rhs.n) & this.bitmask, this.bits); + } + + public div(rhs: UnsignedInteger): UnsignedInteger { + assert(this.bits == rhs.bits); + return new UnsignedInteger(this.n / rhs.n, this.bits); + } + + // No sign extension. + public shr(rhs: UnsignedInteger): UnsignedInteger { + assert(this.bits == rhs.bits); + // Note that this.n is > 0 by class invariant. + return new UnsignedInteger(this.n >> rhs.n, this.bits); + } + + public shl(rhs: UnsignedInteger): UnsignedInteger { + assert(this.bits == rhs.bits); + return new UnsignedInteger((this.n << rhs.n) & this.bitmask, this.bits); + } + + public and(rhs: UnsignedInteger): UnsignedInteger { + assert(this.bits == rhs.bits); + return new UnsignedInteger(this.n & rhs.n, this.bits); + } + + public or(rhs: UnsignedInteger): UnsignedInteger { + assert(this.bits == rhs.bits); + return new UnsignedInteger(this.n | rhs.n, this.bits); + } + + public xor(rhs: UnsignedInteger): UnsignedInteger { + assert(this.bits == rhs.bits); + return new UnsignedInteger(this.n ^ rhs.n, this.bits); + } + + public not(): UnsignedInteger { + return new UnsignedInteger(~this.n & this.bitmask, this.bits); + } + + public toBigInt(): bigint { + return this.n; + } + + public equals(rhs: UnsignedInteger) { + return this.bits == rhs.bits && this.toBigInt() == rhs.toBigInt(); + } + + // Factory methods. + // public static Uint8(v: number | bigint): UnsignedInteger { + // const bitmask = 2n**8n - 1n; + // return new UnsignedInteger(BigInt(v) & bitmask, 8n); + // } + + // Add casting operators. +} + +export class Uint8 extends UnsignedInteger { + constructor(n: number | bigint) { + super(BigInt(n), 8n); + } +} + +export class Uint16 extends UnsignedInteger { + constructor(n: number | bigint) { + super(BigInt(n), 16n); + } +} + +export class Uint32 extends UnsignedInteger { + constructor(n: number | bigint) { + super(BigInt(n), 32n); + } +} + +export class Uint64 extends UnsignedInteger { + constructor(n: number | bigint) { + super(BigInt(n), 64n); + } +} + +export class Uint128 extends UnsignedInteger { + constructor(n: number | bigint) { + super(BigInt(n), 128n); + } +} + +export class FieldValue implements FieldValueType { + private readonly rep: Fr; + + constructor(v: number | bigint | Fr) { + this.rep = new Fr(v); + } + + public add(rhs: FieldValue): FieldValue { + return new FieldValue(this.rep.add(rhs.rep)); + } + + public sub(rhs: FieldValue): FieldValue { + return new FieldValue(this.rep.sub(rhs.rep)); + } + + public mul(rhs: FieldValue): FieldValue { + return new FieldValue(this.rep.mul(rhs.rep)); + } + + public div(rhs: FieldValue): FieldValue { + return new FieldValue(this.rep.div(rhs.rep)); + } + + public toBigInt(): bigint { + return this.rep.toBigInt(); + } +} + +export enum TypeTag { + UNINITIALIZED, + UINT8, + UINT16, + UINT32, + UINT64, + UINT128, + FIELD, +} + +// export class TaggedMemoryCell { +// private _value: MemoryValueType; +// private _tag: TypeTag; + +// constructor() { +// this._value = new UInt8Value(0n); +// this._tag = TypeTag.UINT8; +// } + +// public value() { +// return this._value; +// } +// //private constructor(private value: MemoryValueType, private tag: TypeTag) { +// // private constructor(value: MemoryValueType, tag: TypeTag) { +// // //assert(value >= 0); +// // } + +// // public static ofUint8(v: number | bigint): TaggedMemoryCell { +// // return new TaggedMemoryCell(BigInt(v)); +// // } + + +// } \ No newline at end of file From 7ff4eb6df45084d0097d0da0a679b0d1d4e98f39 Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Thu, 25 Jan 2024 10:43:59 +0000 Subject: [PATCH 2/8] WIP tagged memory --- .../src/avm/avm_machine_state.ts | 40 +-- .../src/avm/avm_memory_types.test.ts | 8 +- .../src/avm/avm_memory_types.ts | 179 ++++++++--- .../src/avm/opcodes/bitwise.test.ts | 188 ++++++------ .../acir-simulator/src/avm/opcodes/bitwise.ts | 80 ++--- .../src/avm/opcodes/instruction.ts | 12 + .../src/avm/opcodes/memory.test.ts | 288 ++++++++++++------ .../acir-simulator/src/avm/opcodes/memory.ts | 56 ++-- 8 files changed, 522 insertions(+), 329 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/avm_machine_state.ts b/yarn-project/acir-simulator/src/avm/avm_machine_state.ts index 53e8599b7c3..bdb9dfcc7c8 100644 --- a/yarn-project/acir-simulator/src/avm/avm_machine_state.ts +++ b/yarn-project/acir-simulator/src/avm/avm_machine_state.ts @@ -1,5 +1,7 @@ import { Fr } from '@aztec/foundation/fields'; +import { TaggedMemory } from './avm_memory_types.js'; + /** * Store's data for an Avm execution frame */ @@ -8,9 +10,8 @@ export class AvmMachineState { public readonly calldata: Fr[]; private returnData: Fr[]; - // TODO: implement tagged memory /** - */ - public memory: Fr[]; + public readonly memory: TaggedMemory; /** * When an internal_call is invoked, the internal call stack is added to with the current pc + 1 @@ -35,7 +36,7 @@ export class AvmMachineState { constructor(calldata: Fr[]) { this.calldata = calldata; this.returnData = []; - this.memory = []; + this.memory = new TaggedMemory(); this.internalCallStack = []; this.pc = 0; @@ -57,37 +58,4 @@ export class AvmMachineState { public getReturnData(): Fr[] { return this.returnData; } - - /** - - * @param offset - - */ - public readMemory(offset: number): Fr { - // TODO: check offset is within bounds - return this.memory[offset] ?? Fr.ZERO; - } - - /** - - * @param offset - - * @param size - - */ - public readMemoryChunk(offset: number, size: number): Fr[] { - // TODO: bounds -> initialise to 0 - return this.memory.slice(offset, offset + size); - } - - /** - - * @param offset - - * @param value - - */ - public writeMemory(offset: number, value: Fr): void { - this.memory[offset] = value; - } - - /** - - * @param offset - - * @param values - - */ - public writeMemoryChunk(offset: number, values: Fr[]): void { - this.memory.splice(offset, values.length, ...values); - } } diff --git a/yarn-project/acir-simulator/src/avm/avm_memory_types.test.ts b/yarn-project/acir-simulator/src/avm/avm_memory_types.test.ts index d887d0c753c..3e6d38cab7a 100644 --- a/yarn-project/acir-simulator/src/avm/avm_memory_types.test.ts +++ b/yarn-project/acir-simulator/src/avm/avm_memory_types.test.ts @@ -1,4 +1,4 @@ -import { Uint8, FieldValue } from './avm_memory_types.js'; +import { Uint8, Uint32, FieldValue } from './avm_memory_types.js'; describe('Uint8', () => { it('Unsigned 8 max value', () => { @@ -14,6 +14,12 @@ describe('Uint8', () => { }); }); +describe('Uint32', () => { + it('ands', () => { + expect(new Uint32(0b11111110010011100100n).and(new Uint32(0b11100100111001001111n))).toEqual(new Uint32(0b11100100010001000100n)); + }); +}); + describe('FieldValue', () => { it('xxx', () => { expect(new FieldValue(27).add(new FieldValue(48))).toEqual(new FieldValue(75)); diff --git a/yarn-project/acir-simulator/src/avm/avm_memory_types.ts b/yarn-project/acir-simulator/src/avm/avm_memory_types.ts index 91d4874ffcb..64a60d477bb 100644 --- a/yarn-project/acir-simulator/src/avm/avm_memory_types.ts +++ b/yarn-project/acir-simulator/src/avm/avm_memory_types.ts @@ -1,6 +1,7 @@ -import { strict as assert } from 'assert'; import { Fr } from '@aztec/foundation/fields'; +import { strict as assert } from 'assert'; + export interface FieldValueType { add(rhs: FieldValueType): FieldValueType; sub(rhs: FieldValueType): FieldValueType; @@ -10,6 +11,7 @@ export interface FieldValueType { // Use sparingly. toBigInt(): bigint; } + export interface IntegralValueType extends FieldValueType { shl(rhs: IntegralValueType): IntegralValueType; shr(rhs: IntegralValueType): IntegralValueType; @@ -21,70 +23,73 @@ export interface IntegralValueType extends FieldValueType { // TODO: optimize calculation of mod, etc. Can only 1 per class? // TODO: optimize return new UnsignedInteger. -class UnsignedInteger implements IntegralValueType { +abstract class UnsignedInteger implements IntegralValueType { private readonly bitmask: bigint; private readonly mod: bigint; protected constructor(private n: bigint, private bits: bigint) { assert(bits > 0); // x % 2^n == x & (2^n - 1) - this.mod = 2n**bits; + this.mod = 1n << bits; this.bitmask = this.mod - 1n; assert(n < this.mod); } + // TODO: comment + protected abstract build(n: bigint): UnsignedInteger; + public add(rhs: UnsignedInteger): UnsignedInteger { assert(this.bits == rhs.bits); - return new UnsignedInteger((this.n + rhs.n) & this.bitmask, this.bits); + return this.build((this.n + rhs.n) & this.bitmask); } public sub(rhs: UnsignedInteger): UnsignedInteger { assert(this.bits == rhs.bits); const res: bigint = this.n - rhs.n; - return new UnsignedInteger(res >= 0 ? res : res + this.mod, this.bits); + return this.build(res >= 0 ? res : res + this.mod); } public mul(rhs: UnsignedInteger): UnsignedInteger { assert(this.bits == rhs.bits); - return new UnsignedInteger((this.n * rhs.n) & this.bitmask, this.bits); + return this.build((this.n * rhs.n) & this.bitmask); } public div(rhs: UnsignedInteger): UnsignedInteger { assert(this.bits == rhs.bits); - return new UnsignedInteger(this.n / rhs.n, this.bits); + return this.build(this.n / rhs.n); } // No sign extension. public shr(rhs: UnsignedInteger): UnsignedInteger { assert(this.bits == rhs.bits); // Note that this.n is > 0 by class invariant. - return new UnsignedInteger(this.n >> rhs.n, this.bits); + return this.build(this.n >> rhs.n); } public shl(rhs: UnsignedInteger): UnsignedInteger { assert(this.bits == rhs.bits); - return new UnsignedInteger((this.n << rhs.n) & this.bitmask, this.bits); + return this.build((this.n << rhs.n) & this.bitmask); } public and(rhs: UnsignedInteger): UnsignedInteger { assert(this.bits == rhs.bits); - return new UnsignedInteger(this.n & rhs.n, this.bits); + return this.build(this.n & rhs.n); } public or(rhs: UnsignedInteger): UnsignedInteger { assert(this.bits == rhs.bits); - return new UnsignedInteger(this.n | rhs.n, this.bits); + return this.build(this.n | rhs.n); } public xor(rhs: UnsignedInteger): UnsignedInteger { assert(this.bits == rhs.bits); - return new UnsignedInteger(this.n ^ rhs.n, this.bits); + return this.build(this.n ^ rhs.n); } public not(): UnsignedInteger { - return new UnsignedInteger(~this.n & this.bitmask, this.bits); + return this.build(~this.n & this.bitmask); } - + public toBigInt(): bigint { return this.n; } @@ -92,49 +97,66 @@ class UnsignedInteger implements IntegralValueType { public equals(rhs: UnsignedInteger) { return this.bits == rhs.bits && this.toBigInt() == rhs.toBigInt(); } - - // Factory methods. - // public static Uint8(v: number | bigint): UnsignedInteger { - // const bitmask = 2n**8n - 1n; - // return new UnsignedInteger(BigInt(v) & bitmask, 8n); - // } - - // Add casting operators. } export class Uint8 extends UnsignedInteger { constructor(n: number | bigint) { super(BigInt(n), 8n); } + + // TODO: should be private + build(n: bigint): Uint8 { + return new Uint8(n); + } } export class Uint16 extends UnsignedInteger { constructor(n: number | bigint) { super(BigInt(n), 16n); } + + // TODO: should be private + build(n: bigint): Uint16 { + return new Uint16(n); + } } export class Uint32 extends UnsignedInteger { constructor(n: number | bigint) { super(BigInt(n), 32n); } + + // TODO: should be private + build(n: bigint): Uint32 { + return new Uint32(n); + } } export class Uint64 extends UnsignedInteger { constructor(n: number | bigint) { super(BigInt(n), 64n); } + + // TODO: should be private + build(n: bigint): Uint64 { + return new Uint64(n); + } } export class Uint128 extends UnsignedInteger { constructor(n: number | bigint) { super(BigInt(n), 128n); } + + // TODO: should be private + build(n: bigint): Uint128 { + return new Uint128(n); + } } export class FieldValue implements FieldValueType { private readonly rep: Fr; - + constructor(v: number | bigint | Fr) { this.rep = new Fr(v); } @@ -168,28 +190,103 @@ export enum TypeTag { UINT64, UINT128, FIELD, + INVALID, } -// export class TaggedMemoryCell { -// private _value: MemoryValueType; -// private _tag: TypeTag; +export class TaggedMemory { + private _mem: FieldValueType[]; -// constructor() { -// this._value = new UInt8Value(0n); -// this._tag = TypeTag.UINT8; -// } + constructor() { + this._mem = []; + } -// public value() { -// return this._value; -// } -// //private constructor(private value: MemoryValueType, private tag: TypeTag) { -// // private constructor(value: MemoryValueType, tag: TypeTag) { -// // //assert(value >= 0); -// // } + public get(offset: number): FieldValueType { + return this.getAs(offset); + } + + public getAs(offset: number): T { + assert(offset < 2n ** 32n); + // TODO: non-existing case. + const e = this._mem[offset]; + return e; + } + + public getSlice(offset: number, size: number): FieldValueType[] { + assert(offset < 2n ** 32n); + // TODO: non-existing case. + return this._mem.slice(offset, offset + size); + } -// // public static ofUint8(v: number | bigint): TaggedMemoryCell { -// // return new TaggedMemoryCell(BigInt(v)); -// // } + public getSliceTags(offset: number, size: number): TypeTag[] { + assert(offset < 2n ** 32n); + // TODO: non-existing case. + return this._mem.slice(offset, offset + size).map(TaggedMemory.getTag); + } + public set(offset: number, v: FieldValueType) { + assert(offset < 2n ** 32n); + this._mem[offset] = v; + } -// } \ No newline at end of file + public setSlice(offset: number, vs: FieldValueType[]) { + assert(offset < 2n ** 32n); + this._mem.splice(offset, vs.length, ...vs); + } + + public getTag(offset: number): TypeTag { + return TaggedMemory.getTag(this._mem[offset]); + } + + public tagsMatchStrict(offsetA: number, offsetB: number): boolean { + return this.getTag(offsetA) == this.getTag(offsetB); + } + + public tagsMatch(offsetA: number, offsetB: number): boolean { + return ( + this.tagsMatchStrict(offsetA, offsetB) || + this.getTag(offsetA) == TypeTag.UNINITIALIZED || + this.getTag(offsetB) == TypeTag.UNINITIALIZED + ); + } + + public static getTag(v: FieldValueType | undefined): TypeTag { + let tag = TypeTag.INVALID; + + if (v === undefined) { + tag = TypeTag.UNINITIALIZED; + } else if (v instanceof FieldValue) { + tag = TypeTag.FIELD; + } else if (v instanceof Uint8) { + tag = TypeTag.UINT8; + } else if (v instanceof Uint16) { + tag = TypeTag.UINT16; + } else if (v instanceof Uint32) { + tag = TypeTag.UINT32; + } else if (v instanceof Uint64) { + tag = TypeTag.UINT64; + } else if (v instanceof Uint128) { + tag = TypeTag.UINT128; + } + + return tag; + } + + // Truncates! + public static integralFromTag(v: bigint, tag: TypeTag): IntegralValueType { + switch (tag) { + case TypeTag.UINT8: + return new Uint8(v & ((1n << 8n) - 1n)); + case TypeTag.UINT16: + return new Uint16(v & ((1n << 16n) - 1n)); + case TypeTag.UINT32: + return new Uint32(v & ((1n << 32n) - 1n)); + case TypeTag.UINT64: + return new Uint64(v & ((1n << 64n) - 1n)); + case TypeTag.UINT128: + return new Uint128(v & ((1n << 128n) - 1n)); + default: + // TODO: better message + throw new Error('wrong tag for integral type'); + } + } +} diff --git a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts index e1de7356041..451ce20d3a6 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts @@ -1,17 +1,9 @@ -import { Fr } from '@aztec/foundation/fields'; - import { mock } from 'jest-mock-extended'; import { AvmMachineState } from '../avm_machine_state.js'; +import { TypeTag, Uint16, Uint32 } from '../avm_memory_types.js'; import { AvmStateManager } from '../avm_state_manager.js'; -import { - And, - /*Not,*/ - Or, - Shl, - Shr, - Xor, -} from './bitwise.js'; +import { And, Not, Or, Shl, Shr, Xor } from './bitwise.js'; describe('Bitwise instructions', () => { let machineState: AvmMachineState; @@ -22,145 +14,155 @@ describe('Bitwise instructions', () => { stateManager = mock(); }); - it('Should AND correctly over Fr type', () => { - const a = new Fr(0b11111110010011100100n); - const b = new Fr(0b11100100111001001111n); - - machineState.writeMemory(0, a); - machineState.writeMemory(1, b); + it('Should AND correctly over integral types', () => { + machineState.memory.set(0, new Uint32(0b11111110010011100100n)); + machineState.memory.set(1, new Uint32(0b11100100111001001111n)); - new And(0, 1, 2).execute(machineState, stateManager); + new And(0, 1, 2, TypeTag.UINT32).execute(machineState, stateManager); - const expected = new Fr(0b11100100010001000100n); - const actual = machineState.readMemory(2); - expect(actual).toEqual(expected); + const actual = machineState.memory.get(2); + expect(actual).toEqual(new Uint32(0b11100100010001000100n)); }); - it('Should OR correctly over Fr type', () => { - const a = new Fr(0b11111110010011100100n); - const b = new Fr(0b11100100111001001111n); + it('Should OR correctly over integral types', () => { + const a = new Uint32(0b11111110010011100100n); + const b = new Uint32(0b11100100111001001111n); - machineState.writeMemory(0, a); - machineState.writeMemory(1, b); + machineState.memory.set(0, a); + machineState.memory.set(1, b); - new Or(0, 1, 2).execute(machineState, stateManager); + new Or(0, 1, 2, TypeTag.UINT32).execute(machineState, stateManager); - const expected = new Fr(0b11111110111011101111n); - const actual = machineState.readMemory(2); + const expected = new Uint32(0b11111110111011101111n); + const actual = machineState.memory.get(2); expect(actual).toEqual(expected); }); - it('Should XOR correctly over Fr type', () => { - const a = new Fr(0b11111110010011100100n); - const b = new Fr(0b11100100111001001111n); + it('Should XOR correctly over integral types', () => { + const a = new Uint32(0b11111110010011100100n); + const b = new Uint32(0b11100100111001001111n); - machineState.writeMemory(0, a); - machineState.writeMemory(1, b); + machineState.memory.set(0, a); + machineState.memory.set(1, b); - new Xor(0, 1, 2).execute(machineState, stateManager); + new Xor(0, 1, 2, TypeTag.UINT32).execute(machineState, stateManager); - const expected = new Fr(0b00011010101010101011n); - const actual = machineState.readMemory(2); + const expected = new Uint32(0b00011010101010101011n); + const actual = machineState.memory.get(2); expect(actual).toEqual(expected); }); describe('SHR', () => { - it('Should shift correctly 0 positions over Fr type', () => { - const a = new Fr(0b11111110010011100100n); - const b = new Fr(0n); + it('Should shift correctly 0 positions over integral types', () => { + const a = new Uint32(0b11111110010011100100n); + const b = new Uint32(0n); - machineState.writeMemory(0, a); - machineState.writeMemory(1, b); + machineState.memory.set(0, a); + machineState.memory.set(1, b); - new Shr(0, 1, 2).execute(machineState, stateManager); + new Shr(0, 1, 2, TypeTag.UINT32).execute(machineState, stateManager); const expected = a; - const actual = machineState.readMemory(2); + const actual = machineState.memory.get(2); expect(actual).toEqual(expected); }); - it('Should shift correctly 2 positions over Fr type', () => { - const a = new Fr(0b11111110010011100100n); - const b = new Fr(2n); + it('Should shift correctly 2 positions over integral types', () => { + const a = new Uint32(0b11111110010011100100n); + const b = new Uint32(2n); - machineState.writeMemory(0, a); - machineState.writeMemory(1, b); + machineState.memory.set(0, a); + machineState.memory.set(1, b); - new Shr(0, 1, 2).execute(machineState, stateManager); + new Shr(0, 1, 2, TypeTag.UINT32).execute(machineState, stateManager); - const expected = new Fr(0b00111111100100111001n); - const actual = machineState.readMemory(2); + const expected = new Uint32(0b00111111100100111001n); + const actual = machineState.memory.get(2); expect(actual).toEqual(expected); }); - it('Should shift correctly 19 positions over Fr type', () => { - const a = new Fr(0b11111110010011100100n); - const b = new Fr(19n); + it('Should shift correctly 19 positions over integral types', () => { + const a = new Uint32(0b11111110010011100100n); + const b = new Uint32(19n); - machineState.writeMemory(0, a); - machineState.writeMemory(1, b); + machineState.memory.set(0, a); + machineState.memory.set(1, b); - new Shr(0, 1, 2).execute(machineState, stateManager); + new Shr(0, 1, 2, TypeTag.UINT32).execute(machineState, stateManager); - const expected = new Fr(0b01n); - const actual = machineState.readMemory(2); + const expected = new Uint32(0b01n); + const actual = machineState.memory.get(2); expect(actual).toEqual(expected); }); }); describe('SHL', () => { - it('Should shift correctly 0 positions over Fr type', () => { - const a = new Fr(0b11111110010011100100n); - const b = new Fr(0n); + it('Should shift correctly 0 positions over integral types', () => { + const a = new Uint32(0b11111110010011100100n); + const b = new Uint32(0n); - machineState.writeMemory(0, a); - machineState.writeMemory(1, b); + machineState.memory.set(0, a); + machineState.memory.set(1, b); - new Shl(0, 1, 2).execute(machineState, stateManager); + new Shl(0, 1, 2, TypeTag.UINT32).execute(machineState, stateManager); const expected = a; - const actual = machineState.readMemory(2); + const actual = machineState.memory.get(2); expect(actual).toEqual(expected); }); - it('Should shift correctly 2 positions over Fr type', () => { - const a = new Fr(0b11111110010011100100n); - const b = new Fr(2n); + it('Should shift correctly 2 positions over integral types', () => { + const a = new Uint32(0b11111110010011100100n); + const b = new Uint32(2n); - machineState.writeMemory(0, a); - machineState.writeMemory(1, b); + machineState.memory.set(0, a); + machineState.memory.set(1, b); - new Shl(0, 1, 2).execute(machineState, stateManager); + new Shl(0, 1, 2, TypeTag.UINT32).execute(machineState, stateManager); - const expected = new Fr(0b1111111001001110010000n); - const actual = machineState.readMemory(2); + const expected = new Uint32(0b1111111001001110010000n); + const actual = machineState.memory.get(2); expect(actual).toEqual(expected); }); - // it('Should shift correctly over bit limit over Fr type', () => { - // const a = new Fr(0b11111110010011100100n); - // const b = new Fr(19n); + it('Should shift correctly over bit limit over integral types', () => { + const a = new Uint16(0b1110010011100111n); + const b = new Uint16(17n); - // machineState.writeMemory(0, a); - // machineState.writeMemory(1, b); + machineState.memory.set(0, a); + machineState.memory.set(1, b); - // new Shl(0, 1, 2).execute(machineState, stateManager); + new Shl(0, 1, 2, TypeTag.UINT16).execute(machineState, stateManager); - // const expected = new Fr(0b01n); - // const actual = machineState.readMemory(2); - // expect(actual).toEqual(expected); - // }); - }); + const expected = new Uint16(0n); + const actual = machineState.memory.get(2); + expect(actual).toEqual(expected); + }); - // it('Should NOT correctly over Fr type', () => { - // const a = new Fr(0b11111110010011100100n); + it('Should truncate when shifting over bit size over integral types', () => { + const a = new Uint16(0b1110010011100111n); + const b = new Uint16(2n); - // machineState.writeMemory(0, a); + machineState.memory.set(0, a); + machineState.memory.set(1, b); - // new Not(0, 1).execute(machineState, stateManager); + new Shl(0, 1, 2, TypeTag.UINT16).execute(machineState, stateManager); - // const expected = new Fr(0b00000001101100011011n); // high bits! - // const actual = machineState.readMemory(1); - // expect(actual).toEqual(expected); - // }); + const expected = new Uint16(0b1001001110011100n); + const actual = machineState.memory.get(2); + expect(actual).toEqual(expected); + }); +}); + + it('Should NOT correctly over integral types', () => { + const a = new Uint16(0b0110010011100100n); + + machineState.memory.set(0, a); + + new Not(0, 1, TypeTag.UINT16).execute(machineState, stateManager); + + const expected = new Uint16(0b1001101100011011n); // high bits! + const actual = machineState.memory.get(1); + expect(actual).toEqual(expected); + }); }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts index ff7802aac61..eb3f9c89877 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts @@ -1,24 +1,25 @@ -import { Fr } from '@aztec/foundation/fields'; - import { AvmMachineState } from '../avm_machine_state.js'; import { AvmStateManager } from '../avm_state_manager.js'; import { Instruction } from './instruction.js'; +import { IntegralValueType, TypeTag } from '../avm_memory_types.js'; /** - */ export class And extends Instruction { static type: string = 'AND'; static numberOfOperands = 3; - constructor(private aOffset: number, private bOffset: number, private destOffset: number) { + constructor(private aOffset: number, private bOffset: number, private destOffset: number, private inTag: TypeTag) { super(); } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a: Fr = machineState.readMemory(this.aOffset); - const b: Fr = machineState.readMemory(this.bOffset); + Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); + + const a = machineState.memory.getAs(this.aOffset); + const b = machineState.memory.getAs(this.bOffset); - const dest = new Fr(a.toBigInt() & b.toBigInt()); - machineState.writeMemory(this.destOffset, dest); + const res = a.and(b); + machineState.memory.set(this.destOffset, res); this.incrementPc(machineState); } @@ -29,16 +30,18 @@ export class Or extends Instruction { static type: string = 'OR'; static numberOfOperands = 3; - constructor(private aOffset: number, private bOffset: number, private destOffset: number) { + constructor(private aOffset: number, private bOffset: number, private destOffset: number, private inTag: TypeTag) { super(); } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a: Fr = machineState.readMemory(this.aOffset); - const b: Fr = machineState.readMemory(this.bOffset); + Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); - const dest = new Fr(a.toBigInt() | b.toBigInt()); - machineState.writeMemory(this.destOffset, dest); + const a = machineState.memory.getAs(this.aOffset); + const b = machineState.memory.getAs(this.bOffset); + + const res = a.or(b); + machineState.memory.set(this.destOffset, res); this.incrementPc(machineState); } @@ -49,16 +52,18 @@ export class Xor extends Instruction { static type: string = 'XOR'; static numberOfOperands = 3; - constructor(private aOffset: number, private bOffset: number, private destOffset: number) { + constructor(private aOffset: number, private bOffset: number, private destOffset: number, private inTag: TypeTag) { super(); } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a: Fr = machineState.readMemory(this.aOffset); - const b: Fr = machineState.readMemory(this.bOffset); + Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); + + const a = machineState.memory.getAs(this.aOffset); + const b = machineState.memory.getAs(this.bOffset); - const dest = new Fr(a.toBigInt() ^ b.toBigInt()); - machineState.writeMemory(this.destOffset, dest); + const res = a.xor(b); + machineState.memory.set(this.destOffset, res); this.incrementPc(machineState); } @@ -69,19 +74,17 @@ export class Not extends Instruction { static type: string = 'NOT'; static numberOfOperands = 2; - constructor(private aOffset: number, private destOffset: number) { + constructor(private aOffset: number, private destOffset: number, private inTag: TypeTag) { super(); } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a: Fr = machineState.readMemory(this.aOffset); + Instruction.checkTags(machineState, this.inTag, this.aOffset); - // TODO: hack -> Bitwise operations should not occur over field elements - // It should only work over integers - const result = ~a.toBigInt(); + const a = machineState.memory.getAs(this.aOffset); - const dest = new Fr(result < 0 ? Fr.MODULUS + /* using a + as result is -ve*/ result : result); - machineState.writeMemory(this.destOffset, dest); + const res = a.not(); + machineState.memory.set(this.destOffset, res); this.incrementPc(machineState); } @@ -92,16 +95,18 @@ export class Shl extends Instruction { static type: string = 'SHL'; static numberOfOperands = 3; - constructor(private aOffset: number, private bOffset: number, private destOffset: number) { + constructor(private aOffset: number, private bOffset: number, private destOffset: number, private inTag: TypeTag) { super(); } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a: Fr = machineState.readMemory(this.aOffset); - const b: Fr = machineState.readMemory(this.bOffset); + Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); - const dest = new Fr(a.toBigInt() << b.toBigInt()); - machineState.writeMemory(this.destOffset, dest); + const a = machineState.memory.getAs(this.aOffset); + const b = machineState.memory.getAs(this.bOffset); + + const res = a.shl(b); + machineState.memory.set(this.destOffset, res); this.incrementPc(machineState); } @@ -112,19 +117,18 @@ export class Shr extends Instruction { static type: string = 'SHR'; static numberOfOperands = 3; - constructor(private aOffset: number, private bOffset: number, private destOffset: number) { + constructor(private aOffset: number, private bOffset: number, private destOffset: number, private inTag: TypeTag) { super(); } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a: Fr = machineState.readMemory(this.aOffset); - const b: Fr = machineState.readMemory(this.bOffset); - - // Here we are assuming that the field element maps to a positive number. - // The >> operator is *signed* in JS (and it sign extends). - // E.g.: -1n >> 3n == -1n. - const dest = new Fr(a.toBigInt() >> b.toBigInt()); - machineState.writeMemory(this.destOffset, dest); + Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); + + const a = machineState.memory.getAs(this.aOffset); + const b = machineState.memory.getAs(this.bOffset); + + const res = a.shr(b); + machineState.memory.set(this.destOffset, res); this.incrementPc(machineState); } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts index c77a1a9ddd3..a41e291cbd6 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts @@ -1,4 +1,7 @@ +import { strict as assert } from 'assert'; + import { AvmMachineState } from '../avm_machine_state.js'; +import { TypeTag } from '../avm_memory_types.js'; import { AvmStateManager } from '../avm_state_manager.js'; export const AVM_OPERAND_BYTE_LENGTH = 4; @@ -17,4 +20,13 @@ export abstract class Instruction { halt(machineState: AvmMachineState): void { machineState.halted = true; } + + static checkTags(machineState: AvmMachineState, tag: TypeTag, ...offsets: number[]) { + for(const off of offsets) { + if(machineState.memory.getTag(off) !== tag) { + const error = `Offset ${off} has tag ${machineState.memory.getTag(off)}, expected ${tag}`; + throw new Error(error); + } + } + } } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts index 3b02ef36c32..ad30c427573 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts @@ -3,6 +3,7 @@ import { Fr } from '@aztec/foundation/fields'; import { mock } from 'jest-mock-extended'; import { AvmMachineState } from '../avm_machine_state.js'; +import { FieldValue, TypeTag, Uint8, Uint16, Uint32, Uint64, Uint128 } from '../avm_memory_types.js'; import { AvmStateManager } from '../avm_state_manager.js'; import { CMov, CalldataCopy, Cast, Mov, Set } from './memory.js'; @@ -15,166 +16,261 @@ describe('Memory instructions', () => { stateManager = mock(); }); - it('Should SET memory correctly', () => { - const value = 123456n; + describe('SET', () => { + it('should correctly set value and tag (uninitialized)', () => { + new Set(/*value=*/ 1234n, /*offset=*/ 1, TypeTag.UINT16).execute(machineState, stateManager); - new Set(value, 1).execute(machineState, stateManager); + const actual = machineState.memory.get(1); + const tag = machineState.memory.getTag(1); - const expected = new Fr(value); - const actual = machineState.readMemory(1); - expect(actual).toEqual(expected); - }); + expect(actual).toEqual(new Uint16(1234n)); + expect(tag).toEqual(TypeTag.UINT16); + }); - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/3987): tags are not implemented yet - this will behave as a mov - describe('CAST', () => { - it('Should work correctly on different memory cells', () => { - const value = new Fr(123456n); + it('should correctly set value and tag (overwriting)', () => { + machineState.memory.set(1, new FieldValue(27)); - machineState.writeMemory(0, value); + new Set(/*value=*/ 1234n, /*offset=*/ 1, TypeTag.UINT32).execute(machineState, stateManager); + + const actual = machineState.memory.get(1); + const tag = machineState.memory.getTag(1); + + expect(actual).toEqual(new Uint32(1234n)); + expect(tag).toEqual(TypeTag.UINT32); + }); + }); - new Cast(/*aOffset=*/ 0, /*dstOffset=*/ 1).execute(machineState, stateManager); + describe('CAST', () => { + it('Should upcast between integral types', () => { + machineState.memory.set(0, new Uint8(20n)); + machineState.memory.set(1, new Uint16(65000n)); + machineState.memory.set(2, new Uint32(1n << 30n)); + machineState.memory.set(3, new Uint64(1n << 50n)); + machineState.memory.set(4, new Uint128(1n << 100n)); + + [ + new Cast(/*aOffset=*/ 0, /*dstOffset=*/ 10, TypeTag.UINT16), + new Cast(/*aOffset=*/ 1, /*dstOffset=*/ 11, TypeTag.UINT32), + new Cast(/*aOffset=*/ 2, /*dstOffset=*/ 12, TypeTag.UINT64), + new Cast(/*aOffset=*/ 3, /*dstOffset=*/ 13, TypeTag.UINT128), + new Cast(/*aOffset=*/ 4, /*dstOffset=*/ 14, TypeTag.UINT128), + ].forEach(i => i.execute(machineState, stateManager)); + + const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 5); + expect(actual).toEqual([ + new Uint16(20n), + new Uint32(65000n), + new Uint64(1n << 30n), + new Uint128(1n << 50n), + new Uint128(1n << 100n), + ]); + const tags = machineState.memory.getSliceTags(/*offset=*/ 10, /*size=*/ 5); + expect(tags).toEqual([TypeTag.UINT16, TypeTag.UINT32, TypeTag.UINT64, TypeTag.UINT128, TypeTag.UINT128]); + }); - const actual = machineState.readMemory(1); - expect(actual).toEqual(value); + it('Should downcast (truncating) between integral types', () => { + machineState.memory.set(0, new Uint8(20n)); + machineState.memory.set(1, new Uint16(65000n)); + machineState.memory.set(2, new Uint32((1n << 30n) - 1n)); + machineState.memory.set(3, new Uint64((1n << 50n) - 1n)); + machineState.memory.set(4, new Uint128((1n << 100n) - 1n)); + + [ + new Cast(/*aOffset=*/ 0, /*dstOffset=*/ 10, TypeTag.UINT8), + new Cast(/*aOffset=*/ 1, /*dstOffset=*/ 11, TypeTag.UINT8), + new Cast(/*aOffset=*/ 2, /*dstOffset=*/ 12, TypeTag.UINT16), + new Cast(/*aOffset=*/ 3, /*dstOffset=*/ 13, TypeTag.UINT32), + new Cast(/*aOffset=*/ 4, /*dstOffset=*/ 14, TypeTag.UINT64), + ].forEach(i => i.execute(machineState, stateManager)); + + const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 5); + expect(actual).toEqual([ + new Uint8(20n), + new Uint8(232), + new Uint16((1n << 16n) - 1n), + new Uint32((1n << 32n) - 1n), + new Uint64((1n << 64n) - 1n), + ]); + const tags = machineState.memory.getSliceTags(/*offset=*/ 10, /*size=*/ 5); + expect(tags).toEqual([TypeTag.UINT8, TypeTag.UINT8, TypeTag.UINT16, TypeTag.UINT32, TypeTag.UINT64]); }); - it('Should work correctly on same memory cell', () => { - const value = new Fr(123456n); + it('Should upcast from integral types to field', () => { + machineState.memory.set(0, new Uint8(20n)); + machineState.memory.set(1, new Uint16(65000n)); + machineState.memory.set(2, new Uint32(1n << 30n)); + machineState.memory.set(3, new Uint64(1n << 50n)); + machineState.memory.set(4, new Uint128(1n << 100n)); + + [ + new Cast(/*aOffset=*/ 0, /*dstOffset=*/ 10, TypeTag.FIELD), + new Cast(/*aOffset=*/ 1, /*dstOffset=*/ 11, TypeTag.FIELD), + new Cast(/*aOffset=*/ 2, /*dstOffset=*/ 12, TypeTag.FIELD), + new Cast(/*aOffset=*/ 3, /*dstOffset=*/ 13, TypeTag.FIELD), + new Cast(/*aOffset=*/ 4, /*dstOffset=*/ 14, TypeTag.FIELD), + ].forEach(i => i.execute(machineState, stateManager)); + + const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 5); + expect(actual).toEqual([ + new FieldValue(20n), + new FieldValue(65000n), + new FieldValue(1n << 30n), + new FieldValue(1n << 50n), + new FieldValue(1n << 100n), + ]); + const tags = machineState.memory.getSliceTags(/*offset=*/ 10, /*size=*/ 5); + expect(tags).toEqual([TypeTag.FIELD, TypeTag.FIELD, TypeTag.FIELD, TypeTag.FIELD, TypeTag.FIELD]); + }); - machineState.writeMemory(0, value); + it('Should downcast (truncating) from field to integral types', () => { + machineState.memory.set(0, new FieldValue((1n << 200n) - 1n)); + machineState.memory.set(1, new FieldValue((1n << 200n) - 1n)); + machineState.memory.set(2, new FieldValue((1n << 200n) - 1n)); + machineState.memory.set(3, new FieldValue((1n << 200n) - 1n)); + machineState.memory.set(4, new FieldValue((1n << 200n) - 1n)); + + [ + new Cast(/*aOffset=*/ 0, /*dstOffset=*/ 10, TypeTag.UINT8), + new Cast(/*aOffset=*/ 1, /*dstOffset=*/ 11, TypeTag.UINT16), + new Cast(/*aOffset=*/ 2, /*dstOffset=*/ 12, TypeTag.UINT32), + new Cast(/*aOffset=*/ 3, /*dstOffset=*/ 13, TypeTag.UINT64), + new Cast(/*aOffset=*/ 4, /*dstOffset=*/ 14, TypeTag.UINT128), + ].forEach(i => i.execute(machineState, stateManager)); + + const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 5); + expect(actual).toEqual([ + new Uint8((1n << 8n) - 1n), + new Uint16((1n << 16n) - 1n), + new Uint32((1n << 32n) - 1n), + new Uint64((1n << 64n) - 1n), + new Uint128((1n << 128n) - 1n), + ]); + const tags = machineState.memory.getSliceTags(/*offset=*/ 10, /*size=*/ 5); + expect(tags).toEqual([TypeTag.UINT8, TypeTag.UINT16, TypeTag.UINT32, TypeTag.UINT64, TypeTag.UINT128]); + }); - new Cast(/*aOffset=*/ 0, /*dstOffset=*/ 0).execute(machineState, stateManager); + it('Should cast between field elements', () => { + machineState.memory.set(0, new FieldValue(12345678n)); + + new Cast(/*aOffset=*/ 0, /*dstOffset=*/ 1, TypeTag.FIELD).execute(machineState, stateManager); - const actual = machineState.readMemory(0); - expect(actual).toEqual(value); + const actual = machineState.memory.get(1); + expect(actual).toEqual(new FieldValue(12345678n)); + const tags = machineState.memory.getTag(1); + expect(tags).toEqual(TypeTag.FIELD); }); }); describe('MOV', () => { - it('Should work correctly on different memory cells', () => { - const value = new Fr(123456n); - - machineState.writeMemory(0, value); + it('Should move integrals on different memory cells', () => { + machineState.memory.set(1, new Uint16(27)); + new Mov(/*offsetA=*/ 1, /*offsetA=*/ 2).execute(machineState, stateManager); - new Mov(/*aOffset=*/ 0, /*dstOffset=*/ 1).execute(machineState, stateManager); + const actual = machineState.memory.get(2); + const tag = machineState.memory.getTag(2); - const actual = machineState.readMemory(1); - expect(actual).toEqual(value); + expect(actual).toEqual(new Uint16(27n)); + expect(tag).toEqual(TypeTag.UINT16); }); - it('Should work correctly on same memory cell', () => { - const value = new Fr(123456n); + it('Should move field elements on different memory cells', () => { + machineState.memory.set(1, new FieldValue(27)); + new Mov(/*offsetA=*/ 1, /*offsetA=*/ 2).execute(machineState, stateManager); - machineState.writeMemory(0, value); + const actual = machineState.memory.get(2); + const tag = machineState.memory.getTag(2); - new Mov(/*aOffset=*/ 0, /*dstOffset=*/ 0).execute(machineState, stateManager); - - const actual = machineState.readMemory(0); - expect(actual).toEqual(value); + expect(actual).toEqual(new FieldValue(27n)); + expect(tag).toEqual(TypeTag.FIELD); }); }); - describe('MOV', () => { - it('Should move A if COND is true, on different memory cells', () => { - const valueA = new Fr(123456n); - const valueB = new Fr(80n); - const valueCondition = new Fr(22n); - - machineState.writeMemory(0, valueA); - machineState.writeMemory(1, valueB); - machineState.writeMemory(2, valueCondition); + describe('CMOV', () => { + it('Should move A if COND is true, on different memory cells (integral condition)', () => { + machineState.memory.set(0, new Uint32(123)); // A + machineState.memory.set(1, new Uint16(456)); // B + machineState.memory.set(2, new Uint8(2)); // Condition new CMov(/*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute(machineState, stateManager); - const actual = machineState.readMemory(3); - expect(actual).toEqual(valueA); + const actual = machineState.memory.get(3); + const tag = machineState.memory.getTag(3); + expect(actual).toEqual(new Uint32(123)); + expect(tag).toEqual(TypeTag.UINT32); }); - it('Should move B if COND is false, on different memory cells', () => { - const valueA = new Fr(123456n); - const valueB = new Fr(80n); - const valueCondition = new Fr(0n); - - machineState.writeMemory(0, valueA); - machineState.writeMemory(1, valueB); - machineState.writeMemory(2, valueCondition); + it('Should move B if COND is false, on different memory cells (integral condition)', () => { + machineState.memory.set(0, new Uint32(123)); // A + machineState.memory.set(1, new Uint16(456)); // B + machineState.memory.set(2, new Uint8(0)); // Condition new CMov(/*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute(machineState, stateManager); - const actual = machineState.readMemory(3); - expect(actual).toEqual(valueB); + const actual = machineState.memory.get(3); + const tag = machineState.memory.getTag(3); + expect(actual).toEqual(new Uint16(456)); + expect(tag).toEqual(TypeTag.UINT16); }); - it('Should move A if COND is true, on overlapping memory cells', () => { - const valueA = new Fr(123456n); - const valueB = new Fr(80n); - const valueCondition = new Fr(22n); - - machineState.writeMemory(0, valueA); - machineState.writeMemory(1, valueB); - machineState.writeMemory(2, valueCondition); + it('Should move A if COND is true, on different memory cells (field condition)', () => { + machineState.memory.set(0, new Uint32(123)); // A + machineState.memory.set(1, new Uint16(456)); // B + machineState.memory.set(2, new FieldValue(1)); // Condition - new CMov(/*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 2).execute(machineState, stateManager); + new CMov(/*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute(machineState, stateManager); - const actual = machineState.readMemory(2); - expect(actual).toEqual(valueA); + const actual = machineState.memory.get(3); + const tag = machineState.memory.getTag(3); + expect(actual).toEqual(new Uint32(123)); + expect(tag).toEqual(TypeTag.UINT32); }); - it('Should move B if COND is false, on overlapping memory cells', () => { - const valueA = new Fr(123456n); - const valueB = new Fr(80n); - const valueCondition = new Fr(0n); + it('Should move B if COND is false, on different memory cells (integral condition)', () => { + machineState.memory.set(0, new Uint32(123)); // A + machineState.memory.set(1, new Uint16(456)); // B + machineState.memory.set(2, new FieldValue(0)); // Condition - machineState.writeMemory(0, valueA); - machineState.writeMemory(1, valueB); - machineState.writeMemory(2, valueCondition); - - new CMov(/*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 2).execute(machineState, stateManager); + new CMov(/*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute(machineState, stateManager); - const actual = machineState.readMemory(2); - expect(actual).toEqual(valueB); + const actual = machineState.memory.get(3); + const tag = machineState.memory.getTag(3); + expect(actual).toEqual(new Uint16(456)); + expect(tag).toEqual(TypeTag.UINT16); }); }); - describe('CALLDATA', () => { + describe('CALLDATACOPY', () => { it('Writes nothing if size is 0', () => { - const previousValue = new Fr(123456n); const calldata = [new Fr(1n), new Fr(2n), new Fr(3n)]; - machineState = new AvmMachineState(calldata); - machineState.writeMemory(0, previousValue); + machineState.memory.set(0, new Uint16(12)); // Some previous data to be overwritten - new CalldataCopy(/*cdOffset=*/ 2, /*copySize=*/ 0, /*dstOffset=*/ 0).execute(machineState, stateManager); + new CalldataCopy(/*cdOffset=*/ 0, /*copySize=*/ 0, /*dstOffset=*/ 0).execute(machineState, stateManager); - const actual = machineState.readMemory(0); - expect(actual).toEqual(previousValue); + const actual = machineState.memory.get(0); + expect(actual).toEqual(new Uint16(12)); }); it('Copies all calldata', () => { - const previousValue = new Fr(123456n); const calldata = [new Fr(1n), new Fr(2n), new Fr(3n)]; - machineState = new AvmMachineState(calldata); - machineState.writeMemory(0, previousValue); + machineState.memory.set(0, new Uint16(12)); // Some previous data to be overwritten new CalldataCopy(/*cdOffset=*/ 0, /*copySize=*/ 3, /*dstOffset=*/ 0).execute(machineState, stateManager); - const actual = machineState.readMemoryChunk(/*offset=*/ 0, /*size=*/ 3); - expect(actual).toEqual(calldata); + const actual = machineState.memory.getSlice(/*offset=*/ 0, /*size=*/ 3); + expect(actual).toEqual([new FieldValue(1), new FieldValue(2), new FieldValue(3)]); }); it('Copies slice of calldata', () => { - const previousValue = new Fr(123456n); const calldata = [new Fr(1n), new Fr(2n), new Fr(3n)]; - machineState = new AvmMachineState(calldata); - machineState.writeMemory(0, previousValue); + machineState.memory.set(0, new Uint16(12)); // Some previous data to be overwritten new CalldataCopy(/*cdOffset=*/ 1, /*copySize=*/ 2, /*dstOffset=*/ 0).execute(machineState, stateManager); - const expected = calldata.slice(1); - const actual = machineState.readMemoryChunk(/*offset=*/ 0, /*size=*/ 2); - expect(actual).toEqual(expected); + const actual = machineState.memory.getSlice(/*offset=*/ 0, /*size=*/ 2); + expect(actual).toEqual([new FieldValue(2), new FieldValue(3)]); }); // TODO: check bad cases (i.e., out of bounds) diff --git a/yarn-project/acir-simulator/src/avm/opcodes/memory.ts b/yarn-project/acir-simulator/src/avm/opcodes/memory.ts index 3e6bbc62afd..87ada59a1ef 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/memory.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/memory.ts @@ -1,6 +1,9 @@ import { Fr } from '@aztec/foundation/fields'; +import { assert } from 'console'; + import { AvmMachineState } from '../avm_machine_state.js'; +import { FieldValue, TaggedMemory, TypeTag } from '../avm_memory_types.js'; import { AvmStateManager } from '../avm_state_manager.js'; import { Instruction } from './instruction.js'; @@ -9,31 +12,38 @@ export class Set extends Instruction { static type: string = 'SET'; static numberOfOperands = 2; - constructor(private value: bigint, private destOffset: number) { + constructor(private value: bigint, private dstOffset: number, private dstTag: TypeTag) { super(); } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - machineState.writeMemory(this.destOffset, new Fr(this.value)); + const res = TaggedMemory.integralFromTag(this.value, this.dstTag); + + machineState.memory.set(this.dstOffset, res); this.incrementPc(machineState); } } -// TODO(https://github.com/AztecProtocol/aztec-packages/issues/3987): tags are not implemented yet - this will behave as a mov -/** - */ export class Cast extends Instruction { static type: string = 'CAST'; static numberOfOperands = 2; - constructor(private aOffset: number, private destOffset: number) { + constructor(private aOffset: number, private dstOffset: number, private dstTag: TypeTag) { super(); } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a = machineState.readMemory(this.aOffset); + const a = machineState.memory.get(this.aOffset); - machineState.writeMemory(this.destOffset, a); + // TODO: consider not using toBigInt() + const casted = + this.dstTag == TypeTag.FIELD + ? new FieldValue(a.toBigInt()) + : TaggedMemory.integralFromTag(a.toBigInt(), this.dstTag); + + // TODO cast + machineState.memory.set(this.dstOffset, casted); this.incrementPc(machineState); } @@ -44,14 +54,14 @@ export class Mov extends Instruction { static type: string = 'MOV'; static numberOfOperands = 2; - constructor(private aOffset: number, private destOffset: number) { + constructor(private aOffset: number, private dstOffset: number) { super(); } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a = machineState.readMemory(this.aOffset); + const a = machineState.memory.get(this.aOffset); - machineState.writeMemory(this.destOffset, a); + machineState.memory.set(this.dstOffset, a); this.incrementPc(machineState); } @@ -59,24 +69,20 @@ export class Mov extends Instruction { /** - */ export class CMov extends Instruction { - static type: string = 'MOV'; + static type: string = 'CMOV'; static numberOfOperands = 4; - constructor( - private aOffset: number, - private bOffset: number, - private condOffset: number, - private destOffset: number, - ) { + constructor(private aOffset: number, private bOffset: number, private condOffset: number, private dstOffset: number) { super(); } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a = machineState.readMemory(this.aOffset); - const b = machineState.readMemory(this.bOffset); - const cond = machineState.readMemory(this.condOffset); + const a = machineState.memory.get(this.aOffset); + const b = machineState.memory.get(this.bOffset); + const cond = machineState.memory.get(this.condOffset); - machineState.writeMemory(this.destOffset, cond.toBigInt() ? a : b); + // TODO: reconsider toBigInt() here. + machineState.memory.set(this.dstOffset, cond.toBigInt() > 0 ? a : b); this.incrementPc(machineState); } @@ -87,13 +93,15 @@ export class CalldataCopy extends Instruction { static type: string = 'CALLDATACOPY'; static numberOfOperands = 3; - constructor(private cdOffset: number, private copySize: number, private destOffset: number) { + constructor(private cdOffset: number, private copySize: number, private dstOffset: number) { super(); } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const calldata = machineState.calldata.slice(this.cdOffset, this.cdOffset + this.copySize); - machineState.writeMemoryChunk(this.destOffset, calldata); + const transformedData = machineState.calldata + .slice(this.cdOffset, this.cdOffset + this.copySize) + .map(f => new FieldValue(f)); + machineState.memory.setSlice(this.dstOffset, transformedData); this.incrementPc(machineState); } From 2af730c49535a5ad1e0f5b43552c98786b217426 Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Thu, 25 Jan 2024 14:25:54 +0000 Subject: [PATCH 3/8] fix tests --- .../src/avm/opcodes/arithmetic.test.ts | 71 ++++++++++--------- .../src/avm/opcodes/arithmetic.ts | 26 ++++--- .../src/avm/opcodes/comparators.ts | 27 ++++--- .../src/avm/opcodes/control_flow.test.ts | 28 ++++---- .../src/avm/opcodes/control_flow.ts | 12 +++- 5 files changed, 85 insertions(+), 79 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts index 76a802231a3..40eef68255b 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts @@ -5,6 +5,7 @@ import { mock } from 'jest-mock-extended'; import { AvmMachineState } from '../avm_machine_state.js'; import { AvmStateManager } from '../avm_state_manager.js'; import { Add, Div, Mul, Sub } from './arithmetic.js'; +import { FieldValue } from '../avm_memory_types.js'; describe('Arithmetic Instructions', () => { let machineState: AvmMachineState; @@ -17,92 +18,92 @@ describe('Arithmetic Instructions', () => { describe('Add', () => { it('Should add correctly over Fr type', () => { - const a = new Fr(1n); - const b = new Fr(2n); + const a = new FieldValue(1n); + const b = new FieldValue(2n); - machineState.writeMemory(0, a); - machineState.writeMemory(1, b); + machineState.memory.set(0, a); + machineState.memory.set(1, b); new Add(0, 1, 2).execute(machineState, stateManager); - const expected = new Fr(3n); - const actual = machineState.readMemory(2); + const expected = new FieldValue(3n); + const actual = machineState.memory.get(2); expect(actual).toEqual(expected); }); it('Should wrap around on addition', () => { - const a = new Fr(1n); - const b = new Fr(Fr.MODULUS - 1n); + const a = new FieldValue(1n); + const b = new FieldValue(Fr.MODULUS - 1n); - machineState.writeMemory(0, a); - machineState.writeMemory(1, b); + machineState.memory.set(0, a); + machineState.memory.set(1, b); new Add(0, 1, 2).execute(machineState, stateManager); - const expected = new Fr(0n); - const actual = machineState.readMemory(3); + const expected = new FieldValue(0n); + const actual = machineState.memory.get(2); expect(actual).toEqual(expected); }); }); describe('Sub', () => { it('Should subtract correctly over Fr type', () => { - const a = new Fr(1n); - const b = new Fr(2n); + const a = new FieldValue(1n); + const b = new FieldValue(2n); - machineState.writeMemory(0, a); - machineState.writeMemory(1, b); + machineState.memory.set(0, a); + machineState.memory.set(1, b); new Sub(0, 1, 2).execute(machineState, stateManager); - const expected = new Fr(Fr.MODULUS - 1n); - const actual = machineState.readMemory(2); + const expected = new FieldValue(Fr.MODULUS - 1n); + const actual = machineState.memory.get(2); expect(actual).toEqual(expected); }); }); describe('Mul', () => { it('Should multiply correctly over Fr type', () => { - const a = new Fr(2n); - const b = new Fr(3n); + const a = new FieldValue(2n); + const b = new FieldValue(3n); - machineState.writeMemory(0, a); - machineState.writeMemory(1, b); + machineState.memory.set(0, a); + machineState.memory.set(1, b); new Mul(0, 1, 2).execute(machineState, stateManager); - const expected = new Fr(6n); - const actual = machineState.readMemory(2); + const expected = new FieldValue(6n); + const actual = machineState.memory.get(2); expect(actual).toEqual(expected); }); it('Should wrap around on multiplication', () => { - const a = new Fr(2n); - const b = new Fr(Fr.MODULUS / 2n - 1n); + const a = new FieldValue(2n); + const b = new FieldValue(Fr.MODULUS / 2n - 1n); - machineState.writeMemory(0, a); - machineState.writeMemory(1, b); + machineState.memory.set(0, a); + machineState.memory.set(1, b); new Mul(0, 1, 2).execute(machineState, stateManager); - const expected = new Fr(Fr.MODULUS - 3n); - const actual = machineState.readMemory(2); + const expected = new FieldValue(Fr.MODULUS - 3n); + const actual = machineState.memory.get(2); expect(actual).toEqual(expected); }); }); describe('Div', () => { it('Should perform field division', () => { - const a = new Fr(2n); - const b = new Fr(3n); + const a = new FieldValue(2n); + const b = new FieldValue(3n); - machineState.writeMemory(0, a); - machineState.writeMemory(1, b); + machineState.memory.set(0, a); + machineState.memory.set(1, b); new Div(0, 1, 2).execute(machineState, stateManager); // Note - const actual = machineState.readMemory(2); + const actual = machineState.memory.get(2); const recovered = actual.mul(b); expect(recovered).toEqual(a); }); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts index 94df9713c83..c6935b3323e 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts @@ -1,5 +1,3 @@ -import { Fr } from '@aztec/foundation/fields'; - import { AvmMachineState } from '../avm_machine_state.js'; import { AvmStateManager } from '../avm_state_manager.js'; import { Instruction } from './instruction.js'; @@ -14,11 +12,11 @@ export class Add extends Instruction { } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a = machineState.readMemory(this.aOffset); - const b = machineState.readMemory(this.bOffset); + const a = machineState.memory.get(this.aOffset); + const b = machineState.memory.get(this.bOffset); const dest = a.add(b); - machineState.writeMemory(this.destOffset, dest); + machineState.memory.set(this.destOffset, dest); this.incrementPc(machineState); } @@ -34,11 +32,11 @@ export class Sub extends Instruction { } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a = machineState.readMemory(this.aOffset); - const b = machineState.readMemory(this.bOffset); + const a = machineState.memory.get(this.aOffset); + const b = machineState.memory.get(this.bOffset); const dest = a.sub(b); - machineState.writeMemory(this.destOffset, dest); + machineState.memory.set(this.destOffset, dest); this.incrementPc(machineState); } @@ -54,11 +52,11 @@ export class Mul extends Instruction { } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a: Fr = machineState.readMemory(this.aOffset); - const b: Fr = machineState.readMemory(this.bOffset); + const a = machineState.memory.get(this.aOffset); + const b = machineState.memory.get(this.bOffset); const dest = a.mul(b); - machineState.writeMemory(this.destOffset, dest); + machineState.memory.set(this.destOffset, dest); this.incrementPc(machineState); } @@ -74,11 +72,11 @@ export class Div extends Instruction { } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a: Fr = machineState.readMemory(this.aOffset); - const b: Fr = machineState.readMemory(this.bOffset); + const a = machineState.memory.get(this.aOffset); + const b = machineState.memory.get(this.bOffset); const dest = a.div(b); - machineState.writeMemory(this.destOffset, dest); + machineState.memory.set(this.destOffset, dest); this.incrementPc(machineState); } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts b/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts index 685a5438c42..498bfe2309b 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts @@ -1,6 +1,5 @@ -import { Fr } from '@aztec/foundation/fields'; - import { AvmMachineState } from '../avm_machine_state.js'; +import { FieldValue } from '../avm_memory_types.js'; import { AvmStateManager } from '../avm_state_manager.js'; import { Instruction } from './instruction.js'; @@ -14,11 +13,11 @@ export class Eq extends Instruction { } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a: Fr = machineState.readMemory(this.aOffset); - const b: Fr = machineState.readMemory(this.bOffset); + const a = machineState.memory.get(this.aOffset); + const b = machineState.memory.get(this.bOffset); - const dest = new Fr(a.toBigInt() == b.toBigInt()); - machineState.writeMemory(this.destOffset, dest); + const dest = new FieldValue(a.toBigInt() == b.toBigInt() ? 1 : 0); + machineState.memory.set(this.destOffset, dest); this.incrementPc(machineState); } @@ -33,11 +32,11 @@ export class Lt extends Instruction { } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a: Fr = machineState.readMemory(this.aOffset); - const b: Fr = machineState.readMemory(this.bOffset); + const a = machineState.memory.get(this.aOffset); + const b = machineState.memory.get(this.bOffset); - const dest = new Fr(a.toBigInt() < b.toBigInt()); - machineState.writeMemory(this.destOffset, dest); + const dest = new FieldValue(a.toBigInt() < b.toBigInt() ? 1 : 0); + machineState.memory.set(this.destOffset, dest); this.incrementPc(machineState); } @@ -53,11 +52,11 @@ export class Lte extends Instruction { } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const a: Fr = machineState.readMemory(this.aOffset); - const b: Fr = machineState.readMemory(this.bOffset); + const a = machineState.memory.get(this.aOffset); + const b = machineState.memory.get(this.bOffset); - const dest = new Fr(a.toBigInt() < b.toBigInt()); - machineState.writeMemory(this.destOffset, dest); + const dest = new FieldValue(a.toBigInt() < b.toBigInt() ? 1 : 0); + machineState.memory.set(this.destOffset, dest); this.incrementPc(machineState); } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts index 906584b3493..8f6a5063347 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts @@ -1,5 +1,3 @@ -import { Fr } from '@aztec/foundation/fields'; - import { mock } from 'jest-mock-extended'; import { AvmMachineState } from '../avm_machine_state.js'; @@ -9,6 +7,7 @@ import { And, Not, Or, Shl, Shr, Xor } from './bitwise.js'; import { Eq, Lt, Lte } from './comparators.js'; import { InternalCall, InternalCallStackEmptyError, InternalReturn, Jump, JumpI } from './control_flow.js'; import { CMov, CalldataCopy, Cast, Mov, Set } from './memory.js'; +import { TypeTag, Uint16 } from '../avm_memory_types.js'; describe('Control Flow Opcodes', () => { let stateManager = mock(); @@ -35,8 +34,8 @@ describe('Control Flow Opcodes', () => { expect(machineState.pc).toBe(0); - machineState.writeMemory(0, new Fr(1n)); - machineState.writeMemory(1, new Fr(2n)); + machineState.memory.set(0, new Uint16(1n)); + machineState.memory.set(1, new Uint16(2n)); const instruction = new JumpI(jumpLocation, 0); instruction.execute(machineState, stateManager); @@ -53,7 +52,7 @@ describe('Control Flow Opcodes', () => { expect(machineState.pc).toBe(0); - machineState.writeMemory(0, new Fr(0n)); + machineState.memory.set(0, new Uint16(0n)); const instruction = new JumpI(jumpLocation, 0); instruction.execute(machineState, stateManager); @@ -123,22 +122,25 @@ describe('Control Flow Opcodes', () => { new Lt(0, 1, 2), new Lte(0, 1, 2), new Eq(0, 1, 2), - new Xor(0, 1, 2), - new And(0, 1, 2), - new Or(0, 1, 2), - new Shl(0, 1, 2), - new Shr(0, 1, 2), - new Not(0, 2), + new Xor(0, 1, 2, TypeTag.UINT16), + new And(0, 1, 2, TypeTag.UINT16), + new Or(0, 1, 2, TypeTag.UINT16), + new Shl(0, 1, 2, TypeTag.UINT16), + new Shr(0, 1, 2, TypeTag.UINT16), + new Not(0, 2, TypeTag.UINT16), new CalldataCopy(0, 1, 2), - new Set(0n, 1), + new Set(0n, 1, TypeTag.UINT16), new Mov(0, 1), new CMov(0, 1, 2, 3), - new Cast(0, 1), + new Cast(0, 1, TypeTag.UINT16), ]; for (const instruction of instructions) { // Use a fresh machine state each run const innerMachineState = new AvmMachineState([]); + innerMachineState.memory.set(0, new Uint16(4n)); + innerMachineState.memory.set(1, new Uint16(8n)); + innerMachineState.memory.set(2, new Uint16(12n)); expect(machineState.pc).toBe(0); instruction.execute(innerMachineState, stateManager); expect(innerMachineState.pc).toBe(1); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts index b988979076e..c48476a4728 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts @@ -1,6 +1,9 @@ +import { Fr } from '@aztec/foundation/fields'; + import { AvmMachineState } from '../avm_machine_state.js'; import { AvmStateManager } from '../avm_state_manager.js'; import { Instruction } from './instruction.js'; +import { IntegralValueType } from '../avm_memory_types.js'; /** - */ export class Return extends Instruction { @@ -12,7 +15,9 @@ export class Return extends Instruction { } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const returnData = machineState.readMemoryChunk(this.returnOffset, this.returnOffset + this.copySize); + // TODO: reconsider this casting + const returnData = machineState.memory.getSlice(this.returnOffset, this.copySize).map(fvt => new Fr(fvt.toBigInt())); + machineState.setReturnData(returnData); this.halt(machineState); @@ -43,9 +48,10 @@ export class JumpI extends Instruction { } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const condition = machineState.readMemory(this.condOffset); + const condition = machineState.memory.getAs(this.condOffset); - if (condition.toBigInt() == 0n) { + if(condition.toBigInt() == 0n) { + // TODO: reconsider this casting this.incrementPc(machineState); } else { machineState.pc = this.jumpOffset; From dbc296f1e66cfc6a0d692a56a446958ade925ee7 Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Thu, 25 Jan 2024 14:30:19 +0000 Subject: [PATCH 4/8] format --- .../acir-simulator/src/avm/avm_memory_types.test.ts | 8 +++++--- .../acir-simulator/src/avm/opcodes/arithmetic.test.ts | 2 +- .../acir-simulator/src/avm/opcodes/bitwise.test.ts | 2 +- yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts | 2 +- .../acir-simulator/src/avm/opcodes/control_flow.test.ts | 2 +- .../acir-simulator/src/avm/opcodes/control_flow.ts | 8 +++++--- .../acir-simulator/src/avm/opcodes/instruction.ts | 6 ++---- .../acir-simulator/src/avm/opcodes/memory.test.ts | 2 +- yarn-project/acir-simulator/src/avm/opcodes/memory.ts | 4 ---- 9 files changed, 17 insertions(+), 19 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/avm_memory_types.test.ts b/yarn-project/acir-simulator/src/avm/avm_memory_types.test.ts index 3e6d38cab7a..9df7de169ba 100644 --- a/yarn-project/acir-simulator/src/avm/avm_memory_types.test.ts +++ b/yarn-project/acir-simulator/src/avm/avm_memory_types.test.ts @@ -1,4 +1,4 @@ -import { Uint8, Uint32, FieldValue } from './avm_memory_types.js'; +import { FieldValue, Uint8, Uint32 } from './avm_memory_types.js'; describe('Uint8', () => { it('Unsigned 8 max value', () => { @@ -16,7 +16,9 @@ describe('Uint8', () => { describe('Uint32', () => { it('ands', () => { - expect(new Uint32(0b11111110010011100100n).and(new Uint32(0b11100100111001001111n))).toEqual(new Uint32(0b11100100010001000100n)); + expect(new Uint32(0b11111110010011100100n).and(new Uint32(0b11100100111001001111n))).toEqual( + new Uint32(0b11100100010001000100n), + ); }); }); @@ -24,4 +26,4 @@ describe('FieldValue', () => { it('xxx', () => { expect(new FieldValue(27).add(new FieldValue(48))).toEqual(new FieldValue(75)); }); -}); \ No newline at end of file +}); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts index 40eef68255b..5bf9dd6bf50 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts @@ -3,9 +3,9 @@ import { Fr } from '@aztec/foundation/fields'; import { mock } from 'jest-mock-extended'; import { AvmMachineState } from '../avm_machine_state.js'; +import { FieldValue } from '../avm_memory_types.js'; import { AvmStateManager } from '../avm_state_manager.js'; import { Add, Div, Mul, Sub } from './arithmetic.js'; -import { FieldValue } from '../avm_memory_types.js'; describe('Arithmetic Instructions', () => { let machineState: AvmMachineState; diff --git a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts index 451ce20d3a6..6fe969513fa 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.test.ts @@ -152,7 +152,7 @@ describe('Bitwise instructions', () => { const actual = machineState.memory.get(2); expect(actual).toEqual(expected); }); -}); + }); it('Should NOT correctly over integral types', () => { const a = new Uint16(0b0110010011100100n); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts index eb3f9c89877..cef510b5289 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts @@ -1,7 +1,7 @@ import { AvmMachineState } from '../avm_machine_state.js'; +import { IntegralValueType, TypeTag } from '../avm_memory_types.js'; import { AvmStateManager } from '../avm_state_manager.js'; import { Instruction } from './instruction.js'; -import { IntegralValueType, TypeTag } from '../avm_memory_types.js'; /** - */ export class And extends Instruction { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts index 8f6a5063347..0363cf1323d 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts @@ -1,13 +1,13 @@ import { mock } from 'jest-mock-extended'; import { AvmMachineState } from '../avm_machine_state.js'; +import { TypeTag, Uint16 } from '../avm_memory_types.js'; import { AvmStateManager } from '../avm_state_manager.js'; import { Add, Mul, Sub } from './arithmetic.js'; import { And, Not, Or, Shl, Shr, Xor } from './bitwise.js'; import { Eq, Lt, Lte } from './comparators.js'; import { InternalCall, InternalCallStackEmptyError, InternalReturn, Jump, JumpI } from './control_flow.js'; import { CMov, CalldataCopy, Cast, Mov, Set } from './memory.js'; -import { TypeTag, Uint16 } from '../avm_memory_types.js'; describe('Control Flow Opcodes', () => { let stateManager = mock(); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts index c48476a4728..6eff3d4b088 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts @@ -1,9 +1,9 @@ import { Fr } from '@aztec/foundation/fields'; import { AvmMachineState } from '../avm_machine_state.js'; +import { IntegralValueType } from '../avm_memory_types.js'; import { AvmStateManager } from '../avm_state_manager.js'; import { Instruction } from './instruction.js'; -import { IntegralValueType } from '../avm_memory_types.js'; /** - */ export class Return extends Instruction { @@ -16,7 +16,9 @@ export class Return extends Instruction { execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { // TODO: reconsider this casting - const returnData = machineState.memory.getSlice(this.returnOffset, this.copySize).map(fvt => new Fr(fvt.toBigInt())); + const returnData = machineState.memory + .getSlice(this.returnOffset, this.copySize) + .map(fvt => new Fr(fvt.toBigInt())); machineState.setReturnData(returnData); @@ -50,7 +52,7 @@ export class JumpI extends Instruction { execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { const condition = machineState.memory.getAs(this.condOffset); - if(condition.toBigInt() == 0n) { + if (condition.toBigInt() == 0n) { // TODO: reconsider this casting this.incrementPc(machineState); } else { diff --git a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts index a41e291cbd6..f36e2e1d4e5 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts @@ -1,5 +1,3 @@ -import { strict as assert } from 'assert'; - import { AvmMachineState } from '../avm_machine_state.js'; import { TypeTag } from '../avm_memory_types.js'; import { AvmStateManager } from '../avm_state_manager.js'; @@ -22,8 +20,8 @@ export abstract class Instruction { } static checkTags(machineState: AvmMachineState, tag: TypeTag, ...offsets: number[]) { - for(const off of offsets) { - if(machineState.memory.getTag(off) !== tag) { + for (const off of offsets) { + if (machineState.memory.getTag(off) !== tag) { const error = `Offset ${off} has tag ${machineState.memory.getTag(off)}, expected ${tag}`; throw new Error(error); } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts index ad30c427573..80cc5a3a4bc 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts @@ -151,7 +151,7 @@ describe('Memory instructions', () => { it('Should cast between field elements', () => { machineState.memory.set(0, new FieldValue(12345678n)); - + new Cast(/*aOffset=*/ 0, /*dstOffset=*/ 1, TypeTag.FIELD).execute(machineState, stateManager); const actual = machineState.memory.get(1); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/memory.ts b/yarn-project/acir-simulator/src/avm/opcodes/memory.ts index 87ada59a1ef..e7155c9394f 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/memory.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/memory.ts @@ -1,7 +1,3 @@ -import { Fr } from '@aztec/foundation/fields'; - -import { assert } from 'console'; - import { AvmMachineState } from '../avm_machine_state.js'; import { FieldValue, TaggedMemory, TypeTag } from '../avm_memory_types.js'; import { AvmStateManager } from '../avm_state_manager.js'; From f4f39450fe28725fb36f30a9f207e2d25a112ac2 Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Thu, 25 Jan 2024 17:12:46 +0000 Subject: [PATCH 5/8] more fixing and formatting --- .../src/avm/avm_memory_types.test.ts | 17 +-- .../src/avm/avm_memory_types.ts | 118 ++++++++---------- .../src/avm/interpreter/interpreter.ts | 4 +- .../src/avm/opcodes/arithmetic.test.ts | 44 ++++--- .../src/avm/opcodes/arithmetic.ts | 3 - .../acir-simulator/src/avm/opcodes/bitwise.ts | 28 ++--- .../src/avm/opcodes/comparators.ts | 12 +- .../src/avm/opcodes/control_flow.ts | 24 +--- .../src/avm/opcodes/instruction.ts | 11 +- .../src/avm/opcodes/memory.test.ts | 40 +++--- .../acir-simulator/src/avm/opcodes/memory.ts | 12 +- 11 files changed, 133 insertions(+), 180 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/avm_memory_types.test.ts b/yarn-project/acir-simulator/src/avm/avm_memory_types.test.ts index 9df7de169ba..6df75930fe1 100644 --- a/yarn-project/acir-simulator/src/avm/avm_memory_types.test.ts +++ b/yarn-project/acir-simulator/src/avm/avm_memory_types.test.ts @@ -1,5 +1,6 @@ -import { FieldValue, Uint8, Uint32 } from './avm_memory_types.js'; +import { Field, Uint8 } from './avm_memory_types.js'; +// TODO: complete describe('Uint8', () => { it('Unsigned 8 max value', () => { expect(new Uint8(255).toBigInt()).toEqual(255n); @@ -14,16 +15,8 @@ describe('Uint8', () => { }); }); -describe('Uint32', () => { - it('ands', () => { - expect(new Uint32(0b11111110010011100100n).and(new Uint32(0b11100100111001001111n))).toEqual( - new Uint32(0b11100100010001000100n), - ); - }); -}); - -describe('FieldValue', () => { - it('xxx', () => { - expect(new FieldValue(27).add(new FieldValue(48))).toEqual(new FieldValue(75)); +describe('Field', () => { + it('Add correctly without wrapping', () => { + expect(new Field(27).add(new Field(48))).toEqual(new Field(75)); }); }); diff --git a/yarn-project/acir-simulator/src/avm/avm_memory_types.ts b/yarn-project/acir-simulator/src/avm/avm_memory_types.ts index 64a60d477bb..69ed95c4a66 100644 --- a/yarn-project/acir-simulator/src/avm/avm_memory_types.ts +++ b/yarn-project/acir-simulator/src/avm/avm_memory_types.ts @@ -2,28 +2,27 @@ import { Fr } from '@aztec/foundation/fields'; import { strict as assert } from 'assert'; -export interface FieldValueType { - add(rhs: FieldValueType): FieldValueType; - sub(rhs: FieldValueType): FieldValueType; - mul(rhs: FieldValueType): FieldValueType; - div(rhs: FieldValueType): FieldValueType; +export interface MemoryValue { + add(rhs: MemoryValue): MemoryValue; + sub(rhs: MemoryValue): MemoryValue; + mul(rhs: MemoryValue): MemoryValue; + div(rhs: MemoryValue): MemoryValue; // Use sparingly. toBigInt(): bigint; } -export interface IntegralValueType extends FieldValueType { - shl(rhs: IntegralValueType): IntegralValueType; - shr(rhs: IntegralValueType): IntegralValueType; - and(rhs: IntegralValueType): IntegralValueType; - or(rhs: IntegralValueType): IntegralValueType; - xor(rhs: IntegralValueType): IntegralValueType; - not(): IntegralValueType; +export interface IntegralValue extends MemoryValue { + shl(rhs: IntegralValue): IntegralValue; + shr(rhs: IntegralValue): IntegralValue; + and(rhs: IntegralValue): IntegralValue; + or(rhs: IntegralValue): IntegralValue; + xor(rhs: IntegralValue): IntegralValue; + not(): IntegralValue; } -// TODO: optimize calculation of mod, etc. Can only 1 per class? -// TODO: optimize return new UnsignedInteger. -abstract class UnsignedInteger implements IntegralValueType { +// TODO: Optimize calculation of mod, etc. Can only do once per class? +abstract class UnsignedInteger implements IntegralValue { private readonly bitmask: bigint; private readonly mod: bigint; @@ -35,7 +34,8 @@ abstract class UnsignedInteger implements IntegralValueType { assert(n < this.mod); } - // TODO: comment + // We need this to be able to build an instance of the subclass + // and not of type UnsignedInteger. protected abstract build(n: bigint): UnsignedInteger; public add(rhs: UnsignedInteger): UnsignedInteger { @@ -104,8 +104,7 @@ export class Uint8 extends UnsignedInteger { super(BigInt(n), 8n); } - // TODO: should be private - build(n: bigint): Uint8 { + protected build(n: bigint): Uint8 { return new Uint8(n); } } @@ -115,8 +114,7 @@ export class Uint16 extends UnsignedInteger { super(BigInt(n), 16n); } - // TODO: should be private - build(n: bigint): Uint16 { + protected build(n: bigint): Uint16 { return new Uint16(n); } } @@ -126,8 +124,7 @@ export class Uint32 extends UnsignedInteger { super(BigInt(n), 32n); } - // TODO: should be private - build(n: bigint): Uint32 { + protected build(n: bigint): Uint32 { return new Uint32(n); } } @@ -137,8 +134,7 @@ export class Uint64 extends UnsignedInteger { super(BigInt(n), 64n); } - // TODO: should be private - build(n: bigint): Uint64 { + protected build(n: bigint): Uint64 { return new Uint64(n); } } @@ -148,33 +144,33 @@ export class Uint128 extends UnsignedInteger { super(BigInt(n), 128n); } - // TODO: should be private - build(n: bigint): Uint128 { + protected build(n: bigint): Uint128 { return new Uint128(n); } } -export class FieldValue implements FieldValueType { +export class Field implements MemoryValue { + public static readonly MODULUS: bigint = Fr.MODULUS; private readonly rep: Fr; constructor(v: number | bigint | Fr) { this.rep = new Fr(v); } - public add(rhs: FieldValue): FieldValue { - return new FieldValue(this.rep.add(rhs.rep)); + public add(rhs: Field): Field { + return new Field(this.rep.add(rhs.rep)); } - public sub(rhs: FieldValue): FieldValue { - return new FieldValue(this.rep.sub(rhs.rep)); + public sub(rhs: Field): Field { + return new Field(this.rep.sub(rhs.rep)); } - public mul(rhs: FieldValue): FieldValue { - return new FieldValue(this.rep.mul(rhs.rep)); + public mul(rhs: Field): Field { + return new Field(this.rep.mul(rhs.rep)); } - public div(rhs: FieldValue): FieldValue { - return new FieldValue(this.rep.div(rhs.rep)); + public div(rhs: Field): Field { + return new Field(this.rep.div(rhs.rep)); } public toBigInt(): bigint { @@ -193,43 +189,42 @@ export enum TypeTag { INVALID, } +// TODO: Consider automatic conversion when getting undefined values. export class TaggedMemory { - private _mem: FieldValueType[]; + static readonly MAX_MEMORY_SIZE = 1n << 32n; + private _mem: MemoryValue[]; constructor() { this._mem = []; } - public get(offset: number): FieldValueType { - return this.getAs(offset); + public get(offset: number): MemoryValue { + return this.getAs(offset); } public getAs(offset: number): T { - assert(offset < 2n ** 32n); - // TODO: non-existing case. + assert(offset < TaggedMemory.MAX_MEMORY_SIZE); const e = this._mem[offset]; return e; } - public getSlice(offset: number, size: number): FieldValueType[] { - assert(offset < 2n ** 32n); - // TODO: non-existing case. + public getSlice(offset: number, size: number): MemoryValue[] { + assert(offset < TaggedMemory.MAX_MEMORY_SIZE); return this._mem.slice(offset, offset + size); } public getSliceTags(offset: number, size: number): TypeTag[] { - assert(offset < 2n ** 32n); - // TODO: non-existing case. + assert(offset < TaggedMemory.MAX_MEMORY_SIZE); return this._mem.slice(offset, offset + size).map(TaggedMemory.getTag); } - public set(offset: number, v: FieldValueType) { - assert(offset < 2n ** 32n); + public set(offset: number, v: MemoryValue) { + assert(offset < TaggedMemory.MAX_MEMORY_SIZE); this._mem[offset] = v; } - public setSlice(offset: number, vs: FieldValueType[]) { - assert(offset < 2n ** 32n); + public setSlice(offset: number, vs: MemoryValue[]) { + assert(offset < TaggedMemory.MAX_MEMORY_SIZE); this._mem.splice(offset, vs.length, ...vs); } @@ -237,24 +232,14 @@ export class TaggedMemory { return TaggedMemory.getTag(this._mem[offset]); } - public tagsMatchStrict(offsetA: number, offsetB: number): boolean { - return this.getTag(offsetA) == this.getTag(offsetB); - } - - public tagsMatch(offsetA: number, offsetB: number): boolean { - return ( - this.tagsMatchStrict(offsetA, offsetB) || - this.getTag(offsetA) == TypeTag.UNINITIALIZED || - this.getTag(offsetB) == TypeTag.UNINITIALIZED - ); - } - - public static getTag(v: FieldValueType | undefined): TypeTag { + // TODO: this might be slow, but I don't want to have the types know of their tags. + // It might be possible to have a map. + public static getTag(v: MemoryValue | undefined): TypeTag { let tag = TypeTag.INVALID; if (v === undefined) { tag = TypeTag.UNINITIALIZED; - } else if (v instanceof FieldValue) { + } else if (v instanceof Field) { tag = TypeTag.FIELD; } else if (v instanceof Uint8) { tag = TypeTag.UINT8; @@ -271,8 +256,8 @@ export class TaggedMemory { return tag; } - // Truncates! - public static integralFromTag(v: bigint, tag: TypeTag): IntegralValueType { + // Truncates the value to fit the type. + public static integralFromTag(v: bigint, tag: TypeTag): IntegralValue { switch (tag) { case TypeTag.UINT8: return new Uint8(v & ((1n << 8n) - 1n)); @@ -285,8 +270,7 @@ export class TaggedMemory { case TypeTag.UINT128: return new Uint128(v & ((1n << 128n) - 1n)); default: - // TODO: better message - throw new Error('wrong tag for integral type'); + throw new Error(`${TypeTag[tag]} is not a valid integral type.`); } } } diff --git a/yarn-project/acir-simulator/src/avm/interpreter/interpreter.ts b/yarn-project/acir-simulator/src/avm/interpreter/interpreter.ts index f3970ade43f..55204f88b47 100644 --- a/yarn-project/acir-simulator/src/avm/interpreter/interpreter.ts +++ b/yarn-project/acir-simulator/src/avm/interpreter/interpreter.ts @@ -71,8 +71,8 @@ export class AvmInterpreter { * Avm-specific errors should derive from this */ export abstract class AvmInterpreterError extends Error { - constructor(message: string) { - super(message); + constructor(message: string, ...rest: any[]) { + super(message, ...rest); this.name = 'AvmInterpreterError'; } } diff --git a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts index 5bf9dd6bf50..51059a38854 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.test.ts @@ -1,9 +1,7 @@ -import { Fr } from '@aztec/foundation/fields'; - import { mock } from 'jest-mock-extended'; import { AvmMachineState } from '../avm_machine_state.js'; -import { FieldValue } from '../avm_memory_types.js'; +import { Field } from '../avm_memory_types.js'; import { AvmStateManager } from '../avm_state_manager.js'; import { Add, Div, Mul, Sub } from './arithmetic.js'; @@ -17,76 +15,76 @@ describe('Arithmetic Instructions', () => { }); describe('Add', () => { - it('Should add correctly over Fr type', () => { - const a = new FieldValue(1n); - const b = new FieldValue(2n); + it('Should add correctly over field elements', () => { + const a = new Field(1n); + const b = new Field(2n); machineState.memory.set(0, a); machineState.memory.set(1, b); new Add(0, 1, 2).execute(machineState, stateManager); - const expected = new FieldValue(3n); + const expected = new Field(3n); const actual = machineState.memory.get(2); expect(actual).toEqual(expected); }); it('Should wrap around on addition', () => { - const a = new FieldValue(1n); - const b = new FieldValue(Fr.MODULUS - 1n); + const a = new Field(1n); + const b = new Field(Field.MODULUS - 1n); machineState.memory.set(0, a); machineState.memory.set(1, b); new Add(0, 1, 2).execute(machineState, stateManager); - const expected = new FieldValue(0n); + const expected = new Field(0n); const actual = machineState.memory.get(2); expect(actual).toEqual(expected); }); }); describe('Sub', () => { - it('Should subtract correctly over Fr type', () => { - const a = new FieldValue(1n); - const b = new FieldValue(2n); + it('Should subtract correctly over field elements', () => { + const a = new Field(1n); + const b = new Field(2n); machineState.memory.set(0, a); machineState.memory.set(1, b); new Sub(0, 1, 2).execute(machineState, stateManager); - const expected = new FieldValue(Fr.MODULUS - 1n); + const expected = new Field(Field.MODULUS - 1n); const actual = machineState.memory.get(2); expect(actual).toEqual(expected); }); }); describe('Mul', () => { - it('Should multiply correctly over Fr type', () => { - const a = new FieldValue(2n); - const b = new FieldValue(3n); + it('Should multiply correctly over field elements', () => { + const a = new Field(2n); + const b = new Field(3n); machineState.memory.set(0, a); machineState.memory.set(1, b); new Mul(0, 1, 2).execute(machineState, stateManager); - const expected = new FieldValue(6n); + const expected = new Field(6n); const actual = machineState.memory.get(2); expect(actual).toEqual(expected); }); it('Should wrap around on multiplication', () => { - const a = new FieldValue(2n); - const b = new FieldValue(Fr.MODULUS / 2n - 1n); + const a = new Field(2n); + const b = new Field(Field.MODULUS / 2n - 1n); machineState.memory.set(0, a); machineState.memory.set(1, b); new Mul(0, 1, 2).execute(machineState, stateManager); - const expected = new FieldValue(Fr.MODULUS - 3n); + const expected = new Field(Field.MODULUS - 3n); const actual = machineState.memory.get(2); expect(actual).toEqual(expected); }); @@ -94,8 +92,8 @@ describe('Arithmetic Instructions', () => { describe('Div', () => { it('Should perform field division', () => { - const a = new FieldValue(2n); - const b = new FieldValue(3n); + const a = new Field(2n); + const b = new Field(3n); machineState.memory.set(0, a); machineState.memory.set(1, b); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts index c6935b3323e..6bfc61ff584 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/arithmetic.ts @@ -2,7 +2,6 @@ import { AvmMachineState } from '../avm_machine_state.js'; import { AvmStateManager } from '../avm_state_manager.js'; import { Instruction } from './instruction.js'; -/** -*/ export class Add extends Instruction { static type: string = 'ADD'; static numberOfOperands = 3; @@ -22,7 +21,6 @@ export class Add extends Instruction { } } -/** -*/ export class Sub extends Instruction { static type: string = 'SUB'; static numberOfOperands = 3; @@ -42,7 +40,6 @@ export class Sub extends Instruction { } } -/** -*/ export class Mul extends Instruction { static type: string = 'MUL'; static numberOfOperands = 3; diff --git a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts index cef510b5289..51facf39d9a 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts @@ -1,9 +1,8 @@ import { AvmMachineState } from '../avm_machine_state.js'; -import { IntegralValueType, TypeTag } from '../avm_memory_types.js'; +import { IntegralValue, TypeTag } from '../avm_memory_types.js'; import { AvmStateManager } from '../avm_state_manager.js'; import { Instruction } from './instruction.js'; -/** - */ export class And extends Instruction { static type: string = 'AND'; static numberOfOperands = 3; @@ -15,8 +14,8 @@ export class And extends Instruction { execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); - const a = machineState.memory.getAs(this.aOffset); - const b = machineState.memory.getAs(this.bOffset); + const a = machineState.memory.getAs(this.aOffset); + const b = machineState.memory.getAs(this.bOffset); const res = a.and(b); machineState.memory.set(this.destOffset, res); @@ -25,7 +24,6 @@ export class And extends Instruction { } } -/** - */ export class Or extends Instruction { static type: string = 'OR'; static numberOfOperands = 3; @@ -37,8 +35,8 @@ export class Or extends Instruction { execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); - const a = machineState.memory.getAs(this.aOffset); - const b = machineState.memory.getAs(this.bOffset); + const a = machineState.memory.getAs(this.aOffset); + const b = machineState.memory.getAs(this.bOffset); const res = a.or(b); machineState.memory.set(this.destOffset, res); @@ -47,7 +45,6 @@ export class Or extends Instruction { } } -/** - */ export class Xor extends Instruction { static type: string = 'XOR'; static numberOfOperands = 3; @@ -59,8 +56,8 @@ export class Xor extends Instruction { execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); - const a = machineState.memory.getAs(this.aOffset); - const b = machineState.memory.getAs(this.bOffset); + const a = machineState.memory.getAs(this.aOffset); + const b = machineState.memory.getAs(this.bOffset); const res = a.xor(b); machineState.memory.set(this.destOffset, res); @@ -69,7 +66,6 @@ export class Xor extends Instruction { } } -/** - */ export class Not extends Instruction { static type: string = 'NOT'; static numberOfOperands = 2; @@ -81,7 +77,7 @@ export class Not extends Instruction { execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { Instruction.checkTags(machineState, this.inTag, this.aOffset); - const a = machineState.memory.getAs(this.aOffset); + const a = machineState.memory.getAs(this.aOffset); const res = a.not(); machineState.memory.set(this.destOffset, res); @@ -102,8 +98,8 @@ export class Shl extends Instruction { execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); - const a = machineState.memory.getAs(this.aOffset); - const b = machineState.memory.getAs(this.bOffset); + const a = machineState.memory.getAs(this.aOffset); + const b = machineState.memory.getAs(this.bOffset); const res = a.shl(b); machineState.memory.set(this.destOffset, res); @@ -124,8 +120,8 @@ export class Shr extends Instruction { execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { Instruction.checkTags(machineState, this.inTag, this.aOffset, this.bOffset); - const a = machineState.memory.getAs(this.aOffset); - const b = machineState.memory.getAs(this.bOffset); + const a = machineState.memory.getAs(this.aOffset); + const b = machineState.memory.getAs(this.bOffset); const res = a.shr(b); machineState.memory.set(this.destOffset, res); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts b/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts index 498bfe2309b..b4f65a66f7b 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/comparators.ts @@ -1,9 +1,8 @@ import { AvmMachineState } from '../avm_machine_state.js'; -import { FieldValue } from '../avm_memory_types.js'; +import { Field } from '../avm_memory_types.js'; import { AvmStateManager } from '../avm_state_manager.js'; import { Instruction } from './instruction.js'; -/** -*/ export class Eq extends Instruction { static type: string = 'EQ'; static numberOfOperands = 3; @@ -16,13 +15,13 @@ export class Eq extends Instruction { const a = machineState.memory.get(this.aOffset); const b = machineState.memory.get(this.bOffset); - const dest = new FieldValue(a.toBigInt() == b.toBigInt() ? 1 : 0); + const dest = new Field(a.toBigInt() == b.toBigInt() ? 1 : 0); machineState.memory.set(this.destOffset, dest); this.incrementPc(machineState); } } -/** -*/ + export class Lt extends Instruction { static type: string = 'Lt'; static numberOfOperands = 3; @@ -35,14 +34,13 @@ export class Lt extends Instruction { const a = machineState.memory.get(this.aOffset); const b = machineState.memory.get(this.bOffset); - const dest = new FieldValue(a.toBigInt() < b.toBigInt() ? 1 : 0); + const dest = new Field(a.toBigInt() < b.toBigInt() ? 1 : 0); machineState.memory.set(this.destOffset, dest); this.incrementPc(machineState); } } -/** -*/ export class Lte extends Instruction { static type: string = 'LTE'; static numberOfOperands = 3; @@ -55,7 +53,7 @@ export class Lte extends Instruction { const a = machineState.memory.get(this.aOffset); const b = machineState.memory.get(this.bOffset); - const dest = new FieldValue(a.toBigInt() < b.toBigInt() ? 1 : 0); + const dest = new Field(a.toBigInt() < b.toBigInt() ? 1 : 0); machineState.memory.set(this.destOffset, dest); this.incrementPc(machineState); diff --git a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts index 6eff3d4b088..83c1e2c48a2 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts @@ -1,11 +1,10 @@ import { Fr } from '@aztec/foundation/fields'; import { AvmMachineState } from '../avm_machine_state.js'; -import { IntegralValueType } from '../avm_memory_types.js'; +import { IntegralValue } from '../avm_memory_types.js'; import { AvmStateManager } from '../avm_state_manager.js'; -import { Instruction } from './instruction.js'; +import { Instruction, InstructionExecutionError } from './instruction.js'; -/** - */ export class Return extends Instruction { static type: string = 'RETURN'; static numberOfOperands = 2; @@ -26,7 +25,6 @@ export class Return extends Instruction { } } -/** -*/ export class Jump extends Instruction { static type: string = 'JUMP'; static numberOfOperands = 1; @@ -40,7 +38,6 @@ export class Jump extends Instruction { } } -/** -*/ export class JumpI extends Instruction { static type: string = 'JUMPI'; static numberOfOperands = 1; @@ -50,10 +47,10 @@ export class JumpI extends Instruction { } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - const condition = machineState.memory.getAs(this.condOffset); + const condition = machineState.memory.getAs(this.condOffset); + // TODO: reconsider this casting if (condition.toBigInt() == 0n) { - // TODO: reconsider this casting this.incrementPc(machineState); } else { machineState.pc = this.jumpOffset; @@ -61,7 +58,6 @@ export class JumpI extends Instruction { } } -/** -*/ export class InternalCall extends Instruction { static type: string = 'INTERNALCALL'; static numberOfOperands = 1; @@ -76,7 +72,6 @@ export class InternalCall extends Instruction { } } -/** -*/ export class InternalReturn extends Instruction { static type: string = 'INTERNALRETURN'; static numberOfOperands = 0; @@ -88,17 +83,8 @@ export class InternalReturn extends Instruction { execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { const jumpOffset = machineState.internalCallStack.pop(); if (jumpOffset === undefined) { - throw new InternalCallStackEmptyError(); + throw new InstructionExecutionError('Internal call empty!'); } machineState.pc = jumpOffset; } } - -/** - * Thrown if the internal call stack is popped when it is empty - */ -export class InternalCallStackEmptyError extends Error { - constructor() { - super('Internal call stack is empty'); - } -} diff --git a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts index f36e2e1d4e5..766e5ee158e 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/instruction.ts @@ -22,9 +22,16 @@ export abstract class Instruction { static checkTags(machineState: AvmMachineState, tag: TypeTag, ...offsets: number[]) { for (const off of offsets) { if (machineState.memory.getTag(off) !== tag) { - const error = `Offset ${off} has tag ${machineState.memory.getTag(off)}, expected ${tag}`; - throw new Error(error); + const error = `Offset ${off} has tag ${TypeTag[machineState.memory.getTag(off)]}, expected ${TypeTag[tag]}`; + throw new InstructionExecutionError(error); } } } } + +export class InstructionExecutionError extends Error { + constructor(message: string) { + super(message); + this.name = 'InstructionExecutionError'; + } +} diff --git a/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts index 80cc5a3a4bc..1c8b49ea678 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/memory.test.ts @@ -3,7 +3,7 @@ import { Fr } from '@aztec/foundation/fields'; import { mock } from 'jest-mock-extended'; import { AvmMachineState } from '../avm_machine_state.js'; -import { FieldValue, TypeTag, Uint8, Uint16, Uint32, Uint64, Uint128 } from '../avm_memory_types.js'; +import { Field, TypeTag, Uint8, Uint16, Uint32, Uint64, Uint128 } from '../avm_memory_types.js'; import { AvmStateManager } from '../avm_state_manager.js'; import { CMov, CalldataCopy, Cast, Mov, Set } from './memory.js'; @@ -28,7 +28,7 @@ describe('Memory instructions', () => { }); it('should correctly set value and tag (overwriting)', () => { - machineState.memory.set(1, new FieldValue(27)); + machineState.memory.set(1, new Field(27)); new Set(/*value=*/ 1234n, /*offset=*/ 1, TypeTag.UINT32).execute(machineState, stateManager); @@ -112,22 +112,22 @@ describe('Memory instructions', () => { const actual = machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 5); expect(actual).toEqual([ - new FieldValue(20n), - new FieldValue(65000n), - new FieldValue(1n << 30n), - new FieldValue(1n << 50n), - new FieldValue(1n << 100n), + new Field(20n), + new Field(65000n), + new Field(1n << 30n), + new Field(1n << 50n), + new Field(1n << 100n), ]); const tags = machineState.memory.getSliceTags(/*offset=*/ 10, /*size=*/ 5); expect(tags).toEqual([TypeTag.FIELD, TypeTag.FIELD, TypeTag.FIELD, TypeTag.FIELD, TypeTag.FIELD]); }); it('Should downcast (truncating) from field to integral types', () => { - machineState.memory.set(0, new FieldValue((1n << 200n) - 1n)); - machineState.memory.set(1, new FieldValue((1n << 200n) - 1n)); - machineState.memory.set(2, new FieldValue((1n << 200n) - 1n)); - machineState.memory.set(3, new FieldValue((1n << 200n) - 1n)); - machineState.memory.set(4, new FieldValue((1n << 200n) - 1n)); + machineState.memory.set(0, new Field((1n << 200n) - 1n)); + machineState.memory.set(1, new Field((1n << 200n) - 1n)); + machineState.memory.set(2, new Field((1n << 200n) - 1n)); + machineState.memory.set(3, new Field((1n << 200n) - 1n)); + machineState.memory.set(4, new Field((1n << 200n) - 1n)); [ new Cast(/*aOffset=*/ 0, /*dstOffset=*/ 10, TypeTag.UINT8), @@ -150,12 +150,12 @@ describe('Memory instructions', () => { }); it('Should cast between field elements', () => { - machineState.memory.set(0, new FieldValue(12345678n)); + machineState.memory.set(0, new Field(12345678n)); new Cast(/*aOffset=*/ 0, /*dstOffset=*/ 1, TypeTag.FIELD).execute(machineState, stateManager); const actual = machineState.memory.get(1); - expect(actual).toEqual(new FieldValue(12345678n)); + expect(actual).toEqual(new Field(12345678n)); const tags = machineState.memory.getTag(1); expect(tags).toEqual(TypeTag.FIELD); }); @@ -174,13 +174,13 @@ describe('Memory instructions', () => { }); it('Should move field elements on different memory cells', () => { - machineState.memory.set(1, new FieldValue(27)); + machineState.memory.set(1, new Field(27)); new Mov(/*offsetA=*/ 1, /*offsetA=*/ 2).execute(machineState, stateManager); const actual = machineState.memory.get(2); const tag = machineState.memory.getTag(2); - expect(actual).toEqual(new FieldValue(27n)); + expect(actual).toEqual(new Field(27n)); expect(tag).toEqual(TypeTag.FIELD); }); }); @@ -215,7 +215,7 @@ describe('Memory instructions', () => { it('Should move A if COND is true, on different memory cells (field condition)', () => { machineState.memory.set(0, new Uint32(123)); // A machineState.memory.set(1, new Uint16(456)); // B - machineState.memory.set(2, new FieldValue(1)); // Condition + machineState.memory.set(2, new Field(1)); // Condition new CMov(/*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute(machineState, stateManager); @@ -228,7 +228,7 @@ describe('Memory instructions', () => { it('Should move B if COND is false, on different memory cells (integral condition)', () => { machineState.memory.set(0, new Uint32(123)); // A machineState.memory.set(1, new Uint16(456)); // B - machineState.memory.set(2, new FieldValue(0)); // Condition + machineState.memory.set(2, new Field(0)); // Condition new CMov(/*aOffset=*/ 0, /*bOffset=*/ 1, /*condOffset=*/ 2, /*dstOffset=*/ 3).execute(machineState, stateManager); @@ -259,7 +259,7 @@ describe('Memory instructions', () => { new CalldataCopy(/*cdOffset=*/ 0, /*copySize=*/ 3, /*dstOffset=*/ 0).execute(machineState, stateManager); const actual = machineState.memory.getSlice(/*offset=*/ 0, /*size=*/ 3); - expect(actual).toEqual([new FieldValue(1), new FieldValue(2), new FieldValue(3)]); + expect(actual).toEqual([new Field(1), new Field(2), new Field(3)]); }); it('Copies slice of calldata', () => { @@ -270,7 +270,7 @@ describe('Memory instructions', () => { new CalldataCopy(/*cdOffset=*/ 1, /*copySize=*/ 2, /*dstOffset=*/ 0).execute(machineState, stateManager); const actual = machineState.memory.getSlice(/*offset=*/ 0, /*size=*/ 2); - expect(actual).toEqual([new FieldValue(2), new FieldValue(3)]); + expect(actual).toEqual([new Field(2), new Field(3)]); }); // TODO: check bad cases (i.e., out of bounds) diff --git a/yarn-project/acir-simulator/src/avm/opcodes/memory.ts b/yarn-project/acir-simulator/src/avm/opcodes/memory.ts index e7155c9394f..7b55ace6fd3 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/memory.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/memory.ts @@ -1,9 +1,8 @@ import { AvmMachineState } from '../avm_machine_state.js'; -import { FieldValue, TaggedMemory, TypeTag } from '../avm_memory_types.js'; +import { Field, TaggedMemory, TypeTag } from '../avm_memory_types.js'; import { AvmStateManager } from '../avm_state_manager.js'; import { Instruction } from './instruction.js'; -/** - */ export class Set extends Instruction { static type: string = 'SET'; static numberOfOperands = 2; @@ -34,9 +33,7 @@ export class Cast extends Instruction { // TODO: consider not using toBigInt() const casted = - this.dstTag == TypeTag.FIELD - ? new FieldValue(a.toBigInt()) - : TaggedMemory.integralFromTag(a.toBigInt(), this.dstTag); + this.dstTag == TypeTag.FIELD ? new Field(a.toBigInt()) : TaggedMemory.integralFromTag(a.toBigInt(), this.dstTag); // TODO cast machineState.memory.set(this.dstOffset, casted); @@ -45,7 +42,6 @@ export class Cast extends Instruction { } } -/** - */ export class Mov extends Instruction { static type: string = 'MOV'; static numberOfOperands = 2; @@ -63,7 +59,6 @@ export class Mov extends Instruction { } } -/** - */ export class CMov extends Instruction { static type: string = 'CMOV'; static numberOfOperands = 4; @@ -84,7 +79,6 @@ export class CMov extends Instruction { } } -/** - */ export class CalldataCopy extends Instruction { static type: string = 'CALLDATACOPY'; static numberOfOperands = 3; @@ -96,7 +90,7 @@ export class CalldataCopy extends Instruction { execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { const transformedData = machineState.calldata .slice(this.cdOffset, this.cdOffset + this.copySize) - .map(f => new FieldValue(f)); + .map(f => new Field(f)); machineState.memory.setSlice(this.dstOffset, transformedData); this.incrementPc(machineState); From 4d5ba0aff40e4b09c904e5ab8e2b75f3f6a47452 Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Thu, 25 Jan 2024 17:14:42 +0000 Subject: [PATCH 6/8] format --- yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts | 2 -- yarn-project/acir-simulator/src/avm/opcodes/memory.ts | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts index 51facf39d9a..e788abcc1f5 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/bitwise.ts @@ -86,7 +86,6 @@ export class Not extends Instruction { } } -/** -*/ export class Shl extends Instruction { static type: string = 'SHL'; static numberOfOperands = 3; @@ -108,7 +107,6 @@ export class Shl extends Instruction { } } -/** -*/ export class Shr extends Instruction { static type: string = 'SHR'; static numberOfOperands = 3; diff --git a/yarn-project/acir-simulator/src/avm/opcodes/memory.ts b/yarn-project/acir-simulator/src/avm/opcodes/memory.ts index 7b55ace6fd3..cd546a448c6 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/memory.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/memory.ts @@ -35,7 +35,6 @@ export class Cast extends Instruction { const casted = this.dstTag == TypeTag.FIELD ? new Field(a.toBigInt()) : TaggedMemory.integralFromTag(a.toBigInt(), this.dstTag); - // TODO cast machineState.memory.set(this.dstOffset, casted); this.incrementPc(machineState); @@ -72,7 +71,7 @@ export class CMov extends Instruction { const b = machineState.memory.get(this.bOffset); const cond = machineState.memory.get(this.condOffset); - // TODO: reconsider toBigInt() here. + // TODO: reconsider toBigInt() here machineState.memory.set(this.dstOffset, cond.toBigInt() > 0 ? a : b); this.incrementPc(machineState); From 051eec1b63bc5acb39ea46feca06d28d69686246 Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Thu, 25 Jan 2024 17:29:37 +0000 Subject: [PATCH 7/8] remove comment --- yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts index 83c1e2c48a2..7967a417c8a 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.ts @@ -14,7 +14,6 @@ export class Return extends Instruction { } execute(machineState: AvmMachineState, _stateManager: AvmStateManager): void { - // TODO: reconsider this casting const returnData = machineState.memory .getSlice(this.returnOffset, this.copySize) .map(fvt => new Fr(fvt.toBigInt())); From bbc10d42ab437559c131ac74df1b2c3e1bf0e024 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Thu, 25 Jan 2024 20:27:45 +0000 Subject: [PATCH 8/8] fix: compilation error --- .../acir-simulator/src/avm/opcodes/control_flow.test.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts index 0363cf1323d..c30825ba912 100644 --- a/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts +++ b/yarn-project/acir-simulator/src/avm/opcodes/control_flow.test.ts @@ -6,7 +6,8 @@ import { AvmStateManager } from '../avm_state_manager.js'; import { Add, Mul, Sub } from './arithmetic.js'; import { And, Not, Or, Shl, Shr, Xor } from './bitwise.js'; import { Eq, Lt, Lte } from './comparators.js'; -import { InternalCall, InternalCallStackEmptyError, InternalReturn, Jump, JumpI } from './control_flow.js'; +import { InternalCall, InternalReturn, Jump, JumpI } from './control_flow.js'; +import { InstructionExecutionError } from './instruction.js'; import { CMov, CalldataCopy, Cast, Mov, Set } from './memory.js'; describe('Control Flow Opcodes', () => { @@ -111,7 +112,7 @@ describe('Control Flow Opcodes', () => { it('Should error if Internal Return is called without a corresponding Internal Call', () => { const returnInstruction = new InternalReturn(); - expect(() => returnInstruction.execute(machineState, stateManager)).toThrow(InternalCallStackEmptyError); + expect(() => returnInstruction.execute(machineState, stateManager)).toThrow(InstructionExecutionError); }); it('Should increment PC on All other Instructions', () => {