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 }