tiny.choir.backends.wasm.emission.plan
Defined in backends.wasm.emission.
API (21)
Actions
Public operations.
Plan.bodyBytesPlan.definitionCountPlan.functionCountPlan.functionIndexPlan.importCountPlan.localCountPlan.localForPlan.localPlansPlan.needsMemoryindex.clearindex.findFunctionindex.findLocalindex.insertFunctionindex.insertValueindex.slotsForindex.storageIsValid
Types and contracts
Public types and contracts.
Namespaces
Public namespaces.
Source
Source: lib/choir/src/backends/wasm/emission/plan/index.zig
zig
const std = @import("std");const ir = @import("../../../../core/root.zig");const plan = @import("root.zig");const empty = std.math.maxInt(u32);const pointer_hash_multiplier: usize = switch (@bitSizeOf(usize)) { 32 => 0x9e3779b9, 64 => 0x9e3779b97f4a7c15, else => @compileError("unsupported pointer width"),};pub fn slotsFor(count: usize) error{CapacityOverflow}!usize { if (count == 0) return 0; const scaled = std.math.mul(usize, count, 4) catch return error.CapacityOverflow; const rounded = std.math.add(usize, scaled, 2) catch return error.CapacityOverflow; const minimum = rounded / 3; return std.math.ceilPowerOfTwo(usize, @max(@as(usize, 4), minimum)) catch error.CapacityOverflow;}pub fn storageIsValid(entry_count: usize, slots: []const u32) bool { const required = slotsFor(entry_count) catch return false; return slots.len == required;}pub fn clear(slots: []u32) void { @memset(slots, empty);}pub fn insertFunction( slots: []u32, functions: []const plan.FunctionPlan, ordinal: u32,) error{ CodeGenFailed, InputChanged }!void { var slot = hashBytes(functions[ordinal].name) & (slots.len - 1); for (0..slots.len) |_| { const existing = slots[slot]; if (existing == empty) { slots[slot] = ordinal; return; } if (std.mem.eql(u8, functions[existing].name, functions[ordinal].name)) { return error.CodeGenFailed; } slot = advance(slot, slots.len); } return error.InputChanged;}pub fn insertValue( slots: []u32, values: []const plan.ValuePlan, value_index: u32,) error{ CodeGenFailed, InputChanged }!void { var slot = hashPointer(values[value_index].value) & (slots.len - 1); for (0..slots.len) |_| { const existing = slots[slot]; if (existing == empty) { slots[slot] = value_index; return; } if (values[existing].value == values[value_index].value) { return error.CodeGenFailed; } slot = advance(slot, slots.len); } return error.InputChanged;}pub fn findFunction( slots: []const u32, functions: []const plan.FunctionPlan, name: []const u8,) ?u32 { if (slots.len == 0) return null; var slot = hashBytes(name) & (slots.len - 1); for (0..slots.len) |_| { const ordinal = slots[slot]; if (ordinal == empty) return null; const entry = functions[ordinal]; if (std.mem.eql(u8, name, entry.name)) return entry.function_index; slot = advance(slot, slots.len); } return null;}pub fn findLocal( slots: []const u32, values: []const plan.ValuePlan, function_plan: plan.FunctionPlan, value: *ir.Value,) ?u32 { if (slots.len == 0) return null; const start: usize = function_plan.value_start; const end = start + function_plan.value_count; var slot = hashPointer(value) & (slots.len - 1); for (0..slots.len) |_| { const value_index = slots[slot]; if (value_index == empty) return null; const entry = values[value_index]; if (entry.value == value) { if (value_index < start or value_index >= end) return null; return entry.local_index; } slot = advance(slot, slots.len); } return null;}fn advance(slot: usize, slot_count: usize) usize { return (slot + 1) & (slot_count - 1);}fn hashBytes(bytes: []const u8) usize { return @truncate(std.hash.Wyhash.hash(0, bytes));}fn hashPointer(value: *const ir.Value) usize { return hashAddress(@intFromPtr(value));}fn hashAddress(address: usize) usize { return std.math.rotl( usize, address *% pointer_hash_multiplier, @bitSizeOf(usize) / 2, );}test "WASM plan index capacity preserves its load bound" { try std.testing.expectEqual(@as(usize, 0), try slotsFor(0)); try std.testing.expectEqual(@as(usize, 4), try slotsFor(1)); try std.testing.expectEqual(@as(usize, 4), try slotsFor(3)); try std.testing.expectEqual(@as(usize, 8), try slotsFor(4));}test "WASM plan index storage requires the exact derived capacity" { var four: [4]u32 = undefined; var eight: [8]u32 = undefined; try std.testing.expect(storageIsValid(0, &.{})); try std.testing.expect(storageIsValid(1, &four)); try std.testing.expect(storageIsValid(3, &four)); try std.testing.expect(storageIsValid(4, &eight)); try std.testing.expect(!storageIsValid(0, &four)); try std.testing.expect(!storageIsValid(1, &eight)); try std.testing.expect(!storageIsValid(4, &four)); try std.testing.expect(!storageIsValid(std.math.maxInt(usize), &.{}));}test "WASM plan index capacity rejects overflow" { try std.testing.expectError(error.CapacityOverflow, slotsFor(std.math.maxInt(usize)));}test "WASM plan indices probe through function and value collisions" { const names = [_][]const u8{ "a", "b", "c", "d", "e" }; var first_name_by_slot = @as([4]?usize, @splat(null)); var name_pair: ?[2]usize = null; for (names, 0..) |name, name_index| { const slot = hashBytes(name) & 3; if (first_name_by_slot[slot]) |first_name| { name_pair = .{ first_name, name_index }; break; } first_name_by_slot[slot] = name_index; } const colliding_names = name_pair.?; const functions = [_]plan.FunctionPlan{ .{ .name = names[colliding_names[0]], .value_start = 0, .value_count = 0, .param_count = 0, .local_count = 0, .function_index = 7, .body_bytes = 0, }, .{ .name = names[colliding_names[1]], .value_start = 0, .value_count = 0, .param_count = 0, .local_count = 0, .function_index = 11, .body_bytes = 0, }, }; var function_slots: [4]u32 = undefined; clear(&function_slots); try insertFunction(&function_slots, &functions, 0); try insertFunction(&function_slots, &functions, 1); try std.testing.expectEqual(@as(?u32, 7), findFunction(&function_slots, &functions, functions[0].name)); try std.testing.expectEqual(@as(?u32, 11), findFunction(&function_slots, &functions, functions[1].name)); var raw_values: [5]ir.Value = undefined; var first_value_by_slot = @as([4]?usize, @splat(null)); var value_pair: ?[2]usize = null; for (&raw_values, 0..) |*value, value_index| { const slot = hashPointer(value) & 3; if (first_value_by_slot[slot]) |first_value| { value_pair = .{ first_value, value_index }; break; } first_value_by_slot[slot] = value_index; } const colliding_values = value_pair.?; const values = [_]plan.ValuePlan{ .{ .value = &raw_values[colliding_values[0]], .local_index = 3, .value_type = .i32, }, .{ .value = &raw_values[colliding_values[1]], .local_index = 5, .value_type = .i32, }, }; var value_slots: [4]u32 = undefined; clear(&value_slots); try insertValue(&value_slots, &values, 0); try insertValue(&value_slots, &values, 1); const function_plan = plan.FunctionPlan{ .name = "values", .value_start = 0, .value_count = 2, .param_count = 0, .local_count = 2, .function_index = 0, .body_bytes = 0, }; try std.testing.expectEqual( @as(?u32, 3), findLocal(&value_slots, &values, function_plan, values[0].value), ); try std.testing.expectEqual( @as(?u32, 5), findLocal(&value_slots, &values, function_plan, values[1].value), );}test "WASM value pointer hash disperses aligned addresses" { const slot_count = 512; var values: [256]ir.Value = undefined; var value_loads = @as([slot_count]u8, @splat(0)); for (&values) |*value| { value_loads[hashPointer(value) & (slot_count - 1)] += 1; } for (value_loads) |load| try std.testing.expect(load <= 2); for ([_]usize{ 8, 16, 32, 64, 128 }) |stride| { var loads = @as([slot_count]u8, @splat(0)); for (0..256) |index| { const address = 0x1000 + index * stride; loads[hashAddress(address) & (slot_count - 1)] += 1; } for (loads) |load| try std.testing.expect(load <= 2); }}Source: lib/choir/src/backends/wasm/emission/plan/root.zig
zig
const binary = @import("../../binary/root.zig");const ir = @import("../../../../core/root.zig");const emission = @import("../root.zig");pub const index = @import("index.zig");pub const planning = @import("planning.zig");pub const FunctionPlan = struct { name: []const u8, value_start: u32, value_count: u32, param_count: u32, local_count: u32, function_index: u32, body_bytes: usize,};pub const ValuePlan = struct { value: *ir.Value, local_index: u32, value_type: binary.ValueType,};pub const Plan = struct { functions: []FunctionPlan, function_index: []u32, values: []ValuePlan, value_index: []u32, import_count: usize, definition_count: usize, needs_memory: bool, sections: emission.SectionSizes, pub fn functionIndex(self: *const Plan, name: []const u8) ?u32 { return index.findFunction(self.function_index, self.functions, name); } pub fn localFor(self: *const Plan, function_ordinal: usize, value: *ir.Value) ?u32 { if (function_ordinal >= self.functions.len) return null; return index.findLocal( self.value_index, self.values, self.functions[function_ordinal], value, ); } pub fn localPlans(self: *const Plan, function_ordinal: usize) []const ValuePlan { const function_plan = self.functions[function_ordinal]; const start: usize = function_plan.value_start + function_plan.param_count; const end = start + function_plan.local_count; return self.values[start..end]; } pub fn functionCount(self: *const Plan) usize { return self.functions.len; } pub fn importCount(self: *const Plan) usize { return self.import_count; } pub fn definitionCount(self: *const Plan) usize { return self.definition_count; } pub fn localCount(self: *const Plan, function_ordinal: usize) usize { return self.functions[function_ordinal].local_count; } pub fn bodyBytes(self: *const Plan, function_ordinal: usize) usize { return self.functions[function_ordinal].body_bytes; } pub fn needsMemory(self: *const Plan) bool { return self.needs_memory; }};Source: lib/choir/src/backends/wasm/emission/root.zig:10
zig
pub const plan = @import("plan/root.zig");Audit
| Definitions | 21 |
|---|---|
| Public names | 33 |
| Members | 18 |
| Version | 26.7.0 |
| Revision | daab053ee433 |