diff --git a/src/vm/core.zig b/src/vm/core.zig index 504f2d66..ab7aa6c5 100644 --- a/src/vm/core.zig +++ b/src/vm/core.zig @@ -55,6 +55,8 @@ pub const CairoVM = struct { /// Used as an instruction cache within the CairoVM instance. instruction_cache: ArrayList(?Instruction), + relocated_memory: ?std.AutoHashMap(u32, Felt252), + // ************************************************************ // * MEMORY ALLOCATION AND DEALLOCATION * // ************************************************************ @@ -99,6 +101,7 @@ pub const CairoVM = struct { .rc_limits = null, .relocation_table = null, .instruction_cache = instruction_cache, + .relocated_memory = null, }; } @@ -794,6 +797,26 @@ pub const CairoVM = struct { return null; } + /// Performs all relocation operations + /// + /// The relocation process: + /// + /// 1. Compute the sizes of each memory segment. + /// 2. Build an array that contains the first relocated address of each segment. + /// 3. Creates the relocated memory transforming the original memory by using the array built in 2. + /// 4. Creates the relocated trace transforming the original trace by using the array built in 2. + /// + pub fn relocate(self: *Self, allocator: Allocator, allow_tmp_segments: bool) !void { + _ = try self.segments.computeEffectiveSize(allow_tmp_segments); + + const relocation_table = try self.segments.relocateSegments(allocator); + + const relocated_memory = try self.segments.relocateMemory(relocation_table, allocator); + + try self.relocateTrace(relocation_table); + self.relocated_memory = relocated_memory; + } + /// Relocates the trace within the Cairo VM, updating relocatable registers to numbered ones. /// /// This function is responsible for relocating the trace within the Cairo VM, converting relocatable registers diff --git a/src/vm/memory/segments.zig b/src/vm/memory/segments.zig index f3aa8932..6c6ea4e1 100644 --- a/src/vm/memory/segments.zig +++ b/src/vm/memory/segments.zig @@ -229,7 +229,7 @@ pub const MemorySegmentManager = struct { /// 0 --(has size)--> 3 /// 1 --(has size)--> 5 /// 2 --(has size)--> 1 - /// Step 2: Step 2: Assign a base to each segment: + /// Step 2: Assign a base to each segment: /// 0 --(has base value)--> 1 /// 1 --(has base value)--> 4 (that is: 1 + 3) /// 2 --(has base value)--> 9 (that is: 4 + 5) @@ -251,6 +251,43 @@ pub const MemorySegmentManager = struct { return relocatable_table.toOwnedSlice(); } + /// Adds the relocate segments to the relocated memory + /// + /// This is step 3 following step 2 above(`relocateSegments`): + /// 1 (base[0] + 0) -> 1 + /// 2 (base[0] + 1) -> 4 + /// 3 (base[0] + 2) -> 7 + /// 4 (base[1] + 0) -> 8 + /// 5 (base[1] + 1) -> 3 (that is: base[0] + 2) + /// .... (memory gaps) + /// 8 (base[1] + 4) -> 2 (that is: base[0] + 1) + /// 9 (base[2] + 0) -> 1 + /// # Returns + /// + /// `A `HashMap` + pub fn relocateMemory(self: *Self, relocation_table: []usize, allocator: Allocator) !std.AutoHashMap(u32, Felt252) { + var relocated_memory = std.AutoHashMap( + u32, + Felt252, + ).init(allocator); + errdefer relocated_memory.deinit(); + for (0..self.memory.num_segments) |i| { + const segment_index = @as(i64, @intCast(i)); + if (self.getSegmentSize(@intCast(i))) |size| { + for (0..size) |j| { + const key = Relocatable.init(segment_index, @as(u32, @truncate(j))); + const cell = self.memory.get(key); + if (cell) |c| { + const relocated_address = try key.relocateAddress(relocation_table); + const value = try c.relocateValue(relocation_table); + try relocated_memory.put(@as(u32, @truncate(relocated_address)), value); + } + } + } + } + return relocated_memory; + } + /// Checks if a memory value is valid within the MemorySegmentManager. /// /// This function validates whether a given memory value is within the bounds @@ -1008,6 +1045,70 @@ test "MemorySegmentManager: relocateSegments for ten segments" { try expectEqualSlices(usize, expected_value.items, actual_value); } +test "MemorySegmentManager: relocate memory" { + const allocator = std.testing.allocator; + var memory_segment_manager = try MemorySegmentManager.init(allocator); + defer memory_segment_manager.deinit(); + for (0..4) |_| { + _ = try memory_segment_manager.addSegment(); + } + try memory_segment_manager.memory.set(allocator, Relocatable.init(0, 0), MaybeRelocatable.fromU256(656720927189829)); + try memory_segment_manager.memory.set(allocator, Relocatable.init(0, 1), MaybeRelocatable.fromU256(876)); + try memory_segment_manager.memory.set(allocator, Relocatable.init(0, 2), MaybeRelocatable.fromU256(87289918263880112)); + try memory_segment_manager.memory.set(allocator, Relocatable.init(1, 0), MaybeRelocatable.fromRelocatable(Relocatable.init(2, 0))); + try memory_segment_manager.memory.set(allocator, Relocatable.init(1, 1), MaybeRelocatable.fromRelocatable(Relocatable.init(3, 0))); + try memory_segment_manager.memory.set(allocator, Relocatable.init(1, 5), MaybeRelocatable.fromU256(10)); + defer memory_segment_manager.memory.deinitData(allocator); + + _ = try memory_segment_manager.computeEffectiveSize(false); + + const relocation_table = try memory_segment_manager.relocateSegments(allocator); + defer allocator.free(relocation_table); + + var relocated_memory = try memory_segment_manager.relocateMemory(relocation_table, allocator); + defer relocated_memory.deinit(); + + var expected_relocated_memory = std.AutoHashMap( + u32, + Felt252, + ).init(allocator); + defer expected_relocated_memory.deinit(); + + try expected_relocated_memory.put(1, Felt252.fromInteger(656720927189829)); + try expected_relocated_memory.put(2, Felt252.fromInteger(876)); + try expected_relocated_memory.put(3, Felt252.fromInteger(87289918263880112)); + try expected_relocated_memory.put(4, Felt252.fromInteger(10)); + try expected_relocated_memory.put(5, Felt252.fromInteger(10)); + try expected_relocated_memory.put(9, Felt252.fromInteger(10)); + + const first = expected_relocated_memory.get(1); + const first_actual = relocated_memory.get(1); + + const second = expected_relocated_memory.get(2); + const second_actual = relocated_memory.get(2); + + const third = expected_relocated_memory.get(3); + const third_actual = relocated_memory.get(3); + + const fourth = expected_relocated_memory.get(4); + const fourth_actual = relocated_memory.get(4); + + const fifth = expected_relocated_memory.get(5); + const fifth_actual = relocated_memory.get(5); + + const sixth = expected_relocated_memory.get(9); + const sixth_actual = relocated_memory.get(9); + + try expectEqual(first, first_actual); + try expectEqual(second, second_actual); + try expectEqual(third, third_actual); + try expectEqual(fourth, fourth_actual); + try expectEqual(fifth, fifth_actual); + try expectEqual(sixth, sixth_actual); + + try expectEqual(expected_relocated_memory.count(), relocated_memory.count()); +} + test "MemorySegmentManager: isValidMemoryValue should return true if Felt" { var memory_segment_manager = try MemorySegmentManager.init(std.testing.allocator); defer memory_segment_manager.deinit();