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 }