Skip to content

Commit 57b4da9

Browse files
rtfeldmanclaude
andcommitted
Fix i128 alignment by serializing int128_values first
The i128 type requires 16-byte alignment. By serializing int128_values FIRST (after the header), its data will be at a 16-byte aligned offset since: 1. The buffer is allocated with 16-byte alignment 2. SafeList serialization pads to @Alignof(T) before writing data The deserialization buffer is already allocated with 16-byte alignment via SERIALIZATION_ALIGNMENT in CompactWriter. This is the proper fix that preserves zero-cost deserialization (just cast the memory) rather than adding runtime conversion overhead. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 3fe8ff0 commit 57b4da9

1 file changed

Lines changed: 9 additions & 6 deletions

File tree

src/canonicalize/NodeStore.zig

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4065,11 +4065,13 @@ pub fn matchBranchPatternSpanFrom(store: *NodeStore, start: u32) Allocator.Error
40654065

40664066
/// Serialized representation of NodeStore
40674067
/// Uses extern struct to guarantee consistent field layout across optimization levels.
4068+
/// IMPORTANT: int128_values is placed first (after gpa) to ensure 16-byte alignment
4069+
/// since i128 requires 16-byte alignment and the buffer start is well-aligned.
40684070
pub const Serialized = extern struct {
4069-
gpa: [2]u64, // Reserve enough space for 2 64-bit pointers
4071+
gpa: [2]u64, // Reserve enough space for 2 64-bit pointers (16 bytes total)
4072+
int128_values: collections.SafeList(i128).Serialized, // Must be first data field for 16-byte alignment
40704073
nodes: Node.List.Serialized,
40714074
regions: Region.List.Serialized,
4072-
int128_values: collections.SafeList(i128).Serialized,
40734075
span2_data: collections.SafeList(Span2).Serialized,
40744076
span_with_node_data: collections.SafeList(SpanWithNode).Serialized,
40754077
match_data: collections.SafeList(MatchData).Serialized,
@@ -4090,12 +4092,12 @@ pub const Serialized = extern struct {
40904092
allocator: Allocator,
40914093
writer: *CompactWriter,
40924094
) Allocator.Error!void {
4095+
// Serialize int128_values FIRST to ensure 16-byte alignment (i128 requires it)
4096+
try self.int128_values.serialize(&store.int128_values, allocator, writer);
40934097
// Serialize nodes
40944098
try self.nodes.serialize(&store.nodes, allocator, writer);
40954099
// Serialize regions
40964100
try self.regions.serialize(&store.regions, allocator, writer);
4097-
// Serialize int128_values
4098-
try self.int128_values.serialize(&store.int128_values, allocator, writer);
40994101
// Serialize span2_data
41004102
try self.span2_data.serialize(&store.span2_data, allocator, writer);
41014103
// Serialize span_with_node_data
@@ -4128,7 +4130,8 @@ pub const Serialized = extern struct {
41284130
// fields. We must deserialize in REVERSE order (last to first)
41294131
// so that each deserialization doesn't corrupt fields that haven't been deserialized yet.
41304132

4131-
// Deserialize in reverse order (last to first)
4133+
// Deserialize in reverse order (last serialized to first serialized)
4134+
// int128_values is serialized FIRST (for alignment), so it's deserialized LAST
41324135
const deserialized_index_data = self.index_data.deserialize(base_addr).*;
41334136
const deserialized_pattern_list_data = self.pattern_list_data.deserialize(base_addr).*;
41344137
const deserialized_type_apply_data = self.type_apply_data.deserialize(base_addr).*;
@@ -4140,9 +4143,9 @@ pub const Serialized = extern struct {
41404143
const deserialized_match_data = self.match_data.deserialize(base_addr).*;
41414144
const deserialized_span_with_node_data = self.span_with_node_data.deserialize(base_addr).*;
41424145
const deserialized_span2_data = self.span2_data.deserialize(base_addr).*;
4143-
const deserialized_int128_values = self.int128_values.deserialize(base_addr).*;
41444146
const deserialized_regions = self.regions.deserialize(base_addr).*;
41454147
const deserialized_nodes = self.nodes.deserialize(base_addr).*;
4148+
const deserialized_int128_values = self.int128_values.deserialize(base_addr).*;
41464149

41474150
// Overwrite ourself with the deserialized version, and return our pointer after casting it to NodeStore
41484151
const store = @as(*NodeStore, @ptrFromInt(@intFromPtr(self)));

0 commit comments

Comments
 (0)