tiny.choir.backends.aarch64.placement
Defined in backends.aarch64.
API (17)
Actions
Public operations.
Plan.build: A fixed traversal stack linearizes regions without host recursion.Plan.frameBytesPlan.getPlan.storageBytes
Types and contracts
Public types and contracts.
ErrorEventEvent.KindLocationPlan: One immutable home per SSA result.
Values and defaults
Public values and defaults.
max_eventsmax_frame_slotsmax_live_valuesmax_operationsmax_registersmax_valuesregistersslot_bytes
Source
Source: lib/choir/src/backends/aarch64/placement.zig
zig
const std = @import("std");const ir = @import("../../core/root.zig");const shared = @import("../root.zig").regalloc;const control = @import("root.zig").control;const GPR = @import("root.zig").GPR;pub const max_registers: usize = 7;pub const max_frame_slots: usize = 64;pub const max_live_values: usize = max_registers + max_frame_slots;pub const max_values: usize = 256;pub const max_operations: usize = 1024;pub const max_events: usize = 3 * max_operations;pub const Event = struct { pub const Kind = enum { operation, next_region, end }; kind: Kind, op: *ir.Operation, depth: usize,};pub const slot_bytes: usize = 8;pub const registers = [max_registers]GPR{ .x9, .x10, .x11, .x12, .x13, .x14, .x15 };pub const Location = union(enum) { register: GPR, stack: u16, dead };pub const Error = control.Error || error{ ValueCapacity, FrameCapacity, OperationCapacity, MissingValue };const Entry = struct { value: *ir.Value, owner: *ir.Operation, retained: bool, range: shared.IntervalRange, location: Location = .dead,};/// One immutable home per SSA result. Arguments retain their incoming homes.pub const Plan = struct { entries: [max_values]Entry = undefined, count: usize = 0, events: [max_events]Event = undefined, event_count: usize = 0, loops: [max_operations]shared.LoopInterval = undefined, loop_count: usize = 0, frame_slots: usize = 0, entry_block: ?*ir.Block = null, refused_operation: ?*ir.Operation = null, pub fn storageBytes() usize { return @sizeOf(Plan); } pub fn frameBytes(self: *const Plan) usize { std.debug.assert(self.frame_slots <= max_frame_slots); return std.mem.alignForward(usize, self.frame_slots * slot_bytes, 16); } pub fn get(self: *const Plan, value: *ir.Value) ?Location { std.debug.assert(self.count <= max_values); for (self.entries[0..self.count]) |entry| { if (entry.value == value) return entry.location; } return null; } /// A fixed traversal stack linearizes regions without host recursion. /// Once loop boundaries exist, each actual source use reaches the shared /// loop extension through one borrowed record, without allocation. pub fn build(self: *Plan, block: *ir.Block) Error!void { self.* = .{ .entry_block = block }; const Frame = struct { next: ?*ir.Operation, owner: ?*ir.Operation = null, region: usize = 0, entry: u32 = 0 }; var frames: [control.max_depth + 1]Frame = undefined; frames[0] = .{ .next = first(block) }; var depth: usize = 0; var operations: usize = 0; for (0..max_events) |_| { const frame = &frames[depth]; if (frame.next) |op| { self.refused_operation = op; if (operations == max_operations) return error.OperationCapacity; operations += 1; frame.next = op.next_op; const position = self.appendEvent(.operation, op, depth); if (control.kind(op)) |construct| { if (depth == control.max_depth) return error.DepthCapacity; try control.validate(op, construct); for (op.results.items) |*value| try self.define(value, op, position, true); for (op.regions.items) |*region| { for (region.getEntryBlock().?.arguments.items) |arg| try self.define(arg, op, position, true); } depth += 1; frames[depth] = .{ .next = first(op.regions.items[0].getEntryBlock().?), .owner = op, .entry = position + 1 }; } else { if (op.regions.items.len != 0) return error.ControlShape; for (op.results.items) |*value| try self.define(value, op, position, false); } } else if (frame.owner) |owner| { self.refused_operation = owner; frame.region += 1; if (frame.region < owner.regions.items.len) { _ = self.appendEvent(.next_region, owner, depth - 1); frame.next = first(owner.regions.items[frame.region].getEntryBlock().?); } else { const position = self.appendEvent(.end, owner, depth - 1); if (control.kind(owner).? != .conditional) { std.debug.assert(self.loop_count < max_operations); self.loops[self.loop_count] = .{ .entry = frame.entry, .trailing = position }; self.loop_count += 1; } for (owner.results.items) |*value| try self.use(value, position); for (owner.regions.items) |*region| { for (region.getEntryBlock().?.arguments.items) |arg| try self.use(arg, position); } if (control.kind(owner).? == .counted) { try self.use(owner.operands.items[1].value, position); try self.use(owner.operands.items[2].value, position); } depth -= 1; } } else break; } else unreachable; for (self.events[0..self.event_count], 0..) |event, position| { if (event.kind != .operation) continue; self.refused_operation = event.op; for (event.op.operands.items) |operand| try self.use(operand.value, @intCast(position)); } try self.place(); self.refused_operation = null; } fn first(block: *ir.Block) ?*ir.Operation { return if (block.operations.head) |op| @ptrCast(@alignCast(op)) else null; } fn appendEvent(self: *Plan, kind: Event.Kind, op: *ir.Operation, depth: usize) u32 { std.debug.assert(self.event_count < max_events); const position = self.event_count; self.events[position] = .{ .kind = kind, .op = op, .depth = depth }; self.event_count += 1; return @intCast(position); } fn define(self: *Plan, value: *ir.Value, owner: *ir.Operation, position: u32, retained: bool) Error!void { if (self.count == max_values) return error.ValueCapacity; self.entries[self.count] = .{ .value = value, .owner = owner, .retained = retained, .range = .{ .start = position, .end = position, .end_phase = .definition }, }; self.count += 1; } fn use(self: *Plan, value: *ir.Value, position: u32) Error!void { const entry = self.find(value) orelse { if (value.getOwnerBlock() == @as(*anyopaque, self.entry_block.?)) return; return error.MissingValue; }; if (entry.range.start > position) return error.MissingValue; if (position >= entry.range.end) { entry.range.end = position; entry.range.end_phase = .source; } var use_storage = [_]shared.UsePosition(GPR, u32){.{ .point = shared.PositionPoint.source(position), .requirement = .any, .source_blockers = 0 }}; var candidate = [_]shared.Candidate(GPR, u32){.{ .value = value, .range = entry.range, .use_positions = .{ .items = &use_storage, .capacity = use_storage.len }, .definition = .{ .point = shared.PositionPoint.definition(entry.range.start), .requirement = .any, .source = .any }, .order = 0, .is_constant = false, }}; shared.extendAcrossLoops(GPR, u32, &candidate, self.loops[0..self.loop_count]); entry.range = candidate[0].range; } fn find(self: *Plan, value: *ir.Value) ?*Entry { for (self.entries[0..self.count]) |*entry| { if (entry.value == value) return entry; } return null; } /// Shared interval expiration and register selection borrow stack storage. fn place(self: *Plan) Error!void { var active_storage: [max_registers]shared.Active(GPR) = undefined; var active: shared.ActiveSet(GPR) = .{ .items = .{ .items = active_storage[0..0], .capacity = active_storage.len }, }; var stack_ends: [max_frame_slots]?shared.PositionPoint = @splat(null); for (self.entries[0..self.count], 0..) |*entry, index| { if (!entry.retained and entry.value.hasNoUses()) continue; self.refused_operation = entry.owner; const start = shared.PositionPoint.definition(entry.range.start); const end: shared.PositionPoint = .{ .position = entry.range.end, .phase = entry.range.end_phase }; for (0..max_registers) |_| { if (active.takeExpiredBefore(start) == null) break; } if (shared.firstAvailableRegister(GPR, active, ®isters, Policy{})) |register| { std.debug.assert(active.items.items.len < max_registers); active.items.appendAssumeCapacity(.{ .start = entry.range.start, .end = entry.range.end, .end_phase = entry.range.end_phase, .reg = register, .candidate_index = index, }); entry.location = .{ .register = register }; continue; } var placed = false; for (&stack_ends, 0..) |*last, slot| { if (last.*) |point| if (!point.lessThan(start)) continue; last.* = end; entry.location = .{ .stack = @intCast(slot) }; self.frame_slots = @max(self.frame_slots, slot + 1); placed = true; break; } if (!placed) return error.FrameCapacity; } }};const Policy = struct { pub fn blocksRegister(_: Policy, _: GPR) bool { return false; }};Source: lib/choir/src/backends/aarch64/root.zig:5
zig
pub const placement = @import("placement.zig");Complete call list for backends.aarch64.placement.Plan.build
7 direct calls.
tiny.choir.backends.aarch64.control.kind[function] atlib/choir/src/backends/aarch64/control.zig:12tiny.choir.backends.aarch64.control.validate[function] atlib/choir/src/backends/aarch64/control.zig:33lib.choir.src.backends.aarch64.placement.Plan.appendEvent[method] — private source atlib/choir/src/backends/aarch64/placement.zig:131in nearest public ownertiny.choir.backends.aarch64.placementlib.choir.src.backends.aarch64.placement.Plan.define[method] — private source atlib/choir/src/backends/aarch64/placement.zig:139in nearest public ownertiny.choir.backends.aarch64.placementlib.choir.src.backends.aarch64.placement.Plan.first[function] — private source atlib/choir/src/backends/aarch64/placement.zig:127in nearest public ownertiny.choir.backends.aarch64.placementlib.choir.src.backends.aarch64.placement.Plan.place[method] — private source atlib/choir/src/backends/aarch64/placement.zig:181in nearest public ownertiny.choir.backends.aarch64.placementlib.choir.src.backends.aarch64.placement.Plan.use[method] — private source atlib/choir/src/backends/aarch64/placement.zig:150in nearest public ownertiny.choir.backends.aarch64.placement
Audit
| Definitions | 18 |
|---|---|
| Public names | 18 |
| Members | 18 |
| Version | 26.7.0 |
| Revision | daab053ee433 |