Skip to content

Commit

Permalink
feat: add gestResOperandRelocatable method
Browse files Browse the repository at this point in the history
  • Loading branch information
zmalatrax committed Aug 7, 2024
1 parent 6f89d74 commit bc15651
Show file tree
Hide file tree
Showing 3 changed files with 207 additions and 4 deletions.
11 changes: 10 additions & 1 deletion src/errors/virtualMachine.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ResOperand } from 'hints/hintParamsSchema';
import { OpType, ResOperand } from 'hints/hintParamsSchema';
import { Relocatable } from 'primitives/relocatable';
import { SegmentValue } from 'primitives/segmentValue';

Expand Down Expand Up @@ -65,3 +65,12 @@ export class InvalidBufferResOp extends VirtualMachineError {
super(`Cannot extract buffer from the given ResOperand: ${resOperand}`);
}
}

/** The given `resOperand` cannot be extracted as a Relocatable. */
export class CannotExtractRelocatable extends VirtualMachineError {
constructor(type: OpType) {
super(
`The ResOperand ${type} cannot be extracted as a Relocatable. It doesn't support Immediate nor BinOp multiplicaton.`
);
}
}
133 changes: 131 additions & 2 deletions src/vm/virtualMachine.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { test, expect, describe, spyOn } from 'bun:test';

import { ExpectedFelt, ExpectedRelocatable } from 'errors/primitives';
import { InvalidBufferResOp, UnusedRes } from 'errors/virtualMachine';
import {
CannotExtractRelocatable,
InvalidBufferResOp,
UnusedRes,
} from 'errors/virtualMachine';

import { Felt } from 'primitives/felt';
import { Relocatable } from 'primitives/relocatable';
Expand Down Expand Up @@ -721,7 +725,7 @@ describe('VirtualMachine', () => {
new Felt(21n),
],
])(
'should properly read ResOperand',
'should properly read ResOperand as Felt',
(resOperand: ResOperand, expected: Felt) => {
const vm = new VirtualMachine();
vm.memory.addSegment();
Expand All @@ -735,6 +739,131 @@ describe('VirtualMachine', () => {
expect(vm.getResOperandValue(resOperand)).toEqual(expected);
}
);

test.each([
[
{
type: OpType.Deref,
cell: { register: Register.Ap, offset: 0 },
},
new Relocatable(2, 4),
],
[
{
type: OpType.DoubleDeref,
cell: { register: Register.Ap, offset: 1 },
offset: -1,
},
new Relocatable(2, 4),
],
// [
// {
// type: OpType.Immediate,
// value: new Felt(5n),
// },
// new Felt(5n),
// ],
[
{
type: OpType.BinOp,
op: Operation.Add,
a: { register: Register.Fp, offset: 0 },
b: { type: OpType.Immediate, value: new Felt(5n) },
},
new Relocatable(2, 9),
],
// [
// {
// type: OpType.BinOp,
// op: Operation.Mul,
// a: { register: Register.Fp, offset: 0 },
// b: { type: OpType.Immediate, value: new Felt(5n) },
// },
// new Felt(15n),
// ],
[
{
type: OpType.BinOp,
op: Operation.Add,
a: { register: Register.Ap, offset: 0 },
b: {
type: OpType.Deref,
cell: { register: Register.Ap, offset: 2 },
},
},
new Relocatable(2, 16),
],
// [
// {
// type: OpType.BinOp,
// op: Operation.Mul,
// a: { register: Register.Ap, offset: 0 },
// b: {
// type: OpType.Deref,
// cell: { register: Register.Ap, offset: 1 },
// },
// },
// new Felt(21n),
// ],
])(
'should properly read ResOperand as Relocatable',
(resOperand: ResOperand, expected: Relocatable) => {
const vm = new VirtualMachine();
vm.memory.addSegment();
vm.memory.addSegment();
const address0 = new Relocatable(2, 4);
const address1 = new Relocatable(1, 1);
const value = new Felt(12n);
vm.memory.assertEq(vm.ap, address0);
vm.memory.assertEq(vm.ap.add(1), address1);
vm.memory.assertEq(vm.ap.add(2), value);
expect(vm.getResOperandRelocatable(resOperand)).toEqual(expected);
}
);

test.each([
[
{
type: OpType.Immediate,
value: new Felt(5n),
},
],
[
{
type: OpType.BinOp,
op: Operation.Mul,
a: { register: Register.Fp, offset: 0 },
b: { type: OpType.Immediate, value: new Felt(5n) },
},
],
[
{
type: OpType.BinOp,
op: Operation.Mul,
a: { register: Register.Ap, offset: 0 },
b: {
type: OpType.Deref,
cell: { register: Register.Ap, offset: 2 },
},
},
],
])(
'should throw CannotExtractRelocatable with Immediate BinOp + Operation.Mul ResOperand',
(resOperand: ResOperand) => {
const vm = new VirtualMachine();
vm.memory.addSegment();
vm.memory.addSegment();
const address0 = new Relocatable(2, 4);
const address1 = new Relocatable(1, 1);
const value = new Felt(12n);
vm.memory.assertEq(vm.ap, address0);
vm.memory.assertEq(vm.ap.add(1), address1);
vm.memory.assertEq(vm.ap.add(2), value);
expect(() => vm.getResOperandRelocatable(resOperand)).toThrow(
new CannotExtractRelocatable(resOperand.type)
);
}
);
});
});
});
67 changes: 66 additions & 1 deletion src/vm/virtualMachine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
InvalidCallOp0Value,
UndefinedOp1,
InvalidBufferResOp,
CannotExtractRelocatable,
} from 'errors/virtualMachine';
import { DictNotFound } from 'errors/dictionary';
import { InvalidCellRefRegister, UnknownHint } from 'errors/hints';
Expand Down Expand Up @@ -550,7 +551,7 @@ export class VirtualMachine {
}

/**
* Return the value defined by `resOperand`
* Return the Felt value defined by `resOperand`
*
* Generic patterns:
* - Deref: `[register + offset]`
Expand All @@ -561,6 +562,9 @@ export class VirtualMachine {
* - BinOp (Mul): `[register1 + offset1] * [register2 + offset2]`
* or `[register1 + offset1] * immediate`
*
* @param {ResOperand} resOperand - The ResOperand to extract a Felt from.
* @returns {Felt} The value expressed by the given ResOperand.
*
* NOTE: used in Cairo hints
*/
getResOperandValue(resOperand: ResOperand): Felt {
Expand Down Expand Up @@ -606,6 +610,67 @@ export class VirtualMachine {
}
}

/**
* Return the Relocatable defined by `resOperand`
*
* Generic patterns:
* - Deref: `[register + offset]`
* - DoubleDeref: `[[register + offset1] + offset2]`
* - Immediate: Forbidden operation on `Relocatable`
* - BinOp (Add): `[register1 + offset1] + [register2 + offset2]`
* or `[register1 + offset1] + immediate`
* - BinOp (Mul): Forbidden operation on `Relocatable`
*
* @param {ResOperand} resOperand - The ResOperand to extract a Relocatable from.
* @returns {Relocatable} The value expressed by the given ResOperand.
* @throws {CannotExtractRelocatable} if OpType is Immediate or BinOp with a Mul operation.
*
* NOTE: used in Cairo hints
*/
getResOperandRelocatable(resOperand: ResOperand): Relocatable {
switch (resOperand.type) {
case OpType.Deref:
return this.getRelocatable((resOperand as Deref).cell);

case OpType.DoubleDeref:
const dDeref = resOperand as DoubleDeref;
const deref = this.getRelocatable(dDeref.cell);
const value = this.memory.get(deref.add(dDeref.offset));
if (!value || !isRelocatable(value))
throw new ExpectedRelocatable(value);
return value;

case OpType.Immediate:
throw new CannotExtractRelocatable(resOperand.type);

case OpType.BinOp:
const binOp = resOperand as BinOp;
const a = this.getRelocatable(binOp.a);

let b: Felt | undefined = undefined;
switch (binOp.b.type) {
case OpType.Deref:
b = this.getFelt((binOp.b as Deref).cell);
break;

case OpType.Immediate:
b = (binOp.b as Immediate).value;
break;

default:
throw new ExpectedFelt(b);
}

switch (binOp.op) {
case Operation.Add:
return a.add(b);

case Operation.Mul:
throw new CannotExtractRelocatable(resOperand.type);
}
}
}

/**
* Return the address defined at `resOperand`.
*
Expand Down

0 comments on commit bc15651

Please sign in to comment.