Skip to content

Commit

Permalink
Add getMemoryHoles for Cairo runner (keep-starknet-strange#410)
Browse files Browse the repository at this point in the history
* Add getMemoryHoles for Cairo runner

* fix conflicts
  • Loading branch information
tcoratger authored Feb 29, 2024
1 parent a9a3f58 commit fdc03ee
Show file tree
Hide file tree
Showing 2 changed files with 201 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/vm/core.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1317,7 +1317,7 @@ pub const CairoVM = struct {
pub fn decodeCurrentInstruction(self: *const Self) !Instruction {
const felt = try self.segments.memory.getFelt(self.run_context.getPC());

const instruction = felt.intoU64() catch
const instruction = felt.intoU64() catch
return CairoVMError.InvalidInstructionEncoding;

return decoder.decodeInstructions(instruction);
Expand Down
200 changes: 200 additions & 0 deletions src/vm/runners/cairo_runner.zig
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,37 @@ pub const CairoRunner = struct {
return builtin_segment_info;
}

/// Retrieves the number of memory holes in the CairoRunner's virtual machine (VM) segments.
///
/// Memory holes are regions of memory that are unused or uninitialized.
/// This function calculates the number of memory holes based on the VM's segments
/// and the presence of certain built-in runners.
///
/// # Arguments
///
/// - `self`: A mutable reference to the CairoRunner instance.
///
/// # Returns
///
/// The number of memory holes in the VM segments.
///
/// # Errors
///
/// - Returns a `MemoryError` if any errors occur during the calculation.
pub fn getMemoryHoles(self: *Self) !usize {
// Check if the program has the output builtin
var has_output_builtin: bool = false;
for (self.program.builtins.items) |builtin| {
if (builtin == .output) has_output_builtin = true;
}

// Calculate memory holes based on VM segments and built-in runners
return self.vm.segments.getMemoryHoles(
self.vm.builtin_runners.items.len,
has_output_builtin,
);
}

/// Retrieves the permanent range check limits from the CairoRunner instance.
///
/// This function iterates through the builtin runners of the CairoRunner and gathers
Expand Down Expand Up @@ -1617,3 +1648,172 @@ test "CairoRunner: initial FP with a simple program" {
// Verify that the initial function pointer (FP) is correct.
try expectEqual(Relocatable.init(1, 2), cairo_runner.initial_fp);
}

test "CairoRunner: getMemoryHoles with missing segment used sizes" {
// Initialize a CairoRunner with an empty program, "plain" layout, and empty instructions.
var cairo_runner = try CairoRunner.init(
std.testing.allocator,
try Program.initDefault(std.testing.allocator, true),
"plain",
ArrayList(MaybeRelocatable).init(std.testing.allocator),
try CairoVM.init(std.testing.allocator, .{}),
false,
);

// Defer the deinitialization of the CairoRunner to ensure cleanup.
defer cairo_runner.deinit(std.testing.allocator);

// Set up memory for the Cairo VM with missing segment used sizes.
// Allocator for memory allocation
try cairo_runner.vm.segments.memory.setUpMemory(
std.testing.allocator,
.{.{ .{ 0, 0 }, .{9} }},
);
// Defer memory cleanup
defer cairo_runner.vm.segments.memory.deinitData(std.testing.allocator);

// Mark the memory as accessed (placeholder operation)
cairo_runner.vm.segments.memory.markAsAccessed(.{});

// Test that invoking `getMemoryHoles` function throws the expected error.
try expectError(MemoryError.MissingSegmentUsedSizes, cairo_runner.getMemoryHoles());
}

test "CairoRunner: getMemoryHoles empty" {
// Initialize a CairoRunner with an empty program, "plain" layout, and empty instructions.
var cairo_runner = try CairoRunner.init(
std.testing.allocator,
try Program.initDefault(std.testing.allocator, true),
"plain",
ArrayList(MaybeRelocatable).init(std.testing.allocator),
try CairoVM.init(std.testing.allocator, .{}),
false,
);

// Defer the deinitialization of the CairoRunner to ensure cleanup.
defer cairo_runner.deinit(std.testing.allocator);

// Test that invoking `getMemoryHoles` function returns 0 when there are no memory holes.
try expectEqual(@as(usize, 0), try cairo_runner.getMemoryHoles());
}

test "CairoRunner: getMemoryHoles with segment used size" {
// Initialize a CairoRunner with an empty program, "plain" layout, and empty instructions.
// Allocator for memory allocation
var cairo_runner = try CairoRunner.init(
std.testing.allocator,
try Program.initDefault(std.testing.allocator, true),
"plain",
ArrayList(MaybeRelocatable).init(std.testing.allocator),
try CairoVM.init(std.testing.allocator, .{}),
false,
);

// Defer the deinitialization of the CairoRunner to ensure cleanup.
defer cairo_runner.deinit(std.testing.allocator);

// Set up memory for the Cairo VM with specified segment used sizes.
// Allocator for memory allocation
try cairo_runner.vm.segments.memory.setUpMemory(
std.testing.allocator,
.{
.{ .{ 0, 0 }, .{0} },
.{ .{ 0, 2 }, .{0} },
},
);
// Defer memory cleanup
defer cairo_runner.vm.segments.memory.deinitData(std.testing.allocator);

// Specify segment used size for segment 0
try cairo_runner.vm.segments.segment_used_sizes.put(0, 4);

// Mark the specified memory cells as accessed (placeholder operation)
cairo_runner.vm.segments.memory.markAsAccessed(.{});
cairo_runner.vm.segments.memory.markAsAccessed(Relocatable.init(0, 2));

// Test that invoking `getMemoryHoles` function returns the expected number of memory holes.
try expectEqual(@as(usize, 2), try cairo_runner.getMemoryHoles());
}

test "CairoRunner: getMemoryHoles with empty accesses" {
// Initialize a CairoRunner with an empty program, "plain" layout, and empty instructions.
// Allocator for memory allocation
var cairo_runner = try CairoRunner.init(
std.testing.allocator,
try Program.initDefault(std.testing.allocator, true),
"plain",
ArrayList(MaybeRelocatable).init(std.testing.allocator),
try CairoVM.init(std.testing.allocator, .{}),
false,
);

// Defer the deinitialization of the CairoRunner to ensure cleanup.
defer cairo_runner.deinit(std.testing.allocator);

// Set up memory for the Cairo VM with specified memory cell accesses.
// Allocator for memory allocation
try cairo_runner.vm.segments.memory.setUpMemory(
std.testing.allocator,
.{
.{ .{ 1, 0 }, .{0} },
.{ .{ 1, 2 }, .{2} },
},
);
// Defer memory cleanup
defer cairo_runner.vm.segments.memory.deinitData(std.testing.allocator);

// Mark specified memory cells as accessed in segment 1
cairo_runner.vm.segments.memory.markAsAccessed(Relocatable.init(1, 0));
cairo_runner.vm.segments.memory.markAsAccessed(Relocatable.init(1, 2));

// Initialize an OutputBuiltinRunner for testing
var output_builtin: BuiltinRunner = .{
.Output = OutputBuiltinRunner.init(std.testing.allocator, true),
};

// Initialize segments for the OutputBuiltinRunner
try output_builtin.initSegments(cairo_runner.vm.segments);

// Append the OutputBuiltinRunner to the list of builtin runners in Cairo VM
try cairo_runner.vm.builtin_runners.append(output_builtin);

// Specify segment used sizes for both segments
try cairo_runner.vm.segments.segment_used_sizes.put(0, 4);
try cairo_runner.vm.segments.segment_used_sizes.put(1, 4);

// Test that invoking `getMemoryHoles` function returns the expected number of memory holes.
try expectEqual(@as(usize, 2), try cairo_runner.getMemoryHoles());
}

test "CairoRunner: getMemoryHoles basic test" {
// Initialize a CairoRunner with an empty program, "plain" layout, and empty instructions.
// Allocator for memory allocation
var cairo_runner = try CairoRunner.init(
std.testing.allocator,
try Program.initDefault(std.testing.allocator, true),
"plain",
ArrayList(MaybeRelocatable).init(std.testing.allocator),
try CairoVM.init(std.testing.allocator, .{}),
false,
);

// Defer the deinitialization of the CairoRunner to ensure cleanup.
defer cairo_runner.deinit(std.testing.allocator);

// Initialize an OutputBuiltinRunner for testing
var output_builtin: BuiltinRunner = .{
.Output = OutputBuiltinRunner.init(std.testing.allocator, true),
};

// Initialize segments for the OutputBuiltinRunner
try output_builtin.initSegments(cairo_runner.vm.segments);

// Append the OutputBuiltinRunner to the list of builtin runners in Cairo VM
try cairo_runner.vm.builtin_runners.append(output_builtin);

// Specify segment used sizes for segment 0
try cairo_runner.vm.segments.segment_used_sizes.put(0, 4);

// Test that invoking `getMemoryHoles` function returns 0 as there are no memory holes.
try expectEqual(@as(usize, 0), try cairo_runner.getMemoryHoles());
}

0 comments on commit fdc03ee

Please sign in to comment.