From ccef6dd16a57a79a08bc3b3abf9333deab3ebc36 Mon Sep 17 00:00:00 2001 From: Rodrigo Date: Mon, 20 Nov 2023 08:25:37 -0500 Subject: [PATCH] Feature: Alloc Constant Size Hint (#170) add alloc constant size hint --- pkg/hintrunner/hint.go | 34 ++++++++++ pkg/hintrunner/hint_test.go | 116 +++++++++++++++++++++++------------ pkg/hintrunner/hintrunner.go | 3 + 3 files changed, 114 insertions(+), 39 deletions(-) diff --git a/pkg/hintrunner/hint.go b/pkg/hintrunner/hint.go index 6635d4b36..cc277226d 100644 --- a/pkg/hintrunner/hint.go +++ b/pkg/hintrunner/hint.go @@ -906,3 +906,37 @@ func (hint *GetNextDictKey) Execute(vm *VM.VirtualMachine, ctx *HintRunnerContex mv := mem.MemoryValueFromFieldElement(&nextKey) return vm.Memory.WriteToAddress(&nextKeyAddr, &mv) } + +type AllocConstantSize struct { + Size ResOperander + Dst CellRefer +} + +func (hint *AllocConstantSize) String() string { + return "AllocConstantSize" +} + +func (hint *AllocConstantSize) Execute(vm *VM.VirtualMachine, ctx *HintRunnerContext) error { + size, err := ResolveAsUint64(vm, hint.Size) + if err != nil { + return fmt.Errorf("size to big: %w", err) + } + + if ctx.ConstantSizeSegment.Equal(&mem.UnknownAddress) { + ctx.ConstantSizeSegment = vm.Memory.AllocateEmptySegment() + } + + dst, err := hint.Dst.Get(vm) + if err != nil { + return fmt.Errorf("get dst %w", err) + } + + val := mem.MemoryValueFromMemoryAddress(&ctx.ConstantSizeSegment) + if err = vm.Memory.WriteToAddress(&dst, &val); err != nil { + return err + } + + // todo(rodro): Is possible for this to overflow + ctx.ConstantSizeSegment.Offset += size + return nil +} diff --git a/pkg/hintrunner/hint_test.go b/pkg/hintrunner/hint_test.go index c2e8a9049..bf72bd3c7 100644 --- a/pkg/hintrunner/hint_test.go +++ b/pkg/hintrunner/hint_test.go @@ -7,7 +7,7 @@ import ( "testing" VM "github.com/NethermindEth/cairo-vm-go/pkg/vm" - "github.com/NethermindEth/cairo-vm-go/pkg/vm/memory" + mem "github.com/NethermindEth/cairo-vm-go/pkg/vm/memory" f "github.com/consensys/gnark-crypto/ecc/stark-curve/fp" "github.com/holiman/uint256" "github.com/stretchr/testify/require" @@ -29,7 +29,7 @@ func TestAllocSegment(t *testing.T) { require.Equal(t, 3, len(vm.Memory.Segments)) require.Equal( t, - memory.MemoryValueFromSegmentAndOffset(2, 0), + mem.MemoryValueFromSegmentAndOffset(2, 0), readFrom(vm, VM.ExecutionSegment, vm.Context.Ap+5), ) @@ -38,7 +38,7 @@ func TestAllocSegment(t *testing.T) { require.Equal(t, 4, len(vm.Memory.Segments)) require.Equal( t, - memory.MemoryValueFromSegmentAndOffset(3, 0), + mem.MemoryValueFromSegmentAndOffset(3, 0), readFrom(vm, VM.ExecutionSegment, vm.Context.Fp+9), ) @@ -48,7 +48,7 @@ func TestTestLessThanTrue(t *testing.T) { vm := defaultVirtualMachine() vm.Context.Ap = 0 vm.Context.Fp = 0 - writeTo(vm, VM.ExecutionSegment, 0, memory.MemoryValueFromInt(23)) + writeTo(vm, VM.ExecutionSegment, 0, mem.MemoryValueFromInt(23)) var dst ApCellRef = 1 var rhsRef FpCellRef = 0 @@ -66,7 +66,7 @@ func TestTestLessThanTrue(t *testing.T) { require.NoError(t, err) require.Equal( t, - memory.MemoryValueFromInt(1), + mem.MemoryValueFromInt(1), readFrom(vm, VM.ExecutionSegment, 1), "Expected the hint to evaluate to True when lhs is less than rhs", ) @@ -85,7 +85,7 @@ func TestTestLessThanFalse(t *testing.T) { vm := defaultVirtualMachine() vm.Context.Ap = 0 vm.Context.Fp = 0 - writeTo(vm, VM.ExecutionSegment, 0, memory.MemoryValueFromInt(17)) + writeTo(vm, VM.ExecutionSegment, 0, mem.MemoryValueFromInt(17)) var dst ApCellRef = 1 var rhsRef FpCellRef = 0 @@ -102,7 +102,7 @@ func TestTestLessThanFalse(t *testing.T) { require.NoError(t, err) require.Equal( t, - memory.EmptyMemoryValueAsFelt(), + mem.EmptyMemoryValueAsFelt(), readFrom(vm, VM.ExecutionSegment, 1), tc.expectedMsg, ) @@ -124,7 +124,7 @@ func TestTestLessThanOrEqTrue(t *testing.T) { vm := defaultVirtualMachine() vm.Context.Ap = 0 vm.Context.Fp = 0 - writeTo(vm, VM.ExecutionSegment, 0, memory.MemoryValueFromInt(23)) + writeTo(vm, VM.ExecutionSegment, 0, mem.MemoryValueFromInt(23)) var dst ApCellRef = 1 var rhsRef FpCellRef = 0 @@ -141,7 +141,7 @@ func TestTestLessThanOrEqTrue(t *testing.T) { require.NoError(t, err) require.Equal( t, - memory.MemoryValueFromInt(1), + mem.MemoryValueFromInt(1), readFrom(vm, VM.ExecutionSegment, 1), tc.expectedMsg, ) @@ -153,7 +153,7 @@ func TestTestLessThanOrEqFalse(t *testing.T) { vm := defaultVirtualMachine() vm.Context.Ap = 0 vm.Context.Fp = 0 - writeTo(vm, VM.ExecutionSegment, 0, memory.MemoryValueFromInt(17)) + writeTo(vm, VM.ExecutionSegment, 0, mem.MemoryValueFromInt(17)) var dst ApCellRef = 1 var rhsRef FpCellRef = 0 @@ -171,7 +171,7 @@ func TestTestLessThanOrEqFalse(t *testing.T) { require.NoError(t, err) require.Equal( t, - memory.EmptyMemoryValueAsFelt(), + mem.EmptyMemoryValueAsFelt(), readFrom(vm, VM.ExecutionSegment, 1), "Expected the hint to evaluate to False when lhs is larger", ) @@ -199,9 +199,9 @@ func TestLinearSplit(t *testing.T) { err := hint.Execute(vm) require.NoError(t, err) xx := readFrom(vm, VM.ExecutionSegment, 0) - require.Equal(t, xx, memory.MemoryValueFromInt(223344)) + require.Equal(t, xx, mem.MemoryValueFromInt(223344)) yy := readFrom(vm, VM.ExecutionSegment, 1) - require.Equal(t, yy, memory.MemoryValueFromInt(14)) + require.Equal(t, yy, mem.MemoryValueFromInt(14)) vm = defaultVirtualMachine() vm.Context.Ap = 0 @@ -220,9 +220,9 @@ func TestLinearSplit(t *testing.T) { err = hint.Execute(vm) require.NoError(t, err) xx = readFrom(vm, VM.ExecutionSegment, 0) - require.Equal(t, xx, memory.MemoryValueFromInt(223343)) + require.Equal(t, xx, mem.MemoryValueFromInt(223343)) yy = readFrom(vm, VM.ExecutionSegment, 1) - require.Equal(t, yy, memory.MemoryValueFromInt(14+42)) + require.Equal(t, yy, mem.MemoryValueFromInt(14+42)) } func TestWideMul128(t *testing.T) { @@ -257,12 +257,12 @@ func TestWideMul128(t *testing.T) { require.Equal( t, - memory.MemoryValueFromFieldElement(low), + mem.MemoryValueFromFieldElement(low), readFrom(vm, VM.ExecutionSegment, 1), ) require.Equal( t, - memory.MemoryValueFromInt(1<<7), + mem.MemoryValueFromInt(1<<7), readFrom(vm, VM.ExecutionSegment, 2), ) } @@ -303,11 +303,11 @@ func TestDebugPrint(t *testing.T) { vm.Context.Ap = 0 vm.Context.Fp = 0 - writeTo(vm, VM.ExecutionSegment, 0, memory.MemoryValueFromSegmentAndOffset(VM.ExecutionSegment, 2)) - writeTo(vm, VM.ExecutionSegment, 1, memory.MemoryValueFromSegmentAndOffset(VM.ExecutionSegment, 5)) - writeTo(vm, VM.ExecutionSegment, 2, memory.MemoryValueFromInt(10)) - writeTo(vm, VM.ExecutionSegment, 3, memory.MemoryValueFromInt(20)) - writeTo(vm, VM.ExecutionSegment, 4, memory.MemoryValueFromInt(30)) + writeTo(vm, VM.ExecutionSegment, 0, mem.MemoryValueFromSegmentAndOffset(VM.ExecutionSegment, 2)) + writeTo(vm, VM.ExecutionSegment, 1, mem.MemoryValueFromSegmentAndOffset(VM.ExecutionSegment, 5)) + writeTo(vm, VM.ExecutionSegment, 2, mem.MemoryValueFromInt(10)) + writeTo(vm, VM.ExecutionSegment, 3, mem.MemoryValueFromInt(20)) + writeTo(vm, VM.ExecutionSegment, 4, mem.MemoryValueFromInt(30)) var starRef ApCellRef = 0 var endRef ApCellRef = 1 @@ -346,7 +346,7 @@ func TestSquareRoot(t *testing.T) { require.NoError(t, err) require.Equal( t, - memory.MemoryValueFromInt(6), + mem.MemoryValueFromInt(6), readFrom(vm, VM.ExecutionSegment, 1), ) @@ -362,7 +362,7 @@ func TestSquareRoot(t *testing.T) { require.NoError(t, err) require.Equal( t, - memory.MemoryValueFromInt(5), + mem.MemoryValueFromInt(5), readFrom(vm, VM.ExecutionSegment, 2), ) } @@ -395,11 +395,11 @@ func TestUint256SquareRootLow(t *testing.T) { require.NoError(t, err) - expectedSqrt0 := memory.MemoryValueFromInt(11) - expectedSqrt1 := memory.MemoryValueFromInt(0) - expectedRemainderLow := memory.MemoryValueFromInt(0) - expectedRemainderHigh := memory.MemoryValueFromInt(0) - expectedSqrtMul2MinusRemainderGeU128 := memory.MemoryValueFromInt(0) + expectedSqrt0 := mem.MemoryValueFromInt(11) + expectedSqrt1 := mem.MemoryValueFromInt(0) + expectedRemainderLow := mem.MemoryValueFromInt(0) + expectedRemainderHigh := mem.MemoryValueFromInt(0) + expectedSqrtMul2MinusRemainderGeU128 := mem.MemoryValueFromInt(0) actualSqrt0 := readFrom(vm, VM.ExecutionSegment, 1) actualSqrt1 := readFrom(vm, VM.ExecutionSegment, 2) @@ -442,11 +442,11 @@ func TestUint256SquareRootHigh(t *testing.T) { require.NoError(t, err) - expectedSqrt0 := memory.MemoryValueFromInt(0) - expectedSqrt1 := memory.MemoryValueFromInt(16) - expectedRemainderLow := memory.MemoryValueFromInt(0) - expectedRemainderHigh := memory.MemoryValueFromInt(0) - expectedSqrtMul2MinusRemainderGeU128 := memory.MemoryValueFromInt(0) + expectedSqrt0 := mem.MemoryValueFromInt(0) + expectedSqrt1 := mem.MemoryValueFromInt(16) + expectedRemainderLow := mem.MemoryValueFromInt(0) + expectedRemainderHigh := mem.MemoryValueFromInt(0) + expectedSqrtMul2MinusRemainderGeU128 := mem.MemoryValueFromInt(0) actualSqrt0 := readFrom(vm, VM.ExecutionSegment, 1) actualSqrt1 := readFrom(vm, VM.ExecutionSegment, 2) @@ -489,11 +489,11 @@ func TestUint256SquareRoot(t *testing.T) { require.NoError(t, err) - expectedSqrt0 := memory.MemoryValueFromInt(0) - expectedSqrt1 := memory.MemoryValueFromInt(32) - expectedRemainderLow := memory.MemoryValueFromInt(51) - expectedRemainderHigh := memory.MemoryValueFromInt(0) - expectedSqrtMul2MinusRemainderGeU128 := memory.MemoryValueFromInt(0) + expectedSqrt0 := mem.MemoryValueFromInt(0) + expectedSqrt1 := mem.MemoryValueFromInt(32) + expectedRemainderLow := mem.MemoryValueFromInt(51) + expectedRemainderHigh := mem.MemoryValueFromInt(0) + expectedSqrtMul2MinusRemainderGeU128 := mem.MemoryValueFromInt(0) actualSqrt0 := readFrom(vm, VM.ExecutionSegment, 1) actualSqrt1 := readFrom(vm, VM.ExecutionSegment, 2) @@ -507,3 +507,41 @@ func TestUint256SquareRoot(t *testing.T) { require.Equal(t, expectedRemainderHigh, actualRemainderHigh) require.Equal(t, expectedSqrtMul2MinusRemainderGeU128, actualSqrtMul2MinusRemainderGeU128) } + +func TestAllocConstantSize(t *testing.T) { + vm := defaultVirtualMachine() + + sizes := [3]Immediate{ + Immediate(f.NewElement(15)), + Immediate(f.NewElement(13)), + Immediate(f.NewElement(2)), + } + expectedAddrs := [3]mem.MemoryAddress{ + {SegmentIndex: 2, Offset: 0}, + {SegmentIndex: 2, Offset: 15}, + {SegmentIndex: 2, Offset: 28}, + } + + ctx := HintRunnerContext{ + ConstantSizeSegment: mem.UnknownAddress, + } + + for i := 0; i < len(sizes); i++ { + hint := AllocConstantSize{ + Dst: ApCellRef(i), + Size: sizes[i], + } + + err := hint.Execute(vm, &ctx) + require.NoError(t, err) + + val := readFrom(vm, 1, uint64(i)) + ptr, err := val.MemoryAddress() + require.NoError(t, err) + + require.Equal(t, &expectedAddrs[i], ptr) + } + + require.Equal(t, ctx.ConstantSizeSegment, mem.MemoryAddress{SegmentIndex: 2, Offset: 30}) + +} diff --git a/pkg/hintrunner/hintrunner.go b/pkg/hintrunner/hintrunner.go index af3979e1e..130d2120a 100644 --- a/pkg/hintrunner/hintrunner.go +++ b/pkg/hintrunner/hintrunner.go @@ -185,6 +185,8 @@ func (sdm *SquashedDictionaryManager) PopIndex() (uint64, error) { type HintRunnerContext struct { DictionaryManager DictionaryManager SquashedDictionaryManager SquashedDictionaryManager + // points towards free memory of a segment + ConstantSizeSegment mem.MemoryAddress } type HintRunner struct { @@ -201,6 +203,7 @@ func NewHintRunner(hints map[uint64]Hinter) HintRunner { context: HintRunnerContext{ DictionaryManager{}, SquashedDictionaryManager{}, + mem.UnknownAddress, }, hints: hints, }