lib/accy/src/executable/composition/artifact.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const choir = @import("choir");
3 const accy_root = @import("../../root.zig");
4 const executable = @import("../root.zig");
5 const composition = @import("root.zig");
6
7 const Allocator = std.mem.Allocator;
8 const Artifact = choir.backends.artifact.Artifact;
9
10 pub const BoundaryMetadata = struct {
11 element_type: choir.composition.ElementType,
12 dimensions: []const u64,
13 byte_size: usize,
14
15 fn deinit(self: *BoundaryMetadata, allocator: Allocator) void {
16 if (self.dimensions.len != 0) allocator.free(@constCast(self.dimensions));
17 self.* = undefined;
18 }
19 };
20
21 pub const CpuObjectMetadata = struct {
22 allocator: Allocator,
23 fingerprint: u64,
24 artifact_fingerprint: u64,
25 artifacts: []Artifact,
26 input_boundaries: []BoundaryMetadata,
27 output_boundaries: []BoundaryMetadata,
28
29 pub fn init(
30 allocator: Allocator,
31 fragment: *const executable.CompiledFragment,
32 ) !CpuObjectMetadata {
33 const plan = fragment.artifactPlan();
34 if (plan.kernels.items.len == 0) return error.InvalidArtifact;
35 const artifacts = try allocator.alloc(Artifact, plan.kernels.items.len);
36 var artifact_count: usize = 0;
37 errdefer {
38 var index = artifact_count;
39 while (index != 0) {
40 index -= 1;
41 artifacts[index].deinit();
42 }
43 allocator.free(artifacts);
44 }
45
46 for (plan.kernels.items, 0..) |kernel, index| {
47 if (kernel.artifact.backend != .cpu or kernel.artifact.format != .cpu_object) return error.InvalidArtifact;
48 const object = switch (kernel.artifact.payload) {
49 .bytes => |bytes| bytes,
50 else => return error.InvalidArtifact,
51 };
52 var artifact = try choir.backends.artifact.objectFileArtifact(
53 allocator,
54 .{
55 .architecture = .x86_64,
56 .triple = "x86_64-unknown-linux-gnu",
57 .cpu = "x86-64",
58 },
59 .{
60 .name = "sysv",
61 .calling_convention = "c",
62 .object_format = "elf",
63 .pointer_width_bits = 64,
64 .endianness = .little,
65 },
66 kernel.artifact.entry_name,
67 object,
68 &.{},
69 );
70 errdefer artifact.deinit();
71 try artifact.options.add(.{ .key = "accy.kernel_id", .value = .{ .unsigned = kernel.kernel_id } });
72 try artifact.options.add(.{ .key = "accy.work_item_id", .value = .{ .unsigned = kernel.work_item_id } });
73 try artifact.options.add(.{ .key = "accy.argument_count", .value = .{ .unsigned = kernel.artifact.argument_count } });
74 try artifact.options.add(.{ .key = "accy.scalar_argument_count", .value = .{ .unsigned = kernel.artifact.scalar_argument_count } });
75 try artifact.verification.replace(artifact.allocator, .{ .state = .passed, .stage = "accy.cpu_object" });
76 artifacts[index] = artifact;
77 artifact_count += 1;
78 }
79
80 const input_boundaries = try retainBoundaries(allocator, plan, plan.input_slot_ids);
81 errdefer deinitBoundaries(allocator, input_boundaries);
82 const output_boundaries = try retainBoundaries(allocator, plan, plan.output_slot_ids);
83 errdefer deinitBoundaries(allocator, output_boundaries);
84
85 return .{
86 .allocator = allocator,
87 .fingerprint = fragment.fingerprint(),
88 .artifact_fingerprint = fragment.artifactFingerprint(),
89 .artifacts = artifacts,
90 .input_boundaries = input_boundaries,
91 .output_boundaries = output_boundaries,
92 };
93 }
94
95 pub fn entrySymbol(self: *const CpuObjectMetadata) ?[]const u8 {
96 for (self.artifacts) |artifact| {
97 if (artifact.linkage.provided_symbols.items.len != 0) return artifact.linkage.provided_symbols.items[0].name;
98 }
99 return null;
100 }
101
102 pub fn deinit(self: *CpuObjectMetadata) void {
103 var artifact_index = self.artifacts.len;
104 while (artifact_index != 0) {
105 artifact_index -= 1;
106 self.artifacts[artifact_index].deinit();
107 }
108 deinitBoundaries(self.allocator, self.output_boundaries);
109 deinitBoundaries(self.allocator, self.input_boundaries);
110 self.allocator.free(self.artifacts);
111 self.* = undefined;
112 }
113 };
114
115 fn retainBoundaries(
116 allocator: Allocator,
117 plan: *const accy_root.artifact.BackendArtifactPlan,
118 slot_ids: []const usize,
119 ) ![]BoundaryMetadata {
120 const boundaries = try allocator.alloc(BoundaryMetadata, slot_ids.len);
121 var initialized: usize = 0;
122 errdefer {
123 var index = initialized;
124 while (index != 0) {
125 index -= 1;
126 boundaries[index].deinit(allocator);
127 }
128 allocator.free(boundaries);
129 }
130 for (slot_ids, 0..) |slot_id, index| {
131 const slot = plan.slotById(slot_id) orelse return error.InvalidArtifact;
132 const byte_size = std.math.cast(usize, slot.byte_size orelse return error.UnsupportedOperation) orelse return error.InvalidArtifact;
133 if (byte_size == 0) return error.UnsupportedOperation;
134 const dimensions: []u64 = if (slot.dims.len == 0) &.{} else try allocator.alloc(u64, slot.dims.len);
135 errdefer if (dimensions.len != 0) allocator.free(dimensions);
136 for (slot.dims, dimensions) |dimension, *retained| {
137 retained.* = std.math.cast(u64, dimension) orelse return error.UnsupportedOperation;
138 if (retained.* == 0) return error.UnsupportedOperation;
139 }
140 const element_type = composition.elementType(slot.dtype);
141 const static_byte_size = choir.composition.source.boundary.staticByteSize(element_type, dimensions) orelse return error.InvalidArtifact;
142 if (static_byte_size != byte_size) return error.InvalidArtifact;
143 boundaries[index] = .{
144 .element_type = element_type,
145 .dimensions = dimensions,
146 .byte_size = byte_size,
147 };
148 initialized += 1;
149 }
150 return boundaries;
151 }
152
153 fn deinitBoundaries(allocator: Allocator, boundaries: []BoundaryMetadata) void {
154 var index = boundaries.len;
155 while (index != 0) {
156 index -= 1;
157 boundaries[index].deinit(allocator);
158 }
159 allocator.free(boundaries);
160 }