From a493384c0d79d43497bac4b9e1d7ce7999ddd3ff Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Thu, 8 Feb 2024 16:35:44 +0000 Subject: [PATCH 1/2] chore(avm): remove field support for comparators and bitwise ops --- .../src/avm/avm_memory_types.test.ts | 15 -- .../simulator/src/avm/avm_memory_types.ts | 22 +-- yarn-project/simulator/src/avm/errors.ts | 12 +- .../src/avm/opcodes/comparators.test.ts | 175 +++--------------- .../simulator/src/avm/opcodes/comparators.ts | 16 +- .../docs/public-vm/gen/_InstructionSet.mdx | 18 +- .../InstructionSet/InstructionSet.js | 19 +- 7 files changed, 72 insertions(+), 205 deletions(-) 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 9aeccaa73b90..872fb7deffb0 100644 --- a/yarn-project/simulator/src/avm/avm_memory_types.test.ts +++ b/yarn-project/simulator/src/avm/avm_memory_types.test.ts @@ -167,21 +167,6 @@ describe('Field', () => { expect(result).toStrictEqual(new Field(2n)); }); - it(`Should check equality of two Fields correctly`, () => { - const field1 = new Field(5); - const field2 = new Field(5); - const field3 = new Field(10); - expect(field1.equals(field2)).toBe(true); - 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 12df18223c17..c0ab3571a56b 100644 --- a/yarn-project/simulator/src/avm/avm_memory_types.ts +++ b/yarn-project/simulator/src/avm/avm_memory_types.ts @@ -10,9 +10,6 @@ export abstract class MemoryValue { public abstract mul(rhs: MemoryValue): 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; @@ -32,6 +29,9 @@ export abstract class IntegralValue extends MemoryValue { public abstract or(rhs: IntegralValue): IntegralValue; public abstract xor(rhs: IntegralValue): IntegralValue; public abstract not(): IntegralValue; + + public abstract equals(rhs: MemoryValue): boolean; + public abstract lt(rhs: MemoryValue): boolean; } // TODO: Optimize calculation of mod, etc. Can only do once per class? @@ -196,14 +196,6 @@ export class Field extends MemoryValue { return new Field(this.rep.div(rhs.rep)); } - public equals(rhs: Field): boolean { - return this.rep.equals(rhs.rep); - } - - public lt(rhs: Field): boolean { - return this.rep.lt(rhs.rep); - } - public toBigInt(): bigint { return this.rep.toBigInt(); } @@ -284,7 +276,13 @@ export class TaggedMemory { */ public checkTag(tag: TypeTag, offset: number) { if (this.getTag(offset) !== tag) { - throw new TagCheckError(offset, TypeTag[this.getTag(offset)], TypeTag[tag]); + throw TagCheckError.forOffset(offset, TypeTag[this.getTag(offset)], TypeTag[tag]); + } + } + + public static checkIsIntegralTag(tag: TypeTag) { + if (![TypeTag.UINT8, TypeTag.UINT16, TypeTag.UINT32, TypeTag.UINT64, TypeTag.UINT128].includes(tag)) { + throw TagCheckError.forTag(TypeTag[tag], 'integral'); } } diff --git a/yarn-project/simulator/src/avm/errors.ts b/yarn-project/simulator/src/avm/errors.ts index 735abb33df5b..24d52eea026d 100644 --- a/yarn-project/simulator/src/avm/errors.ts +++ b/yarn-project/simulator/src/avm/errors.ts @@ -42,8 +42,16 @@ export class InstructionExecutionError extends AvmExecutionError { * Error thrown on failed AVM memory tag check. */ export class TagCheckError extends AvmExecutionError { - constructor(offset: number, gotTag: string, expectedTag: string) { - super(`Memory offset ${offset} has tag ${gotTag}, expected ${expectedTag}`); + public static forOffset(offset: number, gotTag: string, expectedTag: string): TagCheckError { + return new TagCheckError(`Tag mismatch at offset ${offset}, got ${gotTag}, expected ${expectedTag}`); + } + + public static forTag(gotTag: string, expectedTag: string): TagCheckError { + return new TagCheckError(`Tag mismatch, got ${gotTag}, expected ${expectedTag}`); + } + + constructor(message: string) { + super(message); this.name = 'TagCheckError'; } } diff --git a/yarn-project/simulator/src/avm/opcodes/comparators.test.ts b/yarn-project/simulator/src/avm/opcodes/comparators.test.ts index c8001c7759fc..2986c5498779 100644 --- a/yarn-project/simulator/src/avm/opcodes/comparators.test.ts +++ b/yarn-project/simulator/src/avm/opcodes/comparators.test.ts @@ -4,24 +4,29 @@ import { TagCheckError } from '../errors.js'; import { initContext } from '../fixtures/index.js'; import { Eq, Lt, Lte } from './comparators.js'; -describe('Comparators', () => { +type ComparatorClass = typeof Eq | typeof Lt | typeof Lte; +describe.each([ + [Eq, (a: number, b: number) => (a == b ? 1 : 0)], + [Lt, (a: number, b: number) => (a < b ? 1 : 0)], + [Lte, (a: number, b: number) => (a <= b ? 1 : 0)], +])('Comparators', (clsValue: ComparatorClass, cmpf: (a: number, b: number) => number) => { let context: AvmContext; beforeEach(() => { context = initContext(); }); - describe('Eq', () => { + describe(clsValue.name, () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ - Eq.opcode, // opcode + clsValue.opcode, // opcode 0x01, // indirect TypeTag.UINT64, // inTag ...Buffer.from('12345678', 'hex'), // aOffset ...Buffer.from('23456789', 'hex'), // bOffset ...Buffer.from('3456789a', 'hex'), // dstOffset ]); - const inst = new Eq( + const inst = new clsValue( /*indirect=*/ 0x01, /*inTag=*/ TypeTag.UINT64, /*aOffset=*/ 0x12345678, @@ -29,7 +34,7 @@ describe('Comparators', () => { /*dstOffset=*/ 0x3456789a, ); - expect(Eq.deserialize(buf)).toEqual(inst); + expect(clsValue.deserialize(buf)).toEqual(inst); expect(inst.serialize()).toEqual(buf); }); @@ -37,164 +42,30 @@ describe('Comparators', () => { context.machineState.memory.setSlice(0, [new Uint32(1), new Uint32(2), new Uint32(3), new Uint32(1)]); [ - new Eq(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), - new Eq(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 11), - new Eq(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 3, /*dstOffset=*/ 12), + new clsValue(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), + new clsValue(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 11), + new clsValue(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 3, /*dstOffset=*/ 12), ].forEach(i => i.execute(context)); const actual = context.machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 4); - expect(actual).toEqual([new Uint32(0), new Uint32(0), new Uint32(1)]); + expect(actual).toEqual([new Uint32(cmpf(1, 2)), new Uint32(cmpf(1, 2)), new Uint32(cmpf(1, 1))]); }); - it('Works on field elements', async () => { - context.machineState.memory.setSlice(0, [new Field(1), new Field(2), new Field(3), new Field(1)]); - - [ - new Eq(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), - new Eq(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 11), - new Eq(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 3, /*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(0), new Field(1)]); - }); - - it('InTag is checked', async () => { - context.machineState.memory.setSlice(0, [new Field(1), new Uint32(2), new Uint16(3)]); - - const ops = [ - new Eq(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), - new Eq(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 10), - new Eq(/*indirect=*/ 0, TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 2, /*dstOffset=*/ 10), - new Eq(/*indirect=*/ 0, TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 1, /*dstOffset=*/ 10), - ]; - - for (const o of ops) { - await expect(() => o.execute(context)).rejects.toThrow(TagCheckError); - } - }); - }); - - describe('Lt', () => { - it('Should deserialize correctly', () => { - const buf = Buffer.from([ - Lt.opcode, // opcode - 0x01, // indirect - TypeTag.UINT64, // inTag - ...Buffer.from('12345678', 'hex'), // aOffset - ...Buffer.from('23456789', 'hex'), // bOffset - ...Buffer.from('3456789a', 'hex'), // dstOffset - ]); - const inst = new Lt( - /*indirect=*/ 0x01, - /*inTag=*/ TypeTag.UINT64, - /*aOffset=*/ 0x12345678, - /*bOffset=*/ 0x23456789, - /*dstOffset=*/ 0x3456789a, - ); - - expect(Lt.deserialize(buf)).toEqual(inst); - expect(inst.serialize()).toEqual(buf); - }); - - it('Works on integral types', async () => { - context.machineState.memory.setSlice(0, [new Uint32(1), new Uint32(2), new Uint32(0)]); - - [ - new Lt(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 0, /*dstOffset=*/ 10), - new Lt(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 11), - new Lt(/*indirect=*/ 0, TypeTag.UINT32, /*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 Uint32(0), new Uint32(1), new Uint32(0)]); - }); - - 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)]); - - const ops = [ - new Lt(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), - new Lt(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 10), - new Lt(/*indirect=*/ 0, TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 2, /*dstOffset=*/ 10), - new Lt(/*indirect=*/ 0, TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 1, /*dstOffset=*/ 10), - ]; - - for (const o of ops) { - await expect(() => o.execute(context)).rejects.toThrow(TagCheckError); - } - }); - }); - - describe('Lte', () => { - it('Should deserialize correctly', () => { - const buf = Buffer.from([ - Lte.opcode, // opcode - 0x01, // indirect - TypeTag.UINT64, // inTag - ...Buffer.from('12345678', 'hex'), // aOffset - ...Buffer.from('23456789', 'hex'), // bOffset - ...Buffer.from('3456789a', 'hex'), // dstOffset - ]); - const inst = new Lte( - /*indirect=*/ 0x01, - /*inTag=*/ TypeTag.UINT64, - /*aOffset=*/ 0x12345678, - /*bOffset=*/ 0x23456789, - /*dstOffset=*/ 0x3456789a, - ); - - expect(Lte.deserialize(buf)).toEqual(inst); - expect(inst.serialize()).toEqual(buf); - }); - - it('Works on integral types', async () => { - context.machineState.memory.setSlice(0, [new Uint32(1), new Uint32(2), new Uint32(0)]); - - [ - new Lte(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 0, /*dstOffset=*/ 10), - new Lte(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 11), - new Lte(/*indirect=*/ 0, TypeTag.UINT32, /*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 Uint32(1), new Uint32(1), new Uint32(0)]); - }); - - 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('Does not work on field elements', async () => { + await expect(() => + new clsValue(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10).execute( + context, + ), + ).rejects.toThrow(TagCheckError); }); it('InTag is checked', async () => { context.machineState.memory.setSlice(0, [new Field(1), new Uint32(2), new Uint16(3)]); const ops = [ - new Lte(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), - new Lte(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 10), - new Lte(/*indirect=*/ 0, TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 2, /*dstOffset=*/ 10), - new Lte(/*indirect=*/ 0, TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 1, /*dstOffset=*/ 10), + new clsValue(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 10), + new clsValue(/*indirect=*/ 0, TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 2, /*dstOffset=*/ 10), + new clsValue(/*indirect=*/ 0, TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 1, /*dstOffset=*/ 10), ]; for (const o of ops) { diff --git a/yarn-project/simulator/src/avm/opcodes/comparators.ts b/yarn-project/simulator/src/avm/opcodes/comparators.ts index 3f8962487386..f53a187572d9 100644 --- a/yarn-project/simulator/src/avm/opcodes/comparators.ts +++ b/yarn-project/simulator/src/avm/opcodes/comparators.ts @@ -1,4 +1,5 @@ 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'; @@ -12,9 +13,10 @@ export class Eq 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.get(this.aOffset); - const b = context.machineState.memory.get(this.bOffset); + const a = context.machineState.memory.getAs(this.aOffset); + const b = context.machineState.memory.getAs(this.bOffset); // Result will be of the same type as 'a'. const dest = a.build(a.equals(b) ? 1n : 0n); @@ -34,9 +36,10 @@ 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.get(this.aOffset); - const b = context.machineState.memory.get(this.bOffset); + const a = context.machineState.memory.getAs(this.aOffset); + const b = context.machineState.memory.getAs(this.bOffset); // Result will be of the same type as 'a'. const dest = a.build(a.lt(b) ? 1n : 0n); @@ -56,9 +59,10 @@ 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.get(this.aOffset); - const b = context.machineState.memory.get(this.bOffset); + const a = context.machineState.memory.getAs(this.aOffset); + const b = context.machineState.memory.getAs(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/_InstructionSet.mdx b/yellow-paper/docs/public-vm/gen/_InstructionSet.mdx index 0e7456c8a16a..9626996edf65 100644 --- a/yellow-paper/docs/public-vm/gen/_InstructionSet.mdx +++ b/yellow-paper/docs/public-vm/gen/_InstructionSet.mdx @@ -555,7 +555,7 @@ Equality 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](./state-model#tags-and-tagged-memory) to check inputs against and tag the destination with. + - **inTag**: The [tag/size](./state-model#tags-and-tagged-memory) to check inputs against and tag the destination with. `field` type is NOT supported for this instruction. - **Args**: - **aOffset**: memory offset of the operation's left input - **bOffset**: memory offset of the operation's right input @@ -576,7 +576,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](./state-model#tags-and-tagged-memory) to check inputs against and tag the destination with. + - **inTag**: The [tag/size](./state-model#tags-and-tagged-memory) to check inputs against and tag the destination with. `field` type is NOT supported for this instruction. - **Args**: - **aOffset**: memory offset of the operation's left input - **bOffset**: memory offset of the operation's right input @@ -597,7 +597,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](./state-model#tags-and-tagged-memory) to check inputs against and tag the destination with. + - **inTag**: The [tag/size](./state-model#tags-and-tagged-memory) to check inputs against and tag the destination with. `field` type is NOT supported for this instruction. - **Args**: - **aOffset**: memory offset of the operation's left input - **bOffset**: memory offset of the operation's right input @@ -618,7 +618,7 @@ Bitwise AND (a & b) - **Category**: Compute - Bitwise - **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](./state-model#tags-and-tagged-memory) to check inputs against and tag the destination with. + - **inTag**: The [tag/size](./state-model#tags-and-tagged-memory) to check inputs against and tag the destination with. `field` type is NOT supported for this instruction. - **Args**: - **aOffset**: memory offset of the operation's left input - **bOffset**: memory offset of the operation's right input @@ -639,7 +639,7 @@ Bitwise OR (a | b) - **Category**: Compute - Bitwise - **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](./state-model#tags-and-tagged-memory) to check inputs against and tag the destination with. + - **inTag**: The [tag/size](./state-model#tags-and-tagged-memory) to check inputs against and tag the destination with. `field` type is NOT supported for this instruction. - **Args**: - **aOffset**: memory offset of the operation's left input - **bOffset**: memory offset of the operation's right input @@ -660,7 +660,7 @@ Bitwise XOR (a ^ b) - **Category**: Compute - Bitwise - **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](./state-model#tags-and-tagged-memory) to check inputs against and tag the destination with. + - **inTag**: The [tag/size](./state-model#tags-and-tagged-memory) to check inputs against and tag the destination with. `field` type is NOT supported for this instruction. - **Args**: - **aOffset**: memory offset of the operation's left input - **bOffset**: memory offset of the operation's right input @@ -681,7 +681,7 @@ Bitwise NOT (inversion) - **Category**: Compute - Bitwise - **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](./state-model#tags-and-tagged-memory) to check inputs against and tag the destination with. + - **inTag**: The [tag/size](./state-model#tags-and-tagged-memory) to check inputs against and tag the destination with. `field` type is NOT supported for this instruction. - **Args**: - **aOffset**: memory offset of the operation's input - **dstOffset**: memory offset specifying where to store operation's result @@ -701,7 +701,7 @@ Bitwise leftward shift (a << b) - **Category**: Compute - Bitwise - **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](./state-model#tags-and-tagged-memory) to check inputs against and tag the destination with. + - **inTag**: The [tag/size](./state-model#tags-and-tagged-memory) to check inputs against and tag the destination with. `field` type is NOT supported for this instruction. - **Args**: - **aOffset**: memory offset of the operation's left input - **bOffset**: memory offset of the operation's right input @@ -722,7 +722,7 @@ Bitwise rightward shift (a >> b) - **Category**: Compute - Bitwise - **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](./state-model#tags-and-tagged-memory) to check inputs against and tag the destination with. + - **inTag**: The [tag/size](./state-model#tags-and-tagged-memory) to check inputs against and tag the destination with. `field` type is NOT supported for this instruction. - **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 01014150f3e8..63758ff67261 100644 --- a/yellow-paper/src/preprocess/InstructionSet/InstructionSet.js +++ b/yellow-paper/src/preprocess/InstructionSet/InstructionSet.js @@ -8,6 +8,7 @@ const TOPICS_IN_SECTIONS = [ ]; const IN_TAG_DESCRIPTION = "The [tag/size](./state-model#tags-and-tagged-memory) to check inputs against and tag the destination with."; +const IN_TAG_DESCRIPTION_NO_FIELD = IN_TAG_DESCRIPTION + " `field` type is NOT supported for this instruction."; const DST_TAG_DESCRIPTION = "The [tag/size](./state-model#tags-and-tagged-memory) to tag the destination with but not to check inputs against."; const INDIRECT_FLAG_DESCRIPTION = "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]`."; @@ -94,7 +95,7 @@ const INSTRUCTION_SET_RAW = [ "Category": "Compute - Comparators", "Flags": [ {"name": "indirect", "description": INDIRECT_FLAG_DESCRIPTION}, - {"name": "inTag", "description": IN_TAG_DESCRIPTION}, + {"name": "inTag", "description": IN_TAG_DESCRIPTION_NO_FIELD}, ], "Args": [ {"name": "aOffset", "description": "memory offset of the operation's left input"}, @@ -113,7 +114,7 @@ const INSTRUCTION_SET_RAW = [ "Category": "Compute - Comparators", "Flags": [ {"name": "indirect", "description": INDIRECT_FLAG_DESCRIPTION}, - {"name": "inTag", "description": IN_TAG_DESCRIPTION}, + {"name": "inTag", "description": IN_TAG_DESCRIPTION_NO_FIELD}, ], "Args": [ {"name": "aOffset", "description": "memory offset of the operation's left input"}, @@ -132,7 +133,7 @@ const INSTRUCTION_SET_RAW = [ "Category": "Compute - Comparators", "Flags": [ {"name": "indirect", "description": INDIRECT_FLAG_DESCRIPTION}, - {"name": "inTag", "description": IN_TAG_DESCRIPTION}, + {"name": "inTag", "description": IN_TAG_DESCRIPTION_NO_FIELD}, ], "Args": [ {"name": "aOffset", "description": "memory offset of the operation's left input"}, @@ -151,7 +152,7 @@ const INSTRUCTION_SET_RAW = [ "Category": "Compute - Bitwise", "Flags": [ {"name": "indirect", "description": INDIRECT_FLAG_DESCRIPTION}, - {"name": "inTag", "description": IN_TAG_DESCRIPTION}, + {"name": "inTag", "description": IN_TAG_DESCRIPTION_NO_FIELD}, ], "Args": [ {"name": "aOffset", "description": "memory offset of the operation's left input"}, @@ -170,7 +171,7 @@ const INSTRUCTION_SET_RAW = [ "Category": "Compute - Bitwise", "Flags": [ {"name": "indirect", "description": INDIRECT_FLAG_DESCRIPTION}, - {"name": "inTag", "description": IN_TAG_DESCRIPTION}, + {"name": "inTag", "description": IN_TAG_DESCRIPTION_NO_FIELD}, ], "Args": [ {"name": "aOffset", "description": "memory offset of the operation's left input"}, @@ -189,7 +190,7 @@ const INSTRUCTION_SET_RAW = [ "Category": "Compute - Bitwise", "Flags": [ {"name": "indirect", "description": INDIRECT_FLAG_DESCRIPTION}, - {"name": "inTag", "description": IN_TAG_DESCRIPTION}, + {"name": "inTag", "description": IN_TAG_DESCRIPTION_NO_FIELD}, ], "Args": [ {"name": "aOffset", "description": "memory offset of the operation's left input"}, @@ -208,7 +209,7 @@ const INSTRUCTION_SET_RAW = [ "Category": "Compute - Bitwise", "Flags": [ {"name": "indirect", "description": INDIRECT_FLAG_DESCRIPTION}, - {"name": "inTag", "description": IN_TAG_DESCRIPTION}, + {"name": "inTag", "description": IN_TAG_DESCRIPTION_NO_FIELD}, ], "Args": [ {"name": "aOffset", "description": "memory offset of the operation's input"}, @@ -226,7 +227,7 @@ const INSTRUCTION_SET_RAW = [ "Category": "Compute - Bitwise", "Flags": [ {"name": "indirect", "description": INDIRECT_FLAG_DESCRIPTION}, - {"name": "inTag", "description": IN_TAG_DESCRIPTION}, + {"name": "inTag", "description": IN_TAG_DESCRIPTION_NO_FIELD}, ], "Args": [ {"name": "aOffset", "description": "memory offset of the operation's left input"}, @@ -245,7 +246,7 @@ const INSTRUCTION_SET_RAW = [ "Category": "Compute - Bitwise", "Flags": [ {"name": "indirect", "description": INDIRECT_FLAG_DESCRIPTION}, - {"name": "inTag", "description": IN_TAG_DESCRIPTION}, + {"name": "inTag", "description": IN_TAG_DESCRIPTION_NO_FIELD}, ], "Args": [ {"name": "aOffset", "description": "memory offset of the operation's left input"}, From 8e952b57e9278b57f436719d8f2949e2638bb12f Mon Sep 17 00:00:00 2001 From: fcarreiro Date: Thu, 8 Feb 2024 19:31:22 +0000 Subject: [PATCH 2/2] eq should be supported --- .../src/avm/avm_memory_types.test.ts | 8 + .../simulator/src/avm/avm_memory_types.ts | 7 +- .../src/avm/opcodes/comparators.test.ts | 154 +++++++++++++++--- .../simulator/src/avm/opcodes/comparators.ts | 5 +- .../docs/public-vm/gen/_InstructionSet.mdx | 2 +- .../InstructionSet/InstructionSet.js | 2 +- 6 files changed, 152 insertions(+), 26 deletions(-) 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 872fb7deffb0..cdb581467de0 100644 --- a/yarn-project/simulator/src/avm/avm_memory_types.test.ts +++ b/yarn-project/simulator/src/avm/avm_memory_types.test.ts @@ -167,6 +167,14 @@ describe('Field', () => { expect(result).toStrictEqual(new Field(2n)); }); + it(`Should check equality of two Fields correctly`, () => { + const field1 = new Field(5); + const field2 = new Field(5); + const field3 = new Field(10); + expect(field1.equals(field2)).toBe(true); + expect(field1.equals(field3)).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 c0ab3571a56b..f4fb0b80e9c5 100644 --- a/yarn-project/simulator/src/avm/avm_memory_types.ts +++ b/yarn-project/simulator/src/avm/avm_memory_types.ts @@ -10,6 +10,8 @@ export abstract class MemoryValue { public abstract mul(rhs: MemoryValue): MemoryValue; public abstract div(rhs: MemoryValue): MemoryValue; + public abstract equals(rhs: MemoryValue): boolean; + // We need this to be able to build an instance of the subclasses. public abstract build(n: bigint): MemoryValue; @@ -30,7 +32,6 @@ export abstract class IntegralValue extends MemoryValue { public abstract xor(rhs: IntegralValue): IntegralValue; public abstract not(): IntegralValue; - public abstract equals(rhs: MemoryValue): boolean; public abstract lt(rhs: MemoryValue): boolean; } @@ -196,6 +197,10 @@ export class Field extends MemoryValue { return new Field(this.rep.div(rhs.rep)); } + public equals(rhs: Field): boolean { + return this.rep.equals(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 2986c5498779..591c657239e0 100644 --- a/yarn-project/simulator/src/avm/opcodes/comparators.test.ts +++ b/yarn-project/simulator/src/avm/opcodes/comparators.test.ts @@ -4,29 +4,24 @@ import { TagCheckError } from '../errors.js'; import { initContext } from '../fixtures/index.js'; import { Eq, Lt, Lte } from './comparators.js'; -type ComparatorClass = typeof Eq | typeof Lt | typeof Lte; -describe.each([ - [Eq, (a: number, b: number) => (a == b ? 1 : 0)], - [Lt, (a: number, b: number) => (a < b ? 1 : 0)], - [Lte, (a: number, b: number) => (a <= b ? 1 : 0)], -])('Comparators', (clsValue: ComparatorClass, cmpf: (a: number, b: number) => number) => { +describe('Comparators', () => { let context: AvmContext; beforeEach(() => { context = initContext(); }); - describe(clsValue.name, () => { + describe('Eq', () => { it('Should deserialize correctly', () => { const buf = Buffer.from([ - clsValue.opcode, // opcode + Eq.opcode, // opcode 0x01, // indirect TypeTag.UINT64, // inTag ...Buffer.from('12345678', 'hex'), // aOffset ...Buffer.from('23456789', 'hex'), // bOffset ...Buffer.from('3456789a', 'hex'), // dstOffset ]); - const inst = new clsValue( + const inst = new Eq( /*indirect=*/ 0x01, /*inTag=*/ TypeTag.UINT64, /*aOffset=*/ 0x12345678, @@ -34,7 +29,7 @@ describe.each([ /*dstOffset=*/ 0x3456789a, ); - expect(clsValue.deserialize(buf)).toEqual(inst); + expect(Eq.deserialize(buf)).toEqual(inst); expect(inst.serialize()).toEqual(buf); }); @@ -42,20 +37,138 @@ describe.each([ context.machineState.memory.setSlice(0, [new Uint32(1), new Uint32(2), new Uint32(3), new Uint32(1)]); [ - new clsValue(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), - new clsValue(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 11), - new clsValue(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 3, /*dstOffset=*/ 12), + new Eq(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), + new Eq(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 11), + new Eq(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 3, /*dstOffset=*/ 12), ].forEach(i => i.execute(context)); const actual = context.machineState.memory.getSlice(/*offset=*/ 10, /*size=*/ 4); - expect(actual).toEqual([new Uint32(cmpf(1, 2)), new Uint32(cmpf(1, 2)), new Uint32(cmpf(1, 1))]); + expect(actual).toEqual([new Uint32(0), new Uint32(0), new Uint32(1)]); + }); + + it('Works on field elements', async () => { + context.machineState.memory.setSlice(0, [new Field(1), new Field(2), new Field(3), new Field(1)]); + + [ + new Eq(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), + new Eq(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 11), + new Eq(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 3, /*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(0), new Field(1)]); + }); + + it('InTag is checked', async () => { + context.machineState.memory.setSlice(0, [new Field(1), new Uint32(2), new Uint16(3)]); + + const ops = [ + new Eq(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), + new Eq(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 10), + new Eq(/*indirect=*/ 0, TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 2, /*dstOffset=*/ 10), + new Eq(/*indirect=*/ 0, TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 1, /*dstOffset=*/ 10), + ]; + + for (const o of ops) { + await expect(() => o.execute(context)).rejects.toThrow(TagCheckError); + } + }); + }); + + describe('Lt', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + Lt.opcode, // opcode + 0x01, // indirect + TypeTag.UINT64, // inTag + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('23456789', 'hex'), // bOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset + ]); + const inst = new Lt( + /*indirect=*/ 0x01, + /*inTag=*/ TypeTag.UINT64, + /*aOffset=*/ 0x12345678, + /*bOffset=*/ 0x23456789, + /*dstOffset=*/ 0x3456789a, + ); + + expect(Lt.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); + }); + + it('Works on integral types', async () => { + context.machineState.memory.setSlice(0, [new Uint32(1), new Uint32(2), new Uint32(0)]); + + [ + new Lt(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 0, /*dstOffset=*/ 10), + new Lt(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 11), + new Lt(/*indirect=*/ 0, TypeTag.UINT32, /*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 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('InTag is checked', async () => { + context.machineState.memory.setSlice(0, [new Field(1), new Uint32(2), new Uint16(3)]); + + const ops = [ + new Lt(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), + new Lt(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 10), + new Lt(/*indirect=*/ 0, TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 2, /*dstOffset=*/ 10), + new Lt(/*indirect=*/ 0, TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 1, /*dstOffset=*/ 10), + ]; + + for (const o of ops) { + await expect(() => o.execute(context)).rejects.toThrow(TagCheckError); + } + }); + }); + + describe('Lte', () => { + it('Should deserialize correctly', () => { + const buf = Buffer.from([ + Lte.opcode, // opcode + 0x01, // indirect + TypeTag.UINT64, // inTag + ...Buffer.from('12345678', 'hex'), // aOffset + ...Buffer.from('23456789', 'hex'), // bOffset + ...Buffer.from('3456789a', 'hex'), // dstOffset + ]); + const inst = new Lte( + /*indirect=*/ 0x01, + /*inTag=*/ TypeTag.UINT64, + /*aOffset=*/ 0x12345678, + /*bOffset=*/ 0x23456789, + /*dstOffset=*/ 0x3456789a, + ); + + expect(Lte.deserialize(buf)).toEqual(inst); + expect(inst.serialize()).toEqual(buf); + }); + + it('Works on integral types', async () => { + context.machineState.memory.setSlice(0, [new Uint32(1), new Uint32(2), new Uint32(0)]); + + [ + new Lte(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 0, /*dstOffset=*/ 10), + new Lte(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 11), + new Lte(/*indirect=*/ 0, TypeTag.UINT32, /*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 Uint32(1), new Uint32(1), new Uint32(0)]); }); it('Does not work on field elements', async () => { await expect(() => - new clsValue(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10).execute( - context, - ), + new Lte(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10).execute(context), ).rejects.toThrow(TagCheckError); }); @@ -63,9 +176,10 @@ describe.each([ context.machineState.memory.setSlice(0, [new Field(1), new Uint32(2), new Uint16(3)]); const ops = [ - new clsValue(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 10), - new clsValue(/*indirect=*/ 0, TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 2, /*dstOffset=*/ 10), - new clsValue(/*indirect=*/ 0, TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 1, /*dstOffset=*/ 10), + new Lte(/*indirect=*/ 0, TypeTag.FIELD, /*aOffset=*/ 0, /*bOffset=*/ 1, /*dstOffset=*/ 10), + new Lte(/*indirect=*/ 0, TypeTag.UINT32, /*aOffset=*/ 0, /*bOffset=*/ 2, /*dstOffset=*/ 10), + new Lte(/*indirect=*/ 0, TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 2, /*dstOffset=*/ 10), + new Lte(/*indirect=*/ 0, TypeTag.UINT16, /*aOffset=*/ 1, /*bOffset=*/ 1, /*dstOffset=*/ 10), ]; for (const o of ops) { diff --git a/yarn-project/simulator/src/avm/opcodes/comparators.ts b/yarn-project/simulator/src/avm/opcodes/comparators.ts index f53a187572d9..cff0b9baa383 100644 --- a/yarn-project/simulator/src/avm/opcodes/comparators.ts +++ b/yarn-project/simulator/src/avm/opcodes/comparators.ts @@ -13,10 +13,9 @@ export class Eq 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) ? 1n : 0n); diff --git a/yellow-paper/docs/public-vm/gen/_InstructionSet.mdx b/yellow-paper/docs/public-vm/gen/_InstructionSet.mdx index 9626996edf65..d11198262d2a 100644 --- a/yellow-paper/docs/public-vm/gen/_InstructionSet.mdx +++ b/yellow-paper/docs/public-vm/gen/_InstructionSet.mdx @@ -555,7 +555,7 @@ Equality 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](./state-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](./state-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 63758ff67261..ae297566cb4b 100644 --- a/yellow-paper/src/preprocess/InstructionSet/InstructionSet.js +++ b/yellow-paper/src/preprocess/InstructionSet/InstructionSet.js @@ -95,7 +95,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"},