lib/accy/src/preparation/run.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const choir = @import("choir");
3 const sys = @import("sys");
4
5 const stage = @import("stage.zig");
6 const target_profile = @import("target.zig");
7
8 const passes = choir.passes;
9
10 pub const BackendPreparationStats = struct {
11 pass_runs: u64 = 0,
12 pass_failures: u64 = 0,
13 passes_modified: u64 = 0,
14 analysis_hits: u64 = 0,
15 analysis_misses: u64 = 0,
16 analyses_invalidated: u64 = 0,
17 };
18
19 pub const BackendPreparationFailureKind = passes.PassFailureKind;
20
21 pub const BackendPreparationFailure = struct {
22 pipeline_name: ?[]const u8 = null,
23 failure_kind: ?BackendPreparationFailureKind = null,
24 pass_name: ?[]const u8 = null,
25 target_op_name: ?[]const u8 = null,
26 target_symbol_name: ?[]const u8 = null,
27 verifier_error_name: ?[]const u8 = null,
28 worker_count: usize = 0,
29
30 pub fn deinit(self: *BackendPreparationFailure, allocator: std.mem.Allocator) void {
31 if (self.pass_name) |name| allocator.free(name);
32 if (self.target_op_name) |name| allocator.free(name);
33 if (self.target_symbol_name) |name| allocator.free(name);
34 if (self.verifier_error_name) |name| allocator.free(name);
35 self.* = .{};
36 }
37
38 pub fn capture(
39 self: *BackendPreparationFailure,
40 allocator: std.mem.Allocator,
41 pipeline_name: []const u8,
42 reproducer: *const passes.PassFailureReproducer,
43 ) !void {
44 self.deinit(allocator);
45 errdefer self.deinit(allocator);
46
47 self.pipeline_name = pipeline_name;
48 self.failure_kind = reproducer.failure_kind;
49 self.worker_count = reproducer.worker_count;
50 self.pass_name = try dupeOptional(allocator, reproducer.pass_name);
51 self.target_op_name = try dupeOptional(allocator, reproducer.target_op_name);
52 self.target_symbol_name = try dupeOptional(allocator, reproducer.target_symbol_name);
53 self.verifier_error_name = try dupeOptional(allocator, reproducer.verifier_error_name);
54 }
55
56 fn dupeOptional(allocator: std.mem.Allocator, value: ?[]const u8) !?[]const u8 {
57 if (value) |text| return try allocator.dupe(u8, text);
58 return null;
59 }
60 };
61
62 pub const BackendPreparationTiming = struct {
63 inner: passes.TimingInstrumentation,
64
65 pub const TimingOptions: type = passes.PassTimingOptions;
66 pub const AllocationSnapshot: type = passes.PassAllocationSnapshot;
67 pub const AllocationSnapshotProvider: type =
68 passes.PassAllocationSnapshotProvider;
69
70 pub fn init(allocator: std.mem.Allocator) BackendPreparationTiming {
71 return .{ .inner = passes.TimingInstrumentation.init(allocator) };
72 }
73
74 pub fn initWithOptions(allocator: std.mem.Allocator, options: TimingOptions) BackendPreparationTiming {
75 return .{ .inner = passes.TimingInstrumentation.initWithOptions(allocator, options) };
76 }
77
78 pub fn deinit(self: *BackendPreparationTiming) void {
79 self.inner.deinit();
80 self.* = undefined;
81 }
82
83 fn instrumentation(self: *BackendPreparationTiming) passes.PassInstrumentation {
84 return self.inner.instrumentation();
85 }
86
87 pub fn getPassTime(self: *const BackendPreparationTiming, name: []const u8) ?i128 {
88 return self.inner.getPassTime(name);
89 }
90
91 pub fn getPassCount(self: *const BackendPreparationTiming, name: []const u8) ?u64 {
92 return self.inner.getPassCount(name);
93 }
94
95 pub fn getPassOpCountBefore(self: *const BackendPreparationTiming, name: []const u8) ?u64 {
96 return self.inner.getPassOpCountBefore(name);
97 }
98
99 pub fn getPassOpCountAfter(self: *const BackendPreparationTiming, name: []const u8) ?u64 {
100 return self.inner.getPassOpCountAfter(name);
101 }
102
103 pub fn getPassOpCountDelta(self: *const BackendPreparationTiming, name: []const u8) ?i128 {
104 return self.inner.getPassOpCountDelta(name);
105 }
106
107 pub fn setAllocationSnapshotProvider(
108 self: *BackendPreparationTiming,
109 provider: ?AllocationSnapshotProvider,
110 ) void {
111 self.inner.setAllocationSnapshotProvider(provider);
112 }
113
114 pub fn getPassAllocCount(self: *const BackendPreparationTiming, name: []const u8) ?u64 {
115 return self.inner.getPassAllocCount(name);
116 }
117
118 pub fn getPassFreeCount(self: *const BackendPreparationTiming, name: []const u8) ?u64 {
119 return self.inner.getPassFreeCount(name);
120 }
121
122 pub fn getPassAllocBytes(self: *const BackendPreparationTiming, name: []const u8) ?u64 {
123 return self.inner.getPassAllocBytes(name);
124 }
125
126 pub fn getAnalysisTime(self: *const BackendPreparationTiming, name: []const u8) ?i128 {
127 return self.inner.getAnalysisTime(name);
128 }
129
130 pub fn getAnalysisCount(self: *const BackendPreparationTiming, name: []const u8) ?u64 {
131 return self.inner.getAnalysisCount(name);
132 }
133
134 pub fn collectsPassIrSizes(self: *const BackendPreparationTiming) bool {
135 return self.inner.collectsPassIrSizes();
136 }
137 };
138
139 pub const BackendPreparationRunOptions = struct {
140 timing: ?*BackendPreparationTiming = null,
141 failure: ?*BackendPreparationFailure = null,
142 target_profile: ?target_profile.BackendTargetProfile = null,
143 generated_scan_schedules: []const target_profile.GeneratedScanScheduleDecision = &.{},
144 generated_row_pipeline_schedules: []const target_profile.GeneratedRowPipelineScheduleDecision = &.{},
145 tensor: stage.TensorLoweringOptions = .{},
146 now: *const fn () i128 = sys.time.nanoTimestamp,
147 };
148
149 pub const BackendPreparationRun = struct {
150 total_ns: u64,
151 contract_ns: u64 = 0,
152 tensor_ns: u64 = 0,
153 dispatch_ns: u64 = 0,
154 memory_ns: u64 = 0,
155 kernel_ns: u64 = 0,
156 target_ns: u64 = 0,
157 initial_choir_ops: u64,
158 final_choir_ops: u64,
159 semantic_fingerprint: ?u64 = null,
160 contract_fingerprint: u64 = 0,
161 tensor_fingerprint: u64 = 0,
162 /// A caller reads this field to show whether the dispatch plans of two runs look alike. The
163 /// field holds a 64-bit summary of the plans made by the dispatch stage, zero when the stage
164 /// has not run. It is for display, and no product key or reuse decision reads it.
165 dispatch_fingerprint: u64 = 0,
166 /// A caller reads this field to show whether the memory plans of two runs look alike. The field
167 /// holds a 64-bit summary of the buffer, memory-space and layout plans made by the memory
168 /// stage, zero when the stage has not run. It is for display, and no product key or reuse
169 /// decision reads it.
170 memory_fingerprint: u64 = 0,
171 /// A caller reads this field to show whether the kernel plans of two runs look alike. The field
172 /// holds a 64-bit summary of the kernel outline and generated kernels made by the kernel stage,
173 /// zero when the stage has not run. It is for display, and no product key or reuse decision
174 /// reads it.
175 kernel_fingerprint: u64 = 0,
176 target_fingerprint: u64 = 0,
177 contract_stats: BackendPreparationStats = .{},
178 tensor_stats: BackendPreparationStats = .{},
179 dispatch_stats: BackendPreparationStats = .{},
180 memory_stats: BackendPreparationStats = .{},
181 kernel_stats: BackendPreparationStats = .{},
182 target_stats: BackendPreparationStats = .{},
183 target_profile: ?target_profile.BackendTargetProfile = null,
184 };
185
186 pub fn addTimingInstrumentation(pm: *passes.PassManager, timing: *BackendPreparationTiming) !void {
187 try pm.addInstrumentation(timing.instrumentation());
188 }
189
190 pub fn backendPreparationStats(stats: passes.PassManagerStats) BackendPreparationStats {
191 return .{
192 .pass_runs = stats.pass_runs,
193 .pass_failures = stats.pass_failures,
194 .passes_modified = stats.passes_modified,
195 .analysis_hits = stats.analysis_hits,
196 .analysis_misses = stats.analysis_misses,
197 .analyses_invalidated = stats.analyses_invalidated,
198 };
199 }
200
201 pub fn stageTotal(run: BackendPreparationRun) u64 {
202 return run.contract_ns +|
203 run.tensor_ns +|
204 run.dispatch_ns +|
205 run.memory_ns +|
206 run.kernel_ns +|
207 run.target_ns;
208 }