lib/choir/src/passes/pass/base.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const subject = @import("root.zig");
3
4 const DependentDialects = subject.DependentDialects;
5 const PassContext = subject.PassContext;
6 const PassMutationScope = subject.PassMutationScope;
7 const PassRerunPolicy = subject.PassRerunPolicy;
8 const PassResult = subject.PassResult;
9 const PassStateConcurrency = subject.PassStateConcurrency;
10
11 pub const Pass = struct {
12 work_contract: ?subject.work.Contract = null,
13
14 name: []const u8,
15
16 description: []const u8,
17
18 run_fn: ?*const fn (*PassContext) PassResult = null,
19
20 state: ?*anyopaque = null,
21
22 run_with_state_fn: ?*const fn (?*anyopaque, *PassContext) PassResult = null,
23
24 state_concurrency: PassStateConcurrency = .exclusive,
25
26 rerun_policy: PassRerunPolicy = .always,
27
28 state_clone_fn: ?*const fn (?*anyopaque, std.mem.Allocator) anyerror!?*anyopaque = null,
29
30 state_deinit_fn: ?*const fn (?*anyopaque, std.mem.Allocator) void = null,
31
32 mutation_scope: PassMutationScope = .whole_module,
33
34 dependent_dialects: DependentDialects = &.{},
35
36 textual_options: ?[]const u8 = null,
37
38 owns_textual_options: bool = false,
39
40 pub fn run(self: Pass, ctx: *PassContext) PassResult {
41 if (self.run_with_state_fn) |run_with_state| {
42 return run_with_state(self.state, ctx);
43 }
44 if (self.run_fn) |run_pass| {
45 return run_pass(ctx);
46 }
47 return .failure;
48 }
49
50 pub fn readOnly(self: Pass) bool {
51 return self.mutation_scope == .read_only;
52 }
53
54 pub fn isolatedMutation(self: Pass) bool {
55 return self.mutation_scope == .isolated;
56 }
57
58 pub fn wholeModuleMutation(self: Pass) bool {
59 return self.mutation_scope == .whole_module;
60 }
61
62 pub fn parallelStateSafe(self: Pass) bool {
63 if (self.state == null) return true;
64 if (self.state_concurrency == .shared) return true;
65 return self.state_clone_fn != null;
66 }
67
68 pub fn validRerunContract(self: Pass) bool {
69 return switch (self.rerun_policy) {
70 .always => true,
71 .skip_if_unchanged => self.state == null and
72 self.run_with_state_fn == null and
73 self.run_fn != null,
74 };
75 }
76
77 pub fn sameRerunIdentity(self: Pass, previous: Pass) bool {
78 if (self.rerun_policy != .skip_if_unchanged) return false;
79 if (previous.rerun_policy != .skip_if_unchanged) return false;
80 if (!self.validRerunContract() or !previous.validRerunContract()) return false;
81 if (self.run_fn != previous.run_fn) return false;
82 return std.mem.eql(
83 u8,
84 self.textual_options orelse "",
85 previous.textual_options orelse "",
86 );
87 }
88
89 pub fn cloneForParallelTarget(self: Pass, allocator: std.mem.Allocator) anyerror!Pass {
90 var cloned = self;
91 cloned.owns_textual_options = false;
92 if (self.state) |_| {
93 if (self.state_clone_fn) |clone_fn| {
94 cloned.state = try clone_fn(self.state, allocator);
95 return cloned;
96 }
97 if (self.state_concurrency == .shared) {
98 cloned.state_deinit_fn = null;
99 return cloned;
100 }
101 return error.PassStateNotParallel;
102 }
103 cloned.state_deinit_fn = null;
104 return cloned;
105 }
106
107 pub fn deinit(self: *Pass, allocator: std.mem.Allocator) void {
108 if (self.state_deinit_fn) |deinit_fn| deinit_fn(self.state, allocator);
109 if (self.owns_textual_options) {
110 if (self.textual_options) |text| allocator.free(text);
111 }
112 self.* = undefined;
113 }
114 };