tiny.choir.StableHasher
Defined in product.hashing.stable.
API (12)
Actions
Public operations.
Types and contracts
Public types and contracts.
Fields and members
Public fields and members.
Source
Source: lib/choir/src/product/hashing/stable.zig:12
zig
pub const StableHasher = struct { pub const claim: alloc_phase.capacity.Declaration = .{ .source = .{ .id = "choir.stable_hasher", .kind = .phase_static, .limit_source = .caller, .storage = .{ .covered = &.{ .{ .id = "the_complete_stablevaluenumbering_claim_plus_inline_d8f50bb9374f", .lifetime = .steady, .detail = "the complete StableValueNumbering claim plus inline Wyhash and scalar state", }, }, .excluded = &.{ "borrowed mutable IR and referenced type and attribute payloads", }, }, .capacity = .{ .inputs = &.{ alloc_phase.capacity.bindInput(Limits, "facts_value_count", "facts.value_count"), alloc_phase.capacity.bindInput(Limits, "facts_operation_depth", "facts.operation_depth"), alloc_phase.capacity.bindInput(Limits, "facts_attribute_depth", "facts.attribute_depth"), }, .type_selectors = &.{ alloc_phase.capacity.bindType(capacity_model.ValueEntry, "valueentry"), alloc_phase.capacity.bindType(capacity_model.OperationFrame, "operationframe"), alloc_phase.capacity.bindType(capacity_model.AttributeFrame, "attributeframe"), alloc_phase.capacity.bindType(std.hash.Wyhash, "hasher"), }, .nodes = &.{ .{ .input = 0 }, .{ .scale = .{ .node = 0, .coefficient = .{ .size_of_concrete_type = 0 } } }, .{ .input = 1 }, .{ .scale = .{ .node = 2, .coefficient = .{ .size_of_concrete_type = 1 } } }, .{ .input = 2 }, .{ .scale = .{ .node = 4, .coefficient = .{ .size_of_concrete_type = 2 } } }, .{ .constant = 1 }, .{ .scale = .{ .node = 6, .coefficient = .{ .size_of_concrete_type = 3 } } }, .{ .add = .{ .left = 1, .right = 3 } }, .{ .add = .{ .left = 8, .right = 5 } }, .{ .add = .{ .left = 9, .right = 7 } }, }, .assertions = &.{.{ .scope = .closure_total, .measure = .retained, .relation = .exact, .expression = 10, }}, }, .overload = .{ .kind = .reject_before_seal, .detail = "nesting, arithmetic, OOM, stale facts, or definition drift rejects before activation", }, .risks = .{ .transitive = .{ .status = .open, .detail = "fingerprint reaches the indirect OperationPropertiesModel.getInherentAttr hook", }, .foreign = .{ .status = .open, .detail = "property hook implementations may reacquire allocator policy or cross foreign boundaries", }, }, .dependencies = &.{"choir.stable_value_numbering"}, .obligations = &.{ .{ .key = "hasher_capacity", .role = .capacity_model }, .{ .key = "hasher_sealed_fingerprint_transitive_risk", .role = .transitive_risk }, .{ .key = "hasher_sealed_fingerprint_foreign_risk", .role = .foreign_risk }, .{ .key = "hasher_oom_retry", .role = .overload }, .{ .key = "hasher_stale_limits", .role = .overload }, }, }, .bindings = .{ .owner = @This(), .seal = .{ .family = alloc_phase.capacity.selector(@This().activate), .premise = .{ .class = .checked_semantic_fact, .authority = .checker, }, }, .teardown = .{ .family = alloc_phase.capacity.selector(@This().deinit), .premise = .{ .class = .checked_semantic_fact, .authority = .checker, }, }, }, }; phase: alloc_phase.capacity.Phase, capacity: Capacity, numbering: numbering.StableValueNumbering, hasher: std.hash.Wyhash, definitions_seen: u64, pub const Limits = capacity_model.Limits; pub const Capacity = capacity_model.Capacity; const Self = @This(); pub fn init(allocator: Allocator, limits: Limits) !Self { const value_numbering = try numbering.StableValueNumbering.init(allocator, limits); return .{ .phase = .initialization, .capacity = value_numbering.capacity, .numbering = value_numbering, .hasher = std.hash.Wyhash.init(0), .definitions_seen = 0, }; } pub fn activate(self: *Self) error{ AlreadyActive, InputChanged }!void { if (self.phase != .initialization) return error.AlreadyActive; try self.numbering.activate(); self.phase = .steady; } pub fn fingerprint(self: *Self) u64 { self.requireSteady(); self.hasher = std.hash.Wyhash.init(0); self.definitions_seen = 0; var iterator = walk.Iterator.init( self.numbering.operation_frames, self.numbering.root, ); while (iterator.next()) |event| switch (event) { .operation => |operation| self.visitOperation(operation), .region => {}, .block => |block| self.visitBlock(block), }; if (self.definitions_seen != self.capacity.facts.value_count) { @panic("stable hashing definitions changed after activation"); } return self.hasher.final(); } pub fn deinit(self: *Self, allocator: Allocator) void { if (self.phase == .teardown) @panic("stable hasher teardown is terminal"); self.phase = .teardown; self.numbering.deinit(allocator); self.hasher = undefined; self.definitions_seen = undefined; } fn visitOperation(self: *Self, operation: *ir.Operation) void { self.update(operation.getName().name); var attrs = operation.getAttrs(); while (attrs.next()) |attr| { self.update(attr.name); self.visitAttribute(attr.value); } self.updateU64(operation.getNumResults()); for (operation.results.items) |*result| { self.visitType(result.type); self.registerValue(result); } self.updateU64(operation.getNumOperands()); for (operation.operands.items) |operand| self.visitValueUse(operand.value); self.updateU64(operation.getNumRegions()); } fn visitBlock(self: *Self, block: *ir.Block) void { self.updateU64(block.getNumArguments()); for (block.arguments.items) |argument| { self.visitType(argument.type); self.registerValue(argument); } } fn registerValue(self: *Self, value: *const ir.Value) void { const id = self.numbering.valueId(value) orelse @panic("stable hashing definition was not indexed"); if (id != self.definitions_seen) { @panic("stable hashing definition order changed after activation"); } self.definitions_seen += 1; } fn visitValueUse(self: *Self, value: *const ir.Value) void { if (self.numbering.valueId(value)) |id| { if (id < self.definitions_seen) { self.updateU64(id); return; } } self.updateU64(std.math.maxInt(u64)); } fn visitType(self: *Self, typ: ir.Type) void { if (typ.getDialectStorage()) |storage| { self.update(storage.name); if (storage.param_key.len > 0) self.update(storage.param_key); return; } self.updateU64(@intFromPtr(typ.impl)); } fn visitAttribute(self: *Self, root: ir.Attribute) void { var current: ?ir.Attribute = root; var depth: usize = 0; const frames = self.numbering.attribute_frames; while (true) { if (current) |attr| { self.update(attr.abstract.name); if (std.mem.eql(u8, attr.abstract.name, ir.builtin_attr_names.integer)) { if (attr.cast(ir.Attribute.IntegerAttr)) |integer| { self.updateU64(@bitCast(integer.value)); self.update(&[_]u8{integer.width}); self.update(&[_]u8{if (integer.is_signed) 1 else 0}); } else { self.updateU64(@intFromPtr(attr.impl)); } } else if (std.mem.eql(u8, attr.abstract.name, ir.builtin_attr_names.float_)) { if (attr.cast(ir.Attribute.FloatAttr)) |float| { self.updateU64(@bitCast(float.value)); self.update(&[_]u8{float.width}); } else { self.updateU64(@intFromPtr(attr.impl)); } } else if (std.mem.eql(u8, attr.abstract.name, ir.builtin_attr_names.bool_)) { if (attr.cast(ir.Attribute.BoolAttr)) |boolean| { self.update(&[_]u8{if (boolean.value) 1 else 0}); } else { self.updateU64(@intFromPtr(attr.impl)); } } else if (std.mem.eql(u8, attr.abstract.name, ir.builtin_attr_names.string)) { if (attr.cast(ir.Attribute.StringAttr)) |string| { self.update(string.value); } else { self.updateU64(@intFromPtr(attr.impl)); } } else if (std.mem.eql(u8, attr.abstract.name, ir.builtin_attr_names.symbol_ref)) { if (attr.cast(ir.Attribute.SymbolRefAttr)) |symbol| { self.updateU64(@intCast(symbol.root_reference.len)); self.update(symbol.root_reference); self.updateU64(@intCast(symbol.nested_references.len)); for (symbol.nested_references) |nested| { self.updateU64(@intCast(nested.len)); self.update(nested); } } else { self.updateU64(@intFromPtr(attr.impl)); } } else if (std.mem.eql(u8, attr.abstract.name, ir.builtin_attr_names.string_list)) { if (attr.cast(ir.Attribute.StringListAttr)) |list| { self.updateU64(@intCast(list.values.len)); for (list.values) |value| { self.updateU64(@intCast(value.len)); self.update(value); } } else { self.updateU64(@intFromPtr(attr.impl)); } } else if (std.mem.eql(u8, attr.abstract.name, ir.builtin_attr_names.type_list)) { if (attr.cast(ir.Attribute.TypeListAttr)) |list| { self.updateU64(@intCast(list.values.len)); for (list.values) |typ| self.visitType(typ); } else { self.updateU64(@intFromPtr(attr.impl)); } } else if (std.mem.eql(u8, attr.abstract.name, ir.builtin_attr_names.array)) { if (attr.cast(ir.Attribute.ArrayAttr)) |array| { self.updateU64(@intCast(array.values.len)); if (array.values.len > 0) { if (depth >= frames.len) { @panic("attribute traversal exceeded inspected depth"); } frames[depth] = .{ .values = array.values, .next_index = 1 }; depth += 1; current = array.values[0]; continue; } } else { self.updateU64(@intFromPtr(attr.impl)); } } else if (attr.cast(ir.Attribute.DialectAttr)) |dialect| { self.update(dialect.payload); } else { self.updateU64(@intFromPtr(attr.impl)); } } current = null; while (depth > 0) { const frame = &frames[depth - 1]; if (frame.next_index < frame.values.len) { current = frame.values[frame.next_index]; frame.next_index += 1; break; } depth -= 1; } if (current == null) return; } } fn update(self: *Self, bytes: []const u8) void { self.hasher.update(bytes); } fn updateU64(self: *Self, value: u64) void { self.hasher.update(std.mem.asBytes(&value)); } fn requireSteady(self: *const Self) void { if (self.phase != .steady) @panic("stable hasher used outside its steady phase"); }};Source: lib/choir/src/root.zig:63
zig
pub const StableHasher = product.StableHasher;Also reachable as
product.StableHasher, product.hashing.StableHasher.
Complete caller list for StableHasher.deinit
7 direct callers.
tiny.choir.product.hashing.operationFingerprint[function] atlib/choir/src/product/hashing/root.zig:20lib.choir.src.product.hashing.stable.checkStableHasherInitFailures[function] — private source atlib/choir/src/product/hashing/stable.zig:339in nearest public ownertiny.choir.product.hashing.stablelib.choir.src.product.hashing.stable.testFingerprint[function] — private source atlib/choir/src/product/hashing/stable.zig:331in nearest public ownertiny.choir.product.hashing.stablelib.choir.src.product.hashing.stable.test_stable_hasher_consumes_the_maximum_inspected_attribute_depth[function] — test source atlib/choir/src/product/hashing/stable.zig:710in nearest public ownertiny.choir.product.hashing.stablelib.choir.src.product.hashing.stable.test_stable_hasher_initialization_cleans_every_allocation_failure_and_retries[function] — test source atlib/choir/src/product/hashing/stable.zig:567in nearest public ownertiny.choir.product.hashing.stablelib.choir.src.product.hashing.stable.test_stable_hasher_preserves_the_established_empty-module_protocol[function] — test source atlib/choir/src/product/hashing/stable.zig:374in nearest public ownertiny.choir.product.hashing.stablelib.choir.src.product.hashing.stable.test_stable_hasher_rejects_definition_drift_before_activation[function] — test source atlib/choir/src/product/hashing/stable.zig:618in nearest public ownertiny.choir.product.hashing.stable
Complete caller list for StableHasher.init
9 direct callers.
tiny.choir.product.hashing.operationFingerprint[function] atlib/choir/src/product/hashing/root.zig:20lib.choir.src.product.hashing.stable.checkStableHasherInitFailures[function] — private source atlib/choir/src/product/hashing/stable.zig:339in nearest public ownertiny.choir.product.hashing.stablelib.choir.src.product.hashing.stable.testFingerprint[function] — private source atlib/choir/src/product/hashing/stable.zig:331in nearest public ownertiny.choir.product.hashing.stablelib.choir.src.product.hashing.stable.test_stable_hasher_consumes_the_maximum_inspected_attribute_depth[function] — test source atlib/choir/src/product/hashing/stable.zig:710in nearest public ownertiny.choir.product.hashing.stablelib.choir.src.product.hashing.stable.test_stable_hasher_initialization_cleans_every_allocation_failure_and_retries[function] — test source atlib/choir/src/product/hashing/stable.zig:567in nearest public ownertiny.choir.product.hashing.stablelib.choir.src.product.hashing.stable.test_stable_hasher_preserves_the_established_empty-module_protocol[function] — test source atlib/choir/src/product/hashing/stable.zig:374in nearest public ownertiny.choir.product.hashing.stablelib.choir.src.product.hashing.stable.test_stable_hasher_rejects_definition_drift_before_activation[function] — test source atlib/choir/src/product/hashing/stable.zig:618in nearest public ownertiny.choir.product.hashing.stablelib.choir.src.product.hashing.stable.test_stable_hasher_rejects_stale_limits_before_allocation[function] — test source atlib/choir/src/product/hashing/stable.zig:637in nearest public ownertiny.choir.product.hashing.stablelib.choir.src.product.hashing.stable.test_stable_hasher_seals_exact_backing_before_its_first_fingerprint[function] — test source atlib/choir/src/product/hashing/stable.zig:398in nearest public ownertiny.choir.product.hashing.stable
Audit
| Definitions | 8 |
|---|---|
| Public names | 32 |
| Members | 5 |
| Version | 26.7.0 |
| Revision | daab053ee433 |