lib/choir/src/dialects/builtin.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const alloc_arena = @import("alloc_arena");
3 const ir = @import("../core/root.zig");
4
5 pub const BuiltinDialect = struct {
6 pub const name = "builtin";
7 const op_templates = ir.dialects.operationTemplate.dialect(@This());
8 pub const spec = ir.dialects.dialectSpec(@This(), .{});
9
10 pub const ModuleOp = struct {
11 op: *ir.Operation,
12
13 const def = op_templates.explicit(@This(), .{
14 .mnemonic = "module",
15 .operands = 0,
16 .results = 0,
17 .regions = .{"body"},
18 .successors = 0,
19 .dynamic_traits = .{ ir.traits.SymbolTable, ir.traits.IsolatedFromAbove, ir.traits.NoTerminator, ir.traits.SingleBlock },
20 });
21 pub const operation_spec = def.operation_spec;
22 pub const operation_name = def.operation_name;
23 pub const createOperation = def.createOperation;
24 pub const getRegion = def.getRegion;
25
26 pub fn create(ctx: *ir.Context, loc: ir.Location) !ModuleOp {
27 try loadSpec(ctx);
28 var body = ir.context.initRegion(ctx);
29 defer body.deinit();
30 var body_builder = ir.OperationBuilder.init(ctx);
31 _ = try body_builder.createBlock(&body, &.{}, &.{});
32 return try @This().createOperation(ctx, loc, &.{}, &.{}, &.{&body}, &.{});
33 }
34
35 pub fn getBody(self: ModuleOp) *ir.Region {
36 return self.getRegion("body");
37 }
38
39 pub fn getBodyBlock(self: ModuleOp) *ir.Block {
40 return self.getBody().getEntryBlock().?;
41 }
42 };
43
44 fn loadSpec(ctx: *ir.Context) !void {
45 ir.dialects.loadDialectSpec(ctx, spec) catch |err| switch (err) {
46 error.ContextFrozen => {},
47 else => return err,
48 };
49 }
50 };
51
52 test "BuiltinDialect.ModuleOp creates container with region" {
53 const testing = std.testing;
54 var arena = alloc_arena.Arena.init(std.testing.allocator);
55 defer arena.deinit();
56 const allocator = arena.allocator();
57
58 var ctx = try ir.Context.init(allocator, ir.Context.Limits.testing);
59 defer ctx.deinit(allocator);
60
61 const loc = ir.Location.getUnknown();
62 const module = try BuiltinDialect.ModuleOp.create(&ctx, loc);
63
64 try testing.expectEqualStrings("builtin.module", module.op.name.name);
65 const body = module.getBody();
66 try testing.expect(body.getEntryBlock() != null);
67 try testing.expect(module.op.hasTraitId(ir.traits.SymbolTable.id));
68 try testing.expect(module.op.hasTraitId(ir.traits.IsolatedFromAbove.id));
69 try testing.expect(module.op.hasTraitId(ir.traits.NoTerminator.id));
70 try testing.expect(module.op.hasTraitId(ir.traits.SingleBlock.id));
71
72 const info = ctx.lookupOperation(BuiltinDialect.ModuleOp.operation_name).?;
73 try testing.expect(info.shape.regions.allows(1));
74 try testing.expect(!info.shape.regions.allows(0));
75 try testing.expect(info.shape.successors.allows(0));
76 try testing.expect(!info.shape.successors.allows(1));
77 }
78
79 test "Operations attach to module block" {
80 const testing = std.testing;
81 var arena = alloc_arena.Arena.init(std.testing.allocator);
82 defer arena.deinit();
83 const allocator = arena.allocator();
84
85 var ctx = try ir.Context.init(allocator, ir.Context.Limits.testing);
86 defer ctx.deinit(allocator);
87
88 const loc = ir.Location.getUnknown();
89 const module = try BuiltinDialect.ModuleOp.create(&ctx, loc);
90 const block = module.getBodyBlock();
91
92 var builder = ir.OperationBuilder.init(&ctx);
93 builder.setInsertionPoint(block);
94
95 const arith = @import("arith/root.zig").ArithDialect;
96 const i64_type = try arith.getScalarType(&ctx, .i64);
97 var state = ir.Operation.State.init("arith.constant", loc);
98 state.addTypes(&.{i64_type});
99 const op = try builder.create(state);
100
101 try testing.expect(op.parent_block == block);
102 try testing.expect(!block.operations.isEmpty());
103 try testing.expect(block.operations.head == @as(?*anyopaque, op));
104 }