diff --git a/yarn-project/simulator/src/avm/avm_memory_types.test.ts b/yarn-project/simulator/src/avm/avm_memory_types.test.ts index 62f0aff7451..d491616d853 100644 --- a/yarn-project/simulator/src/avm/avm_memory_types.test.ts +++ b/yarn-project/simulator/src/avm/avm_memory_types.test.ts @@ -177,6 +177,13 @@ describe('Field', () => { expect(field1.equals(field3)).toBe(false); }); + it(`Should check if one Field is less than another correctly`, () => { + const field1 = new Field(5); + const field2 = new Field(10); + expect(field1.lt(field2)).toBe(true); + expect(field2.lt(field1)).toBe(false); + }); + it(`Should convert Field to BigInt correctly`, () => { const field = new Field(5); expect(field.toBigInt()).toStrictEqual(5n); diff --git a/yarn-project/simulator/src/avm/avm_memory_types.ts b/yarn-project/simulator/src/avm/avm_memory_types.ts index b8b709b2d32..9b26c3f8b4d 100644 --- a/yarn-project/simulator/src/avm/avm_memory_types.ts +++ b/yarn-project/simulator/src/avm/avm_memory_types.ts @@ -13,6 +13,7 @@ export abstract class MemoryValue { public abstract div(rhs: MemoryValue): MemoryValue; public abstract equals(rhs: MemoryValue): boolean; + public abstract lt(rhs: MemoryValue): boolean; // We need this to be able to build an instance of the subclasses. public abstract build(n: bigint): MemoryValue; @@ -37,8 +38,6 @@ export abstract class IntegralValue extends MemoryValue { public abstract or(rhs: IntegralValue): IntegralValue; public abstract xor(rhs: IntegralValue): IntegralValue; public abstract not(): IntegralValue; - - public abstract lt(rhs: IntegralValue): boolean; } /** @@ -164,6 +163,10 @@ export class Field extends MemoryValue { return this.rep.equals(rhs.rep); } + public lt(rhs: Field): boolean { + return this.rep.lt(rhs.rep); + } + public toBigInt(): bigint { return this.rep.toBigInt(); } diff --git a/yarn-project/simulator/src/avm/opcodes/comparators.test.ts b/yarn-project/simulator/src/avm/opcodes/comparators.test.ts index 591c657239e..c8001c7759f 100644 --- a/yarn-project/simulator/src/avm/opcodes/comparators.test.ts +++ b/yarn-project/simulator/src/avm/opcodes/comparators.test.ts @@ -110,11 +110,19 @@ describe('Comparators', () => { expect(actual).toEqual([new Uint32(0), new Uint32(1), new Uint32(0)]); }); - it('Does not work on field elements', async () => { - await expect(() => - new Lt(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10).execute(context), - ).rejects.toThrow(TagCheckError); + it('Works on field elements', async () => { + context.machineState.memory.setSlice(0, [new Field(1), new Field(2), new Field(0)]); + + [ + new Lt(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 0, /*dstOffset=*/ 10), + new Lt(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 11), + new Lt(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 12), + ].forEach(i => i.execute(context)); + + const actual = context.machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 4); + expect(actual).toEqual([new Field(0), new Field(1), new Field(0)]); }); + it('InTag is checked', async () => { context.machineState.memory.setSlice(0, [new Field(1), new Uint32(2), new Uint16(3)]); @@ -166,10 +174,17 @@ describe('Comparators', () => { expect(actual).toEqual([new Uint32(1), new Uint32(1), new Uint32(0)]); }); - it('Does not work on field elements', async () => { - await expect(() => - new Lte(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10).execute(context), - ).rejects.toThrow(TagCheckError); + it('Works on field elements', async () => { + context.machineState.memory.setSlice(0, [new Field(1), new Field(2), new Field(0)]); + + [ + new Lte(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 0, /*dstOffset=*/ 10), + new Lte(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 11), + new Lte(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 12), + ].forEach(i => i.execute(context)); + + const actual = context.machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 4); + expect(actual).toEqual([new Field(1), new Field(1), new Field(0)]); }); it('InTag is checked', async () => { diff --git a/yarn-project/simulator/src/avm/opcodes/comparators.ts b/yarn-project/simulator/src/avm/opcodes/comparators.ts index cff0b9baa38..3f896248738 100644 --- a/yarn-project/simulator/src/avm/opcodes/comparators.ts +++ b/yarn-project/simulator/src/avm/opcodes/comparators.ts @@ -1,5 +1,4 @@ import type { AvmContext } from '../avm_context.js'; -import { IntegralValue, TaggedMemory } from '../avm_memory_types.js'; import { Opcode } from '../serialization/instruction_serialization.js'; import { ThreeOperandInstruction } from './instruction_impl.js'; @@ -35,10 +34,9 @@ export class Lt extends ThreeOperandInstruction { async execute(context: AvmContext): Promise { context.machineState.memory.checkTags(this.inTag, this.aOffset, this.bOffset); - TaggedMemory.checkIsIntegralTag(this.inTag); - const a = context.machineState.memory.getAs(this.aOffset); - const b = context.machineState.memory.getAs(this.bOffset); + const a = context.machineState.memory.get(this.aOffset); + const b = context.machineState.memory.get(this.bOffset); // Result will be of the same type as 'a'. const dest = a.build(a.lt(b) ? 1n : 0n); @@ -58,10 +56,9 @@ export class Lte extends ThreeOperandInstruction { async execute(context: AvmContext): Promise { context.machineState.memory.checkTags(this.inTag, this.aOffset, this.bOffset); - TaggedMemory.checkIsIntegralTag(this.inTag); - const a = context.machineState.memory.getAs(this.aOffset); - const b = context.machineState.memory.getAs(this.bOffset); + const a = context.machineState.memory.get(this.aOffset); + const b = context.machineState.memory.get(this.bOffset); // Result will be of the same type as 'a'. const dest = a.build(a.equals(b) || a.lt(b) ? 1n : 0n); diff --git a/yellow-paper/docs/public-vm/gen/_instruction-set.mdx b/yellow-paper/docs/public-vm/gen/_instruction-set.mdx index 0e6a9bfc235..33ec121a11c 100644 --- a/yellow-paper/docs/public-vm/gen/_instruction-set.mdx +++ b/yellow-paper/docs/public-vm/gen/_instruction-set.mdx @@ -591,7 +591,7 @@ Less-than check (a < b) - **Category**: Compute - Comparators - **Flags**: - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. - - **inTag**: The [tag/size](./memory-model#tags-and-tagged-memory) to check inputs against and tag the destination with. `field` type is NOT supported for this instruction. + - **inTag**: The [tag/size](./memory-model#tags-and-tagged-memory) to check inputs against and tag the destination with. - **Args**: - **aOffset**: memory offset of the operation's left input - **bOffset**: memory offset of the operation's right input @@ -612,7 +612,7 @@ Less-than-or-equals check (a <= b) - **Category**: Compute - Comparators - **Flags**: - **indirect**: Toggles whether each memory-offset argument is an indirect offset. Rightmost bit corresponds to 0th offset arg, etc. Indirect offsets result in memory accesses like `M[M[offset]]` instead of the more standard `M[offset]`. - - **inTag**: The [tag/size](./memory-model#tags-and-tagged-memory) to check inputs against and tag the destination with. `field` type is NOT supported for this instruction. + - **inTag**: The [tag/size](./memory-model#tags-and-tagged-memory) to check inputs against and tag the destination with. - **Args**: - **aOffset**: memory offset of the operation's left input - **bOffset**: memory offset of the operation's right input diff --git a/yellow-paper/src/preprocess/InstructionSet/InstructionSet.js b/yellow-paper/src/preprocess/InstructionSet/InstructionSet.js index b4163df2e8f..e87c2d0f70b 100644 --- a/yellow-paper/src/preprocess/InstructionSet/InstructionSet.js +++ b/yellow-paper/src/preprocess/InstructionSet/InstructionSet.js @@ -130,7 +130,7 @@ const INSTRUCTION_SET_RAW = [ "Category": "Compute - Comparators", "Flags": [ {"name": "indirect", "description": INDIRECT_FLAG_DESCRIPTION}, - {"name": "inTag", "description": IN_TAG_DESCRIPTION_NO_FIELD}, + {"name": "inTag", "description": IN_TAG_DESCRIPTION}, ], "Args": [ {"name": "aOffset", "description": "memory offset of the operation's left input"}, @@ -149,7 +149,7 @@ const INSTRUCTION_SET_RAW = [ "Category": "Compute - Comparators", "Flags": [ {"name": "indirect", "description": INDIRECT_FLAG_DESCRIPTION}, - {"name": "inTag", "description": IN_TAG_DESCRIPTION_NO_FIELD}, + {"name": "inTag", "description": IN_TAG_DESCRIPTION}, ], "Args": [ {"name": "aOffset", "description": "memory offset of the operation's left input"},