lib/choir/src/backends/target.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const ir = @import("../core/root.zig");
3 const rewrite = ir.rewrite;
4 const passes = @import("../passes/root.zig");
5
6 pub const ConversionRewriteFn = *const fn (*ir.Operation, *rewrite.PatternRewriter) rewrite.PatternResult;
7
8 pub const ConversionPatternEntry = struct {
9 spec: rewrite.RewritePatternSpec,
10 rewrite: ConversionRewriteFn,
11 };
12
13 pub const ConversionLegalitySpec = struct {
14 legal_ops: []const []const u8 = &.{},
15 recursively_legal_ops: []const []const u8 = &.{},
16 illegal_ops: []const []const u8 = &.{},
17 legal_dialects: []const []const u8 = &.{},
18 recursively_legal_dialects: []const []const u8 = &.{},
19 illegal_dialects: []const []const u8 = &.{},
20
21 pub fn apply(self: ConversionLegalitySpec, target: *passes.ConversionTarget) !void {
22 for (self.legal_ops) |op_name| try target.addLegalOp(op_name);
23 for (self.recursively_legal_ops) |op_name| try target.addRecursivelyLegalOp(op_name);
24 for (self.illegal_ops) |op_name| try target.addIllegalOp(op_name);
25 for (self.legal_dialects) |dialect_name| try target.addLegalDialect(dialect_name);
26 for (self.recursively_legal_dialects) |dialect_name| try target.addRecursivelyLegalDialect(dialect_name);
27 for (self.illegal_dialects) |dialect_name| try target.addIllegalDialect(dialect_name);
28 }
29
30 pub fn isLegalOpName(self: ConversionLegalitySpec, op_name: []const u8) bool {
31 return containsName(self.legal_ops, op_name);
32 }
33
34 pub fn isRecursivelyLegalOpName(self: ConversionLegalitySpec, op_name: []const u8) bool {
35 return containsName(self.recursively_legal_ops, op_name);
36 }
37
38 pub fn isIllegalOpName(self: ConversionLegalitySpec, op_name: []const u8) bool {
39 return containsName(self.illegal_ops, op_name);
40 }
41
42 pub fn isLegalDialectName(self: ConversionLegalitySpec, dialect_name: []const u8) bool {
43 return containsName(self.legal_dialects, dialect_name);
44 }
45
46 pub fn isRecursivelyLegalDialectName(self: ConversionLegalitySpec, dialect_name: []const u8) bool {
47 return containsName(self.recursively_legal_dialects, dialect_name);
48 }
49
50 pub fn isIllegalDialectName(self: ConversionLegalitySpec, dialect_name: []const u8) bool {
51 return containsName(self.illegal_dialects, dialect_name);
52 }
53 };
54
55 pub const TargetSpec = struct {
56 name: []const u8,
57 description: []const u8 = "",
58 target_dialect_name: []const u8,
59 legality: ConversionLegalitySpec = .{},
60 conversion_patterns: []const rewrite.RewritePatternSpec = &.{},
61 pass_name: []const u8 = "",
62 pass_description: []const u8 = "",
63 pipeline_name: []const u8 = "",
64 pipeline_description: []const u8 = "",
65
66 pub fn applyLegality(self: TargetSpec, target: *passes.ConversionTarget) !void {
67 try self.legality.apply(target);
68 }
69
70 pub fn conversionPatternRootCount(self: TargetSpec) usize {
71 return self.conversion_patterns.len;
72 }
73
74 pub fn hasConversionPatternRoot(self: TargetSpec, root_op_name: []const u8) bool {
75 for (self.conversion_patterns) |pattern| {
76 if (std.mem.eql(u8, pattern.root_op_name, root_op_name)) return true;
77 }
78 return false;
79 }
80
81 pub fn marksIllegalOp(self: TargetSpec, op_name: []const u8) bool {
82 return self.legality.isIllegalOpName(op_name);
83 }
84
85 pub fn legalizesDialect(self: TargetSpec, dialect_name: []const u8) bool {
86 return self.legality.isLegalDialectName(dialect_name);
87 }
88 };
89
90 fn containsName(names: []const []const u8, needle: []const u8) bool {
91 for (names) |name| {
92 if (std.mem.eql(u8, name, needle)) return true;
93 }
94 return false;
95 }
96
97 test "conversion legality spec applies target facts" {
98 const testing = std.testing;
99 const legal_dialects = [_][]const u8{ "builtin", "func" };
100 const illegal_ops = [_][]const u8{ "gpu.thread_id", "gpu.block_id" };
101 const spec = ConversionLegalitySpec{
102 .legal_dialects = legal_dialects[0..],
103 .illegal_ops = illegal_ops[0..],
104 };
105
106 var target = passes.ConversionTarget.init(testing.allocator);
107 defer target.deinit();
108 try spec.apply(&target);
109
110 try testing.expect(target.legal_dialects.contains("builtin"));
111 try testing.expect(target.legal_dialects.contains("func"));
112 try testing.expect(target.illegal_ops.contains("gpu.thread_id"));
113 try testing.expect(spec.isLegalDialectName("builtin"));
114 try testing.expect(spec.isIllegalOpName("gpu.block_id"));
115 try testing.expect(!spec.isIllegalOpName("gpu.grid_id"));
116 }
117
118 test "target spec exposes conversion pattern roots" {
119 const testing = std.testing;
120 const patterns = [_]rewrite.RewritePatternSpec{
121 .{ .name = "gpu.thread_id", .root_op_name = "gpu.thread_id" },
122 .{ .name = "gpu.block_id", .root_op_name = "gpu.block_id" },
123 };
124 const spec = TargetSpec{
125 .name = "test-target",
126 .target_dialect_name = "test",
127 .conversion_patterns = patterns[0..],
128 };
129
130 try testing.expectEqual(@as(usize, 2), spec.conversionPatternRootCount());
131 try testing.expect(spec.hasConversionPatternRoot("gpu.thread_id"));
132 try testing.expect(!spec.hasConversionPatternRoot("gpu.grid_id"));
133 }