Skip to content

Commit

Permalink
Hideset: ensure tokens aren't duplicated during union
Browse files Browse the repository at this point in the history
This fixes a crash where the set intersection would be larger
than min(a_len, b_len) because of duplicated items in one of
the sets
  • Loading branch information
ehaas committed May 2, 2024
1 parent f230f45 commit 6fc04f0
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 21 deletions.
47 changes: 26 additions & 21 deletions src/aro/Hideset.zig
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ const Index = enum(u32) {
};

map: std.AutoHashMapUnmanaged(Identifier, Index) = .{},
/// Used for computing intersection of two lists; stored here so that allocations can be retained
/// Used for computing union/intersection of two lists; stored here so that allocations can be retained
/// until hideset is deinit'ed
intersection_map: std.AutoHashMapUnmanaged(Identifier, void) = .{},
tmp_map: std.AutoHashMapUnmanaged(Identifier, void) = .{},
linked_list: Item.List = .{},
comp: *const Compilation,

Expand All @@ -72,7 +72,7 @@ const Iterator = struct {

pub fn deinit(self: *Hideset) void {
self.map.deinit(self.comp.gpa);
self.intersection_map.deinit(self.comp.gpa);
self.tmp_map.deinit(self.comp.gpa);
self.linked_list.deinit(self.comp.gpa);
}

Expand All @@ -83,7 +83,7 @@ pub fn clearRetainingCapacity(self: *Hideset) void {

pub fn clearAndFree(self: *Hideset) void {
self.map.clearAndFree(self.comp.gpa);
self.intersection_map.clearAndFree(self.comp.gpa);
self.tmp_map.clearAndFree(self.comp.gpa);
self.linked_list.shrinkAndFree(self.comp.gpa, 0);
}

Expand All @@ -109,8 +109,13 @@ fn ensureUnusedCapacity(self: *Hideset, new_size: usize) !void {

/// Creates a one-item list with contents `identifier`
fn createNodeAssumeCapacity(self: *Hideset, identifier: Identifier) Index {
return self.createNodeAssumeCapacityExtra(identifier, .none);
}

/// Creates a one-item list with contents `identifier`
fn createNodeAssumeCapacityExtra(self: *Hideset, identifier: Identifier, next: Index) Index {
const next_idx = self.linked_list.len;
self.linked_list.appendAssumeCapacity(.{ .identifier = identifier });
self.linked_list.appendAssumeCapacity(.{ .identifier = identifier, .next = next });
return @enumFromInt(next_idx);
}

Expand All @@ -121,24 +126,24 @@ pub fn prepend(self: *Hideset, loc: Source.Location, tail: Index) !Index {
return @enumFromInt(new_idx);
}

/// Copy a, then attach b at the end
/// Attach elements of `b` to the front of `a` (if they're not in `a`)
pub fn @"union"(self: *Hideset, a: Index, b: Index) !Index {
var cur: Index = .none;
if (a == .none) return b;
if (b == .none) return a;
self.tmp_map.clearRetainingCapacity();

var it = self.iterator(b);
while (it.next()) |identifier| {
try self.tmp_map.put(self.comp.gpa, identifier, {});
}

var head: Index = b;
try self.ensureUnusedCapacity(self.len(a));
var it = self.iterator(a);
it = self.iterator(a);
while (it.next()) |identifier| {
const new_idx = self.createNodeAssumeCapacity(identifier);
if (head == b) {
head = new_idx;
if (!self.tmp_map.contains(identifier)) {
head = self.createNodeAssumeCapacityExtra(identifier, head);
}
if (cur != .none) {
self.linked_list.items(.next)[@intFromEnum(cur)] = new_idx;
}
cur = new_idx;
}
if (cur != .none) {
self.linked_list.items(.next)[@intFromEnum(cur)] = b;
}
return head;
}
Expand All @@ -163,20 +168,20 @@ fn len(self: *const Hideset, list: Index) usize {

pub fn intersection(self: *Hideset, a: Index, b: Index) !Index {
if (a == .none or b == .none) return .none;
self.intersection_map.clearRetainingCapacity();
self.tmp_map.clearRetainingCapacity();

var cur: Index = .none;
var head: Index = .none;
var it = self.iterator(a);
var a_len: usize = 0;
while (it.next()) |identifier| : (a_len += 1) {
try self.intersection_map.put(self.comp.gpa, identifier, {});
try self.tmp_map.put(self.comp.gpa, identifier, {});
}
try self.ensureUnusedCapacity(@min(a_len, self.len(b)));

it = self.iterator(b);
while (it.next()) |identifier| {
if (self.intersection_map.contains(identifier)) {
if (self.tmp_map.contains(identifier)) {
const new_idx = self.createNodeAssumeCapacity(identifier);
if (head == .none) {
head = new_idx;
Expand Down
9 changes: 9 additions & 0 deletions test/cases/repeated preprocessor tokens.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#define NO_ERROR_VALIDATION

#define h(x)0(x(0)0
#define s()
#define K h(
#define H h
#define L H
#define SS
K H()L(s)H SS)

0 comments on commit 6fc04f0

Please sign in to comment.